我做的 AI Agent 干活又快又利索、可交出来的东西总在一些低级地方出错:算术算错、漏掉任务明确要求的一个环节、格式不符规定,而它对这些错误浑然不觉,排查很久才意识到它从生成完那一刻起就再没回头看过自己的产出、压根没拿结果对照过最初的目标
这是一次让我把 AI Agent 的工作流,从"让它把活干完",重新理解成"让它把活干完、再回头验一遍才算数"的事故。我做的 Agent 干活又快又利索,接到任务一口气就做完、把结果交了上来。可交出来的东西,总在一些让人无语的低级地方出错:一份汇总报告里的数字加错了、任务里白纸黑字要求的某个环节它直接漏掉了、输出格式跟规定的不一样。更让我不解的是,它对这些错误浑然不觉,信心十足地把带着错的结果当成最终答案交付。我排查了很久才意识到一件关键的事:它从"生成完"那一刻起,就再也没有回头看过自己的产出,压根没拿结果对照过最初的目标和要求。这篇就把这次"Agent 做完了却没做对、还不自知"的事故,从头到尾复盘一遍。
故障现场:结果又快又自信,却带着自己毫不知情的错
我的 Agent 流程很直接:接收任务 → 理解 → 调工具/生成内容 → 输出结果。比如"根据这份销售数据,生成一份包含总额、环比、和 Top3 产品的月报",它会读数据、算一算、写成一段报告输出。流程跑得飞快,看起来很专业。
可问题反复出现:有一次它报告里的"总额"把几个分项加错了(少加了一项);有一次任务明确要"Top3 产品",它只列了 Top2 就收尾了;还有一次要求输出 JSON,它输出了一段带 Markdown 包裹的文本。这些错误都不隐蔽,但凡拿结果回头对一遍任务要求就能发现。可 Agent 没有发现——它生成完报告,就直接当成最终答案交了,中间没有任何"我再核对一下"的动作。我一开始以为是模型能力不行,换了更强的模型,低级错误是少了点,但"偶尔漏一项、偶尔格式错、却毫不自知"的毛病依旧。直到我把它的执行轨迹一步步看下来,才看清根因——它的流程里,从来就没有"生成之后、交付之前,拿产出对照目标自查一遍"这个环节;它把"生成出一个答案"直接等同于"交付一个正确答案"了。
# 我最初的 Agent: 生成即交付, 中间没有任何自我验证
def run_agent(task):
plan = understand(task)
result = generate(plan) # 生成出一个"看起来合理"的结果
return result # ★ 直接交付! 没有回头对照 task 检查对不对
# 问题:
# - generate 的产出是"差不多对", 不是"核对过确实对"
# - 算术错、漏环节、格式不符 —— 这些只要拿 result 对一遍 task 就能发现
# - 但流程里压根没有"对一遍"这一步, 错误就这样原样交付了
# - Agent 对自己的错"毫不知情", 因为它从没被要求回头看
# 我的错误假设: "模型生成出来的, 就是对的" —— 把"做完"当成了"做对"
问题被钉死在这个缺失的环节上:我的 Agent 流程里只有"生成",没有"验证";而大模型的一次生成,本质是"产出一个看起来合理、大概率对"的结果,它不保证这个结果真的满足了任务的每一条要求。"生成出一个答案"和"这个答案正确"之间,隔着一道"拿答案对照要求逐条核对"的工序;而我把这道工序完全省掉了,默认"它既然生成出来了,那就是对的"。于是那些只要回头看一眼就能逮住的错误——加错的数、漏掉的 Top3、错误的格式——全都毫无阻拦地交付了出去。我让 Agent "做完了",却从没让它"检查自己做得对不对"。
第一件事:想明白"生成"和"做对"之间,缺了"验证"这一步
把这次事故彻底想清楚,关键是理解大模型的"生成"是一个"尽力产出一个合理答案"的过程,它优化的是"看起来对、读起来顺",而不是"逐条满足了任务的硬性要求";要让产出真的可靠,必须在"生成"之后补上一个独立的"验证/反思"环节——拿产出对照原始目标和要求,逐条核对、挑错、若有错就修正,通过了才交付。
人做复杂的事,天然会有"做完再检查一遍"的习惯:算完账会再核一遍数、写完报告会回头读一遍看有没有漏项、交东西前会对着要求清单打勾。这个"回头自查"的动作,是质量的最后一道闸。但 Agent 不会自发地做这件事——除非你显式地把它设计进流程里。它的一次生成,就像人"凭着感觉一口气写完",写的时候自信满满,但没回头检查,就难免有自己没察觉的疏漏。"生成"解决的是"有没有产出",而"验证"解决的是"产出对不对";这是两个不同的目标,需要两个独立的步骤,不能指望一次生成就把两件事都包圆了。我的 Agent 缺的,正是这后一步。
# 关键认知: "生成" 和 "验证" 是两个独立的目标, 需要两个步骤
# 一次 generate 内部, 模型在优化的是:
# "产出一段看起来合理、符合语言习惯、大概率正确的内容"
# 它【没有】在专门优化:
# "逐条核对任务的每一个硬性要求是否都被满足了"
# 所以需要把流程从 [生成→交付] 改成 [生成→验证→(不过则修正)→交付]:
def run_agent(task):
plan = understand(task)
result = generate(plan)
# ★ 补上验证环节: 拿 result 对照 task, 逐条核对
issues = verify(result, task) # 算术对吗? 要求的环节齐吗? 格式对吗?
while issues: # 有问题就修正, 再验证, 直到通过
result = revise(result, issues, task)
issues = verify(result, task)
return result # 验证通过了, 才交付
# verify 在问什么(本质是把"做对"显式地检查出来):
# - 任务要求的每一项, result 里都有吗? (漏环节?)
# - 数字/计算 经得起复算吗? (算错?)
# - 输出格式/结构 符合规定吗? (格式不符?)
# - 有没有偏离最初的目标? (跑题?)
想通这一层,我才明白自己错在哪:我把 Agent 的"一次生成",当成了一个"既负责产出、又负责保证正确"的全能步骤,但它其实只负责前者。"做完"和"做对"之间,永远隔着"检查"这一步;人会本能地补上它,Agent 不会,除非我把它明确地设计进流程。我省掉这一步,等于让 Agent 凭着第一感觉交活、从不复核——再强的模型,在"没人(包括它自己)回头看一眼"的流程里,也一定会把那些"一查就出"的错,原样交到用户手上。产出不等于正确,中间那道"验证"的闸,省不得。
第二件事:正解——加一个显式的"反思/自我验证"闭环
找到根因,正解就清晰了:在 Agent 的"生成"和"交付"之间,插入一个显式的"验证/反思"环节(generate → verify → revise → 再 verify 的闭环)——让它(或一个独立的 critic 角色)拿产出对照原始任务的每一条要求逐项核对,把硬性约束写成可检查的清单,发现问题就带着问题去修正、再验证,直到通过才交付。能用确定性手段(代码、规则、schema)验的,就别只靠模型自评。
# 正解: generate-verify-revise 闭环, 把"做对"显式地兜住
def run_agent(task, max_rounds=3):
plan = understand(task)
result = generate(plan)
for _ in range(max_rounds):
issues = verify(result, task) # 逐条核对要求
if not issues:
return result # 验证通过, 交付
result = revise(result, issues, task) # 带着问题去修正
# 几轮仍不过 → 别硬交, 标记需人工介入或返回最佳努力+已知问题
return finalize_with_warnings(result, issues)
# verify 的两种实现, 优先用确定性的:
def verify(result, task):
issues = []
# ① 确定性检查(最可靠): 能用代码/规则/schema 验的, 绝不只靠模型
if task.want_json and not is_valid_json(result):
issues.append("输出不是合法 JSON")
if task.required_sections:
for sec in task.required_sections: # 要求的环节是否齐全
if sec not in result:
issues.append(f"缺少要求的环节: {sec}")
if task.has_numbers:
if not recompute_matches(result, task.data): # 数字复算核对
issues.append("数字与源数据复算不一致")
# ② 模型自评/独立 critic(补确定性检查覆盖不到的语义层面)
issues += llm_critic(result, task) # "对照目标, 找出问题; 没问题就回 OK"
return issues
这套做法的精髓,是把"生成"和"验证"拆成两个独立的职责,并用一个"不通过就修正、修正完再验"的闭环,把质量真正兜住。验证环节优先用确定性手段(能跑代码复算的就复算、能用 schema 校验格式的就校验、能列清单核对环节齐不齐的就核对)——这些比模型自评可靠得多;确定性手段覆盖不到的语义层面(论证是否站得住、是否跑题),再用模型自评或一个独立的 critic 角色补上。关键是让"对照目标自查"成为流程里一个雷打不动的步骤,而不是可有可无的运气。不是寄望于一次生成就完美,而是承认第一版常有疏漏、用验证闭环把疏漏在交付前逮住并修掉。
【给 Agent 加质量闭环, 我现在认死的几条】
1. 生成 ≠ 正确: "做完"和"做对"是两步, 中间隔着"验证"
2. 在 生成 和 交付 之间, 必须有一个显式的 验证/反思 环节
3. 验证 = 拿产出逐条对照任务的硬性要求(环节齐?算对?格式合?跑题?)
4. 能用确定性手段验的(代码复算/schema/清单), 绝不只靠模型自评
5. 不通过就 修正 再 验证, 形成闭环; 但要有轮数上限防死循环
6. 几轮仍不过别硬交: 标记人工介入, 或返回结果+已知问题清单
7. 独立 critic 比"自己夸自己"更可靠: 让验证角色带着挑刺的视角去看
第三件事:其他"只管产出、不管验证产出对不对"的同类坑
顺着"产出不等于正确、缺了验证这一步"这条线,我把 Agent 系统里同类的坑都排查了一遍,它们都源于"把'生成了/做了'当成了'做对了'":
第一个,调用工具后不检查工具的返回是否成功/合理。Agent 调了个工具拿到返回就往下用,可那个返回可能是报错、是空、是不合预期的格式;不验证就用,错误会一路传染下去。
第二个,多步任务每步不校验就进入下一步。前一步的产出有问题,后一步基于它继续做,错误层层放大,最后整个结果崩坏,却很难定位是哪一步先错的。
第三个,生成代码不跑测试就当完成。Agent 写完代码直接说"完成了",但没编译、没跑测试;能不能运行、对不对,全靠猜——而这恰恰是最容易确定性验证的。
第四个,检索到信息不核实就直接采信。RAG/搜索拿到的内容可能不相关、过时、甚至矛盾,Agent 不加判断就拿来生成答案,把垃圾输入变成自信的错误输出。
第四件事:无验证 vs 有验证闭环——一张对照表
我把"生成即交付"和"生成→验证→修正闭环"摆在一起对比,核心看"错误能不能在交付前被逮住":
| 维度 | 生成即交付(无验证) | 生成→验证→修正(有闭环) |
|---|---|---|
| 对待第一版产出 | 当成最终正确答案 | 当成待核对的草稿 |
| 低级错误(算错/漏项/格式) | 原样交付, 毫不自知 | 验证时被逮住并修正 |
| 错误暴露的时机 | 用户使用时才发现 | 交付前自己先发现 |
| 对模型的依赖 | 全押在"一次生成就对" | 承认会错, 用闭环兜住 |
| 失败时的行为 | 自信地交付错误结果 | 几轮不过则标记人工介入 |
| 质量稳定性 | 靠运气, 时好时坏 | 有下限, 硬性要求被保证 |
看清这张表,设计就有谱了:把第一版产出当"草稿"而非"定稿",在交付前用验证闭环逐条核对硬性要求、不过就修正,能确定性验证的就别靠模型自评。我这次踩坑,就是把第一版直接当定稿交付,押注"一次生成就对",而模型恰恰不保证这件事。验证闭环不是不信任模型,而是承认"任何一次生成都可能有疏漏"这个事实,并为它准备好一道兜底的闸。
第五件事:我曾经对 Agent"生成即正确"想当然的几个误区
这次事故也把我对 Agent 产出的一堆"想当然"照了个底朝天:
| 我以为 | 实际上 |
|---|---|
| 模型生成出来的结果就是对的 | 生成优化的是"看起来合理", 不保证逐条满足硬性要求 |
| 换更强的模型就不会出低级错 | 能力上限高了, 但没人回头查, 偶发疏漏照样原样交付 |
| Agent 会自己发现明显的错误 | 除非显式设计验证环节, 它生成完就不再回头看 |
| 让模型"自己说对不对"就够了 | 能确定性验的(算术/格式)用代码更可靠, 自评会漏会偏 |
| 加验证太麻烦, 多数时候没必要 | 正是"多数对、偶尔错且不自知"最坑, 闭环才能保下限 |
这些误区的根子是同一个:我把"Agent 产出了一个结果"默认成了"这个结果是正确的",而没意识到"产出"和"正确"之间,缺了"验证"这道必经的工序。当我让 Agent 一口气生成完就交付时,我其实是在赌"它这一次没出错";而对一个偶尔会算错、会漏项、会格式跑偏的生成过程来说,这个赌注迟早会输,输的代价就是用户收到一个 Agent 自己都不知道错在哪的结果。给产出补上一道验证的闸,本质是把质量从"靠运气"变成"有保证"。
第六件事:设计 Agent、排查"产出有低级错却不自知"时,我现在的自检习惯
现在每当我设计 Agent 流程、或排查"Agent 交付的结果带着它自己不知道的错",我都会先按这张图问自己:
这张图的精髓,是"生成不等于正确;在生成和交付之间,必须有一个对照目标逐条核对的验证环节,能确定性验的别只靠自评"。设计就把流程做成 生成→验证→修正 的闭环、把硬性要求拆成可检查清单、优先用代码/schema 做确定性验证、排查就先看流程里到底有没有"交付前回头对照目标自查"这一步。这套习惯,让我从"让 Agent 把活干完"变成了"让 Agent 干完再验过才算数"——核心始终是:大模型的一次"生成"本质是产出一个"看起来合理、读起来通顺、大概率正确"的结果,它优化的是表面的合理性,而不保证逐条满足了任务的每一项硬性要求(要求的环节是否齐全、数字计算是否经得起复算、输出格式结构是否符合规定、有没有偏离最初目标);所以"生成出一个答案"和"这个答案正确"是两件事,中间永远隔着一道"拿产出对照原始目标和要求逐条核对"的验证工序——人做事会本能地补上这道闸(算完再核、写完再读、交付前对清单打勾),而 Agent 不会自发地做、除非你把它显式地设计进流程;正解是在生成与交付之间插入一个显式的反思/自我验证环节,形成 generate→verify→(不过则)revise→再 verify 的闭环,把硬性要求拆成可检查项,能用确定性手段(代码复算、schema 校验、清单核对)验的绝不只靠模型自评、确定性覆盖不到的语义层面再用独立 critic 补上,并设轮数上限、几轮仍不过就标记人工介入而非自信地交付错误结果。
我立下的几条规矩
这场"Agent 做完了却没做对、还不自知"的事故,换来了我设计 Agent 时,刻进骨子里的几条铁律:
- 生成 ≠ 正确:"做完"和"做对"是两步,中间必须隔着"验证"。
- 在生成和交付之间,放一个雷打不动的"对照目标自查"环节。
- 把任务的硬性要求拆成可检查清单:环节齐?算对?格式合?跑题没?
- 能用代码/schema/复算确定性验证的,绝不只靠模型自己说对不对。
- 不通过就带着问题修正、再验证,形成闭环;但设轮数上限防死循环。
- 几轮仍不过别硬交:标记人工介入,或返回结果+已知问题清单。
- 把第一版产出当草稿而非定稿;独立 critic 比自己夸自己更可靠。
附:我现在给 Agent 落地"生成-验证-修正"质量闭环的骨架
这是我现在给 Agent 加质量闭环固定套的骨架——把这次踩坑的教训(生成与验证分离、确定性验证优先、不过则修正、设上限)固化成一套结构,让"做完没做对还不自知"那种坑再不会埋进流程:
from dataclasses import dataclass, field
@dataclass
class Check:
name: str
fn: callable # 返回 None 表示通过, 返回字符串表示问题描述
deterministic: bool # 是不是确定性检查(代码/schema), 优先级更高
class VerifiedAgent:
def __init__(self, checks, max_rounds=3):
# 确定性检查排在前面, 优先暴露硬错误
self.checks = sorted(checks, key=lambda c: not c.deterministic)
self.max_rounds = max_rounds
def run(self, task):
result = self.generate(task)
for round_no in range(1, self.max_rounds + 1):
issues = [msg for c in self.checks
if (msg := c.fn(result, task)) is not None]
if not issues:
return {"ok": True, "result": result, "rounds": round_no}
# 带着具体问题去修正, 而不是重新瞎生成
result = self.revise(result, issues, task)
# 几轮仍不过: 不硬交, 交还人工 + 暴露已知问题
return {"ok": False, "result": result, "issues": issues,
"need_human": True}
# 用法: 把任务的硬性要求, 写成一条条可执行的检查
checks = [
Check("合法JSON", lambda r, t: None if is_json(r) else "输出非合法JSON", True),
Check("环节齐全", lambda r, t: missing_sections(r, t), True),
Check("数字复算", lambda r, t: None if recompute_ok(r, t) else "数字对不上", True),
Check("未跑题", lambda r, t: llm_critic_offtopic(r, t), False), # 语义层靠 critic
]
agent = VerifiedAgent(checks)
out = agent.run(task)
if not out["ok"]:
escalate_to_human(out) # 验证没过, 宁可交人工, 不自信地交付错的
这套骨架把我这次的教训钉死在了结构里:每一条硬性要求都写成一个可执行的检查、确定性检查(JSON 合法、环节齐全、数字复算)排在前面优先暴露、语义层面才交给 critic;生成出的结果先当草稿过验证、不通过就带着具体问题去修正再验、几轮仍不过就交还人工而非硬着头皮交付。这样,那些"一查就出"的低级错(漏 Top3、加错总额、格式不符),在交付前就被这道闸逐条逮住,而不再是当初那个"生成完就自信交付、错了也不自知"的裸奔流程。把"区分做完与做对、永远在产出和交付之间补一道验证"这个道理,沉淀成 Agent 的固定骨架,这是我对这次"带病交付"最实在的交代——毕竟,真正决定用户拿到的东西对不对的,不是 Agent 生成得多流畅,而是那道交付前认真回头核对的闸。
写在最后
回头看,这场由"缺少自我验证"引发的"Agent 自信地交付带错结果"事故,真正教给我的,远不止"加一个 verify 步骤"这一个技巧。它让我对"'把一件事做出来'和'把一件事做对',从来都不是同一件事;在'产出'和'正确'之间,永远横着一道叫'检查/验证'的工序——而这道工序,恰恰是最容易在'看起来已经做完了'的成就感里、被悄悄省略掉的一步",有了一次刻骨的体会。我栽跟头,是因为我把"Agent 产出了一个结果"默认等同于了"这个结果是对的"——我被它"又快又利索、自信满满"的样子骗了,以为流畅的产出就意味着可靠的质量;我没意识到,它的"生成"追求的是"产出一个看起来合理的东西",而不是"逐条核对过、确认满足了每条要求";这两个目标之间,差的正是"回头拿结果对照一遍目标"这道工序;而我的流程里压根没有这道工序,于是 Agent 就像一个"凭感觉一口气写完、从不回头检查就交卷"的人,那些一查就出的低级错,自然原样落在了卷面上、还浑然不觉。这让我领悟到一个关于"产出与正确、做完与做对"的深刻认知:任何创造性的产出过程(生成、制造、执行、表达),其本身都只对"产出一个东西"负责,而不天然对"这个东西正确无误、满足了全部要求"负责;后者需要一道独立的、专门的"验证"工序——拿产出去对照最初的目标和约束,逐条核对、挑错、修正;这道验证工序是质量的最后一道闸,也是最容易被省略的一道:因为"东西已经做出来了"带来的完成感,会强烈地诱使人(和 Agent)直接交付,跳过那个"再回头看一遍"的、看似多余实则关键的步骤;而真正决定产出可靠性的,往往不是"生成得多漂亮",而是"有没有一道认真的验证把疏漏挡在交付之前"——尤其当产出者自己对疏漏毫无察觉时,这道外部于"生成"的检查,就是唯一的防线。这给了我一种看待"一切'产出/交付'之事"时的清醒:每当我(或我的系统)产出了一个东西、准备交付时,要追问"我只是把它'做出来了',还是真的拿它对照过要求、确认它'做对了'?在产出和交付之间,有没有一道认真的验证?那些一查就能发现的硬性要求(齐不齐、对不对、合不合规),我核对过了吗"——把"生成"和"验证"当成两个独立的步骤,绝不让完成的成就感诱使我跳过回头自查那一步;"区分'做完'与'做对'、在产出和交付之间永远补上一道验证的闸",是做对 Agent、也是做对一切产出之事的关键。认清生成不等于正确、做完和做对中间隔着验证、要把自查显式设计进流程——这,是我用一次 Agent 自信交付带错结果的事故,换来的、关于 AI Agent、也关于如何对待自己每一份产出的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次设计 Agent、或自己交付一份东西前,先停一下问"我验证过它真的对了吗,还是只是把它做完了?",并补上那道回头核对的工序,那我对着那些 Agent 自信交付、却带着低级错的结果挠头的许多个夜晚,就值了。
—— 别看了 · 2026