轻量Agent不靠行数证明自己:先写清状态与停止条件

发布时间:2026/10/25 11:23:36
轻量Agent不靠行数证明自己:先写清状态与停止条件

“核心只有几百行”可以说明代码规模,却不能说明Agent可靠。只要状态转移、停止条件和外部副作用没有说清,短代码照样会重复创建分支、无限修补,或者把没有完成的任务返回成成功。我更在意一个新维护者能不能沿着日志找到它为什么停下。

Claude平台10月9日公布动态工作流beta,说明多阶段任务正在得到更明确的执行载体。对轻量框架而言,这并不意味着马上重写成多Agent平台;先区分固定流程和模型决策,通常更能减少调试范围。

把停止条件放到循环外面审视

一次循环至少需要目标、当前状态、剩余预算和结果证据。完成应由可检查的条件决定,例如产物存在且校验通过;“模型说已经完成”只能作为候选信号。

预算不只有step数,还包括时间、费用和写操作次数。这里先用步数演示,不能把它理解成对所有资源都已经限流。

状态名应该对应可观察动作

图中的PLAN、EXECUTE、VERIFY和REPAIR分别有输入输出。执行失败进入修复;验证通过进入DONE;预算耗尽进入STOP。生产代码还应区分用户取消、需要人工判断和运行错误,避免一个failed状态承载所有原因。

用固定决策验证循环,而不是假装调用模型

代码中的decisions是预先给定的测试输入,不是语言模型返回。它让我们稳定地检查终止路径、未知动作拒绝和步数耗尽。保存为example.mjs后运行node example.mjs。

import assert from 'node:assert/strict'; function run(decisions, maxSteps) { if (!Number.isInteger(maxSteps) || maxSteps < 1) throw Error('invalid_budget'); const trace = []; for (let step = 0; step < maxSteps; step++) { const action = decisions[step] ?? 'wait'; if (!['work', 'done', 'wait'].includes(action)) throw Error('unknown_action'); trace.push(action); if (action === 'done') return {status:'done', trace}; } return {status:'budget_exhausted', trace}; } assert.deepEqual(run(['work','done'],3), {status:'done',trace:['work','done']}); assert.equal(run(['work','work'],2).status, 'budget_exhausted'); assert.throws(() => run(['delete_everything'],2), /unknown_action/); assert.throws(() => run([],0), /invalid_budget/); console.log('bounded loop: ok');

本地运行输出:

bounded loop: ok

小内核也需要清楚的扩展缝隙

模型适配器负责把返回内容解析成动作,工具执行器负责权限与幂等,循环只编排状态。把三者分开之后,替换模型不必同时修改持久化和日志。反过来,如果任务流程本来固定,用普通工作流函数会比让模型猜下一步更直接。

我不会用一次离线断言去推导镜像减少多少、冷启动快多少。要比较运行时,需要相同任务、版本、硬件与采样方法;本文没有这些基准材料。

适用范围与未测部分

这是控制流测试,未接真实模型、工具执行或持久化;没有测量生产成功率和性能。控制流约束仍需在真实工具接入后继续验证。

参考资料

  • Claude Platform release notes(2026-10-22)
  • Investigating unintended model actions(2026-10-09)