桌上 驻场之前 目录

第 3 节 · 约 45 分钟

看完这一步,才选下一步。

把本节选择边界接到持久化写入回执:缺审批停止、签后参数变化、现场版本变化与提交后超时,都有可运行反例。

第 2 节修的是可靠写入。这一节把它放进一个有边界的 Agent:观察结果、选择动作、调用工具,再决定继续还是停。

先分清:会调工具,不一定需要 Agent

如果规则早已确定为「查状态 → 核验审批 → 关闭」,就写固定工作流。工作流也能分支、重试、等待人,不因为有这些能力就变成 Agent。这里说的 Agent,是由模型根据当前观察选择下一步工具或结束;主程序仍负责工具白名单、输入类型、权限和步数上限。不是把一段回答当命令执行。

为什么要多一个选择环节?用户说「核查 CASE-3188;如果真是重复件,申请关闭」。第一次只知道工单号。读到 OPEN 后,模型还需要查相似件;若相似件不匹配,应停止关单;匹配后才提出关闭请求。下一步依赖刚返回的事实。对于永远走同一路径的任务,固定工作流更容易测试,没必要加这个环。

一次循环只交出一个结构化动作,例如 {"tool":"search_similar","ticket_id":"CASE-3188"}。主程序执行允许的工具,把返回结果追加进观察记录,再调用模型。模型看到的是工具结果,不是自己上一轮想象的结果。获得回执、遇到证据冲突、缺审批或达到四次决策上限,都有明确的停止出口。

图 1:这条回路里,谁决定什么?按顺序追踪一次读工具返回。
  1. 观察用户目标 + 已返回的工具结果
  2. 模型选动作查工单 / 查相似件 / 提议关闭 / 停止
  3. 代码守边界白名单、参数、权限、审批、预算
  4. 工具返回加入观察 → 下一轮;有回执或需人工 → 结束

模型负责选择,代码负责准入;审批不能由模型在请求体里写一个 approved: true 来替代。

一条完整 trace:不是每一轮都在写

下面是假想工单的教学记录。第二节的可靠关闭函数放在最后一个工具后面;前面的查证没有写入。读工具也必须按当前用户的权限过滤。本课离线示范只用合成数据,不实现真实租户权限或外部模型连接。

轮次与观察选择 → 实际调用返回后怎么走
1:只有用户目标和工单号get_case(CASE-3188)OPEN、version=7;追加观察,继续
2:工单可处理,但是否重复未知search_similar(CASE-3188)同一事件已有 CASE-3000;证据和状态无冲突,继续
3:已有重复证据模型提议 close_case(note="重复件,关闭")代码发现缺审批:返回 WAIT_APPROVAL,写入 0 次,结束本次运行
恢复:人审定具体工单与 note,可信代码签发信封重新读状态、复核证据,提议不变的关闭验证签名、操作号、参数摘要;扩展版再查有效期。通过才调用写工具
工具已提交,客户端超时可靠关闭函数 lookup(原操作号)找到回执 rcpt_A;交回同一回执,提交数 1,DONE。无需模型再决定重试

恢复时要重新核对现场事实,不能把昨天的 OPEN 当今天的 OPEN。参数或证据需要改变时,旧审批不再代表新动作;停下来重新确认。操作号由调用方为这次具体意图分配并保存,同一次意图的重试沿用它;用户提出另一项真实变更时,应建立新的意图,而非永久复用一个号。

图 2:提议关闭之后的四个出口。它们是代码规则,不交给模型猜。
缺审批 / 已过期 / 摘要改变
WAIT_APPROVAL:不写,交回具体待确认动作
状态冲突 / 工具名不允许 / 参数无效
HANDOFF:保留观察,停止自动执行
获得有效回执
DONE:给出回执,结束,不再关一次
四轮仍没形成可执行动作
BUDGET_STOP:报告已查到什么,不继续空转

跑一条,再改一个条件

下载完整实验包,按上一节的安装步骤进入解压后的目录。示范使用可替换的选择函数模拟模型输出,便于离线复现;它展示 Agent 执行器如何消费决策,并没有接入 LLM。将来接模型时,只替换选择函数,写入边界仍留在代码中。

python reference/agent_loop.py --scenario approved
python reference/agent_loop.py --scenario conflict
python reference/agent_loop.py --scenario expired
python reference/agent_loop.py --scenario loop

依次应看到 DONE / writes=1、HANDOFF / writes=0、WAIT_APPROVAL / writes=0、BUDGET_STOP / writes=0。逐轮读输出里的 observation、decision 和 result,指出下一步为什么变了。代码见执行器示范;原来的 src/ticket_write/service.py 仍保留待修缺陷。

如果示范没有这些结果,先核对运行目录、Python 版本和 traceback;如果你自己的实现重复查同一工具,打印每轮观察,检查是否真的把新结果放回上下文。不要靠增加最大轮数掩盖没有进展。

你来做

先检查执行边界。选项描述的是本课的局部设计,选对还不代表实现已经可靠。

换一张工单,不照抄上面的路径

CASE-4092 查到 OPEN;相似件服务却回报它已撤回。模型仍提议关闭,审批也恰好有效。请写出已观察事实、下一步动作、由谁拦截、写入次数及恢复所需证据。再回答:若模型连续四轮只查同一工单,你的程序在哪结束?

写完后对照:参考判断与自评

状态冲突先 HANDOFF,写入 0 次。有效审批说明某个动作被批准,不会消除证据矛盾。需确认各来源版本、更新时间与权威状态,再判断是否重签。四轮仍查同一工单时由代码返回 BUDGET_STOP,并保留已有观察。

  • 是否明确区分模型提议与代码真正执行?
  • 是否给出可核对的停止原因和写入次数?
  • 是否说明恢复前要补什么事实,而非只写“交给人工”?

常见错误:把“有审批”当作“事实已经一致”;把读工具成功当作任务已经完成。与参考不同的实现也可以成立,但必须保住这些条件。

留下能逐轮解释的 trace 和明确停止规则。下一节检验的,就是这些规则被破坏时你能不能发现。