HARNESS 101

The Agent Loop

"一个工具 + 一个 while 循环 = 一个 Agent 的雏形"
从零推导这个循环为什么存在、怎么工作。

1

问题起源:Claude 碰不到真实世界

打开 Claude,问"1+1等于几"——Claude 回答"2"。一轮结束。这是一次请求-响应,没有循环。

但如果你问"我当前目录下有什么文件?"——Claude 不知道。它看不到你的电脑。它有推理能力,但碰不到真实世界。

我当前目录下有什么文件?
C
我无法访问你的文件系统。
除非——你给 Claude 一个方式,让它能请求执行命令。比如约定:回复里用 RUN: 开头 = 请求执行命令。
我当前目录下有什么文件?
C
我需要查看目录。
🔧 RUN: ls

这就是"工具请求"——Agent 的原点

Claude 的回复不再是纯文本回答,而是一个工具请求。模型从"回答问题"变成了"请求行动"。这就是 Agent 的"行动"。

2

人肉循环:你在 Claude 和终端之间搬运信息

Claude 请求 ls,你执行后得到 a.py b.py c.py。你得把这个结果再发一条消息告诉 Claude:

我当前目录下有什么文件?
C
🔧 RUN: ls
ls 的结果是:a.py b.py c.py
C
你的目录下有 3 个文件:a.py, b.py, c.py

如果任务更复杂——"找出所有 Python 文件,检查有没有 TODO 注释"——Claude 可能需要调多次工具:

找出所有 Python 文件,检查有没有 TODO 注释
C
先列出所有 Python 文件。
🔧 RUN: ls *.py
结果:a.py b.py
C
搜索 TODO。
🔧 RUN: grep TODO a.py b.py
a.py:5: # TODO: fix this bug
b.py: (无)
C
a.py 第5行有一个 TODO:fix this bug。
核心观察:每次都是同样模式——Claude 请求工具 → 你执行并返回 → Claude 看结果,决定"还要调?还是可以回答了?"。这个过程可能重复 1 次、10 次。你不知道到底要几次。你只知道:只要 Claude 还在请求工具,你就继续执行并返回。你就是一个人肉循环
3

循环诞生:把人肉循环变成代码

把上面的"你来回来去搬运信息"抽象成逻辑,就是两层判断:

伪代码

while True: # ← 反复执行,这就是循环 回复 = Claude(对话历史, 可用工具列表) # 把整个对话发给 Claude 把回复记入对话历史 # 记住 Claude 说了什么 if 回复 文本回答: # Claude 觉得可以回答了 break # 退出循环,显示给用户 if 回复 工具请求: # Claude 还想调工具 结果 = 执行工具(回复.工具参数) # 帮它执行 把结果以 user 身份记入对话历史 # 告诉 Claude 结果 # → 回到 while 开头,再发给 Claude
🔄 Agent Loop(while 循环体)
🧠 LLM 思考+决策
⚡ 执行工具
🔍 观察结果、再思考
✅ 决定完成
↻ 只要 LLM 还在调工具,就把结果喂回去
4

两层循环 & 名字解释

其实有两个循环:外层 REPL + 内层 Agent Loop

外层 REPL:用户连续提多个任务。("创建 a.py"→"再写个 b.py"→"看看 git 状态")外层每轮调用一次内层。

内层 Agent Loop:单个任务内部,Claude 调多次工具,直到完成。

# 外层:REPL(用户交互) # 内层:Agent Loop(工具执行) while True: def agent_loop(messages): query = input(...) while True: messages.append(user_msg) response = client.messages.create(...) agent_loop(messages) #→ if stop_reason != "tool_use": return print(result) # 执行工具,塞回结果,继续循环

外层循环人控制节奏,内层循环模型控制节奏。外层 messages 跨任务保留——所以后面说"再改一下那个文件",模型记得。

Agent

就是 Claude(LLM)。能感知(读对话历史)、推理(理解任务)、行动(调用工具)。它不是一个特殊程序,它就是那个模型本身。

Agent Loop

一个 while 循环,让 LLM 可以多次调用工具,每次拿到结果后决定下一步,直到它自己判断任务完成。没有这个循环,LLM 调一次工具就停了,你必须手动把结果贴回去再问一遍。

5

代码拆解:s01 的四个组成部分

s01 总共 120 行,由四个独立组件构成。Agent Loop 只是其中之一:

A

初始化:身份 + 环境 + 能力

在循环之外,只执行一次。决定 Agent 是谁、在哪、能做什么。

SYSTEM = "You are a coding agent. Act, don't explain." # 角色设定 client = Anthropic() # API 连接 TOOLS = [{"name": "bash", "input_schema": {"command": "string"}}] # 能力列表
💡 System Prompt 塑造模型的全部行为模式。短但精确。
B

工具执行函数:Agent 的"手"

真正在操作系统上执行命令。三个安全约束:黑名单拦截、120s超时、50000字符截断。

def run_bash(command): if any(d in command for d in ["rm -rf /", "sudo", ...]): return "Blocked" r = subprocess.run(command, timeout=120, capture_output=True) return (r.stdout + r.stderr)[:50000]
💡 工具是手,harness 决定手能伸多远。这三个约束不是模型的事。
C

Agent Loop:内层循环

这是核心。接收 messages,反复"调LLM → 执行工具 → 塞结果",直到 stop_reason 不再是 tool_use。

def agent_loop(messages): while True: resp = client.messages.create(messages=messages, tools=TOOLS, ...) messages.append({"role": "assistant", "content": resp.content}) if resp.stop_reason != "tool_use": return # 模型决定停止 for block in resp.content: if block.type == "tool_use": output = run_bash(block.input["command"]) messages.append({"role": "user", "content": [tool_results]}) # 冒充 user
💡 Q: 为什么 while True?A: 你不知道几次。Q: 为什么传整个 messages?A: Claude 没记忆,你替它记。
D

外层 REPL:用户交互循环

接收用户输入,调用 agent_loop,打印结果,等下一个输入。history 跨任务共享。

history = [] while True: query = input("s01 >> ") history.append({"role": "user", "content": query}) agent_loop(history) # → 进入内层循环 print(history[-1]["content"]) # → 打印 Claude 的最终回答
💡 这个双层循环结构是所有 AI 编程 Agent 的骨架(Claude Code、Codex、Aider 都是)。
6

关键细节:response.content 不是纯文本

response.content 是一个block 列表。理解它的结构是理解 Agent Loop 的前提:

📝 text block — 纯文本回答
{
  "role": "assistant",
  "content": [
    { "type": "text",
      "text": "这里有 3 个文件" }
  ]
}
stop_reason = "end_turn"
🔧 tool_use block — 工具调用
{
  "role": "assistant",
  "content": [
    { "type": "tool_use",
      "id": "toolu_001",
      "name": "bash",
      "input": {"command": "ls"} }
  ]
}
stop_reason = "tool_use"
tool_use_id 是每个工具调用的唯一标识。返回 tool_result 时必须带上匹配的 id。如果一次响应里有两个 tool_use block(模型同时请求两个命令),你不能搞混它们的 id。
7

真实数据追踪:messages 在每一步的变化

任务:"创建一个 hello.py 输出 Hello World"。

铁律:Anthropic API 要求 role 严格交替 user → assistant → user → assistant → ...Harness 冒充 user 塞 tool_result,就是为了维持这个交替。
状态 0 用户输入后
messages = [ {"role": "user", "content": "创建 hello.py 输出 Hello World"} ]
role序列: [user] → 合法,可以调用 API
状态 1 Loop 第1次迭代 — Claude 请求工具
messages = [
  {"role": "user", "content": "创建 hello.py ..."},
  {"role": "assistant", "content": [{"type": "tool_use", "id": "toolu_001", ...}]}
]
role序列: [user, assistant] — stop_reason="tool_use",不能停
状态 2 Harness 执行 + 冒充 user 塞结果
messages = [
  {"role": "user", ...},
  {"role": "assistant", ...},      ← tool_use
  {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_001", ...}]}
]
role序列: [user, assistant, user] → 合法!可再次调用 API
状态 3 Loop 第2次迭代 — Claude 决定回答
messages = [
  {"role": "user", ...},
  {"role": "assistant", ...},      ← tool_use
  {"role": "user", ...},          ← tool_result
  {"role": "assistant", "content": [{"type": "text", "text": "已创建 hello.py"}]}
]
role序列: [user, assistant, user, assistant] — stop_reason="end_turn" ✅

规律

role 严格交替永不打破。② Harness 冒充 user维持交替。③ Claude 控制退出,stop_reason 决定一切。

Agent Loop = 不断往 messages 末尾追加 assistant(tool_use) + user(tool_result),直到 stop_reason 变成 end_turn。

8

从 s01 带走的三个核心认识

01

Agent = 模型 + 循环 + 工具

不需要"工作流引擎"、不需要"决策树"。一个 while 循环 + API 就够了。

02

循环什么时候停?模型说了算

退出条件是 stop_reason,不是硬编码的"检查任务完成"。信任模型的判断。

03

模型没记忆,你替它记

每次 API 调用无状态。messages 列表就是模型的"记忆"。你负责维护它。

9

落地案例:Agent Loop 在真实 AI 产品中的应用

学完 Agent Loop 之后,你会发现几乎所有 AI Agent 产品底层都是这个 while 循环。以下是三个真实的 Agent 产品如何应用这个模式。

🤖 场景一:Claude Code 的 Agent Loop(你正在用的)

打开 Claude Code,说"帮我写一个登录页面"。Claude Code 的内部就是你在 s01 学到的那个 while 循环,只不过它比你写的多了几十个工具:

# Claude Code 的核心(简化),和你写的 s01 一模一样 while stop_reason == "tool_use": response = LLM(messages, tools=[Read, Write, Edit, Bash, Glob, Grep, WebFetch, ...]) for block in response.tool_uses: result = execute(block) messages.append(result)

你学完 s01 之后,回到 Claude Code 里观察它的一次工具调用:它会先 Read 文件 → 分析内容 → Edit 修改 → 也许再 Bash 运行测试 → 看输出 → 如果失败再改。这个连续的"思考-行动-观察"就是 Agent Loop。你现在能看懂它在干什么了。

🌐 场景二:Browser-Use Agent(让 AI 操作浏览器)

browser-use 是一个开源项目,让 AI Agent 像人一样操作网页——点击按钮、填写表单、滚动页面。它的核心就是 Agent Loop:

观察:截取当前网页截图 + DOM 结构 → 发给 LLM。
决策:LLM 返回下一步动作(click 这个按钮 / type 这段文字 / scroll 到下面)。
行动:用 Playwright 执行这个动作。
循环:截取新的网页状态,发给 LLM,直到 LLM 说"任务完成"。

这个工具让非程序员也能自动化网页操作——"帮我在淘宝搜索蓝牙耳机,按销量排序,把前 10 个结果导出到表格"。底层就是 s01 的 while 循环,只不过工具从 bash 换成了 click/type/scroll。

🏠 场景三:智能家居 Agent(Home Assistant + LLM)

Home Assistant 的 LLM Agent 集成 让用户用自然语言控制家里所有设备。它的 agent 循环:

用户:"我要睡觉了,把家里调成睡眠模式。"
LLM:分析意图 → 调 tool_use:关客厅灯、关窗帘、设空调到 26°C、启动安防。
Harness:执行 4 个工具调用 → 返回结果。
LLM:检查结果 → 灯关了 ✓ 窗帘关了 ✓ 空调设好了 ✓ 安防异常 ✗。
LLM:再调 tool_use:检查安防传感器 → 发现阳台门没关 → 提醒用户。

这就是 s01 的循环在物理世界运行。工具从 bash 变成了 IoT 设备 API,但 Agent Loop 的结构完全不变。

核心迁移:Agent Loop 是所有 AI Agent 的"心脏"。不管是编程、浏览网页、控制家居、自动驾驶——感知→决策→行动的循环是共通的。你学完 s01 之后,看任何 AI Agent 产品都可以问自己:"它的工具是什么?它的退出条件是什么?它的 messages 是怎么管理的?"这三个问题能帮你瞬间理解任何 Agent 系统的架构。

s01 只有一个 bash 工具。s02:当你有很多工具时,怎么优雅地注册和调度它们?

s02: Tool Use →