模型在**同一条消息里发多个 tool_use** 同一轮发出多个 agent 工具调用 一次 agent 调用传 prompts 数组 的区别是什么样子的,同一轮发出多个 agent 工具调用 是只的是 #sym:executeTools 的calls有多个吗

最后发布时间 : 2026-10-19 23:48:52 浏览量 :

是的,“同一轮发出多个 agent 工具调用”就是 executeTools 的 calls 里有多个 Name == "agent" 的元素。三种说法其实是三个不同层次的描述,下面拆开看。

1. 三个概念对应的代码层次

说法出现位置代码事实
模型在同一条消息里发多个 tool_useProvider 协议层(assistant message)provider.Response.ToolCalls 长度 > 1
同一轮发出多个 agent 工具调用native.Run 的一次循环executeTools(ctx, executor, response.ToolCalls, rt) 的 calls 里有多个 Name == AgentToolName
一次 agent 调用传 prompts 数组工具内部calls 只有 1 个,agentTool.Execute 里 len(tasks) > 1,走 runBatch

response.ToolCalls 直接就是 executeTools 的 calls,所以前两者是同一件事的两种叫法:协议层叫“多个 tool_use”,执行层叫“calls 有多个”。

2. 形态对比

A. 多个 agent tool_use(标准协议形态)

模型一条 assistant 消息里输出两个调用:

{
  "role": "assistant",
  "content": "我并行派两个子 Agent 去查。",
  "tool_calls": [
    { "id": "call_1", "name": "agent", "arguments": "{\"prompt\":\"查 internal/agent 下的工具注册\"}" },
    { "id": "call_2", "name": "agent", "arguments": "{\"prompt\":\"查 internal/handler 下调用 AgentService 的位置\"}" }
  ]
}

进入 native.Run:

response.ToolCalls = [call_1, call_2]
        ↓
executeTools(calls=[call_1, call_2])
        ↓  两个 goroutine 并发(上限 maxConcurrentAgentCalls=4)
results = [
  {ToolCallID:"call_1", Content:"..."},
  {ToolCallID:"call_2", Content:"..."},
]
        ↓
history 追加 1 条 tool 消息(含 2 个 ToolResult)
        ↓
父模型下一轮看到 2 份独立结果,自己写汇总

事件时间线:2 个 tool_call 事件 + 2 个 tool_result 事件。

B. 一次 agent 调用 + prompts 数组

模型只发一个 tool_use:

{
  "role": "assistant",
  "tool_calls": [
    {
      "id": "call_1",
      "name": "agent",
      "arguments": "{\"prompts\":[{\"name\":\"tools\",\"prompt\":\"查 internal/agent 下的工具注册\"},{\"name\":\"handlers\",\"prompt\":\"查 internal/handler 下调用 AgentService 的位置\"}]}"
    }
  ]
}

进入执行层:

response.ToolCalls = [call_1]          ← calls 只有 1 个
        ↓
executeTools(calls=[call_1])           ← 不会走并发分支(只有 1 个 agent call)
        ↓
agentTool.Execute → runBatch(2 tasks)  ← 并发发生在这里
        ↓
results = [{ToolCallID:"call_1", Content:"## tools\n...\n## handlers\n..."}]
        ↓
父模型下一轮看到 1 份已拼接好的结果

事件时间线:1 个 tool_call 事件 + 1 个 tool_result 事件。

3. 关键差异

维度多个 agent tool_use单个 agent + prompts
executeTools.calls 长度N(N 个 agent 调用)1
并发实现位置executeTools(maxConcurrentAgentCalls=4)agentTool.runBatch(默认 4,上限 MaxAgentConcurrency=8)
tool 消息里的结果数N 个独立 ToolResult1 个合并文本
父模型看到的形态多个独立结果,自行汇总一段带 ## name 标题的 Markdown
失败处理每个调用独立 IsError,互不影响失败项在合并文本里标 [error],整体仍是成功结果
协议兼容性标准(OpenAI/Anthropic 原生支持)自定义 schema,模型需按定义生成
时间线/UIN 个 tool_call/result 块,可逐个展示1 个块,内部过程不可见
节省轮次不省(父模型仍要汇总)省一次“父模型先组织再调用”的决策,但父模型仍要写最终答复
嵌套放大风险N × 每个内部批量单次批量内部上限 8

4. 一个容易混淆的点

executeTools 的 calls 里不只是 agent 调用。例如模型一轮发出:

calls = [bash, agent, agent, view]

实际行为是:

  • bash、view 在主协程顺序执行;
  • 两个 agent 在goroutine 并发执行;
  • 结果数组仍按 [bash, agent, agent, view] 顺序回填;
  • tool_result 事件:bash/view 执行完立即发;两个 agent 谁先完成谁先发(靠 tool_call_id 对应)。

5. 一句话总结

  • “同一条消息多个 tool_use” = “同一轮多个 agent 调用”:都是 response.ToolCalls / executeTools.calls 有多个元素,由执行器并发,父模型拿多份结果自己汇总。
  • prompts 数组:calls 只有一个元素,并发发生在 agentTool 内部,工具自己把多份结果拼成一份返回。

如果目标是“最贴近主流工具、最省维护”,用多个 tool_use 即可;prompts 批量模式是减少工具块数量、让 UI 更安静的可选糖。