· liyu · rag-agent · 20 min read
AI 大模型 Ragent 项目:知识问答在后端经历的八个阶段与三大短路点
从用户在前端输入一句话到打字机式流式响应返回,生产级 RAG & Agent 问答系统后端究竟经历了什么?深度解构 Ragent 项目中 StreamChatPipeline 的八阶段线性编排、三大短路分支、数据总线设计以及前置幂等/限流/Trace 工程防线。
一、开篇:从一次提问到打字机输出,后端发生了什么?
假设你在一家大型电商公司主导智能客服助手的开发。系统接入了 3C 数码、家用电器、服装个护等多个品类的专业知识库,并对接了内部的订单中心、物流中心等微服务 API。
某天,用户在对话框里输入了一句话:
“iPhone 16 Pro 的退货政策是什么?”
随后点击了发送。几秒钟后,屏幕上如同打字机一般流式弹出了答案,不仅精准引用了 3C 数码知识库中的退货条款,还联动展示了该用户当前绑定的订单状态。
在前端看来,这仅仅是一个标准的 SSE(Server-Sent Events)长连接流式响应;但把视角切换到后端架构,从用户按下回车到最后一个 Token 推送完毕,系统必须在毫秒级时间内有序完成一整套复杂的流水线作业:
生产级知识问答处理全景
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 加载会话记忆 │ ──➔ │ 查询改写拆分 │ ──➔ │ 树形意图识别 │ ──➔ │ 歧义/直答短路 │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
│
▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ SSE 流式生成 │ ◄── │ 空结果兜底 │ ◄── │ 结构化组装 │ ◄── │ 多通道并行 │
│ & 任务可中断 │ │ 阻断模型幻觉 │ │ Prompt & 调参│ │ 检索 (KB+MCP)│
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘这些环节以何种顺序执行?哪些场景可以提前跳过?如果用户只是简单打个招呼发一句“你好”,是否还需要去昂贵的向量数据库做向量相似度检索?如果多通道检索一无所获,是否要强行让大模型胡编乱造?
在企业级大模型架构 Ragent 项目中,整个问答流程由核心编排类 StreamChatPipeline 统一调度。它将整个处理链路精准拆解为八个核心阶段与三个关键短路点。
本文作为《AI 知识问答与 Agent 架构实战》系列的总览篇,将系统性拆解这张后端全景架构地图。
二、进入 Pipeline 前的“三大护法”(前置工程防线)
当用户的 HTTP 请求到达 Controller 层,在正式进入 StreamChatPipeline 之前,必须先经过两层防御拦截与一次上下文初始化。
HTTP GET /rag/v3/chat
└── RAGChatController.chat()
├── 🛡️ @IdempotentSubmit(基于 Redis 分布式锁防重复提交)
└── RAGChatServiceImpl.streamChat()
├── 🚦 @ChatRateLimit → ChatRateLimitAspect(Redis 信号量队列式限流)
└── 🔍 invokeWithTrace()(初始化 TransmittableThreadLocal 全链路 Trace)
└── StreamChatPipeline.execute(ctx) ➔ 正式进入流水线1. 幂等提交拦截(@IdempotentSubmit)
- 痛点:弱网或用户焦虑连击发送按钮时,同一会话的多次重复请求会同时涌入 Pipeline,造成大模型算力浪费与多轮记忆写入冲突。
- 解法:Controller 方法标注
@IdempotentSubmit,基于userId + conversationId获取 Redis 分布式锁。若前序请求尚未完成,直接阻断并提示*“当前会话处理中,请稍候再发起对话”*。
2. 队列式并发限流(@ChatRateLimit)
- 痛点:大模型推理实例(GPU/API)并发容量是极其昂贵且有限的(如同餐厅后厨的灶台数量)。高并发洪峰若直接压垮 LLM 实例,会导致所有用户请求全部超时崩溃。
- 解法:基于
ChatQueueLimiter与 Redis 信号量实现并发坑位控制。当并发满载时,后续请求进入等待队列排队;若等待耗时超过最大容忍阈值,优雅返回排队超时,实现高可用削峰填谷。
3. 全链路 Trace 初始化(RagTraceContext)
- 痛点:一次知识问答跨越了 Redis、Milvus/ES 向量库、Rerank 模型、业务微服务以及 LLM 服务,一旦某次回答响应慢了 3 秒,定位性能瓶颈极其困难。
- 解法:基于阿里开源的
TransmittableThreadLocal初始化traceId和taskId,实现跨异步线程池、并行检索流的上下文无损透传,毫秒级记录各个阶段的耗时与出入参。
三、流水线骨架:20 行核心编排与数据总线设计
请求通过前置防线后,控制权交由 StreamChatPipeline.execute()。其主干代码仅有约 20 行,却是整套系统的中枢神经:
public void execute(StreamChatContext ctx) {
// 阶段 1:加载会话上下文记忆
loadMemory(ctx);
// 阶段 2:查询改写与子问题拆分
rewriteQuery(ctx);
// 阶段 3:多子问题并行意图识别
resolveIntents(ctx);
// 阶段 4:歧义检测与引导反问 [⚡短路点 #1]
if (handleGuidance(ctx)) {
return;
}
// 阶段 5:纯系统/闲聊意图直答 [⚡短路点 #2]
if (handleSystemOnly(ctx)) {
return;
}
// 阶段 6:多通道并行检索(知识库向量检索 + MCP 工具调用)
RetrievalContext retrievalCtx = retrieve(ctx);
// 阶段 7:检索召回为空兜底 [⚡短路点 #3]
if (handleEmptyRetrieval(ctx, retrievalCtx)) {
return;
}
// 阶段 8:Prompt 结构化组装与流式生成
streamRagResponse(ctx, retrievalCtx);
}数据总线:StreamChatContext 的解耦艺术
整个 Pipeline 不采用层层传递方法参数的冗余方式,而是全流程共享一个 StreamChatContext 数据总线:
┌─────────────────────────────────────────────────────────────┐
│ StreamChatContext │
├──────────────────────────────┬──────────────────────────────┤
│ 不可变基础入参 (final) │ 逐步填充的中间状态 (@Setter) │
├──────────────────────────────┼──────────────────────────────┤
│ • question (原始提问) │ • history (阶段 1 写入)│
│ • conversationId (会话 ID) │ • rewriteResult (阶段 2 写入)│
│ • taskId (任务 ID) │ • subIntents (阶段 3 写入)│
│ • deepThinking (深度思考) │ │
│ • userId (用户 ID) │ │
│ • callback (SSE 回调) │ │
└──────────────────────────────┴──────────────────────────────┘- 不可变输入(Immutable):
question、conversationId、taskId等入参用final修饰,在对象构建时初始化,避免被任意环节意外篡改,保证多线程安全; - 渐进式状态写入:阶段 1 填充
history,阶段 2 填充rewriteResult,阶段 3 填充subIntents。下游阶段仅按需读取,各阶段之间实现完全解耦,极易扩展与单元测试。
四、知识问答在后端经历的「八个阶段」深度拆解
流水线阶段流转与三个短路分支:
[用户请求] ➔ (1.加载记忆) ➔ (2.查询改写) ➔ (3.意图识别)
│
┌───────────────────────────────┴───────────────────────────────┐
▼ ▼
[4.歧义检测] ──(存在歧义?)──► [⚡短路点 #1: 下发点选引导] │
│ (否) │
▼ │
[5.系统直答] ──(纯闲聊/问候?)─► [⚡短路点 #2: 模板直接调 LLM] │
│ (否) │
▼ ▼
[6.多通道检索] (KB 向量检索 + MCP 工具调用) │
│ │
▼ │
[7.空结果兜底] ──(召回为空?)──► [⚡短路点 #3: 坦诚下发兜底提示] │
│ (否) │
▼ │
[8.Prompt 结构化拼装 ➔ 流式生成响应 ➔ 注册可取消 Handle] ◄────────────────┘阶段 1:loadMemory(加载会话记忆)
- 核心任务:将当前用户输入追加至持久化存储,并加载完整的历史对话记录至
ctx.history。 - 为什么放第一步:大语言模型本身是无状态的,后续的指代消解与意图分类必须依赖对话上下文。例如用户上一轮在问 iPhone 16 Pro,本轮追问“那它支持分期吗”,若无历史记忆,改写引擎根本无法知晓“它”所指为何物。在生产环境中,该阶段还会配套滑动窗口或动态摘要压缩策略。
阶段 2:rewriteQuery(查询改写与子问题拆分)
- 核心任务:利用轻量级 LLM 将口语化、包含代词指代的原始问题,重写为语义完备的检索 Query,并将复合问题解耦。
- 输出物:
rewrittenQuestion:补齐上下文主语。例:“那它能退吗” → “iPhone 16 Pro 的退换货政策与条件”;subQuestions:拆分复合需求。例:“iPhone 16 和 AirPods 4 的保修期分别是多久” → 拆分为 2 个独立的子检索问题。
阶段 3:resolveIntents(多子问题并行意图识别)
- 核心任务:将意图组织为一棵层次化树形结构(Intent Tree),针对每个子问题并行打分,识别其路由目的地。
- 三大意图类型:
KB(知识库检索):命中具体业务垂直知识库(如 3C 数码知识库、家电知识库);MCP(工具调用):命中业务服务接口(如调用订单中心查询快递轨迹、退款进度);SYSTEM(系统直答):打招呼、感谢或免检闲聊。
- 封顶配额算法:若子问题命中过多意图,会导致下游检索与工具调用开销爆炸。系统通过配额封顶算法,在召回丰富度与执行性能之间取得精细平衡。
阶段 4:handleGuidance(歧义引导,⚡短路点 #1)
- 核心逻辑:当阶段 3 发现多个不相容意图的得分极其接近且均高于阈值时(例如用户只问了“退货政策”,3C 数码库和家电库同时高分命中),判定为语义歧义。
- 短路行为:不调用大模型,直接通过 SSE 推送选项式引导卡片(“请问您想咨询的是 3C 数码还是家用电器的退货政策?”),中断后续流程并返回
true。 - 设计哲学:与其自作聪明猜错,不如明确反问确认。
阶段 5:handleSystemOnly(系统直答,⚡短路点 #2)
- 核心逻辑:若检测到所有子问题的命中意图全部属于
SYSTEM类型(如用户说“你好”、“谢谢你”、“你能帮我做什么”)。 - 短路行为:直接跳过耗时的向量检索与工具调用,提取意图节点预设的
promptTemplate,直接调用 LLM 流式输出。 - 设计哲学:不需要检索的闲聊,坚决不碰向量数据库。
阶段 6:retrieve(多通道并行检索 KB + MCP)
- 核心逻辑:针对非短路请求,多通道并行引擎兵分两路:
- KB 路径:通过
MultiChannelRetrievalEngine对命中的各个向量知识库发起并行近似最近邻检索(Dense + Sparse 混合检索),随后执行跨通道去重与 Cross-Encoder Reranker 精排; - MCP 路径:通过
MCPToolExecutor提取结构化参数,异步调用底层微服务获取实时动态业务数据; - 最终将两路数据聚合为
RetrievalContext(包含kbContext与mcpContext)。
- KB 路径:通过
阶段 7:handleEmptyRetrieval(空结果兜底,⚡短路点 #3)
- 核心逻辑:检查
retrievalCtx.isEmpty()。如果各个知识库与 MCP 工具均未返回任何有效参考信息。 - 短路行为:阻断后续 LLM 生成,直接向前端推送固定的兜底文案(“抱歉,知识库中暂未收录与您问题相关的内容”),返回
true。 - 设计哲学:与其让大模型在空上下文中产生幻觉胡编事实,不如坦诚告知未检索到。
阶段 8:streamRagResponse(Prompt 组装与流式生成)
- 核心逻辑:
- 意图聚合:调用
mergeIntentGroup()合并各子问题上下文; - 结构化 Prompt 拼接:按照系统设定、对话历史、精准文档片段、MCP 实时数据的优先级组装结构化 Messages;
- 动态超参微调:纯 KB 知识库场景设定
temperature = 0.0(最大程度忠实于原文);MCP 混合场景设定temperature = 0.3(赋予适度整合组织语言的空间); - 流式推送与取消句柄:调用
llmService.streamChat()逐字推送 SSE,同时向StreamTaskManager注册StreamCancellationHandle,支持前端用户随时点击“停止生成”。
- 意图聚合:调用
五、三大短路点的设计哲学(医院分诊模型)
如果将一次知识问答比作一次去医院看病的全流程:
┌──────────────┬────────┬────────────────────────────┬─────────────────────────┬──────────────────────────┐
│ 短路点 │ 阶段 │ 触发条件 │ 短路处理行为 │ 医院分诊类比 │
├──────────────┼────────┼────────────────────────────┼─────────────────────────┼──────────────────────────┤
│ #1 歧义引导 │ 阶段 4 │ 多个垂直意图高分冲突 │ 下发点选卡片,不调 LLM │ 分诊台确认挂哪个专科 │
│ #2 系统直答 │ 阶段 5 │ 全部为 SYSTEM 闲聊/问候 │ 跳过检索,直接调 LLM │ 常规咨询,直接窗口取药 │
│ #3 空结果兜底│ 阶段 7 │ 知识库与 MCP 均无有效召回 │ 下发固定兜底,不调 LLM │ 检查无异常,坦诚如实告知 │
└──────────────┴────────┴────────────────────────────┴─────────────────────────┴──────────────────────────┘短路机制的核心价值在于:在系统能够确定结论(或确定无法解答)的第一时间果断止损。不仅避免了无效的向量计算与大模型 Token 消耗,更大幅降低了端到端的平均响应延迟。
六、全链路 18 篇知识地图与架构总结
StreamChatPipeline 的八个阶段与三大前置防护,构成了企业级 RAG & Agent 系统的完整骨架。后续的 17 个专项篇章将分别对各个细分模块进行源码级剖析:
| 篇号 | 探讨主题 | 归属阶段 | 核心技术关键词 |
|---|---|---|---|
| 01 | 一次知识问答在后端经历的八个阶段 | 全景总览 | StreamChatPipeline / 线性编排 |
| 02 | 多轮对话上下文记忆机制 | 阶段 1 | 记忆存储 / 会话隔离 |
| 03 | 长对话 Token 爆炸与智能压缩 | 阶段 1 | 摘要提取 / 滑动窗口 |
| 04 | 用户输入与检索 Query 的代词消解 | 阶段 2 | Query Rewrite / 子问题解耦 |
| 05 | 多品类知识库的树形意图分类体系 | 阶段 3 | Intent Tree / 层次化路由 |
| 06 | 大模型意图节点批量打分实战 | 阶段 3 | Few-Shot Prompt / 打分模板 |
| 07 | 意图爆炸与多样性配额分配 | 阶段 3 | 封顶算法 / 算力与召回权衡 |
| 08 | 多意图冲突与用户交互式澄清 | 阶段 4 | 歧义引导 / 交互式卡片 |
| 09 | 意图树向底层物理检索通道的映射 | 阶段 3→6 | 路由引擎 / 权重调配 |
| 10 | 多知识库多通道并行向量检索架构 | 阶段 6 | 混合检索 / 并发异步召回 |
| 11 | 召回文档去重、截断与 Rerank 精排 | 阶段 6 | Cross-Encoder / 语义重排 |
| 12 | 知识库与 MCP 外部工具混合调用链路 | 阶段 6 | Model Context Protocol / 外部扩展 |
| 13 | 自然语言向 MCP 结构化参数提取 | 阶段 6 | Function Call / 参数校验 |
| 14 | 结构化 Prompt 组装与超参动态调配 | 阶段 8 | 上下文拼接 / 动态 Temperature |
| 15 | 打字机体验:SSE 流式输出完整链路 | 阶段 8 | SSE / WebFlux / 缓冲池推送 |
| 16 | 用户点击“停止生成”后的集群协作 | 阶段 8 | Task Cancellation / 信号广播 |
| 17 | 全链路可观测性与耗时性能定位 | 全链路 | TransmittableThreadLocal / Trace |
| 18 | 流量洪峰下的队列式并发削峰限流 | Pipeline 前 | Redis 信号量 / 队列等待 |
七、结语
在真实的生产级工业落地中,RAG 从来不是一行 retriever.get_relevant_documents() 再拼上 Prompt 那么简单。
一个健壮的 RAG & Agent 系统,既需要对上游多轮对话意图的精准洞察与歧义控制,也需要对下游向量检索、微服务接口与大模型推理资源的严密调度。正是这种清晰的阶段分工、严谨的数据总线与果断的短路设计,才使得系统在复杂业务洪峰之下,依然能够保持丝滑、稳定且无幻觉的高水准表现。