在这里插入图片描述

目录

  1. 程序性错误的五类型
  2. 与幻觉的区分判定
  3. 错误传播的乘法衰减
  4. 步骤级归因方法
  5. 执行不稳定与分支漂移
  6. 按类型分治的修复
  7. 总结
  8. 外部引用

摘要

程序性错误指模型事实都对、流程却出错的失败形态。
本文把这类错误拆成五种类型,给出与幻觉的两段式区分方法。
再量化错误传播,讲清归因手段、稳定性口径与分治修复。

1. 程序性错误的五类型

大模型评测里,答错常被笼统归为幻觉。但多步任务中还有另一类失败:每一步引用的事实都对,结果仍然错了。

这类失败的根源不在知识,而在流程。流程错误有稳定的形态,可以分类,可以判定,也可以分治修复。本章先给出五类型框架。

1.1 五类型总览:流程错在哪一层

认知心理学把知识分成两类。陈述性知识回答“是什么”,程序性知识回答“怎么做”。

大模型的程序性知识体现在多步任务里:拆解目标、编排步骤、调用工具、核对返回、汇总结论。任何一环走样,都属于程序性错误。

按出错位置划分,五类型覆盖了计划层、时序层、分支层、接口层与兜底层。

程序性错误:流程层面的失败

结构类:步骤之间出错

接口类:与外部交互出错

步骤遗漏:漏做必要环节

顺序颠倒:先后错位触发故障

条件误判:分支走错

参数错传:实参张冠李戴

异常路径缺失:出错后无兜底

图 1:程序性错误五类型结构图

五类型分两族:结构族管步骤之间的组织方式,接口族管与工具和环境的交互质量。判定时先定族,再定类型,最后找证据。

类型出错位置一句话定义典型现场
步骤遗漏计划层漏做必要环节数据迁移忘了先建索引
顺序颠倒时序层先后错位触发故障先删旧表再做备份
条件误判分支层分支走错检索为空该重试却直接放弃
参数错传接口层实参与形参不匹配把城市名传给国家参数
异常路径缺失兜底层出错后无兜底动作上游超时仍继续生成结论

表 1:五类型速查表

速查表的用途是沟通口径。排查故障时,先和团队对齐错误属于哪一层,再往下钻细节,能省掉大量无效争论。

1.2 结构族:漏步骤、乱顺序、错分支

步骤遗漏最隐蔽。必要环节没出现在轨迹里,没有任何报错,直到下游产出异常才暴露。

比如代码发布任务,模型直接执行部署,跳过了测试环节。部署本身成功,故障在几小时后才出现。判定特征是清单差集:要求环节减去实际环节,缺项即证据。

顺序颠倒的破坏力更强。每个步骤单独看都正确,先后一换就出故障。

典型例子是先执行删除、后执行备份。两个动作都按规范完成,数据却已经丢了。判定特征是顺序约束被违反:写明 A 必须先于 B,轨迹里 B 的位置却靠前。

条件误判发生在分支点。模型选了错误的分支,或者把条件判反了。

比如工具返回空列表,正确路径是改写查询重试,模型却走进了直接总结的分支。判定特征是期望分支与实际分支不一致,且分歧点附近有明确条件表达式。

三类结构错误有一个共同点:轨迹本身语法合法,每步都在做事,错在组织方式。这也是它们容易被漏检的原因,日志里没有任何一步真正失败。

边界情况要留意:结构错误常与事实错误交织。模型先幻觉了一个前提,再按错误前提走了错误分支。此时修复分支谓词没用,得回到第 2 章的两段式判定,先定位主导错误。

1.3 接口族:参数错传与异常缺失

参数错传是工具调用场景的高频错误。实参的名字、类型或语义与工具预期不匹配,张冠李戴。

常见形态有三种:一是字段错位,把 user_id 传给了 user_name;二是单位错误,把毫秒传给了预期秒的参数;三是类型错误,把字符串 ID 传成了数字。工具往往不报错,只是安静地查错对象。

异常路径缺失指流程只写了顺利路径。上游返回空、超时、权限拒绝时,流程没有对应动作,错误被无声传递。

后果是连锁放大:检索为空,模型拿空结果继续摘要,产出一篇看似通顺实则无据的结论。这类输出表面上像幻觉,根因却是流程缺兜底,修复手段完全不同。

下面用规则式分类器把五类型固化成代码,输入轨迹特征,输出类型与证据。

# 来源:自实现 / proc_error_classifier.py
# 规则式程序性错误分类器:按优先级依次检查五类特征

REQUIRED = ["建表", "鉴权", "写入", "校验"]      # 任务要求的必要环节
ORDER_RULES = [("鉴权", "写入")]                 # 声明顺序约束:前项须先执行
TOOL_SCHEMA = {"query": ["city", "date"], "send": ["user_id", "msg"]}

def classify(trace):
    """trace 为轨迹特征字典,返回(错误类型, 证据)二元组"""
    done = trace.get("steps", [])
    # 规则一:必要环节缺失,优先级最高
    missing = [s for s in REQUIRED if s not in done]
    if missing:
        return "步骤遗漏", "缺失环节: " + ",".join(missing)
    # 规则二:顺序约束被违反
    for first, second in ORDER_RULES:
        if done.index(first) > done.index(second):
            return "顺序颠倒", second + "先于" + first + "执行"
    # 规则三:期望分支与实际分支不一致
    exp, act = trace.get("expect_branch"), trace.get("branch")
    if exp and act and not exp == act:
        return "条件误判", "期望" + exp + "实际" + act
    # 规则四:实参名不在工具形参表中
    for tool, args in trace.get("calls", {}).items():
        bad = [a for a in args if a not in TOOL_SCHEMA.get(tool, [])]
        if bad:
            return "参数错传", tool + "收到非法实参" + ",".join(bad)
    # 规则五:发生过失败但无兜底动作
    if trace.get("had_failure") and not trace.get("fallback"):
        return "异常路径缺失", "步骤失败后无兜底动作"
    return "未检出", "五类规则均未命中"

if __name__ == "__main__":
    bad = {"steps": ["建表", "写入", "鉴权", "校验"],   # 写入跑到了鉴权之前
           "branch": "放弃", "expect_branch": "重试",
           "calls": {"query": ["city", "date"]}, "had_failure": False}
    print(classify(bad))
    # 输出:('顺序颠倒', '写入先于鉴权执行')

分类器的规则顺序就是判定优先级:先看缺不缺环节,再看顺序、分支、参数,最后看兜底。顺序颠倒的证据会先于条件误判返回,因为漏掉鉴权的写入更危险。

工程映射:这套规则可以直接嵌进轨迹审查流水线。每次执行后跑一遍,命中即记录类型与证据,供第 6 章的修复路由器消费。

2. 与幻觉的区分判定

程序性错误和幻觉的修复方向完全不同。前者改流程约束,后者改知识获取方式。混着修,两边都见效甚微。

本章给出一套可操作的判定流程,先用对照表划清边界,再用两段式检查落到轨迹上。

2.1 陈述性与程序性:分界与对照

幻觉是陈述性错误:输出内容与可验证事实不符,比如编造论文编号、虚构不存在的接口。

程序性错误是另一个层面:每个中间产出都经得起核对,错在环节组织、先后次序或分支选择。

维度陈述性错误(幻觉)程序性错误
错误层面内容与事实不符流程与规范不符
检查对象每步输出的真实性步骤集合、顺序、分支、参数
核对手段外部检索、规则校验清单比对、顺序约束、schema 校验
典型表现引用不存在的来源事实都对但结果错误
修复方向接地检索、引用约束流程守卫、清单、兜底分支
复发特征换个问法就消失或变形同类任务稳定复现

表 2:两类错误对照表

最后一行是经验判据:程序性错误有结构性,同类任务会稳定复现。幻觉受采样影响更大,波动明显。复现稳定性本身就是一个弱信号。

灰色地带要承认:两类错误常常串联。模型先幻觉一个不存在的工具参数默认值,再据此走进错误分支。此时两类证据都在,需要判断主导方。

判断标准看因果链起点:最早出现的错误是什么类型,主导方就是什么类型。先错事实再错流程,按陈述性修;先错流程后引出事实错误,按程序性修。

2.2 两段式判定:先查事实,再查流程

判定顺序不能乱。先查流程容易把幻觉误判成流程错:模型引用了假事实,后续分支再正确也没用。

正确顺序是先事实后流程。核心推论只有一句:事实全部核对无误而结果错误,必为流程错。

存在事实不符

事实全部相符

查出流程缺陷

未查出缺陷

拿到失败轨迹

第一段:逐步核对事实

事实核对结果

判定:陈述性错误即幻觉主导

第二段:核对流程

流程核对结果

判定:程序性错误

扩大事实核查范围后重查

图 2:两段式判定决策图

阅读顺序自上而下:第一段是内容审计,逐个核对中间产出的真实性;第二段是结构审计,比对清单、顺序、谓词与参数。两条出口对应两类修复路线。

事实核对流程核对判定结论修复路线
全对有缺陷纯程序性错误流程守卫与清单
有错无缺陷纯陈述性错误检索接地与引用约束
有错有缺陷混合错误按因果链起点定主导
全对无缺陷判定存疑检查核对器覆盖面

表 3:两段式判定四象限表

第一段的核对不要依赖模型自述。有研究发现思维链解释可能不忠实:给提示加偏置后,模型答案随之偏移,解释却只字不提。该研究在 13 个 BBH 任务上让准确率最多下降约 36%。

所以事实核对必须用外部证据:真实检索结果、数据库查询、确定性规则。模型说自己查过了,不算查过。

第二段的流程核对用第 1 章的五类型规则。清单差集查遗漏,顺序约束查颠倒,分支记录查条件,schema 查参数,兜底标记查异常。四象限表给出结论后,进入第 3 章的传播分析。

3. 错误传播的乘法衰减

定位到错误类型只是开始。更关键的问题是:单步质量如何决定整条轨迹的成败。

本章用一个极简模型回答:假设每步错误率相同且相互独立,联合成功率随步数按幂次衰减。这个模型不精确,但足够解释长流程为什么难。

3.1 从单步错误率到联合成功率

设单步错误率为 p,独立假设下 n 步全部成功的概率约为 1 减 p 的 n 次方,记作 (1-p) 的 n 次幂。

直觉推论很直接:p 不变时,步数翻倍,成功率平方化衰减。单步 95 分的流程,二十步之后只剩约 36 分。

第1步 通过率0.95

第2步 累计约0.90

第5步 累计约0.77

第10步 累计约0.60

第20步 累计约0.36

第50步 累计约0.08

图 3:单步错误率 0.05 时的累计成功率衰减链

图中数字是 p 取 0.05 的计算结果。十步之内成功率尚有六成,超过二十步迅速塌向三成,五十步只剩零头。长流程的失败不需要任何一步很差,只需要每步都有一点小概率出错。

另一个有用的量是减半步数:成功率降到一半需要多少步。近似公式是 0.693 除以 p。p 为 0.05 时约 14 步减半,p 为 0.02 时约 34 步减半。

独立假设显然过强。真实轨迹里错误会相关:一步理解偏了,后续同方向的步骤更容易跟着偏。相关性能让实际衰减快于公式。

相关性也可能反向:模型偶尔自我纠正,把前面的错冲掉。此时实际表现好于公式。所以幂次衰减应理解为基准线,用于估算量级和比较方案,而非精确预测。

3.2 联合成功率计算器

把公式落成代码,方便在方案评审时快速查表。输入单步错误率列表与步数列表,输出衰减矩阵。

# 来源:自实现 / joint_success.py
# 联合成功率计算器:输出幂次衰减数据表与减半步数

import math

def joint_rate(p, n):
    """独立假设下,n 步全部成功的概率为 1 减 p 的 n 次方"""
    return (1.0 - p) ** n

def half_life_steps(p):
    """成功率减半所需步数,精确值由对数计算"""
    return math.log(0.5) / math.log(1.0 - p)

if __name__ == "__main__":
    cols = [5, 10, 20, 30, 50, 100]
    print("p值    " + "  ".join(str(c) + "步" for c in cols) + "  减半步数")
    for p in (0.02, 0.05, 0.10):
        row = "  ".join(f"{joint_rate(p, n):.3f}" for n in cols)
        print(f"{p:.2f}   {row}   约{half_life_steps(p):.0f}步")

程序输出如下表,保留三位小数。

单步错误率 p5步10步20步30步50步100步减半步数
0.020.9040.8170.6680.5450.3640.133约34步
0.050.7740.5990.3580.2150.0770.006约14步
0.100.5900.3490.1220.0420.0050.000约7步

表 4:联合成功率衰减表

表中两个数字值得记住:p 为 0.05 时百步成功率不足百分之一;想把整体成功率维持在九成,p 为 0.05 只容得下约两步,p 为 0.01 也只容得下约十步。

推论是设计准则:与其把单步做到极致,不如压缩步数。每合并两个环节,联合成功率就少乘一次衰减因子,效果等价于把单步错误率显著降低。

3.3 长流程的实证证据

乘法衰减不只是纸面模型。Faith and Fate 一文系统测量了Transformer在组合任务上的表现。

现象论文数据
3位数乘3位数零样本GPT-4 约59%,ChatGPT 约55%
复杂度继续增加正确率从接近满分滑向零
错误构成传播型错误多于局部型错误
隐藏错误约82.3%的正确答案内部计算图至少含一个错误节点

表 5:Faith and Fate 关键实验结果

第三行是本文主题的直接证据:单步操作大多正确,失败来自错误沿步骤传播。组合层数加深后,完全正确的节点占比骤降,传播型错误占比反超局部型。

第四行更值得警惕:不少最终答案是对的,中间过程却已带错。这提示最终通过率会高估流程质量,只看结果会漏掉大量潜伏错误。

ReAct 论文的动机也来自同一观察:纯思维链是静态黑盒,内部推理不接地,会导致事实幻觉与错误沿推理链传播。交错推理与行动,让每一步都能拿到外部观察校准,正是切断传播链的手段。

工程映射:评估长流程任务时,必须同时报告步数与成功率,单看成功率没有意义。二十步 0.60 与百步 0.13 是完全不同的工程问题,前者修分支,后者先砍步数。

4. 步骤级归因方法

整条轨迹失败后,第一个工程问题不是怎么修,而是错在哪一步。归因错位,修复就会打偏。

本章定义两个关键概念,再给出两种互补的归因算法:二分定位与依赖图反查。

4.1 引入点与暴露点

最终失败的位置叫暴露点,最早偏离正确轨迹的位置叫引入点。两者经常相距很远。

常见形态是:第 3 步参数用错,第 4、5 步执行无误,第 6 步结论错误才显性化。盯着第 6 步修,永远修不好。

步骤1 建表 正确

步骤2 拉数 正确

步骤3 参数用错 引入点

步骤4 聚合 执行正确

步骤5 报表 执行正确

步骤6 结论 错误暴露

图 4:引入点与暴露点分离示意图

图中只有步骤 3 出错,步骤 4 和 5 的执行逻辑完全正确,只是输入已被污染。归因的目标是找到步骤 3,而不是暴露错误的步骤 6。

引入点的定义依赖参照系:需要一个判定步骤产出是否正确的校验器,术语叫预言机。校验器可以是规则,可以是检索比对,也可以是人工抽检。

没有校验器就没有归因,这是前提条件。校验器的覆盖面决定归因的分辨率:只能查最终答案时,归因粒度就只有整条轨迹。

另一个前提是可重放。归因算法需要在中间任意位置截断轨迹并检查状态。环境有副作用时,比如已经发出的邮件、已经支付的订单,直接重放会放大事故,需要沙箱或影子环境。

4.2 二分定位:对半切轨迹重放

二分定位借用代码调试里的二分查找。思路:检查轨迹前半段状态是否正确,正确则错误在后半,反之在前半,逐次对半缩小。

# 来源:自实现 / bisect_attribution.py
# 二分定位归因器:对半切轨迹,用状态校验器找错误引入点

def make_trace(bad_at, n):
    """构造 mock 轨迹:第 bad_at 步起状态被污染"""
    return [{"step": i, "state_ok": i < bad_at} for i in range(1, n + 1)]

def bisect_first_bad(trace, check):
    """check(mid) 判定前 mid 步状态是否正确,返回引入点与重放次数"""
    lo, hi, replays = 1, len(trace), 0
    while lo < hi:
        mid = (lo + hi) // 2              # 对半切轨迹
        replays += 1                      # 每次判定对应一次重放
        if check(trace, mid):
            lo = mid + 1                  # 前半仍正确,错误在后半
        else:
            hi = mid                      # 前半已出错,错误在前半
    return lo, replays                    # lo 即首个状态错误步骤

if __name__ == "__main__":
    trace = make_trace(bad_at=3, n=8)
    check = lambda t, i: t[i - 1]["state_ok"]   # mock 状态校验器
    step, replays = bisect_first_bad(trace, check)
    print(f"引入点:第{step}步,重放{replays}次")
    # 输出:引入点:第3步,重放3次

代码核心是循环里的两条分支:校验通过就把下界推到中点之后,校验失败就把上界压到中点。八步轨迹只需三次重放即可锁定引入点。

复杂度是对数级:n 步轨迹约需 log2(n) 次重放。一千步的轨迹约十次,一万步约十四次。相比线性逐段排查,成本下降两到三个数量级。

适用前提有两条。一是状态可判定:每个截断点都能回答好或坏。二是错误单调传播:一旦引入便持续存在,不会被后续步骤修正。

第二条不成立时二分会失准。错误在第 5 步被第 7 步洗掉,中点校验会误判后半段为好,定位漂移。这正是下一节依赖图方法要解决的问题。

4.3 依赖图归因:失败输出反查依赖步骤

依赖图归因不看时间顺序,看数据流向。每个步骤的输出引用了哪些前置输出,记录成边,就得到一张有向图。

归因方向是反向遍历:从失败输出出发,沿依赖边回溯,所有可达的步骤构成嫌疑集。嫌疑集与时间信息结合,最早的可疑步骤即引入点候选。

反查依赖

反查依赖

反查依赖

步骤1 建表

步骤3 聚合

步骤2 拉数

步骤4 过滤

步骤6 报表

失败输出

图 5:依赖图反查示意图

实线是正向数据流,虚线是归因时的反查方向。失败输出只依赖步骤 3 和步骤 4,步骤 1 和 2 通过传递闭包进入嫌疑集。反查排除不了步骤 2,但能排除与失败输出毫无数据关联的步骤。

维度二分定位依赖图归因
假设错误随时间单调传播错误沿数据流传播
成本约 log2(n) 次重放一次构图加图遍历
前提状态可判定、可重放每步输入引用可记录
失效场景错误被中途修正依赖记录缺失
强项定位首个引入点处理错误被洗掉的情况

表 6:两种归因方法对比

依赖图的价值在于处理恢复现象:错误发生后被后续步骤纠正。Faith and Fate 测得,约 82.3% 的正确答案内部仍含错误节点。二分面对这类轨迹会漏检,依赖图能保留完整痕迹。

依赖图的成本在记录侧:每个步骤都要登记输入来源。工程上通常在工具调用层统一埋点,把返回值编号并在后续引用时记录,侵入性可控。

同样要强调:归因依据是状态与工具返回,不是模型自述。前文提到的研究已经证明,思维链解释可能系统性地美化真实过程。让模型自己解释错在哪,得到的多半是合理化的故事,而不是因果链。

5. 执行不稳定与分支漂移

同一任务跑两次,一次成功一次失败,这不是玄学。程序性错误的另一个特征是不稳定,它改变了对与错的定义方式。

本章解释漂移的成因,再给出度量口径:对多步任务,成功率才是有意义的指标。

5.1 同一任务两次结果为何不同

根源在采样。温度大于零时,每一步的 token 生成都是概率采样,同一个上下文有多种延续。

多数位置上概率分布集中,采样结果稳定。到了分支点,两条路径的概率接近,微小扰动就会改变走向。这就是分支漂移。

采样偏向重试

采样偏向放弃

同一任务 起点

分支点:检索返回空

改写查询再检索 得到结果

拿空结果直接总结 产出错误

第一次执行:成功

第二次执行:失败

图 7:分支漂移示意图

同一个分支点,两次执行走出两条路。两条路的难度不同:重试路径信息充足,放弃路径先天缺氧。单次成败反映的是采样落点,不是模型能力。

分支点的谓词越模糊,漂移越明显。条件写成"结果不够好就重试",什么叫不够好没有量化,模型每次都要临场解释,解释随采样波动。

把条件改成显式谓词,比如命中数少于三即重试,漂移幅度立刻收窄。这也是第 6 章把谓词化条件列为修复手段的原因。

路径分歧的规模有实证数据。Self-Consistency 论文在 GSM8K 上测得:PaLM-540B 贪心思维链约 56.5,采样多条路径后多数投票约 74.4,绝对提升约 17.9 个百分点。

解码方式GSM8K 正确率
贪心思维链约56.5
多路径采样加多数投票约74.4
绝对提升约17.9个百分点

表 7:Self-Consistency 在 GSM8K 上的结果

提升的前提是路径分歧足够大且多数路径可对。换个角度看,这组数字也说明单条路径的成败波动巨大,一次执行的结论几乎没有统计意义。

5.2 稳定性检测器与成功率口径

度量不稳定需要主动检测:同一任务重复执行 k 次,统计成功率、答案分布与分支分歧。先看检测器实现。

# 来源:自实现 / stability_detector.py
# 稳定性检测器:重复执行并统计一致性与成功率
# mock 用固定分支序列代替真实采样,保证示例输出可复现

from collections import Counter

BRANCH_SEQ = [0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1]

def run_once(branch):
    if branch == 1:                      # 1 表示重试分支,产出正确答案
        return "answer_A", "retry"
    return "answer_B", "giveup"          # 0 表示放弃分支,产出错误答案

def stability(seq):
    """统计成功率、top答案占比与分支分歧情况"""
    results = [run_once(b) for b in seq]
    answers = Counter(a for a, _ in results)
    succ = sum(1 for a, _ in results if a == "answer_A")
    top, cnt = answers.most_common(1)[0]
    return {"执行次数": len(seq), "成功次数": succ,
            "成功率": round(succ / len(seq), 2),
            "top答案": top,
            "top答案占比": round(cnt / len(seq), 2),
            "分支种类数": len(set(b for _, b in results))}

if __name__ == "__main__":
    r = stability(BRANCH_SEQ)
    print("执行", r["执行次数"], "次,成功", r["成功次数"], "次,成功率", r["成功率"])
    print("top答案", r["top答案"], "占比", r["top答案占比"],
          "分支种类数", r["分支种类数"])
    # 输出:执行 20 次,成功 12 次成功率 0.6
    # 输出:top答案 answer_A 占比 0.6 分支种类数 2

一次示例运行的结果整理成指标表。

指标示例值含义
成功率0.6020 次执行中 12 次成功
top答案占比0.60最常见答案覆盖六成执行
分支种类数2两条分支都被走到过

表 8:稳定性检测示例指标

三个指标分工明确:成功率衡量能力水位,top 占比衡量输出一致性,分支种类数定位漂移是否发生在同一分叉点。分支种类数大于等于二而 top 占比低,说明漂移点就在主路径上。

由此得到口径修正:多步任务的对错必须用成功率表述,并附执行次数。20 次里 12 次成功,报告为 0.60,样本太少时置信区间过宽,结论不稳。

工程上建议最少 20 次,重要变更用 50 到 100 次。低于这个量级,把单次失败当回归、把单次成功当修复,都是在噪声里做决策。

稳定性数据还能反哺归因:把失败执行与成功执行在首个分歧步骤上对齐,分歧点即分支漂移点,通常就是需要谓词化的条件。

6. 按类型分治的修复

归因给出类型,修复按类型分治。不存在万能修法,五类错误对应五种手段,混用收效甚微。

本章先给映射总览,再落成路由器代码,最后讨论修复的能力边界。

6.1 修复手段与错误类型的映射

映射原则是针对性:每种错误错在哪一层,修复就做在哪一层。计划层的错补清单,时序层加守卫,分支层做谓词化,接口层上校验器,兜底层补异常分支。

步骤遗漏

补步骤清单

顺序颠倒

加顺序守卫

条件误判

谓词化条件

参数错传

参数校验器

异常路径缺失

异常分支补全

图 9:五类型到五手段的修复映射图

映射是一对一的:左列错误类型来自第 1 章分类器输出,右列是各自的修复手段。左列顺序即判定优先级,右列手段可以叠加使用。

错误类型修复手段落地形式生效条件
步骤遗漏补步骤清单计划模板加运行前勾选环节清单可枚举
顺序颠倒加顺序守卫每步声明前置条件依赖关系明确
条件误判谓词化条件自然语言条件改写为代码谓词条件可量化
参数错传参数校验器schema 校验加语义检查工具接口有规范
异常路径缺失异常分支补全空结果、超时、拒绝各配兜底异常类型可穷举

表 10:修复映射与生效条件表

第四列最容易被忽略:每种手段都有生效条件。条件不可量化的分支,谓词化做不了;接口没有规范文档的工具,校验器无从写起。

落地位置也有讲究:知识性内容放系统提示或示例里,判定性约束放运行时校验器里。提示词负责让模型通常做对,校验器负责在它做错时拦住,两层缺一不可。

顺序守卫的实现要点是前置条件断言:每个工具调用发出前,检查其声明的输入是否已就绪。不满足即拒绝执行并回退到最近检查点,代价是一次重放,收益是截断传播链。

6.2 修复策略路由器

映射表落成代码就是路由器:输入归因器给出的类型与证据,输出结构化修复方案。

# 来源:自实现 / repair_router.py
# 修复策略路由器:错误类型到修复方案的映射与证据回填

REPAIRS = {
    "步骤遗漏": ("补步骤清单", "环节写入计划模板,运行前比对勾选"),
    "顺序颠倒": ("加顺序守卫", "每步声明前置条件,缺项即拒发调用"),
    "条件误判": ("谓词化条件", "条件改写为显式谓词,由代码求值"),
    "参数错传": ("参数校验器", "按 schema 与语义双重校验实参"),
    "异常路径缺失": ("异常分支补全", "空结果超时拒绝各配兜底动作"),
}

def route(error_type, evidence):
    """返回修复方案,证据字段供回填到提示词或校验器"""
    plan = REPAIRS.get(error_type)
    if plan is None:
        return {"手段": "人工分析", "动作": "补充判定规则", "证据": evidence}
    return {"手段": plan[0], "动作": plan[1], "证据": evidence}

if __name__ == "__main__":
    for etype, ev in (("顺序颠倒", "写入先于鉴权执行"),
                      ("异常路径缺失", "检索超时后未触发兜底")):
        r = route(etype, ev)
        print(etype, "->", r["手段"])
        print("  动作:", r["动作"])
        print("  证据:", r["证据"])

路由器的价值在证据字段。归因器产出的证据,比如"写入先于鉴权执行",要原样回填:写进守卫规则作为断言条件,或写进提示词作为反例。

回填后形成闭环:错误发生,归因定位,路由给出手段,证据变成新规则,同类错误下次被拦截。五类错误各自沉淀规则,拦截线越修越密。

未命中的类型走人工分析出口。这不是失败分支,而是规则体系的扩张入口:人工判定后把新类型补进映射表,路由器随迭代生长。

6.3 修复的边界:运行时守卫救不了能力缺失

分治修复有明确的能力边界:它提升稳定性,不创造流程知识。模型根本没学过某个流程时,守卫只会让它反复失败在被拦截的位置。

实证数据来自 AgentTuning。Llama-2-70B-chat 在多个智能体任务上近乎零分,错误分析显示多数是无效指令与重复这类初级错误。

模型ALFWorld知识图谱任务备注
Llama-2-70B-chat约6.0约0.0初级错误为主
AgentLM-70B约86.0约47.0轨迹微调后
GPT-3.5约14.0约27.2当时商用基线

表 11:AgentTuning 关键对比数据

ALFWorld 属于 AgentLM 的训练内任务,数字不能直接外推。更有说服力的是 held-out 任务:总分相对提升约 176%,70B 版本接近 GPT-3.5 的水平。

这组数据划出了修复的分界线:初级程序性错误靠轨迹数据微调根治,提示词与守卫只能缓解。第 1 章分类器输出的错误分布,可以直接当作微调数据的选题依据:哪类错误高频,就重点采集哪类正确轨迹。

外部交互结构也在改进。ReAct 在 HotpotQA 上的表现:标准提示约 28.7,纯行动约 25.7,ReAct 约 27.4,与自洽结合约 35.1。在 ALFWorld 上,少样本 ReAct 比在约一千到十万条任务上训练的模仿与强化学习方法高约 34 个百分点。

两篇论文合起来的结论:结构化的推理与行动交织,配合外部观察校准,能显著减少事实幻觉与传播型错误。这属于流程结构的改进,与运行时守卫、轨迹微调互补而非互替。

成本维度上,三种修复的代价排序大致是:运行时守卫最轻,单次调用开销远低于整条轨迹重放;提示词改造次之;轨迹微调最重,但只有它能处理能力缺失。选型顺序也就明确了:先上守卫止血,再改提示词与结构,高频且守卫无效的错误最后走微调。

7. 总结

程序性错误是知道是什么、做错怎么做的失败,分五型:漏步骤、乱顺序、错分支、错参数、无兜底。判定口诀是两段式:先逐步核对事实,事实全对而结果错,必为流程错。

量化的核心是乘法衰减:单步错误率 p 下,n 步联合成功率约为 (1-p) 的 n 次方,减半步数约 0.693 除以 p。长流程先砍步数,再谈单步质量。

归因区分引入点与暴露点:二分定位约 log2(n) 次重放找首个引入点,依赖图反查处理错误被洗掉的轨迹,两者互补。依据永远是状态与工具返回,不是模型自述。

不稳定的任务是采样随机性在分支点的漂移,正确口径是成功率加执行次数。修复按类型分治:清单、守卫、谓词、校验器、兜底分支各管一层,能力缺失则交给轨迹微调,结构改进交给推理与行动的交织设计。

外部引用

Logo

更多推荐