普通聊天程序调用一次模型就返回答案。Agent 不同:模型可以请求工具,宿主执行工具,再把结果交还模型,让模型根据外部世界的新状态继续决定下一步。

最小结构并不复杂:一个消息列表、一个 Bash 工具和一个循环。

用户请求进入 messages

模型返回文本或 tool_use
        ├── 没有 tool_use → 返回最终文本
        └── 有 tool_use → 宿主执行 Bash

                    追加 tool_result

                       下一轮

messages 是循环的工作状态

每轮模型调用都接收当前 messages。用户请求、模型回复和工具结果依次进入这个列表,因此模型能看到自己刚才请求了什么,以及工具实际返回了什么。

user: 请查看当前目录
assistant: tool_use(name="bash", id="call_1", command="ls")
user: tool_result(tool_use_id="call_1", content="...")
assistant: 当前目录包含……

最后一条 user 工具结果不是人重新输入,而是 API 协议中承载工具结果的消息角色。

模型只提出调用,宿主才执行工具

工具定义告诉模型工具名称、用途和输入结构。模型生成 tool_use 只是结构化请求,不会自动运行 Shell。宿主必须找到实现、校验输入、执行操作、收集输出与退出状态,再生成关联的 tool_result

模型负责选择动作,Harness 负责决定动作能否以及如何发生。

调用 ID 闭合工具协议

每个工具调用都有稳定的 tool_use_id。返回结果时必须携带同一个 ID。当一条响应包含多个 tool_use block 时,也要逐个执行并分别返回对应结果。

即使命令没有输出,也应返回合法内容,不能让调用悬空。工具失败同样应转换成模型可处理的结果,而不是直接破坏整个消息循环。

停止条件检查实际内容块

最直接的退出条件是当前响应中不存在 tool_use。此时模型只返回文本,循环把它交给用户。

不要把“模型暂时没有调用工具”误解为“目标一定完成”。s17 会加入目标完成判断;s01 只建立协议闭环。

Bash 让示例很短,也让边界很危险

Bash 几乎可以完成所有本地操作,因此适合展示 Agent Loop;它也意味着模型可能读取文件、修改目录、联网或执行破坏性命令。上游课程明确把 s01 定位为教学实现,而不是安全沙箱。

最低限度应当:

  • 在临时目录运行实验;
  • 不放入凭据和重要文件;
  • 设置超时并保留退出码;
  • 不把命令字符串视为可信输入;
  • 在真实产品中加入权限、隔离和审计。

s03 会加入执行前权限判断,s04 再把权限提取成 Hook。cwd 只能限定默认工作目录,不能阻止绝对路径、Shell 跳转或子进程访问其他位置。

最小实现没有解决什么

问题后续机制
多种工具如何注册s02 Tool Use
哪些操作允许执行s03 Permission
如何插入审计和拦截逻辑s04 Hooks
长任务如何保持计划s05 TodoWrite
上下文如何隔离和压缩s06、s08
如何判断长期目标真正完成s17 Goal Loop

最小循环的价值不是功能完整,而是提供稳定骨架:后续能力都尽量接入这个循环,而不是创造不兼容的执行协议。

建议覆盖的测试

  • 纯文本响应会立即结束循环;
  • 一个和多个工具调用都获得对应结果;
  • 空输出和非零退出码不会破坏消息结构;
  • 未知工具与参数错误返回错误结果;
  • 每个 tool_result 都引用真实的 tool_use_id
  • 命令超时后子进程能被清理。

小结

Agent Loop 是模型决策与宿主执行之间的反馈循环。messages 保存工作状态,tool_use 表达动作意图,tool_result 带回观察结果,没有工具调用时结束当前轮次。Bash 只是最小示例,不是安全设计。

参考资料