资讯动态

从RAG到Multi-Agent:代码解释器与Supervisor模式如何抑制模型幻觉

发布时间:2026/10/21 3:34:14 来源:尧图企业网站定制
1. 起点并不完美一个会“答错”的 RAG 原型先说结论我做这个金融智能体的最初版本并不是一开始就奔着 Multi-Agent 去的。第一版就是典型的 RAG文档切块、向量检索、把检索结果塞进提示词、让 7B 模型直接生成回答。上线跑了两周准确率看着还行但一碰真实业务问题就露馅模型会一本正经地告诉你“某基金 2024 年最大回撤是 18.7%”然后引用一段完全不相关的季报原文或者问它“两个组合的夏普比率谁更高”它能根据记忆给出一个和检索结果自相矛盾的答案。如果只是“偶尔说错话”大家可能忍一忍就过去了。但金融场景不一样看到一个数字、一个比率用户是要拿去做判断的。答案出错轻则被质疑专业性重则直接导致决策失误。所以这个项目真正要做的事情不是把 RAG 做得更“聪明”而是把智能体的错误率降到可接受范围内。这篇文章是完整复盘核心围绕三件事展开如何抑制 7B 小模型的幻觉、为什么必须引入代码解释器、以及如何用 Supervisor 模式把多个 Agent 组织成一套可控的架构。整个过程偏工程实践不是学术综述所以每一部分都会尽量落到可复现的细节上。无论你是在做客服问答、文档助手还是数据分析 Agent这套演进路径应该都有参考价值。1.1 金融场景对 RAG 的要求和通用问答不一样通用知识问答里用户问“什么是通货膨胀”模型只要说个大概意思没人会逐字核对出处。但金融问答里问题往往是这样的“某私募产品 2024 年一季度末的管理规模是多少”“请根据最新财报计算该公司过去两年的经营活动现金流净额。”“对比策略 A 和策略 B 在最近 60 个交易日的波动率。”这类问题的共同点是什么答案是强时效性、强数字性、强逻辑推断。模型如果只靠训练阶段的记忆或者只靠检索到的一段文本很容易出问题。因为训练记忆里的数字可能早就过期检索回来的文本也可能缺上下文。更麻烦的是有些问题必须做计算而 7B 级别的模型本身算术能力就不稳定让它在生成回答时顺便算数几乎等于让一个口算很差的实习生一边说话一边心算——偶尔能对更多时候是凑答案。因此金融领域的智能体不能把“生成答案”当作唯一环节。它必须能定位数据源、能执行准确的计算、能给出可追溯的结论。这个需求决定了后续架构一定会从单纯的 RAG变成一个“检索 计算 校验”的复合系统。1.2 第一版系统暴露出来的三类幻觉我把实际运行中遇到的幻觉分成了三类后面对症下药的基础就是这份分类第一类是事实性幻觉。模型在回答某个具体数字或事实时自己“脑补”了一个答案。比如检索文档里明明写了某产品 2023 年收益率是 4.52%模型却回答 4.85%。这种情况大多源于模型过度依赖参数记忆或对多个相似片段做了错误混淆。小模型尤其严重因为它没有足够能力在回答时严格按住训练记忆把注意力完全集中在检索文本上。第二类是计算性幻觉。用户要求“最近 30 天年化波动率是多少”模型没做实际计算而是从某段文本里找了一个波动率数字直接填上或者自己算了一遍但步骤是错的。这类问题用提示词很难根治因为模型的语言生成和数学计算共享同一个“思考通道”小模型在执行多步计算时容易跳步、抄错数字。第三类是逻辑一致性幻觉。模型在回答的第一句说“数据来源不足无法判断”第二句又开始推断“从长期表现看该产品可能不错”。前一句和后一句在逻辑上冲突但生成过程是逐 token 解码的模型自己往往意识不到这种矛盾。面对这三类问题我的第一反应不是换更大的模型而是调整架构。原因很现实大模型确实能缓解其中一部分问题但推理成本和部署成本上去了而且即使是大模型在计算精确度和引用来源验证上依然不可靠。在这个项目里我们需要一套能够承载 7B 模型在局部任务中稳定发挥又能通过外部工具弥补模型短板的架构。2. 给 RAG 打好地基切片、检索与引用约束如果你想跳过基础篇直接看 Multi-Agent我特别理解但还是建议把这一章留下。因为我观察到很多团队在做 Agent 化之前连 RAG 的“地基”都没打好。检索质量不过关后面加再多的 Agent 协作都只是在垃圾之上叠加复杂性。2.1 文档切分不要用固定 token 窗口暴力切割金融文档的单元性很强。一份基金合同有“产品要素”“投资范围”“风险揭示”“费率结构”等章节一份年报里有“管理层讨论”“财务报表”“审计意见”几个大块。早期我用了固定 512 token 长度做切分结果经常把一个完整的风险提示条款拦腰截断检索到的片段看起来像样实际却丢了最重要的限定条件。后来改成了“结构感知切分”先识别文档标题层级再按章节、小节、段落切块段落过长时优先按语义完整句切割而不是按字符数硬切。每块文档同时保留文档名、章节路径、页号等元数据。这样做的直接好处是向量检索召回的内容大多有清晰边界模型拿到一块文本就知道自己在读什么而不是面对一堆断气的文字。对金融场景我强烈建议不要在追求召回率时把块切得太碎。太碎的块会导致上下文信息缺失比如产品费率往往是一个表格拆碎了模型就理解不了“管理费 1.5%/年托管费 0.25%/年”这些字段之间的归属关系。我们项目里最终的块平均长度控制在 600 到 1000 个字符之间既能保持语义完整性又不至于让检索噪声太大。2.2 混合检索关键词与向量并行纯向量检索对语义相近但字面差异大的文本效果好但金融文档里有大量“术语黑话”和产品代码。比如用户问“某量化二号”文档里写的可能是“量化2号”或“Quant II”语义相似但字符差异很大。向量检索有时抓不到这种精确匹配这时关键词检索反而更稳。我的做法是 BM25 和向量检索并行各自取 Top N 结果再用重排序模型统一打分。重排这一步很关键它不是简单把两路结果机械合并而是让模型把“用户问题 候选片段”放入同一个语境去计算相关性。实测下来引入重排之后检索结果的相关性有明显提升幻觉出现的频率也随之下降毕竟模型拿到的参考资料更准了。有一个经验值得强调不要让 7B 模型自己去做重排。7B 模型做重排需要把每个候选都读一遍然后输出相关性判断速度慢不说它对细微语义差异的区分能力也有限。我更推荐用轻量级、专用于排序的模型它速度更快、稳定性更好。2.3 答案必须带“验明正身”的引用很多 RAG 系统在回答末尾附上“参考文档xxx”这算引用但对金融场景来说不够。我们的要求是回答中每个关键结论都要能关联到具体的源文档片段最好是直接给出“文档名 章节 原句摘要”。在提示词里我不再让模型“基于资料回答问题”而改成“只使用资料中出现过的事实回答每个要点时用自然语言注明其来源片段例如‘根据 2023 年年度报告第三节的数据’”。如果某个问题资料里根本没有答案模型必须明确说“资料库中没有找到相关信息”而不是编造一个看似合理的结果。这个约束并不复杂但执行时必须有后置校验。我们写了一个简单的规则检查器把回答句子里出现的数字逐一与检索片段中的数字做比对如果发现某个数字在两个来源里都不存在对应值就认为存在“无中生有”风险触发一次重答或拦截。这种“规则层兜底”的做法效果很明显让模型自己去判断“我编了吗”不太靠谱但用外部规则校验数字和引用来源能做到百分之百的确定性。3. 关键转折引入代码解释器把“想”和“算”分开如果只停留在“用更好的检索 引用约束”上系统能做好多问题但那种必须精确计算的问题依然会翻车。比如用户问“把以下十只股票过去一年的日度收益率做一个等权组合计算最大回撤”这种问题你指望 7B 模型直接口算出来基本没戏。这也成了我们决定引入代码解释器的最直接原因。3.1 为什么需要代码解释器概念上代码解释器不是给模型一个 Python 环境那么简单而是在架构层面承认一件事模型擅长语言理解和逻辑规划但不擅长精确执行数值计算。那就干脆分开模型负责描述“如何计算”代码负责执行“具体计算”最后把计算结果作为可靠事实再交给模型组织语言。当时我们用的是 7B 级开源模型它的代码生成能力比数学能力稍好一些生成的代码只要语法正确、逻辑没大错跑出来的结果就是确定的。代码解释器相当于给模型裝了一个“计算器外挂”让模型不亲自算而是说“你帮我按这几个步骤算”。这个转变对幻觉问题的打压效果非常直接。一个真实案例用户问两只基金的相关系数之前模型可能凭空回答“相关系数达到 0.85”引入代码解释器后模型写出计算代码沙箱执行后得到真实的 0.63回答的数值立刻和检索数据对齐了。整个流程下来模型只负责决策“用什么算法、选哪些字段”而不再负责产生数字。3.2 代码解释器的完整执行链路我们的执行链路是这样的模型根据用户问题生成一段 Python 代码代码里会明确写明数据读取路径或数据文件路径例如“读取当前上下文中的 fund_nav.csv计算最近 60 个交易日两两相关系数”。代码被送入沙箱执行环境。我们采用的是受限 Python 执行器屏蔽了网络访问只开放有限的库pandas、numpy、scipy、json、datetime。原则上不安装任何可能访问外部网络的包。执行结果通常是一个 JSON 对象或 Markdown 表格被回传到模型上下文中。模型基于执行结果撰写最终回答并附上“数据计算方式Python 代码解释器执行”的说明。这个链路的关键原则是代码解释器产生的数值结果优先于模型自己生成的数值。提示词里明确写了“如果用户要求计算你必须先编写代码并执行禁止直接推测数值答案。”提示词只能管住大多数情况后面我们还会用“行为检查器”扫描输出发现模型直接输出数字而没有对应代码执行记录就强制转回工具调用。这里需要补充一个细节代码生成时数据文件路径不能由模型随意指定。我们的做法是检索 Agent 先定位数据文件再把文件路径作为结构化参数传给计算 Agent计算 Agent 的代码里只使用传入的路径不接受模型自己拼接的路径避免路径穿越这类低级但致命的安全问题。3.3 一个可复现的代码解释器模板如果你也要在自己的系统里做类似功能可以直接参考这个最小模板。它不是完整生产代码但把“计算”这件事从“生成答案”中剥离出来的思路是完整的# 由代码解释器执行的示例代码 import pandas as pd import json # 输入参数从外部传入模型不能自由修改数据路径 nav_csv_path /data/nav_logs.csv window 60 df pd.read_csv(nav_csv_path) df[nav_date] pd.to_datetime(df[nav_date]) df df.sort_values([fund_code, nav_date]) df[daily_return] df.groupby(fund_code)[nav_value].pct_change() # 计算最近 window 日的相关系数矩阵 recent df[df[nav_date] df[nav_date].max() - pd.Timedelta(dayswindow 10)] pivot recent.pivot(indexnav_date, columnsfund_code, valuesdaily_return) corr_matrix pivot.corr().round(4) # 输出结构化结果便于回传给语言模型 result { correlation_matrix: corr_matrix.to_dict(), data_period: [str(pivot.index.min()), str(pivot.index.max())], warning: 若数据不足将返回空表 } print(json.dumps(result, ensure_asciiFalse))这段代码里的关键点有两个。第一数据文件路径是外部传入的模型只负责写计算逻辑第二结果以 JSON 形式返回方便后续步骤做校验与回填。实际落地时你还可以加一层“结果摘要器”把庞大的 DataFrame 先做聚合统计只把关键数字传回给模型因为 7B 模型的上下文窗口有限塞给它一大堆原始数字反而会影响最后回答的质量。3.4 沙箱与安全别把代码解释器当成万能工具代码解释器听起来很好用但如果不做限制它也可能变成系统里最大的漏洞。我们这里的“沙箱”不是指给它一个容器就万事大吉。如果你的使用场景涉及敏感数据必须做到三点第一代码解释器只能运行白名单库禁止与外部通信禁止访问与任务无关的文件目录。第二所有数据进入执行环境前都要先脱敏日志中不要记录真实客户字段。第三执行环境要有 CPU 和内存上限防止模型生成死循环代码或超大内存操作否则一个请求就能拖垮整台机器。还有一个工程经验不要在代码解释器里给模型任意 print 大对象的权限。比如用户查询几十万行的持仓明细模型可能直接 print 全表把一个 20M 的字符串回传到主模型上下文里不仅慢还浪费 token。我们的做法是给 print 输出做截断超过 4000 字符就自动摘要真正需要的明细数据通过另外的文件下载通道交付。4. 从单 Agent 到 Multi-Agent为什么必须拆加了代码解释器之后系统能回答更多问题了但也开始出现一个新的倾向提示词越来越长。我把检索、计算、报告、引用规则、代码生成规则全部写进一个 Agent 的 System Prompt 里结果是模型一会顾此失彼。让它专注计算时它忘了查最新文档让它专注引用时它算错了数。这个问题的解决办法是把一个大而全的 Agent 拆成多个专业 Agent每个 Agent 只做一件事。4.1 避免 Prompt 膨胀带来的决策混乱一个提示词塞太多职责等价于让一个人同时做检索员、财务分析师、合规审核员和话务员而且要求他每讲一句话都要经过四重身份认证。小模型根本扛不住这种多目标约束思维一乱就漏事。拆分的初衷不是“套一个前沿架构名词”而是降低每个 Agent 的决策复杂度。拆完之后每个 Agent 的提示词都很短、职责很明确模型只需要在小范围内做判断出错概率自然降低。这也是 7B 模型能够胜任多条 Agent 链路的关键不是把它当全能大脑用而是把它放在一个个“窄口径、单任务”的角色里。4.2 四个核心 Agent 的角色划分这个项目最终沉淀下来四个固定角色Agent 名称核心职责必备工具输入/输出检索 Agent定位资料、筛选文档片段、确认数据文件路径混合检索、重排模型、文件索引输入用户问题输出候选片段、数据文件路径计算 Agent生成并执行计算代码处理数值类任务代码解释器、SQL 查询器输入检索片段和计算请求输出 JSON 结构化结果分析 Agent把片段和计算结果组织成完整语义答案无外部工具依赖上下文输入片段、计算结果、用户问题输出最终回答合规 Agent检查答案是否有数字无引用、结论越界、来源不清规则引擎、实体比对模型输入最终回答与检索片段输出通过/重写意见这个分工解决了一个很实际的问题检索和计算是两类完全不同的技术操作。检索 Agent 关注的是“文档集里哪些内容相关”计算 Agent 关注的是“怎么算才正确”如果把两者混在一起模型生成的代码可能引用了不存在的字段或者检索结果没用到计算逻辑里。拆开后每个 Agent 可以在自己的环节里做深度优化输出结果也更规整。4.3 不要做“群聊式”协作要有主流程控制很多人在做 Multi-Agent 时第一反应是让多个 Agent 自由对话今天你问我一句明天我回你一句最后看它们能不能达成共识。这个思路在金融场景里很危险。Agent 之间的自由对话会在两个方向失控一个是语义漂移聊着聊着就跑题另一个是责任真空如果最后的结论是谁都插了一嘴又谁都没负责问题出在哪个环节根本无法追踪。我们的做法是设计一个严格的“主流程管道”。大部分请求走固定的链路检索 Agent 先获取上下文然后按需调用计算 Agent分析 Agent 根据前两步结果写回答合规 Agent 最后把关。只有极少数请求会被 Supervisor 判断为需要并行处理或特殊路由我们会在下一节展开讲。这样做的好处是问题一旦出现可以通过链路日志快速定位是在哪一段丢了信息或引入了错误排查成本大幅降低。5. Supervisor 模式让调度器掌握全局拆分出多个 Agent 之后下一个问题自然冒出来谁来决定一个请求应该走哪条链路每天的请求纷繁复杂有的需要算数有的只需要查资料有的需要多个 Agent 串行协作有的还要并行查询多路数据源。如果每个请求都写死调用固定流程系统会变得笨拙如果完全交给某个 Agent 随机应变又容易失控。于是我们引入了 Supervisor 模式。5.1 Supervisor 的本质是路由裁判不是全知大脑Supervisor 模式的核心思想很简单有一个调度 Agent 负责监听任务判断任务类型把它分配给自己擅长执行的子 Agent最后收集结果、做质量把关。它不负责写正文、不负责跑计算只负责决定“谁来干”和“干完怎么验收”。这个角色的提示词不需要很长但一定要把路由规则写清楚。例如用户问题若包含“计算”“对比”“回撤”“夏普比例”等计算敏感词且检索结果中包含可操作的数据源Supervisor 就必须分配任务给计算 Agent若问题只是常识性概念解释则可以直接走分析 Agent不浪费计算 Agent 的资源。路由决策很重要的一点是Supervisor 要能判断“检索结果是否足够支持回答”。我们要求 Supervisor 在分配任务前先做一次信息充足度评估如果检索片段明显不够会先调用增强检索或追问用户。5.2 一个最小 Supervisor 提示词骨架下面是一个简化的 Supervior 系统提示词供参考你是任务调度中心。你的职责如下 1. 分析用户意图将其归类为资料问答、数据处理、报告撰写、混合任务。 2. 不得直接生成业务结论。你需要选择合适的工作流并调度对应的 Agent。 3. 若任务需要多步骤请输出步骤列表并指明每步由哪个 Agent 执行。 4. 在最终返回前检查所有子 Agent 的输出是否有冲突若有冲突优先采用计算 Agent 的结果。 5. 对于每个请求最多调度 3 个子 Agent。若无法解决明确向用户说明原因。这个提示词的关键不是让它表演规划而是给它一个“受约束的决策空间”。只要分类逻辑写清楚7B 模型也能稳定做路由。我们实测下来把调度规则约束得越死错误率下降越明显。如果完全开放让它自由规划小模型经常会规划出一堆不存在的步骤或者产生自我冲突的计划。这里要特别提醒不要在提示词之外加太多模糊描述比如“你应该考虑用户的真实需求”这类话在小模型身上只会增加不确定性。5.3 让 7B 模型在 Supervisor 和多 Agent 之间分工很多团队会在 Supervior 这个关键位置放一个大模型因为路由确实重要。但我们的经验是路由判断并不像想象中那么难它本质上是一个分类任务7B 模型完全有能力完成。真正需要强模型的地方反而是最后综合分析汇报的环节因为那需要同时整合多方信息、推理结论还要输出得让用户觉得靠谱。所以在实践中我们的策略是“小模型包揽局部强模型处理关键总结”。Supervisor 用 7B 模型做路由决策因为执行链路要快而且要便宜下游各专业 Agent 也分别用 7B 模型让它们各自专注于自己的子任务只有在最终分析 Agent 输出大段综合结论、或合规 Agent 发现潜在冲突时才会调用一个更强的模型来最终把关。这个阶梯式模型布局让整条链路在成本和效果之间达到了较好平衡。如果你没有强模型的调用渠道也完全可以只用一个 7B 模型做完整链路但结论类回答的质量会打折需要在提示词和规则上投入更多精力来弥补。5.4 一个完整请求的调度链路示例用我们系统里一个典型的真实问题来说明用户问“请根据最新周报对比策略 X 和策略 Y 的最近 12 周收益并说明期间最大回撤发生在哪一周。”首先Supervisor 把任务拆成三块资料检索 数据解析 报告生成。于是调度链路是检索 Agent 拿出最近的周报文本并将周报中与策略 X、策略 Y 相关的净值数据整理成结构化表格计算 Agent 读取这个表格计算 12 周区间收益、各周回撤幅度、最大回撤所在周输出 JSON 结果分析 Agent 拿到计算结果和原文片段组织成一段带引用的回答合规 Agent 最后核查引用关系确认“最大回撤 4.7%发生在第 7 周”这个结论确实能在原文数据里找到依据。所有步骤完成后Supervisor 汇总输出用户看到的结果末尾额外附了一行“计算由代码解释器完成数据来源产品双周报告2024-XX 期”。这套链路跑下来最大的变化是用户可以直接追问“你这个 4.7% 是怎么算出来的”我们有权限回到系统日志里调出当时生成的 Python 代码和输入数据表。这个可追溯性对于赢得业务方信任非常关键也是我当时决定引入完整链路日志的最大动力。6. 全流程复盘评测、调优与避坑指南一个架构能不能持续落地最终取决于你有没有一套评判它好坏的机制。没有评测就没有优化依据这是我们在项目前半段吃过亏才悟出来的道理。6.1 建一套面向金融问答的评测集我们最终建立了一个包含约 300 条问题的评测集覆盖三种难度基础问答如“某基金招募说明书中的托管费率是多少”要求答案准确且引用正确。数值提取与计算如“根据最新季报计算某公司资产负债率并与上年同期对比。”要求数值必须与代码执行结果一致。多文档综合如“结合产品合同和最新公告说明该产品的赎回规则是否有变化。”要求能合并多个来源信息。每条问题的标注包含标准答案、必需引用来源、禁止出现的行为比如“不允许直接说不知道但不说明原因”。每次发版我们会跑一遍评测集统计三个核心指标答案正确率、引用覆盖率、计算准确率。其中计算准确率必须达到 100%因为经过代码解释器的问题我不接受任何人算错。6.2 高频踩坑问题与排查速查表在这个项目上我们踩过的坑不少挑最典型的列出来现象根因解决方案模型引用文档 A但内容来自文档 B检索到了 A但模型混用了训练记忆强制输出引用 ID并用引用验证器核对原文代码解释器算对了但最终回答换了个数字分析 Agent 没严格使用计算结果提示词中规定“所有数字必须以计算结果为准”并对输出做数字比对多 Agent 链路经常超时每个 Agent 都在等前一个完整输出串行等待过长把可并行检索的数据源提前拆分减少串行环节模型回答“我无法完成”Supervisor 路由错误或不了解子 Agent 的能力丰富 Supervisor 的“能力地图”列出每个 Agent 能做什么、不能做什么合规 Agent 频繁拦截所有回答规则写得太严只要数字没直接引用就拦截把“引用要求”限定在关键数字和核心结论上而非每个词这里最耗时间的其实是最后一个。合规拦截太严会导致大量正常回答被退回重写用户体验变差。后来我们调整了策略合规 Agent 只检查两类问题一是核心结论有没有来源支撑二是数字是否与数据结果或检索片段一致。其余文风、语气、完整性这一类问题不设硬规则。6.3 成本与性能调优缓存、并发与模型选择多 Agent 架构天然比单 Agent 调用次数多如果不做控制每问一个问题都要串行调用 4 个模型延迟和成本都难看。我们在后来做了一些针对性优化第一给检索结果建缓存。同一类问题或同一批文档被反复查询时命中缓存直接跳过检索 Agent节约大部分向量检索和重排时间。第二把计算 Agent 做成并行实例。当多个用户同时发起计算请求时任务队列可以分发到多台沙箱执行节点而不是串行堆积。第三合理压缩上下文。每个 Agent 只接收与当前职责相关的字段不允许把上一环节的全部输出堆给下一环节。这样既控制 token 消耗也避免小模型被无关信息干扰。模型选择上我们最终稳定在“7B 做路由和子任务 强模型做最终把关”的组合。在运维层面各 Agent 之间所有消息都走统一 JSON 格式并记录完整 traceId。排查问题时不用猜直接看 traceId 下每个 Agent 的输入输出和耗时即可。这套工程基建看似不“性感”但它是整个多 Agent 系统能够在真实环境中存活的基础。6.4 从 RAG 到 Multi-Agent我给后来者的一句话如果你现在刚开始做类似项目我有一个很实际的经验不要一上来就搭一个复杂的多 Agent 系统。先把单 Agent RAG 跑通做好检索、引用、拦截等发现单 Agent 确实存在职责冲突或能力短板时再一步步拆出子 Agent最后才引入 Supervisor 做调度。每一步都先解决一个明确的问题而不是为了架构而架构。我们在项目里经历过前面几个阶段最大的感受是真正解决幻觉问题的不是某一个灵光一闪的提示词也不是单纯换上更大参数模型而是一整套“可验证、可追踪、可回退”的基础设施。代码解释器把数字算准引用校验把来源钉死Supervisor 把分工理顺三个机制叠加在一起才让 7B 模型在金融场景里慢慢变得可信。个人最后再分享一个细节我们留了一个“全链路手工触发”的后门当自动路由判断失败时运营人员可以手动指定某条链路强制执行。这个功能在系统冷启动阶段帮了大忙很多评测集里发现的错误都是通过手工链路定位出来的。工具再智能也要给人留一条可控的退路这是我在这个项目里最深的体会之一。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑