main.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. print("[启动] 后台预热点选解读 Agent...(不阻塞服务就绪)")
  34. get_click_agent()
  35. print("[启动] 后台预热对话解读 Agent...(不阻塞服务就绪)")
  36. get_dialog_agent()
  37. # ── 预热配风计划审查 Agent(消除首次请求的图编译开销)──
  38. from agents.review_agent import (
  39. get_form_review_agent,
  40. get_data_consistency_agent,
  41. get_calc_verification_agent,
  42. get_summary_agent,
  43. )
  44. print("[启动] 后台预热形式审查 Agent...")
  45. get_form_review_agent()
  46. print("[启动] 后台预热数据一致性审查 Agent...")
  47. get_data_consistency_agent()
  48. print("[启动] 后台预热计算核验审查 Agent...")
  49. get_calc_verification_agent()
  50. print("[启动] 后台预热汇总审查 Agent...")
  51. get_summary_agent()
  52. print("[启动] Agent 预热完成,服务已完全就绪")
  53. except Exception as e:
  54. print(f"[警告] Agent 预初始化失败(将在首次请求时懒加载): {e}")
  55. finally:
  56. agent_ready.set()
  57. threading.Thread(target=_warmup_agents, daemon=True, name="agent-warmup").start()
  58. print("[启动] 服务已就绪(Agent 后台预热中),监听 http://0.0.0.0:8000")
  59. print("[启动] API 文档: http://localhost:8000/docs")
  60. print("=" * 60)
  61. yield
  62. # 关闭时清理
  63. print("[关闭] 服务停止")
  64. # ============================================================
  65. # 创建 FastAPI 应用
  66. # ============================================================
  67. app = FastAPI(
  68. title="煤矿通风数据智能解读系统",
  69. description="""
  70. 基于 DeepAgents 框架的煤矿通风监测数据智能解读服务。
  71. ## 功能
  72. - **点选数据解读**: 对单条巷道监测数据进行深度合规分析
  73. - **对话式数据解读**: 通过自然语言对话查询和分析通风数据
  74. - **聊天历史**: 查询和管理对话历史记录
  75. ## 技术栈
  76. - DeepAgents (LangChain) - Agent 编排
  77. - FastAPI - Web 框架
  78. - SQLite - 聊天历史存储
  79. - SSE - 流式响应
  80. """,
  81. version="1.0.0",
  82. lifespan=lifespan,
  83. )
  84. # CORS 配置(允许前端跨域访问)
  85. app.add_middleware(
  86. CORSMiddleware,
  87. allow_origins=["*"],
  88. allow_credentials=True,
  89. allow_methods=["*"],
  90. allow_headers=["*"],
  91. )
  92. # 注册路由
  93. app.include_router(router)
  94. # 静态文件服务(审查报告 .docx 下载)
  95. _static_dir = Path(__file__).parent / "static"
  96. _static_dir.mkdir(parents=True, exist_ok=True)
  97. (_static_dir / "reports").mkdir(exist_ok=True)
  98. app.mount("/static", StaticFiles(directory=str(_static_dir)), name="static")
  99. # ============================================================
  100. # 根路由 → Chat UI
  101. # ============================================================
  102. @app.get("/")
  103. async def root():
  104. """根路径重定向到聊天界面"""
  105. return RedirectResponse(url="/static/index.html")
  106. # ============================================================
  107. # 健康检查
  108. # ============================================================
  109. @app.get("/health")
  110. async def health_check():
  111. """健康检查接口"""
  112. return {
  113. "status": "ok",
  114. "service": "煤矿通风数据智能解读系统",
  115. "version": "1.0.0",
  116. }
  117. # ============================================================
  118. # 直接运行入口
  119. # ============================================================
  120. if __name__ == "__main__":
  121. import uvicorn
  122. uvicorn.run(
  123. "main:app",
  124. host="0.0.0.0",
  125. port=8070,
  126. reload=True,
  127. log_level="info",
  128. )