main.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import sys
  2. from pathlib import Path
  3. from dotenv import load_dotenv
  4. load_dotenv()
  5. from fastapi import FastAPI
  6. from fastapi.middleware.cors import CORSMiddleware
  7. from fastapi.staticfiles import StaticFiles
  8. from fastapi.responses import RedirectResponse
  9. from contextlib import asynccontextmanager
  10. from api import router
  11. # ============================================================
  12. # 应用生命周期
  13. # ============================================================
  14. @asynccontextmanager
  15. async def lifespan(app: FastAPI):
  16. """应用启动/关闭时的生命周期管理"""
  17. import threading
  18. print("=" * 60)
  19. print(" 煤矿通风数据智能解读系统 启动中...")
  20. print("=" * 60)
  21. # === 关键优化:在后台线程预热 Agent,不阻塞 uvicorn 启动 ===
  22. # 原因:create_deep_agent 需要导入 langchain/langgraph 并编译状态图,
  23. # 首次执行耗时可达数分钟,阻塞 uvicorn 等待 lifespan 完成后才接受请求。
  24. # 改为后台预热后,服务可以在几秒内就绪,Agent 在后台慢慢初始化。
  25. #
  26. # 首次请求如果 Agent 还没预热完,routes.py 中的 get_xxx_agent() 会触发懒加载,
  27. # 第一个请求会稍慢(等待编译完成),后续请求就是热实例直接用了。
  28. agent_ready = threading.Event()
  29. def _warmup_agents():
  30. """后台线程:预热 Agent 实例"""
  31. try:
  32. from agents.vent_agent import get_click_agent, get_dialog_agent
  33. from agents.needq_agent import get_needq_calc_agent
  34. print("[启动] 后台预热点选解读 Agent...(不阻塞服务就绪)")
  35. get_click_agent()
  36. print("[启动] 后台预热对话解读 Agent...(不阻塞服务就绪)")
  37. get_dialog_agent()
  38. print("[启动] 后台预热需风量计算 Agent...(不阻塞服务就绪)")
  39. get_needq_calc_agent()
  40. # ── 预热配风计划审查 Agent(消除首次请求的图编译开销)──
  41. from agents.review_agent import (
  42. get_form_review_agent,
  43. get_data_consistency_agent,
  44. get_calc_verification_agent,
  45. get_summary_agent,
  46. )
  47. print("[启动] 后台预热形式审查 Agent...")
  48. get_form_review_agent()
  49. print("[启动] 后台预热数据一致性审查 Agent...")
  50. get_data_consistency_agent()
  51. print("[启动] 后台预热计算核验审查 Agent...")
  52. get_calc_verification_agent()
  53. print("[启动] 后台预热汇总审查 Agent...")
  54. get_summary_agent()
  55. print("[启动] Agent 预热完成,服务已完全就绪")
  56. except Exception as e:
  57. print(f"[警告] Agent 预初始化失败(将在首次请求时懒加载): {e}")
  58. finally:
  59. agent_ready.set()
  60. threading.Thread(target=_warmup_agents, daemon=True, name="agent-warmup").start()
  61. print("[启动] 服务已就绪(Agent 后台预热中),监听 http://0.0.0.0:8000")
  62. print("[启动] API 文档: http://localhost:8000/docs")
  63. print("=" * 60)
  64. yield
  65. # 关闭时清理
  66. print("[关闭] 服务停止")
  67. # ============================================================
  68. # 创建 FastAPI 应用
  69. # ============================================================
  70. app = FastAPI(
  71. title="煤矿通风数据智能解读系统",
  72. description="""
  73. 基于 DeepAgents 框架的煤矿通风监测数据智能解读服务。
  74. ## 功能
  75. - **点选数据解读**: 对单条巷道监测数据进行深度合规分析
  76. - **对话式数据解读**: 通过自然语言对话查询和分析通风数据
  77. - **聊天历史**: 查询和管理对话历史记录
  78. ## 技术栈
  79. - DeepAgents (LangChain) - Agent 编排
  80. - FastAPI - Web 框架
  81. - SQLite - 聊天历史存储
  82. - SSE - 流式响应
  83. """,
  84. version="1.0.0",
  85. lifespan=lifespan,
  86. )
  87. # CORS 配置(允许前端跨域访问)
  88. app.add_middleware(
  89. CORSMiddleware,
  90. allow_origins=["*"],
  91. allow_credentials=True,
  92. allow_methods=["*"],
  93. allow_headers=["*"],
  94. )
  95. # 注册路由
  96. app.include_router(router)
  97. # 静态文件服务(审查报告 .docx 下载)
  98. _static_dir = Path(__file__).parent / "static"
  99. _static_dir.mkdir(parents=True, exist_ok=True)
  100. (_static_dir / "reports").mkdir(exist_ok=True)
  101. app.mount("/static", StaticFiles(directory=str(_static_dir)), name="static")
  102. # ============================================================
  103. # 根路由 → Chat UI
  104. # ============================================================
  105. @app.get("/")
  106. async def root():
  107. """根路径重定向到聊天界面"""
  108. return RedirectResponse(url="/static/index.html")
  109. # ============================================================
  110. # 健康检查
  111. # ============================================================
  112. @app.get("/health")
  113. async def health_check():
  114. """健康检查接口"""
  115. return {
  116. "status": "ok",
  117. "service": "煤矿通风数据智能解读系统",
  118. "version": "1.0.0",
  119. }
  120. # ============================================================
  121. # 直接运行入口
  122. # ============================================================
  123. if __name__ == "__main__":
  124. import uvicorn
  125. uvicorn.run(
  126. "main:app",
  127. host="0.0.0.0",
  128. port=8070,
  129. reload=True,
  130. log_level="info",
  131. )