DeepAgent 多子代理协作:中断授权与 Agent 间通讯机制
本文整理 DeepAgent 在多子代理场景下的两个关键工程问题:
- 子 Agent 发起中断授权时,主 Agent 和其他子 Agent 怎么执行;
- 多个子 Agent 之间想共享数据、互相协作时,应该怎么设计。
适合场景:支付审批、发邮件审批、文件写入审批、多 Agent 并行研究、代码审查、任务分派系统。
1. 先给结论
DeepAgent 的子代理协作不是"多个 Agent 互相聊天"的模式。
更准确的模型是:
text
主 DeepAgent
-> 通过 task 工具调用子 Agent
-> 子 Agent 独立执行任务
-> 子 Agent 返回最终结果
-> 主 DeepAgent 汇总和继续决策
子 Agent 之间默认不能直接通讯。它们的数据通常通过以下方式传递:
- 主 Agent 中转;
- 共享文件系统;
- 数据库 / 对象存储 / Redis / 业务 API;
- 外层 LangGraph state;
- 异步子代理任务 API。
中断授权也不是"全局暂停所有 Agent"。准确说:
同一个
thread_id下,某个子 Agent 触发中断后,当前这次主 DeepAgent run 会暂停;主 Agent 不会继续下一轮推理,直到用同一个thread_id恢复执行。其他独立 thread 不受影响。
2. 没写 Graph,也仍然有 Graph
很多人会说:
text
我没有写 LangGraph,只是 create_deep_agent。
但要注意:
python
agent = create_deep_agent(...)
返回的本身就是一个 LangGraph runnable。
所以即使你没有手写外层业务 Graph,DeepAgent 内部仍然有:
- 状态;
- messages;
- tool call;
- checkpoint;
- interrupt;
- resume;
- subagent 调用。
可以理解成:
text
你没有自己画 Graph,但 create_deep_agent 已经帮你封装了一个执行图。
3. 子 Agent 是怎么被调用的
DeepAgent 的 SubAgentMiddleware 会给主 Agent 注入一个 task 工具。
主 Agent 不是直接"进入"子 Agent,而是调用:
text
task(subagent_type="researcher", description="去搜索某主题论文")
执行流程:
text
主 Agent
-> 发起 task 工具调用
-> SubAgentMiddleware 找到对应子 Agent
-> 子 Agent 使用独立 messages 执行
-> 子 Agent 完成后返回最终消息
-> 这个最终消息变成主 Agent 看到的 ToolMessage
默认情况下,子 Agent 的中间工具调用、搜索过程、内部 messages 不会全量回传给主 Agent。
主 Agent 通常只拿到:
text
子 Agent 的最终报告 / 最终结构化结果
4. 子 Agent 中断授权是怎么实现的
DeepAgent 的中断授权主要有两种方式。
4.1 用 interrupt_on 拦截工具调用
这是最常见方式。
例如支付工具:
python
from langchain.tools import tool
@tool
def create_payment(order_id: str, amount: float, payee: str) -> str:
"""执行支付动作;该工具只应在人工审批通过后被调用。"""
return f"支付成功:订单={order_id},金额={amount},收款方={payee}"
配置子 Agent:
python
payment_subagent = {
"name": "payment-agent",
"description": "负责支付前检查和支付执行。",
"system_prompt": (
"你是支付子代理。你只能在订单信息完整时调用 create_payment。"
"支付动作必须等待人工审批,不得绕过审批。"
),
"tools": [create_payment],
"interrupt_on": {
"create_payment": {
"allowed_decisions": ["approve", "reject"],
}
},
}
这表示:
text
当 payment-agent 想调用 create_payment 时,先暂停,等待人审批。
approve:继续执行工具
reject:拒绝执行工具
4.2 在工具内部调用 interrupt()
如果审批逻辑更业务化,也可以在工具内部中断。
python
from langchain.tools import tool
from langgraph.types import interrupt
@tool
def pay_with_manual_approval(order_id: str, amount: float, payee: str) -> str:
"""先请求人工支付授权,审批通过后再执行支付。"""
approval = interrupt(
{
"type": "payment_approval",
"order_id": order_id,
"amount": amount,
"payee": payee,
"message": f"是否批准支付订单 {order_id},金额 {amount},收款方 {payee}?",
}
)
if not approval.get("approved"):
return f"支付被拒绝:{approval.get('reason', '未提供原因')}"
return f"支付成功:订单={order_id},金额={amount},收款方={payee}"
这种适合:
- 支付;
- 发邮件;
- 发短信;
- 改生产配置;
- 写生产数据库;
- 删除文件;
- 高成本 API 调用。
5. 多个子 Agent 并行时,一个触发中断会怎样
假设主 Agent 同一轮发出 3 个 task:
text
task(agent_a, "处理 A")
task(agent_b, "处理 B,需要支付")
task(agent_c, "处理 C")
底层可以并行跑:
text
主 DeepAgent
-> 子 Agent A
-> 子 Agent B
-> 子 Agent C
如果 子 Agent B 触发支付中断:
text
子 Agent B
-> create_payment
-> interrupt
那么当前主 run 的状态是:
text
当前 thread_id 的主 DeepAgent run 暂停
等待人工 approve / reject / respond
5.1 会不会暂停所有子 Agent?
不会全局暂停所有 Agent。
更准确:
text
暂停的是当前 thread_id 对应的这一次执行链。
也就是:
- 当前主 Agent 不会继续下一轮推理;
- 当前主 Agent 不会提前汇总 A/C 的结果;
- 当前这批 task 要等中断恢复后才能完整返回;
- 其他用户请求、其他 thread_id、其他独立 Agent run 不受影响。
5.2 已经完成的并行子任务怎么办
如果 A/C 已经完成,它们的结果可能已经在当前 run 的内部状态或 checkpoint 中。
但是主 Agent 不会继续消费这些结果,直到 B 的中断被恢复。
重要的是:
中断不是事务,已经执行过的外部副作用不会自动回滚。
例如:
text
Agent A 已经写库
Agent C 已经发消息
Agent B 等待支付审批
如果 B 最后被拒绝,A/C 已经发生的副作用不会自动撤销。
所以生产设计里,危险副作用不要和审批无序并行。
推荐流程:
text
并行:查询、分析、风控、生成计划
串行:人工审批
审批通过后:支付、写库、发通知
6. 多个中断同时发生怎么办
如果并行分支里多个子 Agent 都触发中断,运行时可能返回多个 interrupt。
恢复时要为每个中断提供对应结果。
工具调用审批场景通常是:
python
from langgraph.types import Command
def resume_multiple_tool_interrupts(agent, config):
"""恢复多个工具调用审批中断。"""
return agent.invoke(
Command(
resume={
"decisions": [
{"type": "approve"},
{"type": "reject"},
]
}
),
config=config,
version="v2",
)
如果是并行分支里的多个 interrupt(),更推荐用 interrupt id 映射恢复:
python
from langgraph.types import Command
def resume_interrupts_by_id(agent, config, interrupt_a_id: str, interrupt_b_id: str):
"""根据 interrupt id 恢复多个并行中断。"""
return agent.invoke(
Command(
resume={
interrupt_a_id: {"approved": True},
interrupt_b_id: {"approved": False, "reason": "金额异常"},
}
),
config=config,
)
这样能避免多个并行中断靠顺序匹配导致混乱。
7. 完整 Demo:主 Agent + 多个子 Agent + 支付审批
下面示例重点展示结构,不绑定某个具体模型供应商。
python
from langchain.tools import tool
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import Command
from deepagents import create_deep_agent
@tool
def query_order(order_id: str) -> dict:
"""查询订单信息,返回订单金额、收款方和状态。"""
return {
"order_id": order_id,
"amount": 199.0,
"payee": "merchant_a",
"status": "pending",
}
@tool
def create_payment(order_id: str, amount: float, payee: str) -> str:
"""执行支付动作;人工审批通过后才允许真实执行。"""
return f"支付成功:订单={order_id},金额={amount},收款方={payee}"
@tool
def write_audit_log(order_id: str, message: str) -> str:
"""写入审计日志,用于记录审批前后的关键事件。"""
return f"审计日志已写入:{order_id} - {message}"
order_subagent = {
"name": "order-agent",
"description": "负责查询和整理订单信息。",
"system_prompt": "你是订单子代理,只负责查询订单信息,不执行支付。",
"tools": [query_order],
}
payment_subagent = {
"name": "payment-agent",
"description": "负责执行支付动作,支付前必须等待人工审批。",
"system_prompt": (
"你是支付子代理。你只能根据主 Agent 提供的订单信息调用 create_payment。"
"你不得修改金额或收款方。"
),
"tools": [create_payment],
"interrupt_on": {
"create_payment": {
"allowed_decisions": ["approve", "reject"],
}
},
}
audit_subagent = {
"name": "audit-agent",
"description": "负责写审计日志。",
"system_prompt": "你是审计子代理,只负责记录日志,不执行支付。",
"tools": [write_audit_log],
}
checkpointer = MemorySaver()
agent = create_deep_agent(
model="openai:gpt-5.5",
tools=[],
subagents=[order_subagent, payment_subagent, audit_subagent],
checkpointer=checkpointer,
system_prompt=(
"你是主 Agent,负责支付流程编排。\n"
"规则:\n"
"1. 查询订单必须调用 order-agent。\n"
"2. 支付必须调用 payment-agent。\n"
"3. 审计日志必须调用 audit-agent。\n"
"4. 你不得亲自执行支付。\n"
"5. 支付前必须等待人工审批。"
),
)
config = {"configurable": {"thread_id": "pay-order-10001"}}
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "请处理订单 order-10001 的支付流程。",
}
]
},
config=config,
version="v2",
)
if result.interrupts:
approval_result = agent.invoke(
Command(
resume={
"decisions": [
{"type": "approve"},
]
}
),
config=config,
version="v2",
)
关键点:
checkpointer必须有;thread_id必须一致;- 支付工具只给
payment-agent; - 主 Agent 不直接持有
create_payment; - 审批通过前,
create_payment不应该真正执行; - 支付工具内部仍要做幂等和权限校验。
8. 子 Agent 之间怎么通讯
默认情况下,子 Agent 之间不直接通讯。
不要想象成:
text
researcher <-> critic <-> writer
更像:
text
researcher -> 主 Agent -> critic -> 主 Agent -> writer
9. 通讯方式一:主 Agent 中转
这是最简单、最推荐的方式。
text
主 Agent
-> task(researcher, "搜索论文")
<- researcher 返回论文列表
主 Agent
-> task(critic, "基于下面论文列表评分:...")
<- critic 返回评分结果
主 Agent
-> 写最终报告
优点:
- 最清晰;
- 最容易审计;
- 容易控制流程;
- 不容易出现子 Agent 私下乱调用。
缺点:
- 中间数据太大时会占用主 Agent 上下文;
- 数据结构要在 prompt 里约束清楚。
适合:
- 小到中等规模数据;
- 结构化结果;
- 审计要求高的流程。
10. 通讯方式二:共享文件系统
如果数据比较大,可以让一个子 Agent 写文件,另一个子 Agent 读文件。
text
researcher
-> write_file("/work/pay-order-10001/research.json")
critic
-> read_file("/work/pay-order-10001/research.json")
主 Agent 只负责传路径:
text
请读取 /work/pay-order-10001/research.json,并基于其中内容评分。
推荐路径设计:
text
/work/{thread_id}/{subagent_name}/output.json
/work/{thread_id}/shared/research_result.json
/work/{thread_id}/shared/payment_plan.json
优点:
- 不把大文本塞回 messages;
- 可以保留中间产物;
- 适合大文件、大搜索结果、大报告草稿。
缺点:
- 要做好路径隔离;
- 并行写文件可能冲突;
- 默认
StateBackend是虚拟文件系统,不一定是本机真实磁盘; - 生产环境要配置权限和持久化策略。
11. 通讯方式三:共享数据库 / 对象存储 / 业务 API
生产系统更推荐这种。
例如:
python
from langchain.tools import tool
@tool
def save_agent_result(task_id: str, role: str, data: dict) -> str:
"""保存子代理结果,供主 Agent 或其他子 Agent 后续读取。"""
# 生产环境中可写入数据库、Redis、对象存储或企业内部 API
return f"saved:{task_id}:{role}"
@tool
def load_agent_result(task_id: str, role: str) -> dict:
"""读取指定子代理之前保存的结果。"""
# 生产环境中可从数据库、Redis、对象存储或企业内部 API 读取
return {
"task_id": task_id,
"role": role,
"data": {},
}
推荐数据流:
text
researcher -> save_agent_result(task_id, "researcher", data)
critic -> load_agent_result(task_id, "researcher")
critic -> save_agent_result(task_id, "critic", scores)
主 Agent -> load_agent_result(task_id, "critic")
优点:
- 可审计;
- 可恢复;
- 可跨进程;
- 可支持异步任务;
- 可接权限系统。
缺点:
- 工程复杂度更高;
- 要设计 schema、幂等、权限、过期时间;
- Agent 需要知道 task_id 和数据契约。
12. 通讯方式四:外层 LangGraph 管 state
如果流程很复杂,建议不要让一个主 DeepAgent 管全部。
更稳的是:
text
LangGraph 做流程骨架
DeepAgent 做复杂节点
例如:
text
Graph state:
order_info
risk_result
payment_approval
payment_result
audit_log
Node 1: order DeepAgent
Node 2: risk DeepAgent
Node 3: human approval
Node 4: payment tool
Node 5: audit DeepAgent
这种方式适合:
- 强流程;
- 支付;
- 审批;
- 风控;
- 多阶段业务;
- 需要恢复和审计的系统。
DeepAgent 负责"智能任务",Graph 负责"确定性流程"。
13. 不推荐:子 Agent 私下互相调用
不推荐这样设计:
text
researcher -> task(critic)
critic -> task(writer)
writer -> task(researcher)
原因:
- 调用链难审计;
- 容易递归;
- 成本不可控;
- 中断授权难管理;
- 主 Agent 失去编排权;
- 并行和恢复语义更复杂。
生产里应该让主 Agent 或外层 Graph 负责调度。
14. 支付审批场景的最佳实践
支付 / 扣款 / 转账 / 下单这类场景,不要只靠 prompt。
推荐同时做:
text
1. 支付工具只给 payment-agent,不给主 Agent。
2. payment-agent 配 interrupt_on。
3. allowed_decisions 建议只开放 approve / reject。
4. 不建议开放 edit,避免审批人直接改金额或收款方。
5. 支付工具内部二次校验订单金额、收款方、用户权限。
6. 使用 idempotency_key 防止重复扣款。
7. 审批记录落库:审批人、时间、原始参数、审批结果。
8. 审批通过前不要调用真实支付网关。
9. 不要让支付动作和其他副作用无序并行。
10. 用同一个 thread_id 恢复中断。
支付流程建议:
text
查询订单
-> 风控检查
-> 生成支付计划
-> 人工审批
-> 真正支付
-> 写审计日志
-> 通知用户
不建议:
text
支付、写库、发通知、写审计全部并行跑
15. 常见坑点
15.1 以为中断会暂停整个系统
不会。
中断暂停的是当前 thread_id 对应的执行链。
其他请求、其他用户、其他 thread 不受影响。
15.2 以为中断会自动回滚其他子任务
不会。
如果并行子 Agent 已经执行了外部副作用,比如写库、发消息、调用支付网关,不会因为另一个子 Agent 中断而自动撤销。
15.3 忘记配置 checkpointer
Human-in-the-loop 需要 checkpoint。
没有 checkpointer,就不能可靠恢复到中断点。
15.4 resume 时换了 thread_id
恢复必须使用同一个 thread_id。
否则运行时找不到之前暂停的状态。
15.5 compiled 子 Agent 没配自己的 interrupt
声明式 subagent 默认可以继承主 Agent 的 interrupt_on。
但如果你传的是已经编译好的 child runnable,例如:
python
child_agent = create_deep_agent(...)
再作为 compiled subagent 传入,主 Agent 的 interrupt_on 不会自动注入它。
这种情况下,要在 child_agent 自己创建时配置 interrupt_on 和 checkpointer。
15.6 子 Agent 通讯全靠自然语言
如果靠自然语言描述传数据,很容易出现:
- 字段丢失;
- 数字写错;
- JSON 格式不稳定;
- 上下文过长;
- 审计困难。
生产中建议用结构化数据、文件、数据库或外部 state。
15.7 多子 Agent 并行写同一路径
如果多个子 Agent 都写:
text
/work/result.json
就容易互相覆盖。
应该写:
text
/work/{thread_id}/{subagent_name}/result.json
16. 选择建议
16.1 数据怎么传
| 数据规模 | 推荐方式 |
|---|---|
| 很小 | 主 Agent 直接中转 |
| 中等结构化数据 | 主 Agent 中转 JSON |
| 大文本 / 大文件 | 共享文件系统 |
| 生产业务数据 | 数据库 / 对象存储 / 业务 API |
| 强流程、多阶段审批 | 外层 LangGraph state |
16.2 中断怎么做
| 场景 | 推荐方式 |
|---|---|
| 普通工具审批 | interrupt_on |
| 文件读写审批 | permissions 的 mode="interrupt" |
| 支付 / 转账 / 复杂审批 | 工具内部 interrupt() + 业务校验 |
| 多个并行中断 | interrupt id 映射恢复 |
| 远程异步子 Agent | 在远程子 Agent 自己配置审批 |
17. 总结
DeepAgent 的多子代理协作可以用一句话理解:
主 Agent 负责调度,子 Agent 负责执行;中断暂停当前 thread 的执行链,通讯通过主 Agent 或共享存储完成。
对于中断授权:
- 子 Agent 可以通过
interrupt_on或interrupt()暂停; - 同一个
thread_id中,一个子 Agent 中断会暂停当前主 run; - 不会全局暂停其他 thread;
- 已发生的副作用不会自动回滚;
- 支付类动作必须做幂等、审计和权限校验。
对于 Agent 间通讯:
- 默认子 Agent 不直接互相通讯;
- 小数据走主 Agent 中转;
- 大数据走共享文件;
- 生产数据走数据库 / 对象存储 / 业务 API;
- 强流程走外层 LangGraph state。
最稳的生产架构是:
text
Graph 管流程
主 DeepAgent 管调度
子 DeepAgent 管复杂任务
数据库 / 文件系统 管数据交换
Human-in-the-loop 管高风险动作