欢迎光临
我们一直在努力

别再让 Agent 干等 20 分钟!async subagents 把监督者从阻塞里解放出来

在这里插入图片描述

目录

  • 1. 干等的问题
  • 2. 六维对照
  • 3. 通信底座
  • 4. 配置写法
  • 5. 四个控制动作
  • 6. 有状态的含义
  • 7. 选型与组合
  • 8. 边界与坑
  • 总结
  • 外部引用

摘要

同步子代理把监督者卡在等待里,长任务期间用户得不到任何回应。async subagents(异步子代理)改成启动即返回 job ID,监督者继续陪聊,还能随时查进度、发后续指令、取消任务。本文拆解官方六维对照表、Agent Protocol 通信底座与 AsyncSubAgent 配置写法。

1. 干等的问题

上一篇讲同步子代理时,解决的是上下文膨胀(context bloat,子代理结果灌进主上下文导致 token 暴涨)。代价出在时间上:委派出去的任务会把监督者占住。

监督者(supervisor,负责拆任务和分发的父代理)发起调用后只能等。官方对同步版的定性很直接:调用阻塞监督者直到任务完成。任务越长,这段静默窗口越长。

async subagents(异步子代理)就是冲着这个窗口来的:启动立即返回,监督者继续与用户交互。本章先把问题还原清楚,再看立即返回到底改变了什么。

1.1 同步阻塞场景还原

场景很常见。用户说:帮我调研三个竞品,写一份对比摘要。监督者判断这是独立子任务,委派给同步子代理。

从这一刻起,监督者被挂起。它在这次调用返回前做不了任何别的事。

等待期间,监督者丢掉三种能力:

  • 回应能力:用户的追问进不来,界面只能转圈
  • 修正能力:需求变了,无法给运行中的任务追加指令
  • 止损能力:方向明显跑偏,也无法中途取消

下面这张时间线表还原全程,时长为示例场景,非实测数据。

时刻监督者状态子代理状态用户看到
T0 收到提问,拆解任务 未启动 正常对话
T1 发起同步委派,被挂起 开始工作 界面转圈
T2 阻塞等待 持续工作 长时间无响应
T3 想回话也回不了 持续工作 追问石沉大海
T4 拿到结果,恢复对话 已结束 一次性收到完整答案

静默窗口从 T1 到 T4,长度等于任务时长。任务跑 20 分钟(示例数据),用户就干等 20 分钟。

窗口内监督者的可用性是零。这是同步模式的结构性代价,与模型响应快慢无关。

把这张表与 1.2 节的时序图对照看,两种模式的差异集中在 T1 到 T4 这一段怎么过。

这段等待还有第二层代价。结果在 T4 一次性灌回监督者上下文,又推高 context bloat。上一篇只治了隔离,没治时间占用。

设想用户在 T2 改了需求,只要第四个竞品。同步模式下这条消息只能等 T4 之后才被处理,前面的计算多半作废。

同步版并非一无是处。官方的定位是:async subagents 构建在同步 subagents 之上。短任务、必须等结果的任务,同步版仍是对的选择。

1.2 立即返回的语义

先说白话:同步像在柜台前站着等出餐,异步像取号后找座位坐下。号就是凭据,餐好了会通知。

放到术语里,这个号叫 job ID(任务编号,后台任务的回执)。监督者发起启动动作后立刻拿到 job ID,然后转身继续陪用户聊。

注意两个变化。第一,监督者的对话线程没有被占住。第二,后台任务与监督者各自独立运行,监督者随时能查进度、发后续指令、取消任务。

下面这张时序图把异步模式的交互过程摆在一张图里,同步模式相当于把中间的 loop 段换成无限期挂起。

异步子代理

监督者

用户

异步子代理

监督者

用户

#mermaid-svg-6YPsCcHUtuHPjEin{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6YPsCcHUtuHPjEin .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6YPsCcHUtuHPjEin .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6YPsCcHUtuHPjEin .error-icon{fill:#552222;}#mermaid-svg-6YPsCcHUtuHPjEin .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6YPsCcHUtuHPjEin .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6YPsCcHUtuHPjEin .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6YPsCcHUtuHPjEin .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6YPsCcHUtuHPjEin .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6YPsCcHUtuHPjEin .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6YPsCcHUtuHPjEin .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6YPsCcHUtuHPjEin .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6YPsCcHUtuHPjEin .marker.cross{stroke:#333333;}#mermaid-svg-6YPsCcHUtuHPjEin svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6YPsCcHUtuHPjEin p{margin:0;}#mermaid-svg-6YPsCcHUtuHPjEin .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6YPsCcHUtuHPjEin text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-6YPsCcHUtuHPjEin .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-6YPsCcHUtuHPjEin .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-6YPsCcHUtuHPjEin .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-6YPsCcHUtuHPjEin .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-6YPsCcHUtuHPjEin #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-6YPsCcHUtuHPjEin .sequenceNumber{fill:white;}#mermaid-svg-6YPsCcHUtuHPjEin #sequencenumber{fill:#333;}#mermaid-svg-6YPsCcHUtuHPjEin #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-6YPsCcHUtuHPjEin .messageText{fill:#333;stroke:none;}#mermaid-svg-6YPsCcHUtuHPjEin .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6YPsCcHUtuHPjEin .labelText,#mermaid-svg-6YPsCcHUtuHPjEin .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-6YPsCcHUtuHPjEin .loopText,#mermaid-svg-6YPsCcHUtuHPjEin .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-6YPsCcHUtuHPjEin .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-6YPsCcHUtuHPjEin .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-6YPsCcHUtuHPjEin .noteText,#mermaid-svg-6YPsCcHUtuHPjEin .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-6YPsCcHUtuHPjEin .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6YPsCcHUtuHPjEin .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6YPsCcHUtuHPjEin .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6YPsCcHUtuHPjEin .actorPopupMenu{position:absolute;}#mermaid-svg-6YPsCcHUtuHPjEin .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-6YPsCcHUtuHPjEin .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6YPsCcHUtuHPjEin .actor-man circle,#mermaid-svg-6YPsCcHUtuHPjEin line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-6YPsCcHUtuHPjEin :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

loop

[后台运行期间]

帮我调研三个竞品

launch 启动后台任务

立即返回 job ID

已安排,先聊别的

进度怎样

check 查询状态

返回当前进度

还在跑,已完成一部分

任务完成,交回结果

汇总后给出答案

关键路径在第三步:启动立即返回 job ID,监督者从等待者变成管理者。图里的 loop 块就是监督者在后台运行期间可以反复做的事。

阻塞与否只是表象。官方把两种模式的差异拆成六个维度,下一章逐条对照。

2. 六维对照

两种模式的差异,官方文档给了一张六维对照表。这张表是本文的骨架,后面几章都在展开它的某一行。

先把表格原文完整放下,再挑最关键的三条细说。

2.1 官方六维表全文

下面是官方对照表的完整内容,左侧同步,右侧异步。

维度同步 subagentsasync subagents
Execution model(执行模型) 监督者阻塞直到完成 立即返回 job ID,监督者继续
Concurrency(并发) 并行但阻塞 并行且非阻塞
Mid-task updates(中途更新) 不可能 通过 update_async_task 发后续指令
Cancellation(取消) 不可能 通过 cancel_async_task 取消运行中任务
Statefulness(状态性) 无状态,调用间无持久状态 有状态,在自己的线程上跨交互保持状态
Best for(最适合) agent 应等结果再继续的任务 聊天中交互式管理的长运行复杂任务

逐行给一句白话解释:

  • 执行模型:同步是站着等,异步是拿了号就走
  • 并发:两者都能同时跑多个子代理,差别在监督者是否被拖住
  • 中途更新:同步一旦发出就无法改口,异步可以补话
  • 取消:同步只能等它跑完,异步随时能砍
  • 状态性:同步每次调用都是新人,异步记得住前文
  • 最适合:一次性交付的任务对聊天里养着的长任务

读这张表有个顺序。先看第一行定时基调,再看中间四行定能力,第六行定用途。

两侧的用词取自官方原文,本篇只做了排版,未改写措辞。

第一行里的 job ID 是全文的关键物件。它是后四个动作共用的定位键,第五章展开。

有两行需要单独点出。中途更新与取消这两行,同步侧写的是不可能,不是效果差。这是结构差异,不是参数差异。

中途更新与取消两行还带出了两个具体的 SDK 函数名:update_async_task 与 cancel_async_task。第五章会展开。

状态性一行牵出第六章,最适合一行牵出第七章。一张表读完,全文路线就有了。

2.2 六维里最关键的三条

六行里最要紧的是三行:非阻塞、可中途更新、可取消。

第一条是非阻塞。它由执行模型与并发两行共同构成:子代理并行工作,监督者同时继续与用户交互。这是异步版的立身之本,其余能力都建立在监督者还在线这个前提上。

第二条是可中途更新。update_async_task 让监督者给运行中的任务发后续指令。需求变化是常态,能补话意味着任务不必推倒重来。

第三条是可取消。cancel_async_task 能砍掉跑偏的运行中任务。它省下的不只是等待时间,还有注定作废的计算。

下面这张图把六个维度的两侧对照放进同一张图。

#mermaid-svg-sWxz0y2N3GDRSEHS{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sWxz0y2N3GDRSEHS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sWxz0y2N3GDRSEHS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sWxz0y2N3GDRSEHS .error-icon{fill:#552222;}#mermaid-svg-sWxz0y2N3GDRSEHS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sWxz0y2N3GDRSEHS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sWxz0y2N3GDRSEHS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sWxz0y2N3GDRSEHS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sWxz0y2N3GDRSEHS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sWxz0y2N3GDRSEHS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sWxz0y2N3GDRSEHS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sWxz0y2N3GDRSEHS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sWxz0y2N3GDRSEHS .marker.cross{stroke:#333333;}#mermaid-svg-sWxz0y2N3GDRSEHS svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sWxz0y2N3GDRSEHS p{margin:0;}#mermaid-svg-sWxz0y2N3GDRSEHS .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-sWxz0y2N3GDRSEHS .cluster-label text{fill:#333;}#mermaid-svg-sWxz0y2N3GDRSEHS .cluster-label span{color:#333;}#mermaid-svg-sWxz0y2N3GDRSEHS .cluster-label span p{background-color:transparent;}#mermaid-svg-sWxz0y2N3GDRSEHS .label text,#mermaid-svg-sWxz0y2N3GDRSEHS span{fill:#333;color:#333;}#mermaid-svg-sWxz0y2N3GDRSEHS .node rect,#mermaid-svg-sWxz0y2N3GDRSEHS .node circle,#mermaid-svg-sWxz0y2N3GDRSEHS .node ellipse,#mermaid-svg-sWxz0y2N3GDRSEHS .node polygon,#mermaid-svg-sWxz0y2N3GDRSEHS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sWxz0y2N3GDRSEHS .rough-node .label text,#mermaid-svg-sWxz0y2N3GDRSEHS .node .label text,#mermaid-svg-sWxz0y2N3GDRSEHS .image-shape .label,#mermaid-svg-sWxz0y2N3GDRSEHS .icon-shape .label{text-anchor:middle;}#mermaid-svg-sWxz0y2N3GDRSEHS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sWxz0y2N3GDRSEHS .rough-node .label,#mermaid-svg-sWxz0y2N3GDRSEHS .node .label,#mermaid-svg-sWxz0y2N3GDRSEHS .image-shape .label,#mermaid-svg-sWxz0y2N3GDRSEHS .icon-shape .label{text-align:center;}#mermaid-svg-sWxz0y2N3GDRSEHS .node.clickable{cursor:pointer;}#mermaid-svg-sWxz0y2N3GDRSEHS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sWxz0y2N3GDRSEHS .arrowheadPath{fill:#333333;}#mermaid-svg-sWxz0y2N3GDRSEHS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sWxz0y2N3GDRSEHS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sWxz0y2N3GDRSEHS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sWxz0y2N3GDRSEHS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sWxz0y2N3GDRSEHS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sWxz0y2N3GDRSEHS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sWxz0y2N3GDRSEHS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sWxz0y2N3GDRSEHS .cluster text{fill:#333;}#mermaid-svg-sWxz0y2N3GDRSEHS .cluster span{color:#333;}#mermaid-svg-sWxz0y2N3GDRSEHS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sWxz0y2N3GDRSEHS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sWxz0y2N3GDRSEHS rect.text{fill:none;stroke-width:0;}#mermaid-svg-sWxz0y2N3GDRSEHS .icon-shape,#mermaid-svg-sWxz0y2N3GDRSEHS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sWxz0y2N3GDRSEHS .icon-shape p,#mermaid-svg-sWxz0y2N3GDRSEHS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sWxz0y2N3GDRSEHS .icon-shape .label rect,#mermaid-svg-sWxz0y2N3GDRSEHS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sWxz0y2N3GDRSEHS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sWxz0y2N3GDRSEHS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sWxz0y2N3GDRSEHS :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-sWxz0y2N3GDRSEHS .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-sWxz0y2N3GDRSEHS .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-sWxz0y2N3GDRSEHS .default tspan{fill:#000000!important;}

async subagents

同步 subagents

对照

对照

对照

对照

对照

对照

六维对照

阻塞直到完成

并行但阻塞

中途更新不可能

取消不可能

调用间无持久状态

适合等结果再继续的任务

立即返回 job ID

并行且非阻塞

update 可发后续指令

cancel 可取消任务

跨交互保持状态

适合交互式长任务

图里左右两列一一对应,虚线就是六个维度的对照关系。左侧出现两次不可能,右侧全部翻成可操作的动作。

六个维度的差异要落地,需要一套通信底座把监督者和后台子代理连起来。下一章看这套底座。

3. 通信底座

监督者与异步子代理是两个独立运行的进程。两者之间要传 job ID、查进度、发指令,全靠一套协议。

官方的表述是:async subagents 与任何实现 Agent Protocol 的服务器通信。这句话定了两件事:接口是什么,以及谁都能接入。

3.1 Agent Protocol

先说白话:Agent Protocol 是一份公开的接口约定。只要服务器按这份约定说话,监督者就认。

Agent Protocol 是一个开放协议,代码托管在 github.com/langchain-ai/agent-protocol。开放的意思是它不绑定某一家托管平台,任何实现了它的服务器都能接入。

对使用者来说,这个设计带来三个直接好处:

  • 不锁定:服务器可以放在 LangSmith,也可以自己运维
  • 可替换:换部署位置不用改监督者的控制代码
  • 可组合:不同子代理可以跑在不同服务器上

监督者这一侧的接口被收敛成四个控制动作:launch(启动)、check(检查)、update(更新)、cancel(取消)。五个字都不含具体服务器细节,全部走协议。

这里补一句架构含义。监督者与子代理解耦后,两者的生命周期也解耦。子代理的运行不因监督者一轮对话结束而中断,这正是异步版能保持长运行的根基。

监督者本地需要记的东西其实很少。下面这个演示脚本给出一版 job 登记结构,只存定位键与摘要。

# 来源:自实现 / 演示脚本
from dataclasses import dataclass, field

@dataclass
class JobTicket:
name: str # 子代理 name,对应 AsyncSubAgent 必填字段
job_id: str # launch 立即返回的任务编号
status: str = "running" # check 查到的最新状态摘要
updates: list = field(default_factory=list) # update 发过的后续指令

tickets = {}

def on_launch(name: str, job_id: str) –> None:
tickets[job_id] = JobTicket(name=name, job_id=job_id) # 拿到回执即登记

def on_check(job_id: str, status: str) –> None:
tickets[job_id].status = status # 本地只存摘要,不搬子代理全部状态

def on_update(job_id: str, instruction: str) –> None:
tickets[job_id].updates.append(instruction) # 补话也挂在同一张票上

def on_cancel(job_id: str) –> None:
tickets[job_id].status = "cancelled" # 取消后留记录,第八章会用到这点

这份登记表只存 job ID、状态摘要与指令清单。重活全在子代理的线程上,监督者不必复制它的中间产物。cancel 之后保留记录这一行,对应第八章第四条坑的规避方法。

协议管说什么,部署管在哪说。下一节看放哪里。

3.2 两种部署选择

官方给了两条路:用 LangSmith Deployments,或自托管任何 Agent Protocol 兼容服务器。

LangSmith Deployments 在第 33 篇与第 37 篇都出现过,是 LangChain 家的托管部署服务,适合不想自己运维服务器的场景。

自托管这条路适合有合规要求的场景。只要服务实现 Agent Protocol,监督者照样能接。换部署方式不换控制代码。

下面这张架构图把两侧的关系摆出来。

#mermaid-svg-INClee2vUApnaFvk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-INClee2vUApnaFvk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-INClee2vUApnaFvk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-INClee2vUApnaFvk .error-icon{fill:#552222;}#mermaid-svg-INClee2vUApnaFvk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-INClee2vUApnaFvk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-INClee2vUApnaFvk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-INClee2vUApnaFvk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-INClee2vUApnaFvk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-INClee2vUApnaFvk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-INClee2vUApnaFvk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-INClee2vUApnaFvk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-INClee2vUApnaFvk .marker.cross{stroke:#333333;}#mermaid-svg-INClee2vUApnaFvk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-INClee2vUApnaFvk p{margin:0;}#mermaid-svg-INClee2vUApnaFvk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-INClee2vUApnaFvk .cluster-label text{fill:#333;}#mermaid-svg-INClee2vUApnaFvk .cluster-label span{color:#333;}#mermaid-svg-INClee2vUApnaFvk .cluster-label span p{background-color:transparent;}#mermaid-svg-INClee2vUApnaFvk .label text,#mermaid-svg-INClee2vUApnaFvk span{fill:#333;color:#333;}#mermaid-svg-INClee2vUApnaFvk .node rect,#mermaid-svg-INClee2vUApnaFvk .node circle,#mermaid-svg-INClee2vUApnaFvk .node ellipse,#mermaid-svg-INClee2vUApnaFvk .node polygon,#mermaid-svg-INClee2vUApnaFvk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-INClee2vUApnaFvk .rough-node .label text,#mermaid-svg-INClee2vUApnaFvk .node .label text,#mermaid-svg-INClee2vUApnaFvk .image-shape .label,#mermaid-svg-INClee2vUApnaFvk .icon-shape .label{text-anchor:middle;}#mermaid-svg-INClee2vUApnaFvk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-INClee2vUApnaFvk .rough-node .label,#mermaid-svg-INClee2vUApnaFvk .node .label,#mermaid-svg-INClee2vUApnaFvk .image-shape .label,#mermaid-svg-INClee2vUApnaFvk .icon-shape .label{text-align:center;}#mermaid-svg-INClee2vUApnaFvk .node.clickable{cursor:pointer;}#mermaid-svg-INClee2vUApnaFvk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-INClee2vUApnaFvk .arrowheadPath{fill:#333333;}#mermaid-svg-INClee2vUApnaFvk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-INClee2vUApnaFvk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-INClee2vUApnaFvk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-INClee2vUApnaFvk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-INClee2vUApnaFvk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-INClee2vUApnaFvk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-INClee2vUApnaFvk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-INClee2vUApnaFvk .cluster text{fill:#333;}#mermaid-svg-INClee2vUApnaFvk .cluster span{color:#333;}#mermaid-svg-INClee2vUApnaFvk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-INClee2vUApnaFvk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-INClee2vUApnaFvk rect.text{fill:none;stroke-width:0;}#mermaid-svg-INClee2vUApnaFvk .icon-shape,#mermaid-svg-INClee2vUApnaFvk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-INClee2vUApnaFvk .icon-shape p,#mermaid-svg-INClee2vUApnaFvk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-INClee2vUApnaFvk .icon-shape .label rect,#mermaid-svg-INClee2vUApnaFvk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-INClee2vUApnaFvk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-INClee2vUApnaFvk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-INClee2vUApnaFvk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-INClee2vUApnaFvk .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-INClee2vUApnaFvk .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-INClee2vUApnaFvk .default tspan{fill:#000000!important;}

Agent Protocol 服务器

两种部署选择

job ID 与状态

job ID 与状态

Agent Protocol

监督者 agent

SDK 控制动作

LangSmith Deployments

自托管兼容服务器

researcher 服务器

coder 服务器

关键路径是中间那层:监督者只面对 Agent Protocol 这一套接口,下方接哪种服务器都无所谓。虚线表示 job ID 与任务状态的回传方向。

部署位置谈完,还差一步:把子代理登记进监督者的配置。下一章看官方示例怎么写。

4. 配置写法

async subagents 的定义方式,官方给的是 AsyncSubAgent 规格列表。列表里每一项指向一个 Agent Protocol 服务器。

先逐行拆官方示例,再说明两种传输怎么选。

4.1 官方示例逐行拆

下面是官方文档的配置示例原文。

# 来源:LangChain 官方文档 / deepagents async-subagents 页
from deepagents import AsyncSubAgent, create_deep_agent

async_subagents = [
AsyncSubAgent(
name="researcher",
description="Research agent for information gathering and synthesis",
graph_id="researcher",
# No url → ASGI transport (co-deployed in the same deployment)
),
AsyncSubAgent(
name="coder",
description="Coding agent for code generation and review",
graph_id="coder",
# url="https://coder-deployment.langsmith.dev" # Optional: HTTP transport for remote
),
]

agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
subagents=async_subagents,
)

逐个字段拆解。前三个是官方核实过的必填字段:

  • name:字符串,必填。子代理的唯一标识,监督者启动任务时用它指名道姓
  • description:字符串,必填。说明这个子代理做什么,监督者据此决定委派给谁
  • graph_id:字符串,必填。Agent Protocol 服务器上的 graph ID 或 assistant ID

两处注释各对应一种传输方式。第一项没写 url,第二项注释里给出了写法但保持注释状态,两种形态都摆在示例里。

graph_id 的官方说明在截断处之后还有内容,其余字段同样未在本篇核实。完整的字段表以 AsyncSubAgent API 参考页为准:https://reference.langchain.com/python/deepagents/middleware/async_subagents/AsyncSubAgent。

模型参数沿用示例原样。按第 37 篇的经验,模型与子代理结构是两件独立的事。

4.2 两种传输

两个子代理只差一行 url,行为却分成两种传输。注释原文写得很清楚。

不写 url:走 ASGI transport,同部署内协同部署。监督者与子代理在同一个 deployment 里,调用不跨网络。

写 url:走 HTTP transport,远程连接。子代理跑在别的服务器上,监督者通过 URL 访问。

两种传输的取舍可以列成一张表:

传输方式触发条件子代理位置适合场景
ASGI transport 不写 url 同一 deployment 内 单服务部署,内部调用
HTTP transport 写明 url 远程服务器 跨机器,独立扩缩容

配置角度的差异就这么多。选择取决于部署拓扑,与子代理本身的能力无关。

下面这张图描述配置如何落到连接上。

#mermaid-svg-1iyPgrRVjtSPnfaY{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1iyPgrRVjtSPnfaY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1iyPgrRVjtSPnfaY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1iyPgrRVjtSPnfaY .error-icon{fill:#552222;}#mermaid-svg-1iyPgrRVjtSPnfaY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1iyPgrRVjtSPnfaY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1iyPgrRVjtSPnfaY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1iyPgrRVjtSPnfaY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1iyPgrRVjtSPnfaY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1iyPgrRVjtSPnfaY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1iyPgrRVjtSPnfaY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1iyPgrRVjtSPnfaY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1iyPgrRVjtSPnfaY .marker.cross{stroke:#333333;}#mermaid-svg-1iyPgrRVjtSPnfaY svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1iyPgrRVjtSPnfaY p{margin:0;}#mermaid-svg-1iyPgrRVjtSPnfaY .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-1iyPgrRVjtSPnfaY .cluster-label text{fill:#333;}#mermaid-svg-1iyPgrRVjtSPnfaY .cluster-label span{color:#333;}#mermaid-svg-1iyPgrRVjtSPnfaY .cluster-label span p{background-color:transparent;}#mermaid-svg-1iyPgrRVjtSPnfaY .label text,#mermaid-svg-1iyPgrRVjtSPnfaY span{fill:#333;color:#333;}#mermaid-svg-1iyPgrRVjtSPnfaY .node rect,#mermaid-svg-1iyPgrRVjtSPnfaY .node circle,#mermaid-svg-1iyPgrRVjtSPnfaY .node ellipse,#mermaid-svg-1iyPgrRVjtSPnfaY .node polygon,#mermaid-svg-1iyPgrRVjtSPnfaY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1iyPgrRVjtSPnfaY .rough-node .label text,#mermaid-svg-1iyPgrRVjtSPnfaY .node .label text,#mermaid-svg-1iyPgrRVjtSPnfaY .image-shape .label,#mermaid-svg-1iyPgrRVjtSPnfaY .icon-shape .label{text-anchor:middle;}#mermaid-svg-1iyPgrRVjtSPnfaY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1iyPgrRVjtSPnfaY .rough-node .label,#mermaid-svg-1iyPgrRVjtSPnfaY .node .label,#mermaid-svg-1iyPgrRVjtSPnfaY .image-shape .label,#mermaid-svg-1iyPgrRVjtSPnfaY .icon-shape .label{text-align:center;}#mermaid-svg-1iyPgrRVjtSPnfaY .node.clickable{cursor:pointer;}#mermaid-svg-1iyPgrRVjtSPnfaY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1iyPgrRVjtSPnfaY .arrowheadPath{fill:#333333;}#mermaid-svg-1iyPgrRVjtSPnfaY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1iyPgrRVjtSPnfaY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1iyPgrRVjtSPnfaY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1iyPgrRVjtSPnfaY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1iyPgrRVjtSPnfaY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1iyPgrRVjtSPnfaY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1iyPgrRVjtSPnfaY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1iyPgrRVjtSPnfaY .cluster text{fill:#333;}#mermaid-svg-1iyPgrRVjtSPnfaY .cluster span{color:#333;}#mermaid-svg-1iyPgrRVjtSPnfaY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1iyPgrRVjtSPnfaY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1iyPgrRVjtSPnfaY rect.text{fill:none;stroke-width:0;}#mermaid-svg-1iyPgrRVjtSPnfaY .icon-shape,#mermaid-svg-1iyPgrRVjtSPnfaY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1iyPgrRVjtSPnfaY .icon-shape p,#mermaid-svg-1iyPgrRVjtSPnfaY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1iyPgrRVjtSPnfaY .icon-shape .label rect,#mermaid-svg-1iyPgrRVjtSPnfaY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1iyPgrRVjtSPnfaY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1iyPgrRVjtSPnfaY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1iyPgrRVjtSPnfaY :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-1iyPgrRVjtSPnfaY .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-1iyPgrRVjtSPnfaY .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-1iyPgrRVjtSPnfaY .default tspan{fill:#000000!important;}

AsyncSubAgent 配置

不写 url

写明 url

ASGI transport 同部署内

HTTP transport 远程

同一 deployment 里的服务器

远程 Agent Protocol 服务器

分支点在最上层:url 字段写不写,直接决定走哪条传输路径。两条路径最终都落到 Agent Protocol 服务器,差别只在距离。

配置登记好子代理,运行期的控制全靠四个动作。下一章展开。

5. 四个控制动作

监督者管理后台任务,靠的是 SDK 提供的四个动作:launch(启动)、check(检查)、update(更新)、cancel(取消)。

四个动词对应一个后台任务的完整生命周期。本章先看整体,再看两个中途干预动作的场景。

5.1 launch、check、update、cancel

launch:启动。让指定子代理开始一个后台任务,立即拿回 job ID。这是把监督者从等待里解放出来的那一步。

check:检查。拿着 job ID 查任务当前状态。监督者回应进度追问靠它。

update:更新。给运行中的任务发后续指令,官方函数名 update_async_task。

cancel:取消。砍掉运行中的任务,官方函数名 cancel_async_task。

四个动作拼成一条时间线:

动作时机作用语义
launch 任务开始前 启动并拿 job ID 派活
check 运行中任意时刻 查询当前状态 巡检
update 运行中需要改口时 发后续指令 补话
cancel 运行中发现跑偏时 取消任务 止损

launch 与 check 对应同步模式也能做到的事:发起与等待结果。差别在于同步版把这两步焊死在一起,异步版拆成可独立调用的动作。

update 与 cancel 是异步版独有的两个动作。同步模式下任务一旦发出,监督者既改不了口也喊不了停。

update_async_task 与 cancel_async_task 这两个函数名已核实,具体签名以官方文档为准:https://docs.langchain.com/oss/python/deepagents/async-subagents。

下面这张状态图把四个动作放进任务生命周期。

#mermaid-svg-wUaWIBdmzsY0bOUo{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wUaWIBdmzsY0bOUo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wUaWIBdmzsY0bOUo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wUaWIBdmzsY0bOUo .error-icon{fill:#552222;}#mermaid-svg-wUaWIBdmzsY0bOUo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wUaWIBdmzsY0bOUo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wUaWIBdmzsY0bOUo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wUaWIBdmzsY0bOUo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wUaWIBdmzsY0bOUo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wUaWIBdmzsY0bOUo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wUaWIBdmzsY0bOUo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wUaWIBdmzsY0bOUo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wUaWIBdmzsY0bOUo .marker.cross{stroke:#333333;}#mermaid-svg-wUaWIBdmzsY0bOUo svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wUaWIBdmzsY0bOUo p{margin:0;}#mermaid-svg-wUaWIBdmzsY0bOUo defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-wUaWIBdmzsY0bOUo g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-wUaWIBdmzsY0bOUo g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-wUaWIBdmzsY0bOUo g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-wUaWIBdmzsY0bOUo g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-wUaWIBdmzsY0bOUo g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-wUaWIBdmzsY0bOUo .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-wUaWIBdmzsY0bOUo .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-wUaWIBdmzsY0bOUo .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-wUaWIBdmzsY0bOUo .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-wUaWIBdmzsY0bOUo .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-wUaWIBdmzsY0bOUo .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-wUaWIBdmzsY0bOUo .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-wUaWIBdmzsY0bOUo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wUaWIBdmzsY0bOUo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wUaWIBdmzsY0bOUo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wUaWIBdmzsY0bOUo .edgeLabel .label text{fill:#333;}#mermaid-svg-wUaWIBdmzsY0bOUo .label div .edgeLabel{color:#333;}#mermaid-svg-wUaWIBdmzsY0bOUo .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-wUaWIBdmzsY0bOUo .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-wUaWIBdmzsY0bOUo .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-wUaWIBdmzsY0bOUo .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-wUaWIBdmzsY0bOUo .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-wUaWIBdmzsY0bOUo .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wUaWIBdmzsY0bOUo .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wUaWIBdmzsY0bOUo #statediagram-barbEnd{fill:#333333;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wUaWIBdmzsY0bOUo .cluster-label,#mermaid-svg-wUaWIBdmzsY0bOUo .nodeLabel{color:#131300;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-wUaWIBdmzsY0bOUo .note-edge{stroke-dasharray:5;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-note text{fill:black;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram-note .nodeLabel{color:black;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagram .edgeLabel{color:red;}#mermaid-svg-wUaWIBdmzsY0bOUo #dependencyStart,#mermaid-svg-wUaWIBdmzsY0bOUo #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-wUaWIBdmzsY0bOUo .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wUaWIBdmzsY0bOUo :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

等待委派

launch 启动拿 job ID

update 发后续指令

任务完成

cancel 取消

Idle

Running

Done

Cancelled

图里关键在 Running 上那两个自环。它们就是同步模式做不到的两个动作:check 随时巡检,update 随时补话。

四个动作里,update 与 cancel 值得单独拉出来讲场景。下一节展开。

5.2 update 与 cancel 的场景

update_async_task 的场景是改口。用户在任务运行中补充或修改需求,监督者把新指令转发给运行中的子代理。

举一个还原场景。用户委派调研三个竞品,任务跑了两分钟后改口:换成第四个竞品。监督者用 update_async_task 把变化发过去,子代理在原任务上调整方向。

如果用同步版,这条改口只能排到任务结束之后。前面 20 分钟(示例数据)的计算方向就是错的。

cancel_async_task 的场景是止损。监督者通过 check 发现方向明显跑偏,或用户直接说不要了,就取消运行中的任务。

止损省的不只是时间。跑偏的任务继续跑下去,作废的调用量也在涨。取消得越早,浪费越少。

两个动作的配合关系可以这样列:

  • update 前通常先 check:确认任务还在运行再发指令
  • cancel 前同样先 check:确认状态再决定砍不砍
  • update 改的是方向,cancel 掐的是存在
  • 两者都以 job ID 为定位依据

这个先查后动的顺序可以写成演示脚本。核心是:状态不明确时不发 update,也不发 cancel。

# 来源:自实现 / 演示脚本
def steer(job_id: str, status: str, new_need: str) –> str:
if status != "running": # check 的结果先判状态
return "任务已不在运行,无需干预"
if new_need: # 需求变了走补话
return f"调用 update_async_task,内容:{new_need}"
return "需求未变,不动作"

def halt(job_id: str, status: str, off_track: bool) –> str:
if status != "running": # 已结束的任务取消无意义
return "任务已结束,cancel 无对象"
if off_track: # 方向跑偏才止损
return f"调用 cancel_async_task,对象:{job_id}"
return "方向正常,继续跑"

两个函数都把状态检查放在最前面。跳过这层判断直接发 update 或 cancel,等于对未知状态开火。函数名与参数是本篇演示写法,真实签名以官方页为准:https://docs.langchain.com/oss/python/deepagents/async-subagents。

这也解释了 job ID 为什么重要。它是四个动作共用的定位键,launch 拿到它,后三个动作靠它找到任务。

中途干预能力的前提是子代理记得住前情。第六章讲有状态这件事。

6. 有状态的含义

六维表第五行写着:异步子代理有状态,在自己的线程上跨交互保持状态。同步侧则是无状态,调用间无持久状态。

这句话决定了两种子代理的使用手感。本章拆开讲。

6.1 跨交互保持状态

先说白话:有状态的子代理像驻场员工,记得你上次说过什么。无状态的像临时工,每次见面都是初见。

官方原文的表述是:异步子代理在自己的线程上(on its own thread)跨交互保持状态。线程是状态存续的单位。

这意味着监督者对同一个子代理的多次交互是连续的。上一轮 update 发的指令,下一轮 check 查状态时子代理还认得。

推论是任务可以分段推进。用户今天说做一半,明天补充新要求,子代理在原线程上接着做,不必从头开始。

把跨交互这件事写成演示脚本,能看出监督者要记的东西有多薄。

# 来源:自实现 / 演示脚本
jobs = {}

def day_one(user_ask: str) –> str:
job_id = "示例编号" # launch 立即返回
jobs[job_id] = {"ask": user_ask, "round": 1}
return "已启动,编号在手上,随时可聊别的"

def day_two(job_id: str, new_need: str) –> str:
ticket = jobs.get(job_id) # 只凭编号找回上下文
if ticket is None:
return "编号无效,无法续接"
ticket["round"] += 1 # 子代理线程上自会记得前情
return "已把新要求发往原任务,无需重述背景"

第二天这轮里,监督者只递了 job ID 和一句新要求。任务前情在子代理的线程上,不在监督者的上下文里。这正是有状态带来的省token效果。

对照官方表格的同步侧:无状态,调用间无持久状态。同步子代理每次调用都要把所需上下文重新带一遍。

这里要接回第 37 篇的一个结论:同步子代理的好处正是隔离。每次调用独立、上下文干净,这是特性不是缺陷。有状态与无状态各有其位,第七章讲怎么选。

有状态还带来一个实操推论。既然状态跨交互存续,任务清单与中间产物就挂在子代理的线程上,监督者只需要记住 job ID。

6.2 与同步无状态的对照

用一个方法论类比把两侧讲透。同步子代理像临时工:来一次结一次,不留档案。异步子代理像驻场:有工位、有台账、记得前情。

对照点同步子代理异步子代理
状态存续 调用结束即清空 跨交互保持
上下文携带 每次调用重新带 线程上自然延续
多轮指令 无处追加 update 接续在原线程
适合任务形态 一次交付 分段推进的长任务

对照到沟通成本上:临时工模式每次交代都要从头说清全部背景;驻场模式说一句接着上次就行。

长任务天然是多轮的。需求会变、方向会调、结果要分批看。有状态让子代理接得住这些轮次。

下面这张图描述状态在两侧的存续方式。

#mermaid-svg-8oUn0Tu11xnggDFY{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8oUn0Tu11xnggDFY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8oUn0Tu11xnggDFY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8oUn0Tu11xnggDFY .error-icon{fill:#552222;}#mermaid-svg-8oUn0Tu11xnggDFY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8oUn0Tu11xnggDFY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8oUn0Tu11xnggDFY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8oUn0Tu11xnggDFY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8oUn0Tu11xnggDFY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8oUn0Tu11xnggDFY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8oUn0Tu11xnggDFY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8oUn0Tu11xnggDFY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8oUn0Tu11xnggDFY .marker.cross{stroke:#333333;}#mermaid-svg-8oUn0Tu11xnggDFY svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8oUn0Tu11xnggDFY p{margin:0;}#mermaid-svg-8oUn0Tu11xnggDFY .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8oUn0Tu11xnggDFY .cluster-label text{fill:#333;}#mermaid-svg-8oUn0Tu11xnggDFY .cluster-label span{color:#333;}#mermaid-svg-8oUn0Tu11xnggDFY .cluster-label span p{background-color:transparent;}#mermaid-svg-8oUn0Tu11xnggDFY .label text,#mermaid-svg-8oUn0Tu11xnggDFY span{fill:#333;color:#333;}#mermaid-svg-8oUn0Tu11xnggDFY .node rect,#mermaid-svg-8oUn0Tu11xnggDFY .node circle,#mermaid-svg-8oUn0Tu11xnggDFY .node ellipse,#mermaid-svg-8oUn0Tu11xnggDFY .node polygon,#mermaid-svg-8oUn0Tu11xnggDFY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8oUn0Tu11xnggDFY .rough-node .label text,#mermaid-svg-8oUn0Tu11xnggDFY .node .label text,#mermaid-svg-8oUn0Tu11xnggDFY .image-shape .label,#mermaid-svg-8oUn0Tu11xnggDFY .icon-shape .label{text-anchor:middle;}#mermaid-svg-8oUn0Tu11xnggDFY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8oUn0Tu11xnggDFY .rough-node .label,#mermaid-svg-8oUn0Tu11xnggDFY .node .label,#mermaid-svg-8oUn0Tu11xnggDFY .image-shape .label,#mermaid-svg-8oUn0Tu11xnggDFY .icon-shape .label{text-align:center;}#mermaid-svg-8oUn0Tu11xnggDFY .node.clickable{cursor:pointer;}#mermaid-svg-8oUn0Tu11xnggDFY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8oUn0Tu11xnggDFY .arrowheadPath{fill:#333333;}#mermaid-svg-8oUn0Tu11xnggDFY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8oUn0Tu11xnggDFY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8oUn0Tu11xnggDFY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8oUn0Tu11xnggDFY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8oUn0Tu11xnggDFY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8oUn0Tu11xnggDFY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8oUn0Tu11xnggDFY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8oUn0Tu11xnggDFY .cluster text{fill:#333;}#mermaid-svg-8oUn0Tu11xnggDFY .cluster span{color:#333;}#mermaid-svg-8oUn0Tu11xnggDFY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8oUn0Tu11xnggDFY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8oUn0Tu11xnggDFY rect.text{fill:none;stroke-width:0;}#mermaid-svg-8oUn0Tu11xnggDFY .icon-shape,#mermaid-svg-8oUn0Tu11xnggDFY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8oUn0Tu11xnggDFY .icon-shape p,#mermaid-svg-8oUn0Tu11xnggDFY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8oUn0Tu11xnggDFY .icon-shape .label rect,#mermaid-svg-8oUn0Tu11xnggDFY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8oUn0Tu11xnggDFY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8oUn0Tu11xnggDFY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8oUn0Tu11xnggDFY :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-8oUn0Tu11xnggDFY .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-8oUn0Tu11xnggDFY .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-8oUn0Tu11xnggDFY .default tspan{fill:#000000!important;}

异步模式 有状态

在自己的线程上启动

执行并保持状态

接受 update 指令

跨交互延续状态

同步模式 无状态

收到完整上下文

执行任务

返回结果后状态清空

子代理被调用

左侧链路到 SS3 就终止,状态不存续。右侧 AS2、AS3、AS4 形成回路,状态在线程上滚动累积。

状态模型不同,适合的任务形态就不同。下一章讲怎么选、怎么混用。

7. 选型与组合

六维表第六行写着 Best for:同步版适合 agent 应等结果再继续的任务,异步版适合聊天中交互式管理的长运行复杂任务。

这一行是选型的官方依据。本章把它落成判断方法,再看混合使用。

7.1 什么时候用哪个

先给一句白话判断:答案马上要用就走同步,答案可以慢慢等就走异步。

判断依据有三问,按顺序过:

  • 这一步的输出是下一步的输入吗。是,选同步,监督者必须等结果再继续
  • 任务时长是否明显长于一轮对话。是,选异步,别让监督者干等
  • 任务进行中需求可能变化吗。可能,选异步,需要 update 与 cancel

三问里有任何一问指向异步,就该认真考虑异步版。全否定则同步版更简单。

常见的任务类型对照:

任务特征推荐模式依据
拆解后的子问题,马上要拼进答案 同步 监督者等结果再继续
单轮可完成的小任务 同步 异步徒增复杂度
长时间调研,用户还要继续聊 异步 非阻塞加可中途更新
方向不确定,可能推倒重来 异步 cancel 止损

选错方向的代价是对称的。长任务上同步,监督者被卡住,用户失去响应;短任务上异步,多出一套部署与状态管理,没换来任何好处。

这张决策树把三问流程化。

#mermaid-svg-J40ldPWTFZ1pbqkP{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-J40ldPWTFZ1pbqkP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-J40ldPWTFZ1pbqkP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-J40ldPWTFZ1pbqkP .error-icon{fill:#552222;}#mermaid-svg-J40ldPWTFZ1pbqkP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-J40ldPWTFZ1pbqkP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-J40ldPWTFZ1pbqkP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-J40ldPWTFZ1pbqkP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-J40ldPWTFZ1pbqkP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-J40ldPWTFZ1pbqkP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-J40ldPWTFZ1pbqkP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-J40ldPWTFZ1pbqkP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-J40ldPWTFZ1pbqkP .marker.cross{stroke:#333333;}#mermaid-svg-J40ldPWTFZ1pbqkP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-J40ldPWTFZ1pbqkP p{margin:0;}#mermaid-svg-J40ldPWTFZ1pbqkP .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-J40ldPWTFZ1pbqkP .cluster-label text{fill:#333;}#mermaid-svg-J40ldPWTFZ1pbqkP .cluster-label span{color:#333;}#mermaid-svg-J40ldPWTFZ1pbqkP .cluster-label span p{background-color:transparent;}#mermaid-svg-J40ldPWTFZ1pbqkP .label text,#mermaid-svg-J40ldPWTFZ1pbqkP span{fill:#333;color:#333;}#mermaid-svg-J40ldPWTFZ1pbqkP .node rect,#mermaid-svg-J40ldPWTFZ1pbqkP .node circle,#mermaid-svg-J40ldPWTFZ1pbqkP .node ellipse,#mermaid-svg-J40ldPWTFZ1pbqkP .node polygon,#mermaid-svg-J40ldPWTFZ1pbqkP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-J40ldPWTFZ1pbqkP .rough-node .label text,#mermaid-svg-J40ldPWTFZ1pbqkP .node .label text,#mermaid-svg-J40ldPWTFZ1pbqkP .image-shape .label,#mermaid-svg-J40ldPWTFZ1pbqkP .icon-shape .label{text-anchor:middle;}#mermaid-svg-J40ldPWTFZ1pbqkP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-J40ldPWTFZ1pbqkP .rough-node .label,#mermaid-svg-J40ldPWTFZ1pbqkP .node .label,#mermaid-svg-J40ldPWTFZ1pbqkP .image-shape .label,#mermaid-svg-J40ldPWTFZ1pbqkP .icon-shape .label{text-align:center;}#mermaid-svg-J40ldPWTFZ1pbqkP .node.clickable{cursor:pointer;}#mermaid-svg-J40ldPWTFZ1pbqkP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-J40ldPWTFZ1pbqkP .arrowheadPath{fill:#333333;}#mermaid-svg-J40ldPWTFZ1pbqkP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-J40ldPWTFZ1pbqkP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-J40ldPWTFZ1pbqkP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J40ldPWTFZ1pbqkP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-J40ldPWTFZ1pbqkP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J40ldPWTFZ1pbqkP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-J40ldPWTFZ1pbqkP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-J40ldPWTFZ1pbqkP .cluster text{fill:#333;}#mermaid-svg-J40ldPWTFZ1pbqkP .cluster span{color:#333;}#mermaid-svg-J40ldPWTFZ1pbqkP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-J40ldPWTFZ1pbqkP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-J40ldPWTFZ1pbqkP rect.text{fill:none;stroke-width:0;}#mermaid-svg-J40ldPWTFZ1pbqkP .icon-shape,#mermaid-svg-J40ldPWTFZ1pbqkP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J40ldPWTFZ1pbqkP .icon-shape p,#mermaid-svg-J40ldPWTFZ1pbqkP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-J40ldPWTFZ1pbqkP .icon-shape .label rect,#mermaid-svg-J40ldPWTFZ1pbqkP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J40ldPWTFZ1pbqkP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-J40ldPWTFZ1pbqkP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-J40ldPWTFZ1pbqkP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-J40ldPWTFZ1pbqkP .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-J40ldPWTFZ1pbqkP .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-J40ldPWTFZ1pbqkP .default tspan{fill:#000000!important;}

是

否

否

是

可能

不会

新任务来了

输出是下一步的输入吗

任务明显长于一轮对话吗

运行中需求会变吗

选同步 subagents

选 async subagents

关键分支在 Q1:只要输出被下一步依赖,就走同步。右侧两种结果都落到异步,差别只是理由不同。

7.2 混合使用

真实系统里两种模式通常并存。方法论是:短任务同步、长任务异步。

一个典型的监督者配置可以同时挂两类子代理。快问快答的检索走同步,需要几十分钟(示例数据)的深度调研走异步。

混搭的价值在于各就各位。同步子代理保住响应链的紧凑,异步子代理扛住长任务的不阻塞。

归类的判断可以写成演示脚本。输入是任务的三个特征,输出是走哪条路。

# 来源:自实现 / 演示脚本
def route(next_step_needs_it: bool, long_running: bool, needs_change: bool) –> str:
if next_step_needs_it: # 输出被下一步依赖,必须等
return "同步 subagents"
if not long_running and not needs_change:
return "同步 subagents" # 短且稳定,异步徒增复杂度
return "async subagents" # 长任务或需求会变,走后台

# 三个特征的含义:
# next_step_needs_it 结果是否被主流程立即消费
# long_running 任务是否超出一次交互的耐心阈值
# needs_change 中途是否可能要追加指令或取消

三行分支对应第七章开头那三问。第一问命中就走同步,后面两问只有都否定才落回同步。这与决策树图是同一套逻辑,代码版便于直接搬进监督者的路由层。

配置侧的写法也不冲突。deepagents 里同步子代理与 AsyncSubAgent 是两套定义,挂进同一个 create_deep_agent 即可。具体组合方式以官方文档为准:https://docs.langchain.com/oss/python/deepagents/subagents。

混合使用后,监督者的判断多了一步:委派前先归类。归类错误不报错,只会表现为体验劣化,比如短任务绕了远路。

监督者还需要养成两个新习惯。定期 check 后台任务,别让结果悬空。任务不再需要时及时 cancel,别让计算白跑。

选型与组合讲完,还剩失败模式没过。下一章把它们列成一张对照表。

8. 边界与坑

异步版换来了控制力,也换来了新的出错面。本章列四类典型失败模式,再交代事实边界。

8.1 失败模式对照表

四类坑都来自机制本身,提前识别比事后排查便宜。

失败模式触发原因表现规避方法
短任务上异步 盲目全用异步 多出部署与状态管理,无收益 按第七章三问归类
graph_id 写错 配置抄错或服务器变了 连不上目标子代理 核对服务器上的 graph ID
忘了 check 启动后不再过问 后台任务变孤儿,结果无人接 监督者定期巡检 job ID
取消后不处理引用 cancel 后不清账 后续引用指向已取消任务 cancel 后同步清理结果引用

四类坑按发生阶段排,正好覆盖启动前、启动时、运行中、收尾时。

阶段对应坑一句话判断
启动前 短任务上异步 值不值得付异步成本
启动时 graph_id 写错 配置与服务器对不对得上
运行中 忘了 check 有没有定期巡检
收尾时 取消后不处理引用 票据清了没有

逐条说透。第一条是过度设计。异步的成本是真实的:要维护部署、要管 job ID、要处理状态。短任务付这份钱买不到东西。

第二条是配置错位。graph_id 指向 Agent Protocol 服务器上的 graph 或 assistant,服务器侧对不上就接不通。排查时先核对服务器上实际存在的 ID。

第三条最隐蔽。launch 拿到 job ID 后如果监督者再也不 check,任务就在后台自生自灭。结果可能已经产出,却没人把它交回用户。

这条坑的成因是心理落差。开发者习惯了同步版的自动回收,以为结果会自己回来。异步版里回收是监督者的职责,不是框架的默认行为。

第四条是收尾不干净。cancel 之后 job ID 还在监督者上下文里,若后续把它当作有效结果引用,答案里会出现凭空内容。

四条坑共用一个解法:把 launch、check、update、cancel 当成一个必须走完的循环,而不是发射后不管。

8.2 事实边界

本篇只使用官方文档核实过的事实。以下内容未在本篇核实,以官方页为准。

AsyncSubAgent 的字段表,本篇只核实了前三行:name、description、graph_id。其余字段及 graph_id 说明的完整内容,以 API 参考页为准:https://reference.langchain.com/python/deepagents/middleware/async_subagents/AsyncSubAgent。

update_async_task 与 cancel_async_task 的具体签名,以官方文档为准:https://docs.langchain.com/oss/python/deepagents/async-subagents。

Agent Protocol 的具体端点与协议细节,以协议仓库为准:https://github.com/langchain-ai/agent-protocol。

排查配置问题时,按下面这张流程走。

#mermaid-svg-NV10xSXDGqTFihta{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NV10xSXDGqTFihta .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NV10xSXDGqTFihta .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NV10xSXDGqTFihta .error-icon{fill:#552222;}#mermaid-svg-NV10xSXDGqTFihta .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NV10xSXDGqTFihta .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NV10xSXDGqTFihta .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NV10xSXDGqTFihta .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NV10xSXDGqTFihta .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NV10xSXDGqTFihta .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NV10xSXDGqTFihta .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NV10xSXDGqTFihta .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NV10xSXDGqTFihta .marker.cross{stroke:#333333;}#mermaid-svg-NV10xSXDGqTFihta svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NV10xSXDGqTFihta p{margin:0;}#mermaid-svg-NV10xSXDGqTFihta .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-NV10xSXDGqTFihta .cluster-label text{fill:#333;}#mermaid-svg-NV10xSXDGqTFihta .cluster-label span{color:#333;}#mermaid-svg-NV10xSXDGqTFihta .cluster-label span p{background-color:transparent;}#mermaid-svg-NV10xSXDGqTFihta .label text,#mermaid-svg-NV10xSXDGqTFihta span{fill:#333;color:#333;}#mermaid-svg-NV10xSXDGqTFihta .node rect,#mermaid-svg-NV10xSXDGqTFihta .node circle,#mermaid-svg-NV10xSXDGqTFihta .node ellipse,#mermaid-svg-NV10xSXDGqTFihta .node polygon,#mermaid-svg-NV10xSXDGqTFihta .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NV10xSXDGqTFihta .rough-node .label text,#mermaid-svg-NV10xSXDGqTFihta .node .label text,#mermaid-svg-NV10xSXDGqTFihta .image-shape .label,#mermaid-svg-NV10xSXDGqTFihta .icon-shape .label{text-anchor:middle;}#mermaid-svg-NV10xSXDGqTFihta .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NV10xSXDGqTFihta .rough-node .label,#mermaid-svg-NV10xSXDGqTFihta .node .label,#mermaid-svg-NV10xSXDGqTFihta .image-shape .label,#mermaid-svg-NV10xSXDGqTFihta .icon-shape .label{text-align:center;}#mermaid-svg-NV10xSXDGqTFihta .node.clickable{cursor:pointer;}#mermaid-svg-NV10xSXDGqTFihta .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NV10xSXDGqTFihta .arrowheadPath{fill:#333333;}#mermaid-svg-NV10xSXDGqTFihta .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NV10xSXDGqTFihta .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NV10xSXDGqTFihta .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NV10xSXDGqTFihta .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NV10xSXDGqTFihta .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NV10xSXDGqTFihta .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NV10xSXDGqTFihta .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NV10xSXDGqTFihta .cluster text{fill:#333;}#mermaid-svg-NV10xSXDGqTFihta .cluster span{color:#333;}#mermaid-svg-NV10xSXDGqTFihta div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NV10xSXDGqTFihta .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NV10xSXDGqTFihta rect.text{fill:none;stroke-width:0;}#mermaid-svg-NV10xSXDGqTFihta .icon-shape,#mermaid-svg-NV10xSXDGqTFihta .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NV10xSXDGqTFihta .icon-shape p,#mermaid-svg-NV10xSXDGqTFihta .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NV10xSXDGqTFihta .icon-shape .label rect,#mermaid-svg-NV10xSXDGqTFihta .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NV10xSXDGqTFihta .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NV10xSXDGqTFihta .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NV10xSXDGqTFihta :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-NV10xSXDGqTFihta .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-NV10xSXDGqTFihta .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-NV10xSXDGqTFihta .default tspan{fill:#000000!important;}

不一致

一致

不匹配

匹配

任务仍在

已失效

异步子代理异常

graph_id 与服务器一致吗

url 写法与部署形态匹配吗

job ID 还有效吗

核对服务器上的 graph ID

按同部署或远程改写传输

check 查状态后决定 update 或 cancel

第一层先查静态配置,第二层查传输形态,第三层才查运行期状态。这个顺序能把多数问题拦在前两层,不必等任务跑起来才发现。

机制、配置、控制与坑都过完了,下面把全文收拢成几条可带走的结论。

总结

同步子代理解决了上下文隔离,代价是把监督者按在等待里。async subagents 用立即返回 job ID 换回监督者的响应能力,代价是一套要自己维护的通信与状态管理。

官方六维对照表是全文骨架:执行模型从阻塞变成立即返回,并发从并行但阻塞变成并行且非阻塞,中途更新与取消从不可能变成 update_async_task 与 cancel_async_task 两个可调用动作,状态性从无状态变成线程上跨交互保持。

通信底座是 Agent Protocol,部署可选 LangSmith Deployments 或自托管兼容服务器。配置写法是一个 AsyncSubAgent 列表,name、description、graph_id 三个必填字段,url 写不写决定走 ASGI 还是 HTTP 传输。

选型按官方 Best for 一行判断:等结果再继续的任务用同步,聊天中交互管理的长任务用异步。真实系统里两者并存,短任务同步、长任务异步。

四个控制动作要当循环走完:launch 拿 job ID,check 巡检,update 改口,cancel 止损。发射后不管会让后台任务变孤儿,这是异步版最值得防的坑。

外部引用

  • Async subagents 官方文档:https://docs.langchain.com/oss/python/deepagents/async-subagents
  • Subagents(同步版):https://docs.langchain.com/oss/python/deepagents/subagents
  • AsyncSubAgent API 参考:https://reference.langchain.com/python/deepagents/middleware/async_subagents/AsyncSubAgent
  • Agent Protocol 仓库:https://github.com/langchain-ai/agent-protocol
  • LangSmith Deployments:https://docs.langchain.com/langsmith/deployment
赞(0)
未经允许不得转载:171主机测评 » 别再让 Agent 干等 20 分钟!async subagents 把监督者从阻塞里解放出来
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址