news 2026/9/2 8:02:41

【 长任务 Agent 为什么总会跑偏?我用“三平面分治”重构了一个 200 张发票审核 Agent】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【 长任务 Agent 为什么总会跑偏?我用“三平面分治”重构了一个 200 张发票审核 Agent】

如果你做过长任务 Agent,大概率见过这种情况:

任务跑到一半,Agent 突然“钻牛角尖”——揪着一个无关紧要的细节死磕几十轮,预算烧掉一半,主线任务却纹丝不动。

还有一种更隐蔽:

Agent 最后告诉你“任务完成”,但回头一查,还有三分之一根本没处理,只是 Agent 自己没有意识到。

很多时候,问题不是模型不够聪明,而是任务状态没有被正确管理

这篇文章用一个具体场景——批量审核 200 张供应商发票——看看长任务 Agent 为什么会跑偏,以及我怎么用“三平面分治”解决它。

本文的理论框架参考了黄佳《AI Agent 设计模式》专栏中的“进度追踪”一讲,在此基础上结合具体业务场景做了案例化拆解。理论部分建议阅读原文。


先说结论:Todo List 为什么不够?

很多人第一反应,是给 Agent 加一个todo.md

- [x] 审核发票 1-50 - [x] 审核发票 51-100 - [ ] 审核发票 101-150 - [ ] 审核发票 151-200

短任务没什么问题,但任务一长,很快会暴露三个问题:

  1. 目标漂移:Agent 越来越沉浸在局部问题里,忘了最初到底要完成什么。

  2. 事实污染:ID、金额、账号等关键数据,被 Agent 在自然语言上下文中反复复制、转述,容易串错。

  3. 完成幻觉:Todo 勾完了,不代表任务真的完成了。

所以我更倾向于把 Agent 的状态拆成三个平面:

平面负责什么一句话理解
目标要做什么、不能做什么、什么算完成我要干什么?
事实ID、金额、账号、工具返回值等真实数据真实情况是什么?
进度做到哪了、哪些完成、哪些阻塞我干到哪了?

这里的“三平面”不是说一定要拆成三个数据库,而是把三种不同性质的信息分开管理

核心原则其实只有一句话:

LLM 负责判断,程序负责记真值,编排器负责推进任务。

下面用 200 张发票跑一遍。


一、先把目标冻成契约,而不是直接开干

用户说:

“帮我把上个月的供应商发票核对一遍,没问题的生成付款批次。”

如果直接把这句话扔给 Agent,它很容易自行扩大任务边界。

所以第一步不是调用工具,而是先把目标明确下来:

success_criteria:-只处理 2026 年 7 月开票、状态为“待审核”的发票-金额与采购订单(PO)匹配才能放行-生成付款批次,但不实际发起转账non_goals:-不处理历史遗留的争议发票-不修改供应商主数据-不直接调用银行接口打款

这里最重要的其实是non_goals

比如某张发票发现供应商银行账号疑似发生变化,Agent 很可能会想:

“既然发现问题,那我顺便去供应商系统确认一下,再更新账号。”

看起来很合理。

但这已经超出了当前任务。

所以:

目标不仅要定义“做什么”,还要定义“不做什么”。


二、第 60 张发票:Agent 开始钻牛角尖

处理到第 60 张时,遇到一个问题:

发票上的 PO 号是:

PO-2026-0088

系统里的 PO 号却是:

PO2026088

Agent 开始认真研究:

  • 查历史记录

  • 猜测编码规则

  • 分析是不是系统升级导致的

  • 尝试重建匹配算法

然后一轮又一轮地调用工具。

没有进度追踪

Agent 可能花 40 轮把这个问题解决了。

但此时:

已完成:60 / 200 剩余:140

预算却已经烧掉了一大截。

有进度追踪

这时候可以增加一个简单的漂移检测机制

例如每 10 步检查一次:

最近 8 个动作: 都围绕同一张发票 里程碑: 60 / 200 进度: 没有推进

于是系统判断:

WATCH

然后触发一次复诵

当前里程碑是完成 200 张发票的匹配核验,不是解决单张发票的编码问题。PO 号格式异常的发票记入待人审清单,继续下一张。

Agent 于是把这张发票标记为:

needs_review

记录下来,继续处理下一张。

这里的关键不是“阻止 Agent 思考”。

而是:

允许它解决问题,但不允许一个局部问题吞掉整个任务。


三、第 95 张:Ledger 不是 Todo,而是决策账本

第 95 张发票又出现了问题:

发票金额比 PO 金额高了 3%。

Agent 判断:

合同允许一定范围的金额浮动,因此可以放行。

如果只记录:

INV-20260795 ✓

以后几乎没有追溯价值。

所以进度账本记录的应该不是简单的“做没做”,而是:

做了什么判断,为什么这么判断,依据是什么。

例如:

event:发票 INV-20260795 金额超出 PO 3%decision:判定为合同允许浮动范围内,放行reason:采购合同条款 §4.2 允许 ±5% 浮动evidence_refs:-tool:fetch_po#0795-contract/vendor-acme-2026.md#section-4.2state_delta:write:-STATE.batch_approved_invoices

这样一周之后,如果财务发现这批发票整体超支,可以直接追溯:

哪些发票被放行? ↓ 为什么放行? ↓ 依据了哪条合同? ↓ 这个判断是不是出了问题?

这就是 Ledger 和普通 Todo 最大的区别:

Todo 记录“做没做”,Ledger 记录“为什么这么做”。


四、金额、ID、账号:不要让 LLM 当数据库

还有一个非常容易被忽略的问题。

Agent 在上下文里可能会说:

“发票 INV-20260795 已核验,关联 PO-0795,金额 128,000 元。”

但这些自然语言并不应该成为下一步工具调用的真实参数来源。

真正调用“生成付款批次”时:

供应商 ID PO ID 发票 ID 金额 银行账号

应该由程序从SessionState等结构化状态中读取。

也就是说:

LLM ↓ 做判断 / 提出 Tool Call ↓ 程序 ↓ 从结构化状态读取真实参数 ↓ 调用 API

而不是:

LLM ↓ 生成一段自然语言 ↓ 再从自然语言里解析金额、ID、账号 ↓ 调用 API

为什么要这么较真?

因为长任务里,Agent 很可能会把两个名字相近的供应商搞混。

如果关键参数一直存在于自然语言上下文中,这种错误就可能一路传播。

而如果真实参数始终由程序维护:

LLM 可以犯语言错误,但不能直接污染系统真值。

这就是“事实平面”的意义。


五、200 张全部跑完:Agent 说完成,不代表真的完成

终于,Agent 汇报:

“200 张发票已经全部审核完成。”

如果系统直接相信它:

Agent:完成 ↓ 生成付款批次

那么“完成幻觉”就产生了。

所以最后需要一个验证闸门

例如:

已处理发票数 = 200? ✓ 金额总和是否超过预算阈值? ✓ 所有 needs_review 是否进入人工审核清单? ✗ 发现 3 张漏标

于是系统拒绝进入下一阶段:

completed ↓ Verifier ↓ 失败 ↓ needs_rework

只有验证通过,才允许生成付款批次。

所以:

“完成”不是 Agent 自己宣布出来的,而是系统验证出来的。

这可能是长任务 Agent 最重要的一道保险。


六、如果中途崩了,怎么接着跑?

假设处理到第 130 张时,容器突然崩了。

最差的做法,是让新 Agent 把之前 130 张的完整聊天记录重新读一遍,然后自己猜:

“我上次做到哪里了?”

更合理的方式,是保存一个轻量的resume_packet

goal:success_criteria:...non_goals:...milestone:name:invoice_reviewprogress:130/200recent_ledger:-...-...-...-...-...needs_review:-INV-20260088-...state_keys:-STATE.batch_approved_invoices-STATE.pending_review

新一轮 Agent 只需要恢复:

目标 ↓ 当前进度 130 / 200 ↓ 最近决策 ↓ 待人工审核 ↓ 读取需要的结构化状态 ↓ 继续第 131 张

而不是重新阅读整个历史上下文。

这就是为什么长任务 Agent 最终需要的,不只是“记忆”,而是:

可恢复的执行状态。


七、把整个设计放在一起

现在回头看,整个系统其实没有那么复杂。

长任务 Agent │ ┌──────────────┼──────────────┐ ↓ ↓ ↓ 目标 事实 进度 要干什么? 真实是什么? 干到哪了? │ │ │ Goal Contract SessionState Ledger │ │ │ └──────────────┼──────────────┘ ↓ 编排器 ↓ Tool 执行 ↓ 验证闸门 ↓ 完成 / 继续 / 重做

再把前面的几个机制放进去:

目标 └── Goal Contract └── 复诵:防止目标漂移 事实 └── SessionState / Provenance └── 关键参数不经过自然语言传递 进度 └── Ledger / Milestone └── 漂移检测:防止局部死磕 └── Verifier:防止完成幻觉 └── resume_packet:支持断点恢复

这样看下来,“三平面”其实解决的是三个不同的问题:

问题对应机制
Agent 忘了自己为什么做目标
Agent 把数据搞串了事实
Agent 不知道做到哪了进度

八、最后总结:长任务 Agent 缺的可能不是更强的模型

如果你的 Agent 经常出现:

  • 在一个细节上死磕几十轮

  • 参数在多轮对话中逐渐串错

  • 最后说“完成了”,实际上漏了一堆

  • 中途崩溃后只能重新开始

  • 出问题之后很难追溯到底是哪一步判断出了错

那么问题未必是模型能力不够。

更可能是:

你把目标、事实、进度,全都塞进了 LLM 的上下文里。

而这三种信息,其实应该由不同的机制负责:

目标负责约束方向。

事实负责保证真实。

进度负责推动任务。

最终形成一个很简单的原则:

让模型负责“想”,让程序负责“记”,让编排器负责“推”。

这可能才是长任务 Agent 真正需要的“进度追踪”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 8:01:17

电赛72小时极限冲省一:策略、硬件与软件实战指南

这类标题一看就是奔着拿奖去的,但“极限省一”背后,考验的绝不仅仅是技术实力,更是对赛题规则、时间管理、成本控制和临场应变能力的极限压榨。参加过电赛的老手都清楚,从拿到题目到提交作品,每一分钟、每一分钱、每一…

作者头像 李华
网站建设 2026/9/2 8:01:12

浏览器端运行LLM:WebGPU环境验证与WebLLM推理实战

在实际前端项目中,LLM 并不总是需要部署在 GPU 服务器上。当需求变成“在浏览器里直接运行一个模型”时,真正要解决的核心问题就变成了三件事:浏览器有没有可用的 GPU 计算能力、模型能不能在当前设备上跑得动、整个推理过程能不能被一套稳定…

作者头像 李华
网站建设 2026/9/2 7:59:58

基于MATLAB/Simulink的卫星避碰方案:从轨道动力学到自主决策

简介:本资源是一套面向航天工程专业学生、卫星控制初学者及MATLAB/Simulink仿真实践者的卫星避碰方案设计与仿真教学包,聚焦轨道动力学建模、碰撞风险识别与机动策略生成等核心问题,适用于课程设计、毕业设计及小型航天任务仿真验证场景。压缩…

作者头像 李华
网站建设 2026/9/2 7:57:31

生产工单靠人工流转、进度全靠口头催,该怎么实现数字化跟踪?

本文要点:本文聚焦制造企业在生产工单流转与进度跟踪环节的普遍困境——工单靠纸质或微信群传递、进度依赖口头催促,导致交期失控与责任不清。文章盘点轻流及市面多款生产管理类工具在工单数字化、进度可视化方面的能力差异,覆盖从注塑、机加…

作者头像 李华
网站建设 2026/9/2 7:55:40

搭建 Gerrit 服务详细步骤

搭建 Gerrit 服务 详细步骤 Ubuntu LTS + Java + PostgreSQL + Nginx + TLS + systemd 部署形态 单节点生产服务 访问协议 HTTPS Web + SSH Git 运维范围 认证、备份、升级、排障

作者头像 李华
网站建设 2026/9/2 7:53:14

AM521读取力准LZ-801T传感器:Modbus RTU协议解析与数据采集实战

你有没有遇到过这种情况:手头有一个工业现场常用的称重传感器,比如力准的LZ-801T,它通过RS485接口,遵循Modbus RTU协议输出数据。同时,你还有一个工业控制器或网关,比如AM521,它支持Modbus主站功…

作者头像 李华