让“信息不足”成为智能体的明确状态
文章以报销智能体在检索为空时仍批准请求为例,指出提示词中的“不知道就说明”缺乏可执行约束。建议在编排层定义 insufficient_context 状态、流转规则和责任人,让拒答与业务流程保持一致并可审计。
AI 深度解读
Agent 在没有证据时给出肯定答案,问题未必只出在模型。作者指出,很多系统虽然在提示词里写了「不知道就说不知道」,却没有给「证据不足」定义状态、处理分支和运行记录。模型继续回答,业务流程也继续推进,最后才被归为一次幻觉。
这篇文章值得读的地方,是把一个模糊的提示词问题拆成了工程问题:哪些条件应该在调用模型前检查,拒答如何影响业务状态,又该用什么测试证明规则确实执行过。它讨论的是如何控制失败,不是如何保证模型永远正确。
「不知道」必须能阻止流程继续推进
作者用一个报销 Agent 举例:员工询问航班改签费能否报销,政策 PDF 解析失败,检索返回零个片段,Agent 却回答可以报销,并把申请推进到已批准。
这是一个说明问题的场景,原文没有把它作为真实事故报告。
系统提示词即使包含拒答要求,也可能挡不住这条路径。因为「回答里承认不知道」和「停止审批」是两件事。作者把它拆成三个层次:
第一层是字符串。提示词要求模型表达不确定,但输出措辞不能证明业务状态没有继续前进。
第二层是状态。编排器拥有 insufficient_context,并规定进入这个状态后由谁处理、下一步去哪里。拒答终于能影响流程。
第三层是事件。运行记录写入 constraint_evaluated,留下当时的输入、判定条件和回退原因,事后才能查询规则是否执行。
拒答不仅要出现在回答里,还要改变状态,并留下可回放的记录。
原文强调第三层才让规则可测试。更准确地理解,是只有把判定过程记录下来,团队才能核查某次运行为什么停下,或者为什么没有停下。
哪些判断应该在调用模型前完成
作者主张采用混合编排:确定性骨架负责状态、重试、授权、审计和回滚;需要理解语义的节点才调用模型。
判断证据是否齐备,往往已有可检查的字段:retrieved_chunks 表示检索片段,schema_valid 表示结构是否合法,scope_match 表示是否符合范围,attachment_parsed 表示附件是否解析成功。
他的经验规则是:如果组装提示词之前已有的值足以完成判断,就把判断放进代码。否则,让生成答案的模型同时认证证据是否充分,就让同一个组件兼任作者和审核者。
下面这段代码解决的是「没有合格证据时,禁止进入回答节点」:
def answer_policy_question(run):
evidence = retrieve(run.question)
if not evidence or not evidence.schema_valid:
run.record(constraint="missing_context -> insufficient_context",
constraint_evaluated=True,
fallback_reason="empty_retrieval",
state_transition="pending -> blocked",
model_called=False)
return AskForClarification(reason="empty_retrieval")
return model.answer(question=run.question, evidence=evidence)
重点是 model_called=False:这条分支根本没有调用模型,测试可以直接断言,而不用判断回答语气够不够谨慎。不过,示例把空检索和结构无效都记成 empty_retrieval,实际排障时需要更细的原因区分。
输出也要有类型约束。作者要求 status 是只有两个取值的枚举;只要不是 answered,answer 就必须为 null:
{
"status": "insufficient_context",
"answer": null,
"evidence_ids": [],
"fallback_reason": "empty_retrieval"
}
这里要盯住 answer: null,避免客户端在证据不足时仍展示一段看似有效的答案。结构化提示契约要声明这个硬约束,代码也要执行对应的状态转换。
原文还给出示例记录:run_id 为 r-184,节点为 answer_policy_question,输入包含 retrieved_chunks: 0 和 schema_valid: true,转换结果为 pending -> blocked。这些字段把「规则应该生效」变成了可核对的事实。
为什么测试不能只给最终回答打分
最终回答看起来合理,不代表中间流程正确。作者提出白盒评估:按节点保存输入、输出和验收条件,组成能够回放的测试集。
针对证据不足这一条回退,原文分出三组:
- 正向组有三个案例,使用有依据的上下文,能够明确回答,回退必须不触发。
- 边界组分别覆盖零检索片段、工具响应格式错误、问题超出政策范围,要求进入
insufficient_context,并记录正确的fallback_reason。 - 追问组覆盖补充缺失文档、拒答后重新表述、上下文修复后再次提问,检查同一运行、同一候选下是否残留旧状态。
共九个案例,每个检查状态转换和 constraint_evaluated 两项,得到十八项检查。原文没有列出正向组各自的具体输入,也没有展示实际执行报告。
既要测试该拒绝时能拒绝,也要测试证据充分时能回答。
否则,只奖励拒答的评估会把「什么都不回答」当成好结果。作者认为,同样的方法可以覆盖授权拒绝、回滚和重试耗尽,并比较每次部署前后:声明了多少回退,有多少具备触发案例,又有多少具备禁止触发的案例。
我的判断
我认同作者把故障边界前移。对于解析失败、工具结构错误这类已有明确字段的条件,代码检查比继续叠加提示词更容易验证,也方便定位责任。
但「有字段」不等于「判断已经可靠」。scope_match 如何产生,原文没有说清;检索返回了片段,也不能证明片段足以回答问题。代码能够强制执行规则,却不能自动保证规则合理。
文章还缺少与纯提示词方案的对照数据,没有给出误答率、过度拒答率或延迟变化。它是一套工程主张和示例,不是经过量化验证的效果报告。
作者也明确承认:它修不好歧义输入、语料缺失、错误需求,以及模型读错已有政策的问题。它可能增加一轮澄清,并引入过度拒答。成本统计只是后续收益:团队可以按状态决定修解析器、补语料还是调整规则,不能据此直接推出省钱比例。
如果原型答错几乎没有代价,也无需追责,简单方案可能更便宜。输出面向外部用户、失败必须归因或需要审计记录时,这套骨架才更有价值。
能拿来做什么
- 给 Node 编排服务补上调用前检查,先覆盖空检索、附件解析失败和工具响应结构错误,并为每种情况定义状态转换与处理负责人。
- 让前端按
status渲染结果;遇到insufficient_context时展示补充材料的入口,并让审批等操作遵守服务端状态。 - 为一条关键回退建立九个回放案例,同时检查状态和判定记录,再验证用户补齐上下文后能否恢复正常回答。
- 排查一次无依据回答时,先查对应输入、回退原因和
model_called。如果政策文档凌晨 2 点解析失败,这些记录应当解释调用是否被阻止、申请是否仍然推进。