45 lines
12 KiB
Markdown
45 lines
12 KiB
Markdown
# 异步工具调用的场景与运行机制
|
||
|
||
截至 2026-11-12,编码 Agent 里可核验的场景包括等人回答、跑长命令、派子 Agent、监听持续事件、等待远端工具和生成图片。常见收益是把等待和独立工作重叠、让用户继续交互,或让会话空闲到结果到达,减少模型主动查询进度。结果可以稍后交回同一会话,并在推进到依赖它的步骤时汇合。本文依据官方文档、源码和上游测试快照,未现场运行各产品。 [OpenAI 异步工具指南](https://developers.openai.com/api/docs/guides/async-tool-calling)、[OpenCode 工具文档](https://opencode.ai/docs/tools/)、[Claude Code Monitor](https://code.claude.com/docs/en/tools-reference#monitor-tool)
|
||
|
||
## 一个调用的生命周期
|
||
|
||
1. **发起。** 模型发出工具调用,运行时将它绑定到会话、调用 ID 和实际任务。有的工具调用保持待完成,有的工具先返回任务 ID 或收件回执,之后用独立的会话事件报告结果。 [OpenAI 异步工具指南](https://developers.openai.com/api/docs/guides/async-tool-calling)、[DeepSeek Bash 后台分支](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/shell/tool-bash/src/index.ts#L498-L530)
|
||
2. **继续。** 执行器等待命令退出、远端结果或人回复。Agent 可以处理新的输入,或做当前目标下的独立工作。走到依赖该结果的步骤才等。OpenAI 指南示范用 `wait_for_tasks` 等一组所选任务,应用先按原调用 ID 交付各项结果,再返回等待工具的状态。DeepSeek Harness 的 `job_output(wait: true)` 可以被当前轮的 steer 提前结束,任务继续运行。 [OpenAI Wait Tool](https://developers.openai.com/api/docs/guides/async-tool-calling#add-a-wait-tool)、[DeepSeek 等待与 steer](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/jobs/tool-jobs/src/index.ts#L461-L515)
|
||
3. **交付。** 执行器取得完成、失败或取消状态。结果可以来自进程退出、应用回调、轮询、订阅或用户回复。调用 ID 或任务 ID 负责归属,运行时决定何时让模型看到事件。DeepSeek Harness 可以在安全边界投递,也可按配置唤醒空闲 Agent。Codex 的后台 Hook 在活跃轮次将输出送入下一次模型请求,空闲时等用户的下一轮。 [DeepSeek 完成投递](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/jobs/tool-jobs/src/index.ts#L355-L410)、[Codex 后台 Hook](https://learn.chatgpt.com/docs/hooks#run-hooks-in-the-background)
|
||
|
||
## 工具场景
|
||
|
||
| 场景 | 等待期间可以做什么 | 结果与产品证据 |
|
||
| --- | --- | --- |
|
||
| **向用户提问** | 问兼容范围时先读现有调用方、整理改动影响。得到回答后才选方案。 | Codex CLI 有异步提问能力。Responses API 指南示范先问报告受众,等待期间查资料。OpenCode 内置 `question` 会暂停执行,Claude Agent SDK 的 `AskUserQuestion` 由阻塞的 `canUseTool` 回调收集答案。 [Codex CLI 0.153.0](https://learn.chatgpt.com/docs/changelog?date=2026-09-03#:~:text=Structured%20asynchronous%20questions)、[OpenAI 异步提问示例](https://developers.openai.com/api/docs/guides/async-tool-calling#add-a-tool-to-ask-the-user-for-input)、[OpenCode Question](https://opencode.ai/docs/tools/#question)、[Claude SDK 用户输入](https://code.claude.com/docs/en/agent-sdk/user-input) |
|
||
| **Shell、构建、回归测试** | 测试运行时查看 diff、解释改动或阅读与构建独立的代码。等退出码和日志回来后判断是否通过。 | OpenCode 和 Claude Code 支持后台 shell。DeepSeek Harness 的 Bash 返回 `job_id`,完成通知可附短输出,长输出通过 `job_output` 取回。 [OpenCode Shell](https://opencode.ai/docs/tools/#shell)、[Claude Code 后台 Bash](https://code.claude.com/docs/en/interactive-mode#background-bash-commands)、[DeepSeek Bash 文档](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/shell/tool-bash/README.md#L60-L62) |
|
||
| **子 Agent** | 主 Agent 实现改动,子 Agent 在独立上下文里只读审查测试边界。主 Agent 在汇总时再用结论。 | OpenCode `subagent` 的 `background: true` 立即返回,结束时通知父会话。Claude Code 支持后台子 Agent,需要权限时可把请求送到主会话。 [OpenCode Subagent](https://opencode.ai/docs/tools/#subagent)、[Claude Code 子 Agent](https://code.claude.com/docs/en/sub-agents#run-subagents-in-foreground-or-background) |
|
||
| **持续监听** | 持续看测试日志、开发服务或 WebSocket 上的新事件,Agent 平时可继续交互,只在相关事件到达时处理。 | Claude Code Monitor 在后台监听命令输出或 WebSocket,运行过程中可产生多次事件,有超时和取消。流式展示普通前台命令的日志也会看到新行,是否放行 Agent 的下一步取决于工具是否让出执行。 [Claude Code Monitor](https://code.claude.com/docs/en/tools-reference#monitor-tool) |
|
||
| **远端工具、回调** | 启动一个慢查询、远端校验或 CI,先整理本地证据。完成事件回来后汇合结论。 | MCP Tasks 扩展规定任务 ID、轮询或订阅、输入请求和终态结果。Claude Code 2.1.319 明确支持把 MCP 服务返回的 Task 放到后台,完成后告知 Claude。GitHub 提供 `workflow_run.completed` 事件,产品可据 run ID 关联原会话,关联与唤醒由应用实现。 [MCP Tasks 扩展](https://modelcontextprotocol.io/extensions/tasks/overview)、[Claude Code 2.1.319](https://code.claude.com/docs/en/changelog#:~:text=Added%20support%20for%20MCP%20tasks%20in%20the%20terminal)、[GitHub workflow_run](https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_run) |
|
||
| **长耗时产物** | 图片生成或远端报表导出时先写文案、核对数据或解释内容。等产物 ID、路径或链接后再预览并交付。 | OpenCode 的实验性图片工具在后台运行,完成后回传保存路径。导出报表和转码属于同一调度模式的示例,具体工具需要自行提供启动、完成和失败语义。 [OpenCode Image Generate](https://opencode.ai/docs/tools/#image-generate) |
|
||
| **后台 Hook** | 一次会话事件触发额外的背景资料采集或检查,主操作先继续。 | Codex 支持在后台执行命令 Hook,并在下一次可用模型请求中交付信息。Hook 由生命周期事件触发。需要约束触发操作的审批、拒绝或重写仍走同步 Hook。 [Codex 后台 Hook](https://learn.chatgpt.com/docs/hooks#run-hooks-in-the-background) |
|
||
|
||
OpenCode 2.2.6 的无交互 `opencode run` 会等自己发起的后台 shell、子 Agent 和图片任务结算,允许模型处理结果后再退出。持续运行的开发服务或 watcher 因此可能一直占住这次 CLI run。后台执行解决的是会话里的等待方式,进程什么时候退出由 CLI 生命周期决定。 [OpenCode 2.2.6](https://opencode.ai/changelog/v2.2.6)
|
||
|
||
## 结果如何回到 Agent
|
||
|
||
**Codex CLI 0.167.1。** `request_user_input_async` 发出一个包含问题、与调用 ID 关联的 agent message,然后立即结算工具调用,返回 `{"accepted":true}`。这个回执表示已提交问题。TUI 把用户回答编码成带 `questionItemId` 的 `<send_user_message_question_reply>` 消息。会话仍在运行时,提交走 `turn/steer`。上游场景测试覆盖了这一路径,下一次模型请求的快照里,实际回答是一条 role 为 `user` 的消息,此时原工具调用已经结束。 [Codex 提问工具](https://github.com/openai/codex/blob/rust-v0.167.1/codex-rs/core/src/tools/handlers/request_user_input_async.rs#L131-L147)、[TUI 回答编码](https://github.com/openai/codex/blob/rust-v0.167.1/codex-rs/tui/src/bottom_pane/async_questions/state.rs#L126-L170)、[TUI 提交路径](https://github.com/openai/codex/blob/rust-v0.167.1/codex-rs/tui/src/app/user_turn_submission.rs#L27-L54)、[场景测试](https://github.com/openai/codex/blob/rust-v0.167.1/codex-rs/core/tests/suite/scenarios.rs#L893-L910)、[请求快照](https://github.com/openai/codex/blob/rust-v0.167.1/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_async_question_and_answer.snap#L38-L50)
|
||
|
||
**Responses API 原生异步工具。** 应用把自定义提问函数声明为 `async: true`。模型可以发出待完成的调用并继续,应用负责展示问题、收答和绑定原 `call_id`。答案作为稍后的 `function_call_output` 发回,续接最新的 `previous_response_id`。如已经发起显式等待,先回传原工具结果,再回传 wait 状态。这是一种可用的 API 协议,Codex CLI 采用上段所述的回执和用户消息。 [OpenAI 异步提问与结果回传](https://developers.openai.com/api/docs/guides/async-tool-calling#return-the-users-answer)
|
||
|
||
**DeepSeek Harness 的后台任务。** 它也用会话消息承载稍后才到的结果。完成通知由 `createUserMessage` 构造,来源标为 `tool-jobs`。Agent 忙时通过 `inject` 在下一步处理。启用了唤醒投递且预算允许时,空闲 Agent 通过 `followup` 继续。长输出可留在任务服务,模型收到通知后按任务 ID 读取。 [DeepSeek 结果投递](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/jobs/tool-jobs/src/index.ts#L377-L410)
|
||
|
||
## 容易混用的交互
|
||
|
||
| 机制 | 等待或交付发生在哪里 |
|
||
| --- | --- |
|
||
| **同步工具** | 当前模型步骤等待结果。宿主可用 `await` 或异步 I/O 实现底层执行,会话仍受工具结果约束。 |
|
||
| **并行工具调用** | 同一步发出多项调用,例如并行读三个文件。应用可以并发执行并等待这一批,再发起下次模型推理。多项异步任务也可重叠执行,并让后续推理先做其他工作。具体组合受协议约束,例如 Responses API 的 Multi-agent 模式要求避免把异步工具和 parallel tool calls 混用。 [OpenAI 异步工具限制](https://developers.openai.com/api/docs/guides/async-tool-calling#compatibility) |
|
||
| **显式等待** | 模型在依赖处等待选定任务,其他后台任务仍可推进。Responses API 示例是应用定义的 `wait_for_tasks`,其他运行时可以提供阻塞等待、只读状态或完成通知。 |
|
||
| **Steer 与 Queue** | Steer 把新的用户输入送入当前轮。Queue 让新输入在后续轮次执行。问题答案有自己的关联 ID,即使实现上复用了用户消息通道。 |
|
||
| **确认和授权** | 例如用户确认发布前,草拟说明和核对差异可以继续,发布动作等明确结果。权限决策属于受控操作的前置条件,异步展示批准 UI 也要保留这条依赖。 |
|
||
| **后台运行整轮** | Responses API `background: true` 把整个 response 放到后台并给调用方状态查询,作用范围是 response。独立云 Agent、另开会话和后台子 Agent 各有自己的运行状态。子 Agent 如果通过工具调用启动并把结果交回父会话,也可以构成父会话的一次异步工具调用。 |
|
||
|
||
这些区别可用三个观察点核对。工具启动后模型能否推进,结果以什么 ID 和形态回到模型,空闲时是否自动恢复。协议可以支持其中一部分。例如 Claude Managed Agents 的自定义工具通过事件交回结果,普通单线程调用可以停在 `requires_action`,有其他工作流线程运行时会话整体可以仍是 `running`。单凭 HTTP 请求先返回或结果通过 webhook 送达,无法判断模型在等待期间的进度。 [Claude Managed Agents 会话事件](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#return-a-custom-tool-result)、[OpenAI Background Mode](https://developers.openai.com/api/docs/guides/background)、[Codex Steer 与 Queue](https://learn.chatgpt.com/docs/prompting#steering-and-queuing)、[Claude SDK 审批与输入](https://code.claude.com/docs/en/agent-sdk/user-input)
|