· liyu · rag-agent · 20 min read

AI 大模型 Ragent 项目:知识问答在后端经历的八个阶段与三大短路点

从用户在前端输入一句话到打字机式流式响应返回,生产级 RAG & Agent 问答系统后端究竟经历了什么?深度解构 Ragent 项目中 StreamChatPipeline 的八阶段线性编排、三大短路分支、数据总线设计以及前置幂等/限流/Trace 工程防线。

从用户在前端输入一句话到打字机式流式响应返回,生产级 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 初始化 traceIdtaskId,实现跨异步线程池、并行检索流的上下文无损透传,毫秒级记录各个阶段的耗时与出入参。

三、流水线骨架: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 回调)  │                              │
└──────────────────────────────┴──────────────────────────────┘
  1. 不可变输入(Immutable)questionconversationIdtaskId 等入参用 final 修饰,在对象构建时初始化,避免被任意环节意外篡改,保证多线程安全;
  2. 渐进式状态写入:阶段 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,并将复合问题解耦。
  • 输出物
    1. rewrittenQuestion:补齐上下文主语。例:“那它能退吗” → “iPhone 16 Pro 的退换货政策与条件”;
    2. 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)

  • 核心逻辑:针对非短路请求,多通道并行引擎兵分两路:
    1. KB 路径:通过 MultiChannelRetrievalEngine 对命中的各个向量知识库发起并行近似最近邻检索(Dense + Sparse 混合检索),随后执行跨通道去重与 Cross-Encoder Reranker 精排;
    2. MCP 路径:通过 MCPToolExecutor 提取结构化参数,异步调用底层微服务获取实时动态业务数据;
    3. 最终将两路数据聚合为 RetrievalContext(包含 kbContextmcpContext)。

阶段 7:handleEmptyRetrieval(空结果兜底,⚡短路点 #3)

  • 核心逻辑:检查 retrievalCtx.isEmpty()。如果各个知识库与 MCP 工具均未返回任何有效参考信息。
  • 短路行为:阻断后续 LLM 生成,直接向前端推送固定的兜底文案(“抱歉,知识库中暂未收录与您问题相关的内容”),返回 true
  • 设计哲学与其让大模型在空上下文中产生幻觉胡编事实,不如坦诚告知未检索到。

阶段 8:streamRagResponse(Prompt 组装与流式生成)

  • 核心逻辑
    1. 意图聚合:调用 mergeIntentGroup() 合并各子问题上下文;
    2. 结构化 Prompt 拼接:按照系统设定、对话历史、精准文档片段、MCP 实时数据的优先级组装结构化 Messages;
    3. 动态超参微调:纯 KB 知识库场景设定 temperature = 0.0(最大程度忠实于原文);MCP 混合场景设定 temperature = 0.3(赋予适度整合组织语言的空间);
    4. 流式推送与取消句柄:调用 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 的代词消解阶段 2Query Rewrite / 子问题解耦
05多品类知识库的树形意图分类体系阶段 3Intent Tree / 层次化路由
06大模型意图节点批量打分实战阶段 3Few-Shot Prompt / 打分模板
07意图爆炸与多样性配额分配阶段 3封顶算法 / 算力与召回权衡
08多意图冲突与用户交互式澄清阶段 4歧义引导 / 交互式卡片
09意图树向底层物理检索通道的映射阶段 3→6路由引擎 / 权重调配
10多知识库多通道并行向量检索架构阶段 6混合检索 / 并发异步召回
11召回文档去重、截断与 Rerank 精排阶段 6Cross-Encoder / 语义重排
12知识库与 MCP 外部工具混合调用链路阶段 6Model Context Protocol / 外部扩展
13自然语言向 MCP 结构化参数提取阶段 6Function Call / 参数校验
14结构化 Prompt 组装与超参动态调配阶段 8上下文拼接 / 动态 Temperature
15打字机体验:SSE 流式输出完整链路阶段 8SSE / WebFlux / 缓冲池推送
16用户点击“停止生成”后的集群协作阶段 8Task Cancellation / 信号广播
17全链路可观测性与耗时性能定位全链路TransmittableThreadLocal / Trace
18流量洪峰下的队列式并发削峰限流Pipeline 前Redis 信号量 / 队列等待

七、结语

在真实的生产级工业落地中,RAG 从来不是一行 retriever.get_relevant_documents() 再拼上 Prompt 那么简单

一个健壮的 RAG & Agent 系统,既需要对上游多轮对话意图的精准洞察与歧义控制,也需要对下游向量检索、微服务接口与大模型推理资源的严密调度。正是这种清晰的阶段分工、严谨的数据总线与果断的短路设计,才使得系统在复杂业务洪峰之下,依然能够保持丝滑、稳定且无幻觉的高水准表现。

技术专栏 · 总分体系

AI 大模型 Ragent 实战

已收录 3 篇深度解析(当前第 1 篇)
📍 阅读脉络:总览篇AI 大模型 Ragent 项目:知识问答在后端经历的八个阶段与三大短路点
Share:
Back to Blog

Related Posts

View All Posts »
AI 大模型 Ragent 项目:长会话 Token 爆炸?会话摘要压缩策略与水位线机制深度解析
阶段 1 进阶:摘要压缩算法

AI 大模型 Ragent 项目:长会话 Token 爆炸?会话摘要压缩策略与水位线机制深度解析

当多轮对话聊到 30 甚至 50 轮,滑动窗口滑走了开头的关键约束、全量保留又会挤爆 Token 上下文,RAG 系统该如何破局?深入剖析 Ragent 记忆系统中的增量摘要压缩算法、lastMessageId 水位线推进机制、攒批压缩优化(summaryBatchSize)、Prompt 绝对禁止记录答案的设计哲学以及 Redisson 分布式锁防重实践。

AI 大模型 Ragent 项目:会话记忆系统设计与多轮对话状态管理实践
阶段 1:会话记忆系统

AI 大模型 Ragent 项目:会话记忆系统设计与多轮对话状态管理实践

大语言模型 API 天然是无状态的,如何让每次独立的请求表现为连贯自然的连续对话?深入剖析 Ragent 问答流水线阶段一(loadMemory)背后的三层记忆架构、异步并发拉取与容错降级、滑动窗口规整(normalizeHistory)、loadAndAppend 时序避坑以及结构化 Prompt 上下文注入顺序。

阿里开源 TTL 原理解析:跨线程池上下文透传与 RAG 效率/成本追踪实战

阿里开源 TTL 原理解析:跨线程池上下文透传与 RAG 效率/成本追踪实战

深入剖析阿里巴巴开源 TransmittableThreadLocal (TTL) 的底层机制(Capture、Replay、Restore 与 Holder 弱引用字典),彻底破解线程池复用下的上下文丢失与污染困境。结合生产级 RAG 架构,实战解析如何利用全局 traceId 实现多路并发检索归因、分阶段耗时(TTFT/TBT)拆解与单次请求 Token 级成本精细化核算。