AI 不报错也可能做错:用上下文、检查清单与独立复核拦截静默失败

2026-08-21 21 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

AI 系统最棘手的问题往往不是抛出异常,而是顺利返回一段看似合理、实际上遗漏约束或缺少证据的结果。这类“静默失败”会绕过传统监控:HTTP 状态码是 200,JSON 可以解析,日志里没有堆栈,但业务结论已经偏离目标。

围绕“上下文、检查清单和无遗漏复核”组织 Agent 工作流,关键不是让模型多思考一次,而是把生产、审查和放行拆成可观察、可验证的步骤。

为什么成功响应不等于任务成功

传统服务通常有清楚的失败信号:超时、异常、非零退出码或违反数据库约束。AI 输出的失败边界则模糊得多,常见情况包括:

  • 遗漏约束:回答了主要问题,却忽略日期范围、格式要求或禁止事项。
  • 未经证实的断言:语言很确定,但没有引用输入材料中的证据。
  • 上下文漂移:多轮执行后,Agent 开始优化局部步骤,忘记最终交付目标。
  • 工具调用不完整:搜索或读取操作成功,但没有覆盖全部对象。
  • 审查同源偏差:同一个模型用相近提示词复核自己的结果,重复了原有判断。

因此,监控不能只问“调用是否成功”,还要回答三个问题:任务要求是否完整传递,输出是否逐项满足要求,以及复核过程是否真正覆盖了高风险部分。

把上下文变成可执行契约

长提示词不等于可靠上下文。更实用的方式是把上下文拆成结构化字段,让执行器和审查器读取同一份任务契约:

job_id: report-2025-03
objective: 根据给定材料生成季度风险摘要
inputs:
  - quarterly_report.txt
constraints:
  language: zh-CN
  max_words: 500
  require_evidence: true
  forbidden_claims:
    - 不得推测未披露的财务数据
acceptance_checklist:
  - 覆盖运营、财务和合规风险
  - 每项风险包含输入材料中的证据
  - 明确区分事实与建议
  - 不包含输入材料之外的数字
review_policy:
  minimum_score: 4
  block_on_missing_evidence: true

这份契约有两个作用。一方面,它减少自然语言指令在多 Agent 之间传递时的损耗;另一方面,它给自动化验收提供稳定字段。生产 Agent 可以自由组织文字,但不能自由解释什么叫“完成”。

对于长流程,还应记录上下文版本、输入材料摘要和工具结果。否则,即使发现错误,也很难判断是模型推理失误、检索遗漏,还是上游输入发生了变化。

检查清单要产生证据,而不是只打勾

“请检查答案是否正确”通常只能得到宽泛评价。更有效的复核要求审查器逐项返回状态、证据和问题位置。例如:

{
  "checks": [
    {
      "id": "evidence",
      "passed": false,
      "evidence": [],
      "issue": "财务风险段落没有引用输入材料"
    }
  ],
  "approved": false
}

这里最重要的字段不是 approved,而是每项检查的证据。没有证据的“通过”只能算模型意见,不能算验收结果。

检查项也不宜无限增长。可以把确定性规则交给代码,例如字段完整性、字数、引用格式和数值范围;把语义判断留给模型,例如结论是否被材料支持、是否遗漏关键风险。代码检查便宜且可重复,模型复核则用于处理无法简单编码的边界。

可以这样实践:构建一个失败即阻断的最小工作流

下面示例只使用 Python 标准库,可以直接运行。为保持示例独立,generate_draft() 模拟生产 Agent,semantic_review() 模拟模型审查器;接入真实系统时,可以将这两个函数替换为模型 API 调用,同时保留任务契约、确定性检查和放行逻辑。

from dataclasses import dataclass, field
from typing import List


@dataclass
class TaskContract:
    required_sections: List[str]
    max_words: int
    require_evidence: bool = True


@dataclass
class Draft:
    text: str
    evidence: List[str] = field(default_factory=list)


@dataclass
class ReviewResult:
    approved: bool
    issues: List[str]


def generate_draft(source: str) -> Draft:
    # Replace this function with a worker-agent API call.
    return Draft(
        text=(
            "运营风险:关键供应商交付延迟。\n"
            "财务风险:成本可能继续上升。\n"
            "合规风险:新规要求更新数据保留流程。"
        ),
        evidence=["材料第 2 节:关键供应商平均延迟 12 天"]
    )


def deterministic_review(draft: Draft, contract: TaskContract) -> List[str]:
    issues = []
    for section in contract.required_sections:
        if section not in draft.text:
            issues.append(f"缺少必要章节:{section}")

    if len(draft.text.split()) > contract.max_words:
        issues.append("输出超过字数限制")

    if contract.require_evidence and not draft.evidence:
        issues.append("没有提供证据")

    return issues


def semantic_review(source: str, draft: Draft) -> List[str]:
    # Replace this function with an independent reviewer-agent API call.
    issues = []
    if "成本可能继续上升" in draft.text and "成本" not in source:
        issues.append("财务风险包含输入材料无法支持的推测")
    if len(draft.evidence) < 3:
        issues.append("并非每项风险都有对应证据")
    return issues


def run_job(source: str, contract: TaskContract) -> ReviewResult:
    draft = generate_draft(source)
    issues = deterministic_review(draft, contract)
    issues.extend(semantic_review(source, draft))

    # Fail closed: unresolved findings prevent publication.
    result = ReviewResult(approved=not issues, issues=issues)
    print("DRAFT:\n", draft.text)
    print("\nAPPROVED:", result.approved)
    for issue in result.issues:
        print("-", issue)
    return result


if __name__ == "__main__":
    source = (
        "材料第 2 节:关键供应商平均延迟 12 天。"
        "材料第 5 节:新规要求更新数据保留流程。"
    )
    contract = TaskContract(
        required_sections=["运营风险", "财务风险", "合规风险"],
        max_words=500,
    )
    run_job(source, contract)

运行命令:

python oversight.py

该示例会阻止草稿通过,因为财务结论缺少来源支持,而且三项风险没有分别提供证据。真实工作流还应保存草稿、检查结果、模型版本、提示词版本和输入哈希,以便重放与审计。

独立复核也有边界

增加 Reviewer Agent 并不自动消除错误。如果生产者与审查者使用相同模型、相同上下文和相似提示词,它们可能共享盲点。高风险任务可以采用不同复核层:代码验证硬约束,独立模型检查语义,人工审批处理高影响决策。

同时要防止“无限复核”。Agent 反复修改和审查会增加成本,也可能在修复一个问题时引入另一个问题。更稳妥的策略是限制重试次数,为问题分级,并在连续失败后转交人工处理。

上线前的验收清单

一套可落地的监督机制至少应满足这些条件:

  • 任务目标、输入、约束和验收项采用结构化格式保存。
  • 每个检查项都返回证据或明确的问题描述,而不只是布尔值。
  • 确定性规则由代码执行,语义问题再交给模型判断。
  • 未解决的高风险问题默认阻断发布。
  • 生产与复核步骤分别记录模型、提示词、输入和输出版本。
  • 工作流设置最大重试次数,并提供人工升级路径。
  • 使用真实失败案例建立回归测试集,而不是只测试理想输入。

AI 静默失败无法靠一句更强的提示词彻底解决。真正有效的监督来自工程化边界:明确什么算完成,要求每次通过都有证据,并让失败在进入业务系统之前变得可见。


相关推荐