首页 / 文章

Claude工具使用端到端:三轮迭代中的智能体循环

Claude工具使用采用端到端方式:通过代理循环发起一次Messages API请求,其中包含工具定义、工具选择、并行调用以及工具结果处理规则。

发布者:REACTAPP.TOP阅读约 19 分钟4379 词

来源: Part III | ‘tool_use’, End to End: The Agentic Loop in Three Iterations

为何需要自行编写循环代码

通过 Messages API 直接使用 tool_use(先调用 anthropic.Anthropic(),再使用 client.messages.create(...))可以让您自行控制循环逻辑。您的代码负责声明工具、检查停止原因、解析 Claude 的请求、执行这些请求并返回执行结果。手动编写这样的循环是所有工具使用系统的基础,也是 Claude Certified Architect 课程中的标准内容。

首先需要提醒的是:Messages API 是无状态的,每次调用之间不会保留任何信息,因此每个请求都需要包含完整的对话内容;而使用工具的环节通常需要多次请求,而非仅一次,会不断重复直到 Claude 获得所需信息。其余流程则通过单次请求完成整个循环。

这个演示示例是一个小型日程安排助手。用户会询问是否需要带伞去明天的伦敦会议,以及应该何时出门才能准时到达。要回答这个问题需要三项独立的信息:会议详情、天气预报和出行时间。请观察Claude是如何将问题分解为三次迭代处理的,以及有多少工作由代码完成而非模型。

1. 循环的结构

每一次使用工具的交互都遵循一个四阶段的循环:

  1. 在请求中声明所需工具。
  2. Claude会请求其中一项或多项工具,通过stop_reason: "tool_use"来标识。
  3. 你的代码会运行这些工具并收集结果。
  4. 你将结果返回并再次调用API。

第二阶段到第四阶段会重复执行,每次循环都算作一次迭代。当 Claude 不再请求使用工具并返回正常结束信号(end_turn)或其他停止原因时,循环即结束。由于该 API 是无状态的,你需要自行构建对话内容并在每次调用时重新发送。除非你的代码推动对话进展,否则一切都不会向前发展。

2. 定义工具

Claude 只能使用你明确指定的工具。这些工具存储在请求的顶层 tools 参数中,该参数包含一个定义数组。只要存在 tools 参数,API 就会在用户消息之前根据这些工具生成一个隐藏的系统提示语。这个隐藏提示语也会产生费用:每个工具的名称、描述和结构都会在每次请求中占用输入令牌,即便 Claude 从未实际使用过该工具。

每个客户端工具需要三个字段:

  • name 是一个唯一的标识符,必须符合 ^[a-zA-Z0-9_-]{1,64}$ 的格式。该标识符会出现在 tool_use 块中,你的代码可利用它来调用相应的函数。
  • description 以通俗语言描述该工具的用途、适用场景以及每个参数的含义。没有其他内容比这更能影响正确的调用方式,因此应将其写成规范说明而非简单标签。
  • input_schema 是参数的 JSON Schema,其中规定了参数的类型、是否为 required 以及各参数的具体含义。

以下是示例中的三个工具:

"tools": [
  {
    "name": "get_calendar_event",
    "description": "Look up a calendar event by a natural-language query. Returns the event's start time and location.",
    "strict": true,
    "input_schema": {
      "type": "object",
      "properties": {
        "query": { "type": "string", "description": "What to search for, e.g. 'meeting in London tomorrow'" }
      },
      "required": ["query"],
      "additionalProperties": false
    }
  },
  {
    "name": "get_weather",
    "description": "Get the forecast for a city on a given date. Returns condition and temperature in Celsius.",
    "strict": true,
    "input_schema": {
      "type": "object",
      "properties": {
        "city": { "type": "string", "description": "City name, e.g. 'London'" },
        "date": { "type": "string", "description": "ISO date, e.g. '2026-07-14'" }
      },
      "required": ["city", "date"],
      "additionalProperties": false
    }
  },
  {
    "name": "get_travel_time",
    "description": "Estimate door-to-door travel time between two places. Returns minutes.",
    "strict": true,
    "input_schema": {
      "type": "object",
      "properties": {
        "origin": { "type": "string", "description": "Starting location" },
        "destination": { "type": "string", "description": "Ending location" }
      },
      "required": ["origin", "destination"],
      "additionalProperties": false
    }
  }
]

需谨慎定义这些内容。Anthropic关于高效工具的指导可归结为几项习惯:

  • 工具名称要具体明确。 get_current_weather比weather能向Claude提供更多信息。
  • 参数的命名与描述必须清晰无歧义。建议使用user_id而非user,以确保传递正确的值;同时要对每个参数进行描述,因为即使字段命名恰当但描述模糊,仍可能引发格式错误的输入。
  • 保持工具集规模较小。过大的工具集会使生成的系统提示变得杂乱,进而导致模型选择混乱。
  • 对所有内容进行验证。架构仅能约束结构而非意图,因此需检查代码接收的每一项输入。

在这些定义中还有两点值得仔细研究。

  1. strict: true最为重要。它位于每个工具的最高层级,与name、description和input_schema并列,该设置会为工具的参数启用语法约束采样,从而确保模型无法生成违反预设结构的input。如果不启用此设置,Claude虽然大多会输出有效的参数,但仍可能出现错误,比如本应输入数字2却出现了“2”这样的字符串,或者使用了enum中未包含的值,又或是缺少了必填字段。这类错误都会导致调用该功能的代码出现运行时故障。启用strict: true后,任何tool_use块中的input都必定符合input_schema的要求,且其名称也一定是用户自定义的工具之一。
  • 有一个架构规则是必须遵守的:每个对象都必须设置additionalProperties: false,这也是它在上述所有工具中都会出现的原因。严格遵循这一规则是Anthropic结构化输出功能的一部分。在撰写本文时,该功能已可在Opus 4.8等当前型号上使用,而Sonnet 4.5等较旧型号则需要使用structured-outputs-2025-11-13这个测试版标识头。编译后的架构规范会被缓存24小时,因此有了热缓存就无需再进行编译操作。
  • 你还可以在工具定义中添加cache_control,以便在多次请求之间缓存这些定义,从而避免重复处理费用,当工具集规模较大且稳定时,这种方式会带来显著优势。

    定义仅用于描述输入内容

    工具定义仅涵盖输入部分,也就是 Claude 发送给你的内容。这一点让那些要求指定返回类型的框架使用者感到意外。简单来说:

    • 定义中必须包含 name、description 和 input_schema,而 strict、input_examples 和 cache_control 则为可选附加内容。既没有 output_schema,也没有 error_schema。
  • 返回值由您的代码在运行时确定,随后通过第6节中描述的tool_result块返回。content字段可以是字符串或块列表,由于没有对应的架构规范可供校验,API不会对其进行检查。需要注意的一点是:某些工具可能会返回DTO对象,而标准解析器会通过其toString方法将其转换为普通字符串。虽然使用这种转换后的形式也能正常工作,但并非最佳方案,建议将DTO转换为结构化的JSON格式。
  • 错误同样属于运行时问题。若调用失败,应在tool_result中设置is_error: true,并在content中提供说明性消息;该接口并未定义明确的错误类型。默认情况下,SDK会将抛出的异常作为错误结果传递给Claude,由Claude负责处理。
  • 这种不对称设计是刻意为之的。定义的存在是为了向 Claude 指示如何调用工具。生成的结果只是重新加入循环的额外上下文,因此除了通过 tool_use_id 将其与请求关联外,并不需要额外的契约。
  • 需要保证输出格式吗?在返回任何内容之前先在代码中自行检查,或者对 Claude 根据这些结果生成的最终答案应用结构化输出(output_config.format)。MCP 工具会自带 outputSchema,但这属于 MCP 层的功能,而非原生定义的功能。
  • 控制 Claude 是否调用工具

    tool_choice 决定了在某一轮中是否可以调用工具以及调用哪些工具。

    • 该字段位于请求的顶层,与 tools 和 messages 并列,不属于任何特定的工具。
    • 它限制的是 Claude 可以生成的输出内容。推理过程不受影响,因为 Claude 无论如何都会读取整个提示词。
    • 它的作用范围仅限于单个请求,而非全局。该 API 是无状态的,因此 tool_choice 只能控制与其一同发送的响应,即一次迭代。若要在多次迭代中保持该设置,需每次重新发送。在每次迭代中更改该设置是可行的:可以提前指定工具,稍后使用 auto,最后用 none 获取文本答案。

    该对象的格式如下:

    {
      "type": "auto" | "any" | "tool" | "none",
      "name": "get_weather",              // required ONLY when type is "tool"
      "disable_parallel_tool_use": false  // optional; default false
    }
    

    type 是必填项,需从四个值中选择其一。name 仅在 type 为 "tool" 时才需要。disable_parallel_tool_use 是可选项,用于限制每次可调用的工具数量。

    auto。 由 Claude 决定:不使用工具、使用一个工具、使用多个工具,或直接给出文本回复。允许并行调用,也可返回纯文本回复,Claude 还可在调用前进行说明。当提供了 tools 时,此为默认模式。将 disable_parallel_tool_use: true 设为真时,每次只能使用一个工具,但仍可选择不使用工具。该模式适用于需要在不同轮次执行不同操作的聊天助手和智能体循环系统。它是唯一允许 Claude 在行动前出声思考的模式,也是除 none 外唯一支持扩展思考模式的模式。

    any。 Claude必须调用某个工具,但可自行选择。至少会进行一次调用,由于默认启用并行处理,因此可能会返回多个tool_use块,而不一定是仅一个。只有当设置disable_parallel_tool_use: true时才会仅发生一次调用。不允许出现纯文本,也没有前置的推理过程,且这并非默认设置。在需要通过工具获得结果且工具可能不同的情况下使用此选项,比如路由器和分类器之类的场景。

    tool。 Claude必须调用你指定的工具,因此必须提供name参数。该工具至少会被调用一次(也可能同时被多次调用),并且恰好有一次调用会设置disable_parallel_tool_use: true。不得输出纯文本,也不得先给出推理过程。此模式适用于单次结构化提取、作为必经的第一步,或采用通过工具处理JSON数据的模式,例如{"type": "tool", "name": "get_weather"}。如果请求中指定的工具不在tools列表中,系统会返回400错误;除非禁用了并行使用,否则强制指定工具也不限制其只能被调用一次。

    none。 即使相关工具已被定义,Claude 在这一轮也无法调用它们,且 stop_reason 不能为 tool_use。纯文本是被允许的,前置推理内容也同样可行。当未提供 tools 时,这便是默认设置。可利用该模式强制进入仅输出文本的轮次,例如用于最终综合输出,同时无需修改 tools 数组。该模式无需编辑数组即可禁用工具,但切换至此模式仍会使缓存的消息块失效。

    有一些贯穿各处的细节容易被忽略:

    • 强制模式会省略前置内容。 在 any 或 tool 模式下,API 会预先填入助手的回应内容,因此无论用户提出什么请求,tool_use 块都会仅包含工具调用信息,而不包含任何解释或推理内容。若需显示推理过程,则应选择 auto 模式。
  • 扩展思维模式需要 auto 或 none。 无法与强制使用工具的模式结合,API 会拒绝这种组合。
  • disable_parallel_tool_use 的计数规则。 auto 表示最多使用一个,any 和 tool 表示恰好使用一个,而默认值 false 允许在 Claude 4 模型上进行并行调用。
  • 缓存成本。 在不同请求之间更改 tool_choice 会使已缓存的消息块失效。工具定义和系统提示仍会保持缓存,但对话内容需要重新处理。
  • 需与 strict 搭配使用。 tool_choice 负责确保工具被调用,而 strict 负责确保其参数符合规范。二者结合后,每轮对话都能返回类型明确、结构化的结果。
  • 在请求中:

    response = client.messages.create(
        model="claude-opus-4-8",
        max_tokens=1024,
        tools=tools,
        tool_choice={"type": "any", "disable_parallel_tool_use": True},  # must call exactly one tool
        messages=messages,
    )
    

    由此可得出两条设计原则:对于确实需要生成工具结果的步骤,应使用强制模式;而如果文本回复即可满足需求,则保持使用auto模式。此外,当强制使用某个分类工具时,需在其枚举值中添加"other"选项,这样Claude就可以直接拒绝该类别,而非错误地标记它无法分类的输入。

    3. 第一次迭代:Claude请求使用工具

    请求信息会将工具数组、您选择的tool_choice以及用户的问题整合在一起:

    messages = [{"role": "user",
                 "content": "I have a meeting in London tomorrow
                             — should I bring an umbrella, and
                             when should I leave home to be on time?"}]
    
    response = client.messages.create(
        model="claude-opus-4-8",
        max_tokens=1024,
        tools=tools,                    # the three strict definitions from Section 2
        tool_choice={"type": "auto"},   # let Claude decide whether, and what, to call
        messages=messages,
    )
    

    Claude分析了该问题,发现其三个需求中有两个彼此无关:日历查询与天气预报可以同时进行。因此它在同一响应中请求了这两种工具。将独立调用的结果合并返回的方式称为并行工具使用,这会在单次轮次中以多个tool_use块的形式呈现。

    {
      "id": "msg_01...",
      "role": "assistant",
      "stop_reason": "tool_use",
      "content": [
        { "type": "text", "text": "Let me check your meeting details and the London forecast." },
        { "type": "tool_use", "id": "toolu_01Cal", "name": "get_calendar_event",
          "input": { "query": "meeting in London tomorrow" } },
        { "type": "tool_use", "id": "toolu_01Wx", "name": "get_weather",
          "input": { "city": "London", "date": "2026-07-14" } }
      ]
    }
    

    该响应中有三点值得注意。

    1. stop_reason 的值为 tool_use,说明当前轮次尚未完成,Claude正在等待你的输入。
    2. content 数组中包含多种类型的内容。通常会有一个用于叙述的 text 块,其后才是 tool_use 块,因此工具调用并不总是位于固定位置。
    3. 每个 tool_use 块都有一个以 toolu_ 为前缀的 id、一个 name,以及一个已符合你定义结构的 input 对象。

    应通过遍历内容并按类型进行筛选来查找这些工具调用,切勿通过索引查找:

    tool_calls = [block for block in response.content if block.type == "tool_use"]
    

    这样就会产生两次调用。每个 id 都是用于获取该次调用结果的标识,而 id 与结果的对应关系正是 Claude 在有多个待处理问题时匹配答案的方式。

    给 Claude 4 模型的提示:扩展思考功能可能是交错进行的,因此推理既可以在工具调用之前发生,也可以在工具调用之间发生。开启思考功能后,需将思考过程的相关内容与助手的后续回复一起发送,否则后续回复会忘记之前的推理内容。

    4. 运行工具

    现在任务就交给了你的代码。Claude 已请求使用两个工具,但目前尚未执行任何操作。你需要执行它要求的所有操作,收集所有结果,然后通过一次回复将它们一并发送。Claude 会一直等待,直到它发起的每一次调用都有对应的返回结果。

    由于 Claude 并未指定执行方式,是并行还是串行由你决定。响应中只说明了要调用什么功能,而非如何安排调度。像日历查询和天气查询这类独立的调用可以同时进行,以降低延迟。当某个调用依赖于另一个调用的结果时,则需按顺序执行。模型会通过将独立任务分组为并行块,而将依赖性任务推迟到后续轮次来暗示这种依赖关系,第二轮处理就是如此。线程、异步处理或任务队列都是你代码中的选择,而非 API 的规定。

    在实际应用中,应将执行过程视为可能出错的不可靠 I/O 操作:

    • 为每个工具设置超时时间,避免单个缓慢的调用导致整个流程卡住。
    • 尽可能选择可重试的操作,因为在智能体系统中重试和重新执行是不可避免的。
  • 逐次处理故障。当两个并行运行的工具中有一个出现错误时,仍需为其返回结果(详见下文的is_error),而不要中断当前流程。
  • 监控循环运行情况。每个工具的延迟、调用次数、令牌使用量以及错误率,都能反映出智能体运行状态是否正常,或是正在逐渐恶化。
  • 5. 工具运行前后的步骤

    要掌控整个循环,就需要关注两个环节:即“Claude请求使用工具”与“工具开始运行”之间的时间间隔,以及“工具运行完成”与“结果返回”之间的时间间隔。这两个环节应当被赋予明确的名称,因为它们的设计质量直接决定了智能体是安全可靠还是存在风险。

    工具使用前步骤会在执行之前启动。它会检查请求的调用,并决定直接运行、修改或拒绝该调用。在此阶段进行规则执行,由Claude提出建议,最终由你的代码做出决定。

    工具使用后步骤则在执行之后、结果返回给Claude之前运行。它通过删除、截断、重新格式化或记录等方式处理原始输出。

    在客户端SDK的循环中,这些其实只是围绕执行过程封装的函数而已:

    for block in tool_calls:
        if not pre_tool_use(block):                       # validate / guard
            results.append(error_result(block.id, "Blocked by policy."))
            continue
        output = execute(block.name, block.input)         # run the tool
        output = post_tool_use(block, output)             # redact / log / reshape
        results.append(tool_result(block.id, output))
    

    在工具使用前的阶段,真正的约束机制才真正发挥作用,这是实现者的职责,而非模型的职责。常见的生产环境处理模式包括:

    • 根据自身的策略以及数据结构规范来检查传入的参数。
    • 直接拒绝任何具有破坏性的操作:对于shell工具来说是rm -rf,对于数据库工具则是DROP TABLE。
    • 对于任何可能造成重大影响的操作,如向生产环境写入数据,都需要人工审批。
    • 在参数被发送到外部服务之前,先将其中的敏感信息清除。
    • 记录下每一次尝试调用的情况,以便进行审计和合规检查。

    工具使用后的阶段则负责处理返回的结果,在结果进入系统上下文之前移除凭证或个人数据,裁剪过大的数据包,并记录最终结果。

    6. 返回结果

    需以包含tool_result块列表的user消息来回复tool_use消息,列表中的每个块对应响应中的一个tool_use块,且每个块都与其tool_use_id相关联。首先将助手的回复加入正在处理的消息列表,然后再将结果作为新的用户消息加入。第一次迭代的具体形式如下:

    {
      "role": "user",
      "content": [
        {
            "type": "tool_result",
            "tool_use_id": "toolu_01Cal",
            "content": "{\"start\": \"2026-07-14T15:00\", \"location\": \"Canary Wharf, London\"}"
        },
        {
            "type": "tool_result",
            "tool_use_id": "toolu_01Wx",
            "content": "{\"condition\": \"rain\", \"temp_c\": 12}"
        }
      ]
    }
    

    两种内容形式。结果的content可以是普通字符串、如示例中的序列化返回值,也可以是内容块的列表。这些块不仅包含文本,因为text和image可以混合使用,从而使图表或截图工具能够向Claude提供真实的图片。字符串适用于标量数据和JSON数据,而块列表则适用于多模态输出。

    一旦出现故障,请明确说明。 应用 is_error: true 标记失败的调用,并附上说明信息,例如“错误:未找到与该查询匹配的日历事件”,绝不能使用空值或仅写“失败”。在所有与结果相关的习惯中,这一点最为重要。明确的错误信息能让 Claude 使用其他参数重试、选择不同的工具,或向用户说明发生了什么,而隐藏的错误则会使循环陷入僵局。较大的结果还可以包含 cache_control,从而避免后续的重复处理成本。

    延续契约

    有三条严格的规则,每条都是常见的故障点:

    • 每个 tool_use 对应一个 tool_result。 若遗漏其中之一,请求将会因 400 错误而失败,因为模型期望每次工具调用都能得到一一对应的响应。
    • 重新发送相同的 tools 数组。 任何包含 tool_use 或 tool_result 块的请求都必须明确指定这些块所指向的工具,否则将被拒绝。
    • 结果构成独立的轮次。 应将 tool_result 块作为用户消息传递,并置于其内容的开头。该轮次之后的指令是不可使用的。一旦工具处理完成,任何新的指令都需通过单独的用户消息发送。

    所有结果均为不可信输入

    tool_result并非中立数据,而是会进入模型上下文并影响后续处理的文本。无论其来源是公开网络、用户提供的文档,还是任何你无法完全控制的系统,都可能包含旨在劫持智能体的指令,比如要求忽略先前的指示并泄露数据库内容。模型可能会遵从这些指令。这就是提示注入,在智能体循环中属于现实存在的生产风险,而非假设性问题。

    防御措施应体现在你设计的循环结构中:

    • 仅赋予智能体所需的最小权限。它没有的工具意味着它无法被诱使执行相应操作。
    • 在沙箱环境中运行有风险的工具。
  • 消毒功能可将输出转化为可执行的操作,例如通过将文件路径映射到固定目录来防止路径遍历。
  • 在工具使用后的步骤中加入删减功能,移除所有看似指令而非数据的内容。
  • 任何可能带来实际后果的操作都需经过人工审批。
  • 工具的结果绝不能授权工具本身无法完成的事情。

    应将结果视为需要整理的上下文。每个结果条目会在整个对话过程中一直显示在界面中,因此应尽量生成简短且结构清晰的输出:一致的JSON格式或带标签的字段比模型需要解码的散文更佳。当结果过多导致界面被占满时,可使用工具结果清除功能——这是一种上下文管理测试功能,会在达到限制时自动丢弃过时的结果。应在循环规模过大到填满界面之前使用该功能。

    7. 第二次迭代:依赖调用

    由于两种结果都已返回,API再次被调用,这次传入了完整的消息列表和工具定义。日历显示金丝雀码头的时间为15:00,天气预报则提示会下雨,但它仍然无法确定出发时间,因为行程时间取决于它刚刚才获取到的位置信息。正是这种依赖关系导致在第一次迭代时无法并行进行调用,只能留到后续轮次处理。

    {
      "role": "assistant",
      "stop_reason": "tool_use",
      "content": [
        {
          "type": "tool_use",
          "id": "toolu_02Tt",
          "name": "get_travel_time",
          "input": {
            "origin": "home",
            "destination": "Canary Wharf, London"
          }
        }
      ]
    }
    

    你让该工具经历与使用前检查、执行以及使用后处理相同的流程,最终返回一个结果:

    {
      "role": "user",
      "content": [
        { "type": "tool_result", "tool_use_id": "toolu_02Tt", "content": "{\"minutes\": 45}" }
      ]
    }
    

    该循环的结构与第一次迭代相同,只有调用次数及其依赖关系有所差异。需要记住的规律是:循环在每次迭代时形态都不变,会一直重复直到Claude停止提问。

    8. 第三次迭代:综合处理与end_turn

    第三次调用API时,Claude已经掌握了所有所需的信息,无需再使用其他工具,因此它会直接写出答案。end_turn会取代之前的停止原因,循环随之结束。

    {
      "role": "assistant",
      "stop_reason": "end_turn",
      "content": [
        {
          "type": "text",
          "text": "Yes, bring an umbrella — rain is forecast in London tomorrow, around 12°C. Your meeting is at 3:00 PM in Canary Wharf, roughly 45 minutes away, so leave home by about 2:00 PM to arrive with a buffer."
        }
      ]
    }
    

    总共包含三次迭代:两次并行调用、一次依赖调用以及一次综合处理。模型负责决定获取什么内容以及何时获取,而你的代码则负责控制每次调用的执行方式、是否允许调用以及如何返回结果。由于会重新发送完整的历史记录,上下文内容在每轮都会增加,但三轮就足以容纳所需信息。

    9. 代码中的完整循环

    迄今为止的所有操作都集中在客户端 SDK 的一个循环中。该循环会检查stop_reason,通过你预先设定的工具使用步骤和后续步骤来处理每个请求的工具,为每次调用返回一个结果,直到 Claude 完成工作为止。

    import anthropic
    client = anthropic.Anthropic()
    
    messages = [{"role": "user", "content": user_request}]
    MAX_ITERATIONS = 10
    for _ in range(MAX_ITERATIONS):
        response = client.messages.create(
            model="claude-opus-4-8", max_tokens=1024, tools=tools, messages=messages,
        )
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            break   # end_turn (or another reason): done
        results = []
        for block in (b for b in response.content if b.type == "tool_use"):
            if not pre_tool_use(block):   # your guard - may block the call
                results.append({"type": "tool_result", "tool_use_id": block.id,
                                "content": "Blocked by policy.", "is_error": True})
                continue
            try:
                output = execute(block.name, block.input)   # run it (concurrently if independent)
                output = post_tool_use(block, output)   # redact / log / reshape
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
            except Exception as e:
                results.append({"type": "tool_result", "tool_use_id": block.id,
                                "content": f"Error: {e}", "is_error": True})
        messages.append({"role": "user", "content": results})   # one result per tool_use
    print(response.content[-1].text)
    

    该循环阐明了若干要点。由于 API 不会保留任何状态,因此需要自行管理 messages。除 tool_use 之外的任何 stop_reason 都会终止流程。严格的迭代次数限制可防止智能体失控,因此切勿在生产环境中使用无限制的循环。各类故障,包括被阻塞的调用和异常,都会以 is_error 的形式返回,而不会导致当前轮次崩溃。完整代码还涵盖了 max_tokens(若轮次内容被截断,则需提高限制值或继续处理)以及 pause_turn(需重新发送的服务器与工具之间的轮次)。这两种情况都可能出现在同一个循环中。

    需牢记的检查清单

    • 工具需在 tools 数组中通过 name、description 和 input_schema 进行定义,且每次请求都会为这些定义支付输入令牌费用。
    • tool_choice 决定了是否调用工具:取值为 auto、any、tool 或 none。扩展思考功能仅适用于 auto 和 none 模式,而 strict: true 会强制要求参数完全符合规范。
    • 当 stop_reason 设为 "tool_use" 时,控制权会交由您处理。可通过筛选 content 中 type == "tool_use" 的内容来识别相关调用,绝不能依据位置来判断。
    • 并行工具使用指的是在单个响应中包含多个 tool_use 块。执行顺序——无论是并行还是顺序——由您决定,而非模型决定。依赖关系存在的调用会在后续轮次中处理。
    • 运行所有请求的工具并一次性回复:单个用户轮次中,每个tool_use都会通过tool_use_id关联到一个tool_result,且tools数组保持不变。若缺少结果则返回400错误。
    • 执行过程中的空白部分由你负责处理。在运行前进行验证与防护,运行后再进行处理与数据脱敏。这些都是你需要自行编写的步骤,而非可配置的功能。
    • tool_result.content可以是字符串或列表形式。出现错误时需将is_error: true并附上有用的提示信息,同时保持结果简洁且结构清晰。
    • 应将工具返回的结果视为不可信的上下文。通过最小权限原则、沙箱环境、数据净化以及对关键操作进行人工审核,可以有效防止注入攻击。
    • 循环会一直运行到end_turn为止,每个循环都有独立的状态,因此需要对迭代次数设置上限。

    相关阅读

  • 实用笔记:ARD内幕——代理式资源发现规范的实际运作方式 — 详细讲解实用笔记:ARD内幕——代理式资源发现规范的实际运作方式:包括相关契约、校验规则以及供采用该模式的团队使用的即插即用代码模板。