a↗Agent 工程实战中文课程 / v1.0.0⌕ 搜索课程/
本页目录

M01 最小Agent执行循环与模型协议

本章产出:一个可测试、有预算、有类型边界的最小执行循环。使用脚本模型离线验证执行语义,再选择性接入真实模型。

1. 一个while循环里藏着什么

最小循环看起来只有“调用模型→调用工具→回传结果”。真正需要设计的是每条箭头的契约:模型可能拒绝、输出截断、返回多个工具调用、参数不合法,工具可能超时或产生副作用,用户也可能取消。

SWE-agent团队的极简教程适合观察这个闭环,但把模型输出直接当Shell在宿主机运行不属于本课程默认做法。本章只开放无副作用的合成商店查询。S18

2. 把供应商协议归一化,不抹掉重要差异

我们的内部模型输出是一个带标签的联合类型:Final(text)、ToolCalls(calls)、Refusal(reason)、Incomplete(reason)。工具调用有稳定的call_id、name与已解析的参数对象。一个模型响应同时带说明文本与工具调用时,说明文本只作为展示性输出,不能误判为任务最终完成。

适配层负责把具体API转换成这个类型。不要假设所有“OpenAI-compatible”服务都完全支持同样的schema、并行工具、流事件、usage统计或拒绝字段。通过契约测试确认你实际使用的能力。

Tool Calling让模型提出调用意图,客户端负责执行并将结果按关联标识返回;结构化输出约束格式,但不替你核验订单归属、金额或商业政策。S34 S35

课程的Provider接口故意不固定某一个模型名。真实运行必须记录provider、model、SDK版本、配置和响应标识;凭证只能来自服务端环境或密钥管理系统,不写入课程网页或Trace。

3. 最小循环的正确控制路径

检查取消/截止时间/预算
  → 构建本轮上下文
  → 请求模型
  → 分类响应
      Final → 返回候选最终回答
      Refusal → 记录拒绝并安全结束
      Incomplete → 按原因恢复或明确失败
      ToolCalls → 逐个校验和授权 → 执行 → 追加结果 → 下一轮

“Final”只是模型停止行动的信号。业务层还需要核验目标是否完成。循环的职责不应扩展到自行修改业务账本。

一个工具调用必须恰好产生一个可关联的工具结果事件,即使结果是参数错误。否则下一轮模型会看到悬空调用或错误对应关系。为了简单,本章串行执行工具;M08才讨论哪些调用可以并行。

预算至少包含最大模型轮次、最大工具调用数、截止时间、最大输出量。max_turns=5不是“保证五轮内完成”,只是最坏资源边界。超限应成为可诊断的终态,不应悄悄把半成品标为成功。

I02 · 互动推演独立打开 ↗

逐步执行Tool Loop

逐步检查模型响应、调用关联、预算与终态。

合成教学数据 · 未运行真实模型
静态推演与完整规格

I02 逐步执行Tool Loop

关联章节:M01 · 交互类型:trace-stepper · 状态:提供完整状态/验收规格,UI待实现

教学目标

逐步检查模型响应、调用关联、预算与终态。

初始状态

{
  "cursor": 0,
  "model_calls": 0,
  "tool_calls": 0,
  "ledger_count": 0,
  "status": "RUNNING"
}
操作控件
  • 下一步

  • 运行到结束

  • 选择正常/重复ID/预算耗尽案例

  • 预测下一状态

  • 回退到上一步快照

状态变化规则
  • 每一步先让学习者预测动作类型,然后推进一条固定事件。

  • 工具派发前扣除尝试预算;拒绝调用也记录结果。

  • 最终文本不会改变ledger_count。

黄金案例与反馈
normal

输入:


[
  "get_order:c1",
  "get_shipment:c2",
  "final"
]

期望:


{
  "model_calls": 3,
  "tool_calls": 2,
  "status": "MODEL_FINISHED",
  "ledger_count": 0
}

模型结束,但本例从未请求退款。

duplicate

输入:


[
  "get_order:c1",
  "get_shipment:c1"
]

期望:


{
  "status": "PROTOCOL_ERROR",
  "tool_calls": 1
}

重复ID使结果关联失真,第二次不派发。

zero-budget

输入:


{
  "max_tool_calls": 0
}

期望:


{
  "status": "BUDGET_EXCEEDED",
  "tool_calls": 0
}

预算为0时不能先执行再计费。

完成条件
  • 推进正常轨迹到终态

  • 正确识别重复ID和零预算的终止点

反馈层次
  • 提交预测后显示结果;允许跳过预测但记录跳过,不阻止阅读

  • 结果同时显示状态变化、原因与被拒绝动作;不是只给红绿灯

  • 第一次错误给边界提示,第二次提供最小反例;参考推导始终可主动打开

  • 重置恢复固定初始状态并保留历史尝试供用户手动清除

无障碍与静态替代
  • 全部操作有原生button/input/select替代,不依赖拖拽

  • 图与动画提供同步文字状态表;支持prefers-reduced-motion

  • 状态改变用aria-live=polite摘要,不逐字朗读长日志

  • 焦点不跳走;错误提示关联输入;颜色不是唯一编码

无JavaScript时直接展示初始输入、规则和黄金案例表,读者仍能完成纸面推演。状态动画仅作解释,不能成为获取答案的唯一途径。

持久化与重置

使用版本化键agent-course:1:I02,仅保存本地学习状态。支持单卡重置和全站清除;存储失败时以内存模式继续,不阻断阅读。

必须避免的误导

显示的是脚本模型事件,不是真实模型推理或隐藏思维过程。

执行边界

纯本地教学状态机;不执行任意代码,不连接真实资金/模型/宿主Shell。

4. 可复现的脚本模型与真实模型有什么区别

ScriptedModel按照预先准备的响应序列返回,不做语言理解。它能精确复现“第二轮输出坏参数”“第三个工具超时”等问题,适合测试执行器。但脚本模型通过测试不能证明模型能够理解自然语言。

我们把证据分成两种。机制正确性证据来自离线确定性测试;任务能力证据来自实际模型在固定任务集上的多次试验。两者必须分开报告。

例如离线脚本为:查询A100,查询物流,给出带证据的回复。你要检查三轮模型调用、两次工具调用、call_id对应、工具失败不会被当成成功。接上模型后还要检查它是否会选择正确工具、是否编造订单,以及是否会遗漏澄清。

5. 错误不是一个大而无当的Exception

参数错误属于调用协议问题,可以将安全、简短的诊断反馈给模型;身份不符属于权限错误,不应提示另一个租户资源的存在;读工具暂时超时可按预算重试;写工具结果未知必须进入核对流程;模型拒绝应该被保留为独立结果;取消属于控制信号,不应被宽泛异常处理吞掉。

建议内部错误对象包含code、retryable、safe_message和details_ref。详细堆栈保留在受控日志,模型只看到必要信息。可重试并不表示无限重试,重试次数也消耗同一任务预算。

一个错误设计是:捕获所有异常,向模型返回“请再试一次”。它会把权限失败变成攻击面,把永久错误变成费用循环,把取消请求变成无效按钮。

6. 流式输出:可以先显示,不能先执行

模型可能先输出工具名,再逐块输出JSON参数。收到{"amount_cents":1时,不知道最后是1、100还是10000。中间字符串不是可以执行的操作。

正确流程是按响应和call_id聚合增量,等待该工具调用的完成事件,再解析完整JSON、校验schema、检查授权,最后执行。界面可以显示“正在准备退款申请”,但不能在参数尚未闭合时启动退款。两个并行工具的增量可能交错,缓冲区不能只用一个全局字符串。

还要处理响应整体失败:收到若干增量后连接中断,不代表调用已经完整。缓冲区需要状态collecting/completed/failed;重复增量、重复完成事件和断线重连由适配层的协议测试覆盖。课程交互卡只模拟这一状态,不连接供应商。S34

I03 · 互动推演独立打开 ↗

流式参数什么时候才能执行?

完整消息/调用边界与片段文本不能混淆。

合成教学数据 · 未运行真实模型
静态推演与完整规格

I03 流式参数什么时候才能执行?

关联章节:M01 · 交互类型:stream-assembler · 状态:提供纯逻辑参考函数,UI待实现

教学目标

完整消息/调用边界与片段文本不能混淆。

初始状态

{
  "fragments": [],
  "complete": false,
  "dispatched": false
}
操作控件
  • 添加下一片段

  • 标记调用已完整结束

  • 尝试解析

  • 尝试派发(只模拟)

  • 切换截断案例

  • 重置

状态变化规则
  • 每个片段只追加缓冲区,不执行工具。

  • 未complete时返回WAITING,即使片段恰好可解析也不授权执行。

  • complete后解析JSON并校验;失败返回INVALID_JSON或INVALID_ARGUMENTS。

黄金案例与反馈
partial

输入:


{
  "fragments": [
    "{\"order_",
    "id\":\"A100\"}"
  ],
  "complete": false
}

期望:


{
  "status": "WAITING"
}

JSON看起来完整与Provider已确认调用完整不是同一信号。

complete

输入:


{
  "fragments": [
    "{\"order_",
    "id\":\"A100\"}"
  ],
  "complete": true
}

期望:


{
  "status": "VALID_CANDIDATE",
  "arguments": {
    "order_id": "A100"
  }
}

只产生候选参数;后续仍需工具白名单与授权。

truncated

输入:


{
  "fragments": [
    "{\"order_id\":"
  ],
  "complete": true
}

期望:


{
  "status": "INVALID_JSON"
}

不能猜补订单号。

完成条件
  • 成功演示未完成不派发

  • 截断JSON拒绝

  • 说出解析与授权的差异

反馈层次
  • 提交预测后显示结果;允许跳过预测但记录跳过,不阻止阅读

  • 结果同时显示状态变化、原因与被拒绝动作;不是只给红绿灯

  • 第一次错误给边界提示,第二次提供最小反例;参考推导始终可主动打开

  • 重置恢复固定初始状态并保留历史尝试供用户手动清除

无障碍与静态替代
  • 全部操作有原生button/input/select替代,不依赖拖拽

  • 图与动画提供同步文字状态表;支持prefers-reduced-motion

  • 状态改变用aria-live=polite摘要,不逐字朗读长日志

  • 焦点不跳走;错误提示关联输入;颜色不是唯一编码

无JavaScript时直接展示初始输入、规则和黄金案例表,读者仍能完成纸面推演。状态动画仅作解释,不能成为获取答案的唯一途径。

持久化与重置

使用版本化键agent-course:1:I03,仅保存本地学习状态。支持单卡重置和全站清除;存储失败时以内存模式继续,不阻断阅读。

必须避免的误导

不要把JSON.parse成功包装成安全执行。

执行边界

纯本地教学状态机;不执行任意代码,不连接真实资金/模型/宿主Shell。

逻辑参考

web/reference/engines.mjs中的assembleCall;对应测试在web/reference/engines.test.mjs。UI可复用或等价移植,但不得改变黄金结果。

7. 上下文与日志最小结构

每一轮记录:run_id、step_id、输入消息引用、工具schema版本、模型响应分类、usage是否可得、耗时、错误类型。工具事件再记录call_id、名称、参数摘要、结果引用和权限决策。

不要求记录模型不可见的内部推理,也不把“推理文本”当事实解释。保存可观察的输入、输出、工具行为与业务状态即可支持本课程的调试。对可能含凭证和个人信息的内容,优先存哈希、摘要和受访问控制的对象引用。

一个容易踩的坑是所有消息都放进user角色。这样无法保留谁提出调用、哪个结果对应哪个调用,也可能把工具文档中的指令误当用户要求。内部事件模型与供应商消息角色要有明确映射。

8. 如何组织代码,避免一开始就造大框架

先保留四个模块:model只产生候选行动;tools定义工具与鉴权;runner负责循环和预算;events负责可观察记录。领域规则不塞进模型适配器,数据库事务不塞进Prompt模板。

给Runner注入模型、工具注册表和时钟。这三个依赖可被替换,就足够开始故障测试。不要提前抽象复杂插件系统、十种记忆后端或任意图语言。

初版代码最重要的性质是“每条分支都能测试”。当你不能轻易制造模型拒绝、坏JSON或工具超时,说明依赖边界还不清楚。

9. 本章过关检查

你应能从空文件写出基本循环,并解释哪一个层次终止任务、哪里检查预算、哪个字段关联工具结果。提交至少四条Trace:正常查询、坏参数、模型拒绝、预算耗尽。

完成L01。其参考实现仅是可运行的教学内核,不是多租户生产服务;代码里的离线脚本不应被包装成真实LLM评测。下一章开始处理模型提出了“合法JSON但不合法操作”的情况。

K15 · 极简Coding Agent为什么适合作为阅读起点

K15 极简Coding Agent为什么适合作为阅读起点

类型:源码阅读 · 关联章节:M01 · 阅读方式:先做下面的推演,再看参考讨论。

原文在说什么

Minimal Agent Tutorial与mini-SWE-agent提供简洁的Agent执行示例,适合观察模型、行动与环境之间的最小闭环。

对本课程的具体启发(课程推论)

先阅读最小循环,能把框架抽象还原成实际控制流;之后再加权限、预算、事件、恢复,而不是一开始把所有复杂性藏进SDK。

两分钟推演

找出一个最小loop没有解决的生产问题,添加一项机制,并解释新增状态必须保存在哪里。

参考讨论

例如预算、取消、持久化、工具授权。预算只放消息里不够;重启后仍要约束预算就必须有可信持久记录。

不能从这里推出什么

短代码不等于安全或完整。本课程的极简loop刻意不执行任意Shell。

原始来源

本卡是原创中文概括与教学延伸,不是全文翻译,也不表示原作者认可本课程。不要把原文中的结果替换成本项目实测结果。

独立阅读 →
做当章练习 →