“AI Agent"听着挺唬人,但拆开看,核心就是一个
while循环:发消息 → Claude 说"我要调这个工具” → 你执行 → 把结果发回去 → 重复,直到 Claude 说"我完事了"。这篇文章把这层窗户纸捅破。

文章目录
一、先说结论:Agent 没什么神秘的
我在香港做 FinTech,最近在把一个"查数据、算指标、出报告"的活儿交给 Claude 自动跑。研究了一圈所谓的"Agent 框架",最后发现——Agent 的底层,就是一个 while 循环加一个 stop_reason 信号。
拆开看,就三步:
- 发消息给 Claude,带上工具定义
- 看
stop_reason:如果是tool_use,说明 Claude 要调用工具,你执行并把结果发回去 - 重复,直到
stop_reason变成end_turn(Claude 说"完成了")
没有魔法,没有黑箱。这篇文章我把这个循环、stop_reason 信号、以及我实际踩过的三个坑,一次性讲清楚。核心结论先放这:Agent 的价值不在循环本身,而在你给它的工具和循环里那些不起眼的细节(比如漏加一条消息就能让整个 Agent 抽风)。
收藏提示①:文末的 Agent Loop 模板可以直接复用,换上你的工具就能跑。
二、环境信息
| 项 | 版本 |
|---|---|
| Python | 3.13.12 |
| anthropic SDK | 最新版 |
| 模型 | claude-sonnet-4-6(示例用) |
| 核心概念 | stop_reason(tool_use / end_turn)+ tool_use 循环 |
三、stop_reason 是唯一的"信号灯"
整个 Agent 循环,全靠 stop_reason 这个字段驱动:
| stop_reason | 含义 | 你该做什么 |
|---|---|---|
tool_use | Claude 想调用工具 | 执行工具,把结果发回去,继续循环 |
end_turn | Claude 完成了 | 返回最终文本,结束循环 |
| 其他(max_tokens 等) | 意外情况 | 记录并处理,别当完成 |
关键点在于:判断"Claude 是不是做完了",只能看 stop_reason,不能看返回内容是不是文本。 这个坑下面会重点讲。
四、完整的 Agent Loop
下面是核心模式,while True + stop_reason 判断:
import anthropic
import json
client = anthropic.Anthropic()
# 工具定义:让 Claude 能查产品信息
tools = [
{
"name": "search_products",
"description": "按关键词搜索产品库",
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
},
{
"name": "get_product_detail",
"description": "按 ID 获取产品详情",
"input_schema": {
"type": "object",
"properties": {"product_id": {"type": "string"}},
"required": ["product_id"],
},
},
]
def execute_tool(name: str, args: dict) -> str:
"""执行工具,返回结果字符串"""
if name == "search_products":
return json.dumps([{"id": "P001", "name": "Widget Pro", "price": 29.99}])
elif name == "get_product_detail":
return json.dumps({"id": args["product_id"], "name": "Widget Pro", "stock": 142})
return json.dumps({"error": "unknown tool"})
def run_agent(user_input: str, max_steps: int = 10) -> str:
messages = [{"role": "user", "content": user_input}]
for _ in range(max_steps): # 安全上限,不是终止信号
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
tools=tools,
system="你是产品查询助手,用工具回答用户问题。",
messages=messages,
)
# 关键:先 append Claude 的响应,再 append 工具结果
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason == "end_turn":
return "".join(b.text for b in response.content if hasattr(b, "text"))
if response.stop_reason == "tool_use":
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id, # 关键:把结果链接回工具调用
"content": result,
})
messages.append({"role": "user", "content": tool_results})
return "达到最大步数,未完成" # 安全兜底
print(run_agent("帮我查一下 Widget Pro 的库存"))
这套代码跑起来,Claude 会先调 search_products 找到产品 ID,再调 get_product_detail 查库存,最后用文本回答你——这就是一个多步骤 Agent 的完整闭环。
收藏提示②:整个循环就记住三件事——
stop_reason判断状态、tool_use_id链接结果、先 append assistant 响应再 append 工具结果。这三件事对了,Agent 就稳了。

上图:发消息 → 判断
stop_reason→ 左边tool_use(执行工具、发回结果、回到顶部循环)→ 右边end_turn(返回文本、结束)。这就是 Agent 的全部骨架。
五、三个坑:踩过才知道有多隐蔽
这三个坑,每个都能让你的 Agent 悄悄抽风,而且报错还看不出来。
坑 1:漏加 assistant message
很多人写循环时,只 append 工具结果,忘了先 append Claude 自己的响应:
# ❌ 错误:直接 append 工具结果
messages.append({"role": "user", "content": tool_results})
# ✅ 正确:先 append Claude 的响应,再 append 工具结果
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
Claude 需要看到自己上一步的响应,才能把工具结果链接回它之前发起的工具调用。漏了这条,会出现"答非所问"甚至死循环。
坑 2:用内容类型当终止条件
# ❌ 错误:检查返回内容是不是文本
if response.content[0].type == "text":
return response.content[0].text
# ✅ 正确:看 stop_reason
if response.stop_reason == "end_turn":
return ...
为什么错?因为 Claude 可能在返回 tool_use 的同时也带了一段文本(比如"我来帮你查一下")。如果你检查 content[0].type == "text",就会在工具还没执行时就提前终止——Agent 干一半就停了。
坑 3:把迭代上限当主要终止条件
for _ in range(max_steps) 是安全网,不是完成信号。把上限当终止条件,要么任务干到一半被砍(上限设太小),要么死循环烧钱(上限设太大)。真正的完成信号只有一个:stop_reason == "end_turn"。
收藏提示③:这三个坑的根子都是同一件事——Agent 的状态判断只信
stop_reason,不信内容、不信计数。把这个原则刻进脑子,能少踩一堆坑。

上图:三个坑的"错误 vs 正确"一目了然——红色是坑,绿色是正确做法。
六、安全措施:别让 Agent 脱缰
Agent 会犯错的,而且犯错方式很朴素:选错工具、死循环、误读结果。三条护栏:
- 硬性上限:设
max_steps和 token 预算,触发就停,标记人工复核。 - 工具入参校验:执行工具前检查输入是否合理,空 URL、非法 ID 直接挡下。
- 人机检查点:凡是写数据库、发邮件、调外部 API 的,加一道人工确认,Agent 只负责"提案"。
这三条不性感,但决定了 Agent 是"跑起来就崩"还是"能稳定服役"。
七、写在最后
写完这个循环,我对"Agent"这个词彻底祛魅了——它就是一个会调用工具的循环,技术上没有半点神秘。真正的难点从来不在循环,而在:你给 Claude 什么工具、工具描述写得清不清楚、循环里那些细节对不对。
这也是为什么那些动辄吹"通用 Agent"的东西容易翻车,而一个"只干查数据这一件事、但工具定义精确、循环细节到位"的小 Agent 反而能稳定出活。
需要说明的是,stop_reason 的取值、SDK 接口这些会随版本演进,本文以写作时的官方文档为准。如果你正想把重复的活儿交给 Claude 自动跑,这个循环是你绕不开的第一课。
收藏提示④:如果这篇帮你把 Agent 祛魅了,收藏 + 点赞,下次写 Agent 时直接回来抄这个循环模板。


346

被折叠的 条评论
为什么被折叠?



