基于工作流的 RAG 智能体

READ

生成者:OpenAI Codex 智能体。 审核状态:已经过人工确认。

1. 架构定位

xiaokun可以称为基于工作流的 RAG 智能体:编排器按代码规定的顺序执行任务规划、任务准备、答案生成和引用校验;其中问答、摘要、对比通过检索证据增强生成,闲聊另走无证据生成分支。

2. 六个逻辑阶段

顺序 阶段 实际动作与输出 源码依据
1 请求接入与上下文准备 校验请求,恢复或建立会话,获取历史消息和记忆;文章场景读取当前文章 S5:153、240、251;S8:52
2 意图识别与任务规划 处理上下文依赖,构造规划输入;模型输出经解析和清洗形成任务,调用失败或无效 JSON 使用回退任务 S2:51、72;S9:35
3 任务分发与证据准备 优先处理澄清和无效任务,再选择 Handler;需要证据的任务构造查询并整理 EvidenceBundle S3:39、127;S4;S6:96
4 答案生成 需要生成时组装提示词、历史消息和用户问题,调用模型;特定空输出可重试一次 S1:73、80、125、131;S7:9
5 引用校验与有限修订 对需要校验的答案检查引用编号及覆盖,满足特定条件时修订一次,阻断性失败返回兜底 S1:155、163、195、208、236;S10:11
6 最终交付与记录 尝试保存交互记录,发送最终结果和正文,保存会话,再发送结束事件 S5:485—553;S8:133;S12:24

3. 总体执行时序

下图以接入成功、编排正常返回的请求为主;提前失败的分支另见第 6 节。编号仅用于阅读,不代表日志阶段编号。

sequenceDiagram autonumber actor U as 客户端 participant H as HTTP接入层 participant C as 会话服务 participant D as 文章与目录 participant O as 编排器 participant P as 规划器 participant T as Handler与证据检索器 participant M as 模型网关 participant G as 引用校验函数 participant I as 交互记录存储 U->>H: 提交问题及场景、会话参数 H->>H: 校验并清洗输入 H->>C: resolveConversation C-->>H: 会话ID、历史消息、记忆 opt 文章场景 H->>D: FindPublicByID D-->>H: 当前文章 end H->>O: Execute O->>P: Recognize P->>P: 处理上下文并准备回退任务 opt 模型网关可用 P->>M: 请求结构化任务 M-->>P: 模型结果或错误 end P->>P: 解析和清洗结果,必要时使用回退任务 P-->>O: TaskRequest与规划结果标记 O->>T: prepare alt 需要澄清、任务无效或Handler不可用 T->>T: 构造直接返回结果 else 闲聊 T->>T: 准备生成,不检索,不要求引用校验 else 问答、摘要或对比 T->>D: 按目标读取当前文章或查询目录 D-->>T: 资料或查询错误 T->>T: 整理证据,判断是否允许生成 end T-->>O: PreparedTask opt RequiresGeneration为true O->>O: 组装证据提示词和会话上下文 O->>M: Complete opt 启用正文流式交付且产生可发送块 M-->>O: 内容增量 O-->>H: OnAnswer正文块 H-->>U: 首次start及后续delta end M-->>O: 完整结果 Note over O,M: 输出上限空答案可重试一次,详见后图 opt RequiresGrounding为true O->>G: validateGrounding G-->>O: 校验报告及引用 Note over O,G: 特定失败修订一次,阻断性失败兜底 end end O-->>H: 最终执行结果 opt 任务要求新会话 H->>C: resolveNewConversation C-->>H: 新会话ID和上下文 end H->>I: persistInteraction I-->>H: 保存成功或失败 H-->>U: ready,携带最终结果与A2UI H-->>U: delta补齐正文或replace替换正文 H->>C: completeConversation C->>C: 保存消息与规则生成的记忆 Note over C,M: 满足条件时启动后台模型记忆整理,不阻塞done C-->>H: 保存结果 opt 交互记录或会话保存失败 H-->>U: warning end H-->>U: done H->>H: 请求退出时汇总耗时指标

图中调用顺序依据:接入、交付和退出统计见 S5;规划见 S2、S9;分发及检索见 S3、S4、S6;生成校验见 S1、S10;会话及后台整理见 S8。

4. 证据准备:不是每次都搜索,也不是有资料就一定生成

情况 实际行为 依据
任务要求澄清 Registry 直接返回澄清问题,跳过具体 Handler 和答案生成 S3:47
当前文章目标 使用入口已读取的 CurrentResource 作为证据,不再进行目录搜索 S4;S6:134
主题或实体目标 使用目标短语和完整任务目标组织搜索输入,交给目录查询 S3:78;S4;S6:159
搜索对象明确指向本站 检索器额外读取站点概览,之后仍继续相应搜索 S6:171
无证据 关闭生成,返回资料不足或检索失败提示 S3:127
对比任务证据不足 在检索状态不是 error 时,目标数不足 2 或有证据的目标不足 2,关闭生成 S4:compare_handler.go:25
闲聊 不调用证据检索器,启用生成,关闭引用校验 S4:chat_handler.go:11

检索器按查询列表顺序处理查询,去重后给证据编号 E1E2 等,并根据证据和失败记录生成 okpartialerrorempty 状态。一次准备可包含多项查询,不能理解为“只搜索一次”。[S6:96]

当前编排器在 prepare 之后进入生成或直接返回,没有根据生成或校验结果再次调用规划器、Handler 的反馈检索循环。已有的多查询、本站概览补充属于检索阶段内部逻辑。[S1:50—205;S6:96、171]

5. 生成、重试和引用校验时序

下图针对需要引用校验的生成任务,假设模型调用没有返回传输类错误。

sequenceDiagram participant O as 编排器 participant M as 模型网关 participant G as 引用校验函数 O->>M: initial_generation M-->>O: completion opt 正文为空且finish_reason为length,执行上下文仍有效 O->>M: output_limit_retry,推理强度改为none M-->>O: 重试结果 end alt 命中空输出提前返回条件 O->>O: 返回model_output_empty或model_output_limit else 继续引用校验 O->>G: validateGrounding G-->>O: 报告与引用 opt answer_empty或unknown_citation,校验无效且上下文仍有效 O->>M: revision_generation,使用同一份证据 Note over O,M: 修订关闭正文增量回调,温度设为0 M-->>O: 修订结果 alt 修订结果为空且finish_reason为length O->>O: 返回model_output_limit else 修订调用成功且可继续 O->>G: 再次validateGrounding G-->>O: 更新报告与引用 end end alt 校验仍存在阻断性失败 O->>O: 返回兜底,设置answer_validation_failed else 校验通过或只有引用缺失、覆盖不足 O->>O: 保留答案和校验报告 end end

依据:S1:125—238。修订模型调用若报错,保留原校验结果,再按是否阻断决定兜底;不会无限重试。[S1:180—201]

必须区分两种有限重试:

  • 输出上限重试:要求正文为空、结束原因为 length,且上下文仍有效;它重新生成答案,不是引用修订。[S1:131、217]
  • 引用修订:当前仅 answer_emptyunknown_citation 可触发一次;不会重新检索证据。[S1:166、208]

citation_required(缺少引用)和 citation_coverage(覆盖不足)会记录为无效校验,但既不触发上述修订,也不阻断最终答案。不能把当前策略写成“所有引用校验失败都会重写或拒答”。[S1:208、236;S10:55]

校验器检查空答案、证据是否存在、引用编号是否合法和文本块是否带引用,没有比较事实陈述与证据内容是否在语义上相互支持。因此 Grounding.Valid 不能等同于事实正确率;提示词要求只依据证据回答,也不等于程序已经证明答案真实。[S10:11—71;S7:9]

当前请求的推理强度是:对比 medium、摘要 low、其他结果类型 none;这是代码传入网关的配置,不是模型实际推理行为的观测结论。[S1:285]

6. 流式交付、错误与持久化

正文在生成期间发送,需要同时满足 live_progress=truestream_answer=true,并且模型提供内容增量、分段器产出可发送块。仅开启 stream_answer 不足以建立正文回调。[S5:300、357;S1:106]

分段器保留前瞻内容,按 Markdown 块边界发送;需要引用校验时,检测到未知引用会停止后续块发送。它并不等待每个块通过全部引用覆盖检查,尾部内容留到最终交付。[S11:11—46]

sequenceDiagram participant O as 编排器 participant H as HTTP接入层 actor U as 客户端 participant I as 交互记录存储 participant C as 会话服务 opt 已启用生成期间正文交付 O-->>H: 可发送正文块 H-->>U: start,仅首次正文块前发送 H-->>U: delta end O-->>H: 最终结果 H->>I: 保存交互记录及证据记录 I-->>H: 保存结果 H-->>U: ready,最终TaskResult与A2UI alt 最终答案以已发送正文为前缀 H-->>U: delta,仅剩余正文 else 已发送正文与最终答案不一致 H-->>U: replace,完整最终答案 else 此前没有发送正文 H-->>U: delta,完整正文 end H->>C: 保存本轮问答和规则记忆 C-->>H: 保存结果 opt 任一记录保存失败 H-->>U: warning end H-->>U: done

依据:S5:302—325、485—553。前缀分支在代码中先检查是否已有正文;图按三种互斥情况表达。交互保存失败会清空对外的交互 ID,但仍尝试交付答案;最终 SSE 写入失败会提前返回,不能保证客户端一定收到 done。[S5:489、498、520、547]

会话保存成功且清洗后的消息数至少为 4 时,会启动后台记忆整理;后台可以调用模型,更新时检查会话版本和身份,避免覆盖新一轮状态。后台完成与 done 没有固定先后关系,因此不能把它画成“done 后才开始”的串行步骤。[S8:133—195]

任务要求新会话时,生成前先忽略旧历史和记忆;编排成功返回后,HTTP 层才解析新会话并使用其 ID 完成后续记录。[S1:80;S5:455]

输入校验、会话读取、文章读取失败会在编排前返回。编排返回模型错误时,HTTP 层映射错误响应;开启实时进度时走 SSE error,否则走 JSON 错误响应。编排返回后注册的延迟逻辑会在未尝试写入交互记录时补记失败,但不覆盖编排之前的所有失败。[S5:176—270、285、392—453]

7. 最关键的是什么

从实现依赖看,问答、摘要、对比的关键基础是“任务目标正确,证据可用”。 Handler 根据目标构造查询,证据为空会关闭生成,对比还有目标证据数量检查,生成提示词明确以证据作为事实依据。这些都是代码可以直接确认的依赖和门槛。[S3:78、127;S4;S7]

但“证据可用”不等于“证据完整且正确”:当前通用门槛主要检查是否有证据,对比检查目标覆盖数量;最终引用校验也不做事实语义核验。因此仅凭现有代码,不能宣称系统已经自动证明证据足够回答所有问题。[S3:127;S4:compare_handler.go:25;S10]

此前“检索是最关键环节”的表述应理解为基于上述依赖关系的工程判断,不是已通过线上评测证明的瓶颈排名。本文没有证据证明检索在当前数据上比意图识别或生成错误更常见,也没有证据证明它最耗时。

准确的现状记录是:系统已有澄清、空证据停止生成、对比证据不足处理和有限答案修订;编排层尚无根据结果重新规划并补充检索的循环。 是否增加该循环,需要进一步评测,本文不把建议写成现有能力。[S1][S3][S4]

本文只记录当前源码可确认的控制流程及其直接推论,不将提示词约束、模型参数或引用格式检查等同于运行效果保证。

Conversation

评论

正在加载评论