本文档从“前端发起一次 AI 问答”开始,梳理本项目 AI 问答模块涉及的 入站接口(前端 → shudao-chat-py) 与 出站接口(shudao-chat-py → 外部服务),并按不同交互场景给出完整调用链路与时序图。
shudao-vue-frontendshudao-chat-py(FastAPI)shudao-aichat(本项目通过代理/兼容路由调用)/v1/chat/completions)下表只列 AI 问答相关;其他业务接口(考试工坊/PPT/隐患识别等)不在本文范围内。
| 场景 | 方法&路径(前缀 /apiv1) |
作用 | 前端调用点(示例) | 后端实现 |
|---|---|---|---|---|
| 主流式问答(写 DB) | POST /stream/chat-with-db |
SSE 流式输出 + 落库 | Chat.vue | stream_chat_with_db |
| 非流式问答 | POST /send_deepseek_message |
一次性返回完整回答 | Chat.vue、apis.js | send_deepseek_message |
| 历史(列表/详情) | GET /get_history_record |
ai_conversation_id=0 返回会话列表;>0 返回消息列表 |
apis.js | get_history_record |
| 删除对话/消息 | POST /delete_conversation |
软删除会话或某条消息对 | apis.js | delete_conversation |
| 删除历史(会话级) | POST /delete_history_record |
软删除会话主记录 | apis.js | delete_history_record |
| 猜你想问 | POST /guess_you_want |
基于某条 AI 消息生成 3 个关联问题并落库 | apis.js | guess_you_want |
| 在线搜索 | GET /online_search?question=... |
Qwen 提炼关键词 → Dify 工作流 → 返回摘要 | apis.js | online_search |
| 保存在线搜索结果 | POST /save_online_search_result |
保存到 AIMessage.search_source |
apis.js | save_online_search_result |
| 意图识别(独立) | POST /intent_recognition |
只做意图识别(可选写 DB) | apis.js | intent_recognition |
| 点赞/点踩 | POST /like_and_dislike |
保存 AIMessage.user_feedback 并处理积分 |
apis.js | like_and_dislike |
| 报告兼容 SSE(Go 对齐) | POST /report/complete-flow |
SSE:可能代理到 aichat;失败则降级本地 | Chat.vue | complete_flow |
| 停止报告 SSE | POST /sse/stop |
停止 SSE(外部 token 代理到 aichat) | stopSSEStream | stop_sse |
| 回写 AI 消息 | POST /report/update-ai-message |
前端将整理后的内容回写到 DB(外部 token 代理到 aichat) | updateAIMessageContent | update_ai_message |
| 附件解析 | POST /attachments/parse |
文件上传解析(代理到 aichat) | parseAttachment | parse_attachment |
| 目标服务 | 调用点 | 典型接口 | 用途 |
|---|---|---|---|
| 4A Auth | verify_external_token | POST settings.auth.api_url |
校验外部 token,解析 account 信息 |
| 搜索服务(RAG) | _rag_search | POST settings.search.api_url |
向量检索/知识库检索,拼接上下文 |
| Qwen3(主模型) | QwenService.chat、QwenService.stream_chat | POST {settings.qwen3.api_url}/v1/chat/completions |
主问答生成与流式输出 |
| 意图识别模型 | intent_recognition | POST {settings.intent.api_url}/v1/chat/completions |
识别 greeting/faq/知识库查询 等 |
| DeepSeek(备用模型) | DeepSeekService.chat、stream_chat | POST {settings.deepseek.api_url}/v1/chat/completions |
Qwen3 失败时回退 |
| Dify 工作流(在线搜索) | online_search | POST settings.dify.workflow_url |
关键词 → 工作流 → 摘要 |
| aichat 服务(代理) | AIChatProxy | POST {settings.aichat.api_url}/* |
兼容 Go 版报告 SSE、附件解析、停止、回写等 |
后端在 combined_middleware 内做:
Authorization / token / Token 读取 tokenrequest.state.userrequest.state.user 获取当前用户外部 token:
POST settings.auth.api_url 验证 token 合法性accountID / name / role / expUserData.id(用于复用 AIConversation/AIMessage)POST /stream/chat-with-db)这是“边生成边显示 + 保留历史”的标准链路。
请求体(核心字段):
{
"message": "用户问题",
"ai_conversation_id": 0,
"business_type": 0,
"online_search_content": ""
}
响应:text/event-stream,每条事件形态为:
data: {"type":"initial","ai_conversation_id":123,"ai_message_id":456}\n\ndata: <文本chunk>\n\n(chunk 内换行会被转义为 \\n)data: [DONE]\n\n实现见 stream_chat_with_db。
POST /apiv1/stream/chat-with-dbrequest.state.userstream_chat_with_db:
AIConversationAIMessage(type=user)AIMessage(type=ai, content='') 占位initial(返回会话/消息 ID)POST settings.search.api_url,请求体 {"query": message, "n_results": top_k}rag_contextfinal_answer(含 userMessage、rag_context、historyContext、可选 online_search_content)POST {settings.qwen3.api_url}/v1/chat/completions(stream=true)<think>...</think> 且摘要开关启用,会调用 summarize_thinking_contentqwen_service.chat()(仍会触发 DeepSeek 回退逻辑)AIMessage(type=ai).content = full_responseAIConversation.updated_at / business_type / content 预览[DONE]sequenceDiagram
participant FE as 前端
participant MW as 中间件
participant AU as 4A Auth
participant RT as chat.py:/stream/chat-with-db
participant DB as MySQL
participant SS as 搜索服务
participant QW as Qwen3
participant DS as DeepSeek(回退)
FE->>MW: POST /apiv1/stream/chat-with-db
MW->>AU: (可选) POST settings.auth.api_url
AU-->>MW: valid + account
MW-->>RT: request.state.user
RT->>DB: upsert AIConversation
RT->>DB: insert user AIMessage
RT->>DB: insert ai placeholder AIMessage
RT-->>FE: SSE data: {"type":"initial",...}
RT->>SS: POST settings.search.api_url
SS-->>RT: docs
RT->>QW: POST /v1/chat/completions (stream=true)
alt Qwen3 失败
RT->>DS: POST /v1/chat/completions (stream=true)
DS-->>RT: chunk...
else Qwen3 正常
QW-->>RT: chunk...
end
RT-->>FE: SSE chunk...
RT->>DB: update ai_message.content
RT->>DB: update conversation snapshot
RT-->>FE: SSE [DONE]
POST /send_deepseek_message,business_type=0)这条链路更像“同步 RPC”:一次请求直接拿到完整回答。
实现入口见 send_deepseek_message。
POST /apiv1/send_deepseek_messagerequest.state.userAIConversationPOST {settings.intent.api_url}/v1/chat/completionsPOST settings.search.api_urlfinal_answer PromptPOST {settings.qwen3.api_url}/v1/chat/completions(stream=false)qwen_service.chat()){statusCode: 200, data: { reply/content/... } }sequenceDiagram
participant FE as 前端
participant MW as 中间件
participant RT as chat.py:/send_deepseek_message
participant IM as Intent模型
participant SS as 搜索服务
participant QW as Qwen3
participant DS as DeepSeek(回退)
participant DB as MySQL
FE->>MW: POST /apiv1/send_deepseek_message
MW-->>RT: request.state.user
RT->>DB: upsert AIConversation
RT->>IM: POST intent /v1/chat/completions
IM-->>RT: intent_type
opt 命中知识库查询
RT->>SS: POST settings.search.api_url
SS-->>RT: docs
end
RT->>QW: POST qwen3 /v1/chat/completions (stream=false)
alt Qwen3 失败
RT->>DS: POST deepseek /v1/chat/completions
DS-->>RT: answer
else 正常
QW-->>RT: answer
end
RT-->>FE: JSON response(一次性)
POST /report/complete-flow)这条链路是为了对齐 Go 版本接口格式,前端在“报告生成模式”会调用它(见 Chat.vue)。
后端入口: complete_flow
兼容路由会根据 token 类型决定“走 aichat 代理”还是“本地降级”:
POST /stream/chat-with-db 再转换为兼容事件流链路:
POST /apiv1/report/complete-flow(SSE)report_compat.py 构造转发请求体,规范化 ai_conversation_id(新会话必须是 0)其中 aichat 的 base URL 来自:
settings.aichat.api_url(见 AIChatConfig)链路:
POST /apiv1/report/complete-flow(SSE)fallback_to_local_streamPOST http://127.0.0.1:{settings.app.port}/apiv1/stream/chat-with-dbinitial 获取 ai_conversation_id / ai_message_idfull_response[DONE] 时,输出兼容事件:
data: {"type":"online_answer","content": full_response, ...}\n\ndata: {"type":"completed"}\n\nsequenceDiagram
participant FE as 前端
participant RC as report_compat:/report/complete-flow
participant AP as aichat_proxy
participant AC as aichat 服务
participant LC as 本地 chat-with-db
FE->>RC: POST /apiv1/report/complete-flow (SSE)
alt 外部 token
RC->>AP: proxy_sse("/report/complete-flow")
AP->>AC: POST {settings.aichat.api_url}/report/complete-flow (SSE)
AC-->>AP: SSE chunk...
AP-->>FE: SSE chunk...(原样转发)
else 本地 token 或代理失败
RC->>LC: POST http://127.0.0.1:{port}/apiv1/stream/chat-with-db (SSE)
LC-->>RC: SSE initial + chunk... + [DONE]
RC-->>FE: data: {"type":"online_answer",...}
RC-->>FE: data: {"type":"completed"}
end
GET /online_search + POST /save_online_search_result)在线搜索用于补充回答来源或做联网摘要。
GET /online_search实现入口: online_search
调用链路:
GET /apiv1/online_search?question=...POST settings.dify.workflow_urlAuthorization: Bearer settings.dify.auth_token{keywords, result} 给前端POST /save_online_search_result实现入口: save_online_search_result
调用链路:
{ai_message_id, search_result}AIMessage.search_sourcePOST /guess_you_want)实现入口: guess_you_want
调用链路:
{ai_message_id}guess_questions prompt 并调用 qwen_service.chat()questions[],写入 AIMessage.guess_you_want{ai_message_id, questions}POST /attachments/parse)实现入口: parse_attachment
调用链路:
request.state.user 不为空POST {settings.aichat.api_url}/attachments/parsePOST /sse/stop前端调用: stopSSEStream
后端实现: stop_sse
路由策略:
/sse/stopPOST /report/update-ai-message前端调用: updateAIMessageContent
后端实现: update_ai_message
路由策略:
/report/update-ai-messageAIMessage.id = ai_message_id 的 content统一配置类定义在:
通常运行时值来自 shudao-chat-py/config.yaml(以及环境变量覆盖),关键项包括:
auth.api_urlsearch.api_urlqwen3.api_url / qwen3.modelintent.api_url / intent.modeldeepseek.api_urldify.workflow_url / dify.workflow_id / dify.auth_tokenaichat.api_url