课程大纲 › 领域 1:Agent 架构与编排

1.1 设计并实现用于自主执行任务的 Agent 循环

タスクステートメント1.1:自律的なタスク実行のためのエージェントループを設計し実装する

官方大纲原文(日文):知识 3 条 · 技能 3 条

需要掌握的知识

  • エージェントループのライフサイクル:Claudeへのリクエスト送信、stop_reasonの確認("tool_use"か"end_turn"か)、要求されたツールの実行、次の反復に向けた結果の返却
  • モデルが次のアクションについて推論できるように、ツールの結果が会話履歴に追加される仕組み
  • モデル主導の意思決定(Claudeがコンテキストに基づいて次に呼び出すツールを推論する)と、事前に設定された決定木やツールシーケンスとの違い

需要能做到的事

  • stop_reasonが"tool_use"の場合は継続し、"end_turn"の場合は終了する、エージェントループの制御フローを実装する
  • モデルが新しい情報を推論に取り込めるように、反復の間にツールの結果を会話コンテキストに追加する
  • ループ終了の判定に自然言語のシグナルを解析する、主要な停止メカニズムとして根拠のない(恣意的な)反復回数上限を設定する、完了の指標としてアシスタントのテキストコンテンツの有無を確認する、といったアンチパターンを避ける

课程讲解 · AI 草稿,未经人工审核 以官方文档为准

这一条考什么

考你能否亲手写出一个正确的 Agent 循环:发请求、读 stop_reason、执行工具、把 tool_result 追加回对话、再发下一轮。考题会给出几种"判断循环何时结束"的方案,让你选出唯一可靠的那一个;也会考你是否理解"由模型根据上下文决定下一步调用哪个工具"与"预先写死的决策树 / 工具序列"之间的本质区别。

核心概念

Agent 循环的生命周期

一个最小的 Agent 循环只有四步,反复执行:

  1. 把当前的 messages(含系统提示、工具定义)发送给 Claude。
  2. 检查响应的 stop_reason。
  3. 如果是 "tool_use":从响应的 content 中取出所有 tool_use 块,逐个执行对应的工具。
  4. 把助手的整条响应原样追加为 assistant 消息,再把所有工具结果作为一条 user 消息(内含多个 tool_result 块,每个用 tool_use_id 对应)追加进去,回到第 1 步。

当 stop_reason 为 "end_turn" 时,说明模型认为任务已完成、不再需要工具,循环结束。其他取值也要处理:"max_tokens" 表示输出被截断(可以提高上限或让模型继续),"pause_turn" 出现在服务端工具长时间运行时(把响应原样发回即可继续),"refusal" 表示模型拒绝。但考试核心永远是:`tool_use` 继续,`end_turn` 结束。

工具结果如何进入对话历史

Claude API 是无状态的,模型"记得"什么完全取决于你每次发送的 messages。工具执行的结果必须以 tool_result 块的形式放回对话:

{
  "role": "user",
  "content": [
    {"type": "tool_result", "tool_use_id": "toolu_01A...", "content": "{\"order_id\": \"A-1001\", \"status\": \"shipped\"}"},
    {"type": "tool_result", "tool_use_id": "toolu_01B...", "content": "库存不足", "is_error": true}
  ]
}

几条硬规则:每个 tool_use 都必须有对应的 tool_result;同一轮的多个工具结果放在同一条 user 消息中;tool_result 块要排在该消息内容的最前面。只有这样,模型在下一轮才能"看到"新信息并据此推理下一步——比如查到订单已发货,就改为调用退货流程而不是取消订单。

模型驱动 vs. 预设决策树

在 Agent 循环里,下一步做什么是 Claude 根据当前上下文推理出来的:它读到 lookup_order 的结果后,再决定调用 process_refund、escalate_to_human 还是直接回答。这与以下做法截然不同:

  • 预先写好的决策树:if status == "shipped": call X else: call Y。
  • 在请求开始时就规划好固定的工具序列,然后不管中间结果如何都依次执行。
  • 由编排层根据某个字段"自动路由"到下一个工具。

预设流程适合完全可预测的任务,但它把模型降级成了填表工具,遇到意料之外的结果就会出错。Agent 循环的价值恰恰在于让模型在每一步都基于最新的工具结果重新判断。需要硬性保证的规则(例如退款上限)应交给 Hook 或前置条件(见 1.4、1.5),而不是把整个流程写死。

为什么不能用"文本内容"或"自然语言"判断结束

  • 响应中出现 text 块不代表结束:Claude 经常在 tool_use 之前先输出一句"我先查一下订单"。如果见到文本就退出,循环会在工具还没执行时中断。
  • 解析"任务完成了""Done"这类自然语言信号同样不可靠:措辞随时会变,也可能出现在中途的说明里。
  • 把"最多 N 轮"作为主要停止机制也是反模式:随意设定的上限会在复杂任务中途截断工作,在简单任务中又毫无意义。迭代上限(如 Agent SDK 的 max_turns)只应作为防止失控的安全网,并在触发时显式处理(记录、升级),而不是当成正常结束条件。

正确做法与反模式

  • 正确:以 stop_reason 作为唯一的控制流信号,"tool_use" 时执行工具并继续,"end_turn" 时结束。反模式:检查响应里有没有 text 块来判断是否完成。
  • 正确:每一轮都把完整的助手响应和全部 tool_result 追加到 messages。反模式:只保留最后一次工具结果,或把工具结果拼成纯文本塞进系统提示。
  • 正确:让模型基于最新结果决定下一个工具。反模式:开局规划固定工具序列并无视中间结果。
  • 正确:设置一个较大的 max_turns 作为安全阀,触发时走异常处理。反模式:把"5 轮就停"当作主要的结束逻辑。
  • 正确:工具失败时返回 is_error: true 的 tool_result,让模型自行决定重试或换路。反模式:工具出错直接抛异常终止整个循环。

常见陷阱

  • 选项说"检测到响应中包含文本内容块即视为最终答案并退出"——错,文本可以和 tool_use 同时出现,必须看 stop_reason。
  • 选项说"在最后一次预期的工具调用后把 tool_choice 设为 none 来强制停止"——错,这要求你预先知道哪次是最后一次,本质上又回到了预设流程。
  • 选项说"查询结果返回后,编排层根据 status 字段自动路由到下一个工具"或"按决策树映射"——错,标准循环中是把结果加入对话、由模型推理下一步。
  • 选项说"设定最大迭代次数作为主要终止条件以避免死循环"——它只能当安全网,不是主要机制。
  • 选项说"用正则匹配模型输出中的 'task complete' 字样"——自然语言信号不稳定,属于题干明确点名的反模式。

实操练习

目标:用 anthropic Python SDK 写一个带两个工具的最小 Agent 循环,观察 stop_reason 的变化。

  1. 安装 SDK:pip install anthropic,设置环境变量 ANTHROPIC_API_KEY。
  2. 保存下面的脚本为 loop.py 并运行。
  3. 观察每轮打印的 stop_reason 与工具调用。
  4. 把用户问题改成"今天天气怎么样?"(无需工具),确认第一轮就是 end_turn。
import json
import anthropic

client = anthropic.Anthropic()

TOOLS = [
    {
        "name": "lookup_order",
        "description": "按订单号查询订单状态。输入 order_id(形如 A-1001),返回 status 和 amount。",
        "input_schema": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    },
    {
        "name": "process_refund",
        "description": "为已确认的订单发起退款。order_id 必须来自 lookup_order 的结果。",
        "input_schema": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}, "amount": {"type": "number"}},
            "required": ["order_id", "amount"],
        },
    },
]

def run_tool(name, args):
    if name == "lookup_order":
        return {"order_id": args["order_id"], "status": "delivered", "amount": 120.0}
    if name == "process_refund":
        return {"refund_id": "R-778", "amount": args["amount"], "ok": True}
    raise ValueError(f"unknown tool {name}")

messages = [{"role": "user", "content": "订单 A-1001 的商品坏了,请帮我退款。"}]
MAX_TURNS = 20  # 安全网,不是主要停止条件

for turn in range(MAX_TURNS):
    resp = client.messages.create(
        model="claude-sonnet-4-5", max_tokens=1024, tools=TOOLS, messages=messages
    )
    print(f"[turn {turn}] stop_reason={resp.stop_reason}")
    messages.append({"role": "assistant", "content": resp.content})

    if resp.stop_reason == "end_turn":
        print("".join(b.text for b in resp.content if b.type == "text"))
        break
    if resp.stop_reason != "tool_use":
        raise RuntimeError(f"未处理的 stop_reason: {resp.stop_reason}")

    results = []
    for block in resp.content:
        if block.type == "tool_use":
            print("  call", block.name, block.input)
            try:
                out = run_tool(block.name, block.input)
                results.append({"type": "tool_result", "tool_use_id": block.id,
                                "content": json.dumps(out, ensure_ascii=False)})
            except Exception as e:
                results.append({"type": "tool_result", "tool_use_id": block.id,
                                "content": str(e), "is_error": True})
    messages.append({"role": "user", "content": results})
else:
    print("达到安全上限,转人工处理")

检查结果:应看到 tool_use(lookup_order)→ tool_use(process_refund)→ end_turn 三轮;第二次调用的 order_id 和金额来自第一次的工具结果,证明模型是基于追加进对话的新信息做决定的。

自测清单

  • Agent 循环的四个步骤分别是什么?stop_reason 为 "tool_use" 和 "end_turn" 时各该怎么做?
  • 同一轮有三个 tool_use 块时,工具结果应如何组织进 messages?
  • 为什么"响应里出现文本块就退出"会导致循环提前中断?
  • 模型驱动的工具选择与预设决策树相比,优势和适用边界分别是什么?
  • max_turns 这类迭代上限在设计中应该扮演什么角色?

对应题目(4)

题目归类由 AI 判断,仅供参考。

练习这 4 道题