QAService 编排
Written: 2026.06第 09 章跟敲代码:上一讲:Milvus 混合检索深度解析codealong/chapters/ch09_qaservice_orchestration。 这部分代码是本章跟敲版,用来先跑通核心闭环;完整项目源码仍以本讲后文标注的qa_core/、scripts/等路径为准。
下一讲:RAG Pipeline 主流程深度解析
1. 本讲目标
- 理解 QAService 作为服务编排层(Orchestration Layer)的设计理念
- 掌握”服务门面”模式在 RAG 系统中的应用
- 理解两个核心方法的职责分工
- 理解 Generator(生成器)在流式问答中的角色
2. 前置知识 — 服务编排模式
2.1 什么是服务编排
服务编排(Service Orchestration) 是软件架构中的一种模式:用一个中心化的”编排器”来协调多个子服务的调用顺序和数据流转。 打个比方:- 没有编排:每个厨师自己决定做什么菜、用什么食材、先炒哪个后炒哪个 → 混乱
- 有编排:主厨(编排器)决定菜单、分配任务、协调出菜顺序 → 有序
- 意图识别 → 判断用户想干什么
- 历史读取 → 获取会话上下文
- 查询改写 → 补全追问
- 检索计划 → 决定如何检索
- FAQ 检索 → 查标准答案
- 文档检索 → 查业务资料
- 上下文构建 → 组织参考资料
- LLM 生成 → 产生答案
- 历史写入 → 保存对话
2.2 QAService 不做什么(边界)
3. QAService 的两个核心方法
3.1 方法职责对照
3.2 stream_query() — 唯一主干链路
yield from 是 Python 的委托语法。rag_stream_query 是 qa_core.pipeline.rag 模块中 stream_query 函数的 import alias(from qa_core.pipeline.rag import stream_query as rag_stream_query),它是一个生成器函数,每次 yield 产生一个事件。yield from 把这些事件”透传”给调用方(FastAPI WebSocket 路由),所以 QAService 不需要自己维护生成循环。
3.3 debug_retrieval() — 检索诊断半链路
debug_retrieval() 是检索诊断半链路,它故意从 prepare_retrieval() 开始,以便观察检索类意图、source 推断、按需改写、检索计划和召回质量;线上用户问答仍从 stream_query() 进入,并先执行 decide_route()。因此,调试接口没有走 route=direct_answer / faq_exact / retrieval,并不代表在线主链路跳过查询路由。
3.4 source 白名单校验
- API 层:不应该知道 Milvus 过滤规则(它只管 HTTP 参数校验)
- Retrieval 层:不应该承担业务白名单判断(它只管执行检索)
- QAService(编排层):最清楚”前端筛选项 + 意图推断分类”如何进入主链路
4. Generator 模式在 RAG 中的应用
4.1 什么是 Generator
Generator(生成器)是 Python 的一个核心特性,使用yield 关键字:
4.2 为什么 RAG 适合用 Generator
RAG 的问答过程不是一个”输入→等待→输出”的单步操作,而是一个多阶段持续产出的过程:yield 都是一个可以立即推送给前端的事件。用户不需要等全部流程跑完才能看到任何东西。
4.3 前端接收到的体验
5. 应用工厂模式
5.1 get_qa_service() 工厂函数
settings是只读配置,加载一次即可history是历史存储适配器,本身负责按 session_id 隔离会话- 每次请求创建新的 QAService 会重复加载配置,但没有好处
- QAService 只保存
settings(只读)和history(线程安全适配器) - 请求级变量(query、intent、plan、sources 等)都不在 QAService 上,而在方法局部变量中
5.2 在 API 中使用
6. 错误处理与事件协议
6.1 异常不抛给 WebSocket 路由
6.2 事件类型汇总
7. 本讲实践闭环
通过标准:在线问答只有 WebSocket 主入口;同一个业务服务能支撑流式回答和检索诊断,但不把 RAG 细节泄露给 API 层。
7.1 本讲从 0 到 1 实现闭环
这一讲的目标不是再写一个 RAG 算法,而是把前面已经具备的能力包装成一个稳定的应用服务层。实现时按这个顺序推进:- 先定义
QAService,让 API 层以后只调用 service,不直接碰 Milvus、Prompt、Pipeline。 - 实现
stream_query(),把完整 RAG Pipeline 产出的事件原样透传给 WebSocket;问候、越界、转人工等直答也在这条链路内完成。 - 实现
debug_retrieval(),复用检索准备逻辑但不调用最终回答 LLM。 - 最后补一个工厂函数
get_qa_service(),避免每个请求重复初始化 settings、history、retriever。
qa_core/application/service.py::QAService。
qa_core/api/chat.py。
start/status/token/end/error 事件;/api/retrieval/debug 能返回检索诊断信息但不生成最终答案。
来源:命令行验收,对应 scripts/api_e2e_smoke.py。
验收重点:API 层只负责协议和连接,业务编排集中在 service;service 再把请求交给 Pipeline,而不是把每个模块揉在路由函数里。
8. 重点掌握
9. 本讲小结
- QAService 是服务编排层,协调意图、历史、检索、生成、存储,但不直接处理 HTTP 或 Milvus 细节
- stream_query 是唯一在线问答主干链路,通过 Generator 持续产出事件
- debug_retrieval 是检索诊断半链路,只查不生成
- yield from 将 RAG Pipeline 的事件透传给调用方
- 单例工厂确保 settings 和 history 只加载一次,请求级状态全在局部变量中
- 错误以事件形式返回,前端可以优雅展示并允许继续提问

