| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- # -*- coding: utf-8 -*-
- """
- 对话式数据解读接口 —— 统一入口,SSE 流式
- 根据用户意图自动路由到不同智能体:
- - 需风量计算/管控平台查询 → needq_calc_agent
- - 配风计划审查(上传PDF) → 审查管线
- - 数据解读/其他 → dialog_agent(默认)
- """
- from fastapi import APIRouter, File, Form, UploadFile
- from fastapi.responses import StreamingResponse
- from api.sse_core import sse_event_generator
- from api.intent import classify_intent
- from agents.vent_agent import get_dialog_agent
- from agents.needq_agent import get_needq_calc_agent
- from agents.review_agent import stream_review
- from db.chat_store import get_messages, create_session, update_session_title
- router = APIRouter()
- @router.post("/chat")
- async def chat(
- message: str = Form(...),
- session_id: str | None = Form(default=None),
- thread_id: str | None = Form(default=None),
- file: UploadFile | None = File(default=None),
- ):
- """统一对话接口(SSE 流式)—— 支持文本对话和 PDF 配风计划审查。
- 根据用户意图自动路由到不同智能体:
- - 需风量计算/管控平台查询 → needq_calc_agent
- - 配风计划审查(上传PDF) → 审查管线(form-reviewer + data-checker + calc-verifier)
- - 数据解读/其他 → dialog_agent(默认)
- 请求体: multipart/form-data
- - message: 用户消息(必填)
- - session_id: 可选,会话ID
- - thread_id: 可选,LangGraph 线程ID
- - file: 可选,附件(PDF/文档等)
- 响应: SSE 流(text/event-stream)
- """
- # 会话管理
- session_id = session_id or create_session()
- thread_id = thread_id or session_id
- config = {"configurable": {"thread_id": thread_id}}
- # 提取附件文件名(如有)
- filename = file.filename if file else None
- # 自动设置会话标题(取前30字)
- messages = get_messages(session_id)
- if len(messages) <= 1:
- title = message[:30] + ("..." if len(message) > 30 else "")
- update_session_title(session_id, title)
- # ── Agent 推理意图(含附件文件名辅助判断)──
- intent = await classify_intent(message, filename)
- # ── 按意图路由 ──
- if intent == "review" and file is not None:
- # 配风计划审查 + 有文件 → 审查管线
- print(f"[路由] 意图: 配风计划审查 → stream_review (file={filename})")
- return StreamingResponse(
- stream_review(file, message, session_id),
- media_type="text/event-stream",
- headers={
- "Cache-Control": "no-cache",
- "Connection": "keep-alive",
- "X-Accel-Buffering": "no",
- "X-Session-Id": session_id,
- },
- )
- elif intent == "review" and file is None:
- # 审查意图但无文件 → dialog 提示用户上传
- print(f"[路由] 意图: 配风计划审查 → 提示用户上传 PDF")
- agent = get_dialog_agent()
- agent_cn_name = "通风对话助手"
- message = f"用户想进行配风计划审查,但未上传附件。请提示用户上传配风计划PDF文件。用户原始消息:{message}"
- elif intent == "needq_calc":
- agent = get_needq_calc_agent()
- agent_cn_name = "需风量计算专家"
- print(f"[路由] 意图: 需风量计算 → needq_calc_agent")
- # 如有附件但非审查意图,将文件名信息追加到消息中
- if filename:
- message = f"用户上传了文件「{filename}」。{message}"
- else:
- agent = get_dialog_agent()
- agent_cn_name = "通风对话助手"
- print(f"[路由] 意图: 数据解读 → dialog_agent")
- # 如有附件但非审查意图,将文件名信息追加到消息中
- if filename:
- message = f"用户上传了文件「{filename}」。{message}"
- return StreamingResponse(
- sse_event_generator(agent, message, thread_id, session_id, config, agent_cn_name),
- media_type="text/event-stream",
- headers={
- "Cache-Control": "no-cache",
- "Connection": "keep-alive",
- "X-Accel-Buffering": "no",
- "X-Session-Id": session_id,
- },
- )
|