# 异步 Agent Loop 是怎么调度的 DeepSeek 的设计文章在第 4.1 节把长工具调用拆成启动和完成两次交互。启动先返回「已启动」通知,工具继续运行。Agent Loop 可以在当前推理步结束后处理新输入,执行器在工具结束后把结果送回原会话。本文核对了文章中文镜像、DeepSeek Harness `fcc0ad4` 和公开原型 `6337fc8` 的源码、文档与上游测试,时序数字为假设值。 [设计文章 §4.1](https://telegra.ph/Super-agent-%E8%AE%BE%E8%AE%A1%E6%8E%A2%E7%B4%A2%E8%BF%88%E5%90%91%E6%9B%B4%E8%87%AA%E4%B8%BB%E7%9A%84-Agent-%E7%B3%BB%E7%BB%9F-10-31-2#:~:text=4.1%20%E5%85%A8%E5%8F%8C%E5%B7%A5%E4%BA%A4%E4%BA%92) ## 一个会话里发生了什么 在 DeepSeek Harness 的术语里,一次模型请求和它调用的工具组成一个 step,一个 turn 可以包含多个 step。Loop 在 step 边界取出可处理的输入,组装下一次模型请求。同步执行长命令时,当前 step 要等命令的最终结果。命令实现即使用了 JavaScript 的 `async/await`,同一会话仍在等这个 step 结束。异步工具则把工作交给后台执行器,返回 `job_id`,让当前 step 可以结束。此后用户消息和后台完成通知都可以成为模型的输入,后台任务的进度独立推进。 [Turn 与 Step](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/docs/architecture.md#L81-L112)、[Bash 工具的两个分支](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/shell/tool-bash/src/index.ts#L498-L530) 公开原型的 Worker 会在前台 shell 命令等待超过 5 秒时把命令交给后台。Harness 也支持直接使用 `run_in_background`,任务获准后先返回 ID,启动失败可以随后以失败结果结算。完成通知会进入正在运行的 Agent 的下一步,或者在开启唤醒投递时让空闲 Agent 发起后续一轮。短输出可以随通知一起返回,长输出留给 `job_output` 读取。 [原型的 Worker 调度](https://github.com/deepseek-ai/dsh-superagent-prototype/blob/6337fc823ce20251c8de8085948165c81aaa701e/docs/003-execution.md#L23-L45)、[Bash 工具文档](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/shell/tool-bash/README.md#L60-L62)、[完成通知的投递](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/jobs/tool-jobs/src/index.ts#L355-L410) 如果后续工作确实依赖结果,Agent 可以调用 `job_output(wait: true)`。Harness 的这个等待可以被当前 step 的用户 steer 提前结束,返回仍在运行的状态,让新消息进入下一步,原命令继续运行。排给后续 turn 的消息会继续等待。上游测试分别覆盖这两条路径,此处核对了测试代码。 [等待与 steer 的实现](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/jobs/tool-jobs/src/index.ts#L461-L515)、[上游测试](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/jobs/tool-jobs/tests/steer-wait.spec.ts#L88-L150) ## 等一分钟的测试,谁省下了时间 假设修完过期判断后启动一组本地回归,耗时 60 秒。第 10 秒你问「把刚才的 diff 给我看一下」,这一步只读现有改动,假设模型处理和回答用 3 秒,异步版此时可以开始下一步。 | 从测试启动计时 | 同步调用 | 异步调度 | | --- | --- | --- | | 第 0 秒 | 当前 step 开始等待测试结果 | 后台任务开始,工具先返回 `job_id` | | 第 10 秒 | 追问已发送,当前 step 仍在等测试 | Loop 开始处理追问 | | 第 13 秒 | 测试仍在跑 | Agent 已回 diff,测试继续跑 | | 第 60 秒 | 测试结束,Loop 可以处理追问 | 测试结束,完成通知交回原会话 | | 第 63 秒 | Agent 回 diff | Agent 可以已经在汇报回归结果 | 在这个示意里,追问到回答从 53 秒变成 3 秒,测试本身两边都是 60 秒。若追问换成「测试通过了吗」,第 60 秒才有最终结果,前面只能回答当前状态。若等待期间还有独立工作,例如只读检查同一改动的边界条件,就可能把这段工作和回归重叠起来。若每一步都依赖前一步结果,或者工具本身很快,重叠空间就很小。结果自动投回会话还能让 Agent 在命令完成后自行继续,用户可以等汇报再回来。 并发带来的成本落在执行器和会话管理上。任务要有明确的归属、状态和取消方式,完成结果要对应启动它的任务。如果测试途中改了代码,已有结果应标明对应的版本,必要时重新验证。后台进程也会占用资源。Harness 的本地 job 服务提供并发限制,任务在所属 Agent 被释放或 Harness 关闭时结束。持久化与重启恢复需要额外机制。 [本地 Job 服务与生命周期](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/jobs/jobs-local/README.md#L10-L23) ## 和 T3 Code 的关系 T3 的 Queue 表达「等后续一轮」,Steer 表达「交给当前一轮」。输入框接受了消息、Provider 收到了消息、模型把它纳入下一次请求,是三个不同时间点。Provider 决定当前工具会继续、等待还是被中断。T3 与文章直接对应的机制是后台完成事件,例如 Codex 适配器在符合条件的命令结束后提交通知,T3 服务把它送回原线程,当前轮忙时排在后面。开另一个会话可以并行做另一件事,而这个例子关注同一会话如何继续处理输入并接回测试结果。 [T3 的 Queue 与 Steer](https://github.com/pingdotgg/t3code/blob/a536ff7ac0c080190f098965d0a0d244caf5b211/docs/user/composer.md#L63-L80)、[Codex 后台结果](https://github.com/pingdotgg/t3code/blob/a536ff7ac0c080190f098965d0a0d244caf5b211/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts#L5179-L5238)、[T3 同线程续跑](https://github.com/pingdotgg/t3code/blob/a536ff7ac0c080190f098965d0a0d244caf5b211/apps/server/src/orchestration-v2/ProviderContinuationService.ts#L182-L205) ## 周五的演示边界 两遍都跑同一个本地测试,输入同一个只读追问,分别展示同步等待与后台执行。模型用固定桩,测试命令真实运行。记录测试启动、追问到达、Loop 将追问交给模型桩、测试退出、结果进入会话的时间。让观众看见异步版在测试还运行时已经回答,以及结束后会话确实收到退出码。Demo 验证交互顺序,现场时长用于说明等待发生在哪里。