shudao-aichat + shudao-chat-py)本文档专门梳理当前项目中两套“意图识别”实现:
shudao-aichatshudao-main/shudao-chat-py重点说明:
当前项目存在两套明显不同的意图识别体系:
shudao-aichatshudao-chat-pygreeting(问候) / faq(常见问题) / query_knowledge_base(知识库查询)一句话概括:
shudao-aichat 的意图识别是“流程编排中枢”shudao-chat-py 的意图识别是“问答前分类开关”shudao-aichatintent.routerPOST /api/v1/intent/analyzeanalyze_intent()这说明 aichat 的意图识别既可以单独调用,也可以作为完整流程中的一个内部节点复用。
shudao-chat-pyapi_routerchat 路由在 routers/init.py 中挂到 /apiv1POST /apiv1/intent_recognitionsend_deepseek_message 在 chat.py 调用意图识别stream_chat 在 chat.py 调用意图识别stream_chat-with-db 没有单独先做意图识别,而是直接做 RAG 检索,见 chat.py这说明 chat-py 的意图识别复用并不统一。
shudao-aichat 的意图识别流程字段:
user_questionconversation_historyenable_online_model字段包括:
is_professional_questionrouteneed_offline_modelorigin_questionkeywordsintent_descriptionsummaryoffline_instructionintent_scenecompany_namefallback_keywordscompany_aliasesthinking_content这已经决定了它不是简单分类器,而是完整的结构化判定器。
执行内容:
这里的特点是:
conversation_historyenable_online_model 影响构建时会替换:
{user_question}{conversation_history}{enable_online_model}模板中定义了以下内容:
角色定位
见 intent_analysis_prompt.md
专业领域边界
见 intent_analysis_prompt.md
输出 JSON 结构
见 intent_analysis_prompt.md
专业 / 非专业判断规则
见 intent_analysis_prompt.md
关键词提取规则
见 intent_analysis_prompt.md
summary 的组织方式
见 intent_analysis_prompt.md
工作流说明
见 intent_analysis_prompt.md
这份 Prompt 本质上已经把意图识别变成了“结构化问答规划”:
这里在 Prompt 之外又额外加了一层 system 规则:
reasoning_summary 只能是用户可展示的安全摘要这一步是“Prompt 约束 + system 约束”的双重保险。
这里构造了 response_format=json_schema,要求模型必须返回一个固定结构的 JSON。
关键字段包括:
is_professional_questionintent_scenecompany_namecompany_aliasesrouteneed_offline_modeloffline_instructionorigin_questionkeywordsfallback_keywordsintent_descriptionsummaryreasoning_summary这一步的意义是:
OfflineLLMService 的特点:
response_formatjson_schema 时会降级具体降级逻辑:
json_schemajson_objectplain相关代码:
即使模型上游对结构化输出支持不稳定,这层也尽量把返回值稳定在“可解析”范围内。
第一次调用失败时,系统不会马上放弃,而是追加一组更强硬的消息:
{}true/false这一步相当于“人工强约束修正回合”。
json.loadsJSONParseErroraichat 的意图识别对“模型脏输出”的容忍度比较高,已经形成统一的 JSON 容错层。
reasoning_summary项目明确区分:
这是 aichat 这套意图识别的重要特征。
如果模型两次都没有返回可解析 JSON,则:
enable_online_model 推导 route默认规则:
online_then_offline(先在线后离线)online_only(仅在线)offline_only(仅离线)即使意图识别模型挂了,后面的问答主流程仍然可以继续跑下去。
这里会统一清洗:
keywordsfallback_keywordscompany_aliases然后组装 IntentAnalyzeResponse
is_professional_question 缺失时会偏保守地默认为 Trueaichat 的意图识别真正价值体现在后续主流程里。
status:intentIntentAnalyzeRequestanalyze_intent()online_only(仅在线) 或 online_then_offline(先在线后离线)offline_only(仅离线)online_only(仅在线) 直接走在线回答online_then_offline(先在线后离线) 异步启动在线回答,同时继续检索 / 报告internal_query(内部查询) 决定后续检索策略aichat 中的意图识别结果,直接决定:
shudao-chat-py 的意图识别流程和 aichat 相比,chat-py 的意图识别明显轻量很多。
它主要用于解决三个问题:
它不负责:
stream/chat-with-db这意味着 chat-py 内部不同问答入口对意图识别的依赖程度不一致。
其中意图识别模板配置为:
intent_recognitionprompts/yitushibie_template_lite.md模板只定义了 3 类:
greeting(问候)faq(常见问题)query_knowledge_base(知识库查询)并要求返回 JSON:
{
"intent": "意图类别",
"confidence": 0.9,
"search_queries": ["用户原始问题"],
"direct_answer": "直接回答内容或空字符串"
}
和 aichat 不同,这里完全没有:
routeneed_offline_modelintent_scenecompany_namefallback_keywordsreasoning_summary所以它的定位就是“前分类”。
qwen_service.intent_recognitionload_prompt("intent_recognition", userMessage=message) 加载意图识别 Promptuser 消息self.chat(...)QwenService 初始化时给意图识别单独配置了:
self.intent_api_urlself.intent_model见 qwen_service.py
也就是说:
chat-py 的“回答模型”与“意图识别模型”是可以分开的model=self.intent_modelapi_url=self.intent_api_urlsettings.intent.token,会在 qwen_service.py 自动带上 Authorization\{.*\} 匹配最外层 JSONjson.loadsintent / intent_typeresult["intent_type"]相比 aichat:
chat-py 的解析容错更轻若解析失败,直接返回:
{
"intent_type": "general_chat",
"confidence": 0.5,
"reason": "...",
"response": ""
}
其中 general_chat 表示“通用聊天”。
见 qwen_service.py
如果解析成功:
greeting(问候)
direct_answer,若没有则填默认欢迎语faq(常见问题)
direct_answer,若没有则填默认 FAQ 引导response = direct_answer or ""这说明 chat-py 中“意图识别”其实还承担了“固定回复生成器”的角色。
/intent_recognition 的行为messagesave_to_dbai_conversation_idrequest.state.user 取用户qwen_service.intent_recognition(data.message)intent_typeresponse_textsave_to_db=true 且意图是 greeting(问候) / faq(常见问题)
AIConversationai_conversation_id 与 ai_message_id独立接口更多是为“问候 / FAQ 快速返回 + 可选写历史”设计的,而不是为复杂编排设计的。
当 business_type == 0 时:
qwen_service.intent_recognition(message)intent_typequery_knowledge_base(知识库查询)知识库查询技术咨询_rag_search(message, top_k=10)final_answer prompt 组织最终问答qwen_service.chat(messages) 生成答案<think>,再调用 summarize_thinking_content() 生成可展示摘要只用于控制:
它不参与:
qwen_service.intent_recognition(message)_rag_search(message)final_answer prompt 组织消息qwen_service.stream_chat(messages) 流式输出<think>,调用 summarize_thinking_content() 把原始思考转成展示摘要和非流式版本一样,意图识别仍然只承担“RAG 开关”的角色。
settings.search.api_urlqueryn_resultsrag_context这里的 _rag_search() 使用的是“用户原问题”直接检索,而不是意图识别结果中的 search_queries。
也就是说:
search_queries这是 chat-py 意图识别和主流程之间一个比较明显的“设计上有、实现上未充分使用”的点。
aichat 的区别chat-py 的意图识别结果本身并不带 thinking_content。
它的“思考过程摘要”是在后续主回答阶段:
<think>而 aichat 是在意图识别阶段就直接产出一份安全展示型摘要。
| 维度 | shudao-aichat |
shudao-chat-py |
|---|---|---|
| 输入 | 问题 + 历史 + 在线开关 | 主要是单条问题 |
| 输出 | 结构化决策对象 | 轻量分类结果 |
| 分类粒度 | 专业 / 非专业 + 内部查询 + 路由 | greeting(问候) / faq(常见问题) / query_knowledge_base(知识库查询) |
| 关键词 | 主关键词 + fallback 关键词 | Prompt 要求有 search_queries,但主流程几乎未使用 |
| 路由 | offline_only(仅离线) / online_only(仅在线) / online_then_offline(先在线后离线) |
无 |
| 内部查询 | 有 intent_scene / company_name / company_aliases |
无 |
| 展示摘要 | 意图识别阶段直接生成 thinking_content |
在主回答阶段再总结 <think> |
aichatchat-pygreeting(问候) / faq(常见问题) 的直接回答query_knowledge_base(知识库查询) 需要检索aichatjson_schemachat-pygeneral_chat(通用聊天)aichat意图识别是主流程前置控制中心:
chat-py意图识别只决定:
greeting(问候) / faq(常见问题)所以两边的架构定位完全不同。
shudao-aichatthinking_content intent.pyshudao-chat-pyintent_type 和 response_rag_search() chat.py_rag_search() chat.pygreeting(问候) / faq(常见问题),独立接口可选写入 DB chat.py从代码看,两套实现服务于不同阶段的架构目标:
shudao-chat-pyshudao-aichataichat 的优势thinking_content 设计更成熟chat-py 的优势greeting(问候) / faq(常见问题) 这类简单问题处理直接chat-py 当前的局限search_queries 没被主流程充分消费aichat 当前的代价如果只看“意图识别”这个名词,两套实现看起来像是在做同一件事;但从代码职责来看,它们其实不是一个层级的能力:
shudao-chat-py更接近:
greeting(问候) / faq(常见问题) 的前置处理器shudao-aichat更接近:
如果后续只保留一套更完整的方案,从现有代码能力看,明显是 shudao-aichat 这套更适合作为统一的意图识别中枢。
若后续还要继续深挖,建议补以下几份配套分析:
意图识别结果字段字典
aichat 与 chat-py 的问答主链对比
Prompt 对比文档
intent_analysis_prompt.md 和 yitushibie_template_lite.md 逐段对照迁移建议