chat_routes.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. # -*- coding: utf-8 -*-
  2. """
  3. 对话式数据解读接口 —— 统一入口,SSE 流式
  4. 根据用户意图自动路由到不同智能体:
  5. - 需风量计算/管控平台查询 → needq_calc_agent
  6. - 配风计划审查(上传PDF) → 审查管线
  7. - 数据解读/其他 → dialog_agent(默认)
  8. """
  9. from fastapi import APIRouter, File, Form, UploadFile
  10. from fastapi.responses import StreamingResponse
  11. from api.sse_core import sse_event_generator
  12. from api.intent import classify_intent
  13. from agents.vent_agent import get_dialog_agent
  14. from agents.needq_agent import get_needq_calc_agent
  15. from agents.review_agent import stream_review
  16. from db.chat_store import get_messages, create_session, update_session_title
  17. router = APIRouter()
  18. @router.post("/chat")
  19. async def chat(
  20. message: str = Form(...),
  21. session_id: str | None = Form(default=None),
  22. thread_id: str | None = Form(default=None),
  23. file: UploadFile | None = File(default=None),
  24. ):
  25. """统一对话接口(SSE 流式)—— 支持文本对话和 PDF 配风计划审查。
  26. 根据用户意图自动路由到不同智能体:
  27. - 需风量计算/管控平台查询 → needq_calc_agent
  28. - 配风计划审查(上传PDF) → 审查管线(form-reviewer + data-checker + calc-verifier)
  29. - 数据解读/其他 → dialog_agent(默认)
  30. 请求体: multipart/form-data
  31. - message: 用户消息(必填)
  32. - session_id: 可选,会话ID
  33. - thread_id: 可选,LangGraph 线程ID
  34. - file: 可选,附件(PDF/文档等)
  35. 响应: SSE 流(text/event-stream)
  36. """
  37. # 会话管理
  38. session_id = session_id or create_session()
  39. thread_id = thread_id or session_id
  40. config = {"configurable": {"thread_id": thread_id}}
  41. # 提取附件文件名(如有)
  42. filename = file.filename if file else None
  43. # 自动设置会话标题(取前30字)
  44. messages = get_messages(session_id)
  45. if len(messages) <= 1:
  46. title = message[:30] + ("..." if len(message) > 30 else "")
  47. update_session_title(session_id, title)
  48. # ── Agent 推理意图(含附件文件名辅助判断)──
  49. intent = await classify_intent(message, filename)
  50. # ── 按意图路由 ──
  51. if intent == "review" and file is not None:
  52. # 配风计划审查 + 有文件 → 审查管线
  53. print(f"[路由] 意图: 配风计划审查 → stream_review (file={filename})")
  54. return StreamingResponse(
  55. stream_review(file, message, session_id),
  56. media_type="text/event-stream",
  57. headers={
  58. "Cache-Control": "no-cache",
  59. "Connection": "keep-alive",
  60. "X-Accel-Buffering": "no",
  61. "X-Session-Id": session_id,
  62. },
  63. )
  64. elif intent == "review" and file is None:
  65. # 审查意图但无文件 → dialog 提示用户上传
  66. print(f"[路由] 意图: 配风计划审查 → 提示用户上传 PDF")
  67. agent = get_dialog_agent()
  68. agent_cn_name = "通风对话助手"
  69. message = f"用户想进行配风计划审查,但未上传附件。请提示用户上传配风计划PDF文件。用户原始消息:{message}"
  70. elif intent == "needq_calc":
  71. agent = get_needq_calc_agent()
  72. agent_cn_name = "需风量计算专家"
  73. print(f"[路由] 意图: 需风量计算 → needq_calc_agent")
  74. # 如有附件但非审查意图,将文件名信息追加到消息中
  75. if filename:
  76. message = f"用户上传了文件「{filename}」。{message}"
  77. else:
  78. agent = get_dialog_agent()
  79. agent_cn_name = "通风对话助手"
  80. print(f"[路由] 意图: 数据解读 → dialog_agent")
  81. # 如有附件但非审查意图,将文件名信息追加到消息中
  82. if filename:
  83. message = f"用户上传了文件「{filename}」。{message}"
  84. return StreamingResponse(
  85. sse_event_generator(agent, message, thread_id, session_id, config, agent_cn_name),
  86. media_type="text/event-stream",
  87. headers={
  88. "Cache-Control": "no-cache",
  89. "Connection": "keep-alive",
  90. "X-Accel-Buffering": "no",
  91. "X-Session-Id": session_id,
  92. },
  93. )