堵不如疏:让 agent 不要被逼到编造
一个知识库摄取项目跨 9 天 7 阶段的 prompt 演化。v3 编造 3/3 → v4 加 verify-refs 硬门槛 0 编造但充实度减半 → v7 给 agent 每种"不知道怎么办"的情况下显式的出路。
背景
一个项目要把分散在多个数据源的资料聚合到本地 markdown 知识库:200 多份策划案 docx(在飞书)、60 多份配表 xlsm、大量设计稿、一些代码。最初的方案是一个"摄取 agent",每个业务跑一次。
9 天 7 阶段 — 一张时间线
v1:单 agent 字数差 3 倍
最初的 prompt 骨架长这样:
# prompt: v1 单 agent 你是 KB 摄取 agent。 任务:把以下 4 个源整合成一份 markdown 文档: - 飞书策划案 docx - 配表 xlsm - figma 设计稿 - 代码 (lua / c#) 反幻觉: - 引用必须真实 - 路径必须存在 12 章节模板:总览 / 玩家视角 / 奖励投放 / ... 开始。 # 什么都对,什么都没说怎么做
跑出来:
原因不复杂:单 agent 在一次对话里要做的事太多 — 拉 4 类素材、跨源对照、组织章节、写引用、检查命名。任务密度过高时,模型最终是"完成什么算什么"。它会从最不关键的步骤跳过。
v2:拆 agent + 改成读真源
拆成 5 个"域 agent" + 1 个综合 agent + 1 个审计 agent。3 个域 agent 各管一个数据源,综合 agent 跨域整合,审计 agent 用 28 项 y/n checklist 反向检查。
拆完后改了两件事:
- 综合 agent 早期读"摘要"(50K → 5K → 5K,两次压缩损失 80%),改成直接读真源 — 让"读摘要"这条最方便的路径不再存在。这是"堵不如疏"思路的早期形态:不规约 agent 行为,改路径布局。
- 审计 agent 从"打分式"改成 28 项 y/n checklist,判定独立。
重跑同一个业务:35500 字,跟手写持平。
v3:编造事故 — 没有出路时,编一个就是出路
后续加了一个"修复 agent",基于已有 KB 文件改。跑了一批 3 个业务,审计都过,文件看着完整。
抽查发现:3 个业务里 3 个都写了不存在的代码文件路径。
## 主流程 主入口在 `SomeFooView.lua:88`,处理玩家点击 ... 具体逻辑见 `OtherView.lua:120` 中的 OnClick 方法。 # 代码库实际: # SomeFooView.lua → 不存在(真实文件名不同) # OtherView.lua → 不存在 # 但行号 / 引用形式 / 语境措辞看起来完全合理
prompt 里写了"反幻觉,引用必须真实",但产物里仍然出现编造。问题在于:找不到真路径时,prompt 没说"标信息缺口"还是"跳过这一段"还是"留 TODO"。
这条规则没有出路。在没有指定动作的情况下,产物里看到的是:填一个看着合理但实际不存在的引用 — 这成了实际的默认产出。
v4:加 verify-refs 硬门槛
引入一个独立的命令行工具,检查每个引用:文档链接 → 真实可达;代码路径 + 行号 → 文件存在 + 行号在范围内;图片路径 → 文件存在。agent 在 finish 之前必须跑一次,只要有 ✗ 就不能 finish。
之后跑的 45 个业务,编造数为 0。但是 — 平均充实度明显下降。
这就是"堵"的代价。verify 工具卡住编造后,产物形态明显变薄 — 留下的部分集中在引用层面能 verify 的内容上。prompt 里没说"verify 不过怎么办",产物上呈现的是退守:字数减少,内容只写一定能 verify 的部分。安全度上去了,信息量下来了。
v5 → v7:给每种"不知道怎么办"显式出路
后面几版的方向是给 agent 每种情况下显式的出路,而不是只设禁区:
- v5:不知道业务边界?→
ls <p4>/Client/.../UI/<biz>/。不知道格式?→cat <kb>/规范/...写作SOP.md。不知道写多少?→ 充实硬指标(≥ 250 行 + ≥ 3 图)。 - v6:找不到引用?→
lark-cli docs +search搜真源。 - v7:把 4 个动作改成"按顺序必跑,每步必须在产出里出现对应的 bash 调用"。
# prompt: v7 — 4 步必跑,每步必出 bash 调用 你是 KB 综合 agent。 按以下顺序操作,每步必须在产出里出现对应的 bash 调用: 1. 搜真源 $ lark-cli docs +search --query "<biz>" $ lark-cli docs +fetch <token> 2. 验业务边界 $ ls <p4>/Client/Binary/Src/UI/<biz>/ 3. 读范本 $ cat <kb>/规范/范本_<视角>.md 4. 读规范 $ cat <kb>/规范/本地/设计文档写作SOP.md 4 步全跑完才进入写内容。 finish 前必跑: $ python kb-verify-refs.py --biz <biz> verify-refs ✗ > 0 不准 finish。
同样信息密度,v1 是"你应该做的事",v7 是"你必须依次做的事 + 每步的痕迹必须在产出里"。重跑后:字数达标,引用验证通过,业务边界对。
三个版本的充实度 vs 编造率
能观察到的现象
当 prompt 没有给 agent 具体动作时,产物形态趋向最低阻力路径:
- 没有出路时,产物里看到的是"填一个看着合理的" → 出编造
- 加硬门槛禁止编 → 产物形态变成"少写一点" → 出退守
- 加显式出路("找不到 X 时该做 Y")→ 产物形态变成"按出路走" → 出正常产出
跟业界已有方向也对得上。RAG / grounding / verifier 这些方法的有效性,不光在工具本身的强度,也在 prompt 里有没有让 agent 真去走这条路。同样的 retriever 接入,prompt 写"如果需要可以用 retriever",和 prompt 写"必须先调 retriever、产出里要看到 retrieval 步骤",agent 的实际行为差异很大。
三条具体配套:
- prompt 里说"应该用工具" ≠ agent 会用工具
- 完成判据应该包含"路径走过的痕迹" — 让"实际跳过这一步"会留下空缺
- 硬门槛和显式出路要配套 — 单加硬门槛 agent 退守;单加出路而没硬门槛,agent 跳过出路。两者一起,agent 既不能编也不会退
已知边界
- 显式出路也有覆盖不到的地方。agent 遇到 prompt 里没列的情况,还是会自己摸索路径。这一点没有上限解 — 只能边跑边补出路。
- "堵不如疏" 在"产出有可验证形态"的场景成立(KB 摄取、代码生成、有固定格式的报告)。开放性创作场景里,verify 工具帮不上,出路也很难设计成强制流程。
- 跟业界已知方向的关系:RAG 给的是"有真源可用",grounding 给的是"答案能溯源",verifier 给的是"产出能事后检查"。这套实践跟它们都不冲突,补的是 prompt-level 的"实际去走"。前者是工具能力,这套补的是 prompt 路径设计。