面向读者:正在构建 AI Agent 系统的工程师 / 架构师
文档定位:从原理到实战的全面参考手册,覆盖模式选型、架构设计、生产落地与常见陷阱
基础参考:Inngest 联合创始人 Dan Farrelly 的 Sub-Agent 三模式框架 + Anthropic Agent 构建指南 + 业界生产实践
目录
- 一、为什么需要编排模式
- 二、核心概念:上下文压缩与 Sub-Agent
- 三、三种 Sub-Agent 核心模式(深度篇)
- 四、更多编排模式(广度篇)
- 五、工具设计原则
- 六、通用 Agent vs. 专业化 Agent
- 七、生产环境落地指南(实战篇)
- 八、常见陷阱与反模式
- 九、选型决策树
- 十、总结与行动建议
- 十一、未来探索方向
- 参考资料
一、为什么需要编排模式
一个直观的问题:为什么不能把所有任务都塞给一个大模型,让它自己搞定?
答案来自三个工程现实:
1.1 上下文窗口不是银弹
即使模型的上下文窗口已扩展到 100K+ tokens,"能装下"和"能有效利用"是两回事:
- 注意力稀释:上下文越长,模型对关键信息的关注度越低("Lost in the Middle" 问题)
- 延迟线性增长:每增加 1K tokens,推理延迟和成本都在增加
- 质量不可控:长上下文中的噪声信息会导致幻觉率上升
1.2 单体 Agent 的扩展性天花板
当业务复杂度上升,单体 Agent 会遇到:
- 能力边界:一个 System Prompt 无法覆盖所有场景的最优指令
- 调试噩梦:一个 50 步的 Agent 链路中出了问题,定位成本极高
- 并发限制:同步执行所有任务,无法利用并行加速
1.3 编排的本质:分治与组合
编排模式的核心思想是软件工程中最基本的原则——分治法:
复杂任务 → 拆分为可管理的子任务 → 独立执行 → 聚合结果
正如微服务架构拆分单体应用,Agent 编排模式将复杂的 AI 任务拆分为协作的子 Agent,每个子 Agent 有独立的上下文、明确的职责和清晰的接口。
二、核心概念:上下文压缩与 Sub-Agent
在深入具体模式之前,先理解两个基础概念。
2.1 Sub-Agent 是什么
Sub-Agent 是由父 Agent 生成的独立 LLM 执行上下文,用于处理特定范围的任务。关键特征:
- 独立上下文窗口:不与父 Agent 共享完整的上下文历史
- 独立 System Prompt:可以针对子任务定制指令
- 结果摘要返回:完成后只将精简摘要返回给父 Agent
2.2 上下文压缩的威力
这是 Sub-Agent 模式的核心价值。以一个实际场景说明:
场景:父 Agent 需要分析一个代码仓库的安全风险
不使用 Sub-Agent:
├── 读取 8 个文件 → 每个文件 ~2000 tokens → 16,000 tokens
├── 15 次工具调用的输入输出 → ~8,000 tokens
├── 中间推理过程 → ~4,000 tokens
└── 总计:~28,000 tokens 直接堆入父 Agent 上下文
使用 Sub-Agent:
├── Sub-Agent 在独立上下文中完成所有工作
├── 返回给父 Agent 的摘要 → ~750 tokens
└── 上下文节省:90%+
实测数据(来自 Dan Farrelly):Sub-Agent 模式能将添加到父 Agent 上下文中的 token 数量减少 90% 以上。
2.3 深度控制原则
关键设计约束——深度 1 的委托:
父 Agent → 可以生成 Sub-Agent → Sub-Agent 不能再生成 Sub-Agent
为什么?
- 避免递归生成导致的无限循环
- 防止成本失控(指数级增长)
- 简化调试和可观测性
- 实践证明,一层委托已经能覆盖绝大多数场景
三、三种 Sub-Agent 核心模式(深度篇)
3.1 同步模式(Synchronous)
核心语义
"做这件事,我等你"
父 Agent 生成 Sub-Agent 并阻塞等待,直到获得结果。Sub-Agent 完成后将摘要作为工具调用结果返回。
架构图
┌─────────────┐ spawn & wait ┌─────────────┐
│ 父 Agent │ ──────────────────→ │ Sub-Agent │
│ (blocked) │ │ (running) │
│ │ ←── summary ────── │ (done) │
│ (continue) │ └─────────────┘
└─────────────┘
适用场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 数据依赖 | 后续步骤依赖 Sub-Agent 的输出 | 先查库存,再决定是否下单 |
| 查询-决策 | 需要基于查询结果做判断 | 分析日志后决定是否告警 |
| 代码生成 | 生成的代码需要反馈到当前流程 | 生成 SQL 后执行并展示结果 |
| 验证环节 | 需要验证结果才能继续 | 检查 API 响应格式是否正确 |
设计要点
# 伪代码:同步 Sub-Agent 实现
class SyncSubAgent:
def execute(self, task: str, tools: list, parent_context: str) -> str:
"""
关键设计:
1. 独立的上下文窗口
2. 指令偏向简洁(因为结果会被父 Agent 综合)
3. 带有超时机制
"""
sub_agent = create_agent(
system_prompt=f"""
你是一个专注的任务执行者。
任务完成后,用简洁的摘要返回结果。
不需要解释过程,只需要关键结论和数据。
""",
tools=tools,
# Sub-Agent 不能再生成 Sub-Agent
can_spawn_sub_agents=False
)
# 阻塞等待,带超时
result = sub_agent.run(
task=task,
timeout=60, # 秒
max_steps=20 # 最大工具调用次数
)
# 返回压缩后的摘要
return result.summary # ~750 tokens vs 原始 ~28,000 tokens
注意事项
- ⚠️ 超时必设:没有超时机制的同步等待是生产事故的温床
- ⚠️ 步数上限:限制 Sub-Agent 的最大工具调用次数,防止无限循环
- ⚠️ 降级策略:Sub-Agent 超时或失败时,父 Agent 应有兜底方案
- 💡 指令要求简洁:告诉 Sub-Agent "简洁返回",因为父 Agent 会综合结果
3.2 异步模式(Asynchronous)
核心语义
"去做这件事,完成后直接告诉用户"
父 Agent 启动 Sub-Agent 后立即继续,不等待结果。Sub-Agent 独立运行,完成后直接向用户回复。
架构图
┌─────────────┐ fire & forget ┌─────────────┐
│ 父 Agent │ ──────────────────→ │ Sub-Agent │
│ (continue) │ │ (running) │
│ │ │ ... │
│ (done) │ │ (done) │
└─────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ 用户 │
│ (收到结果) │
└─────────────┘
适用场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 长时间任务 | 执行时间远超用户等待容忍度 | 生成 30 页研究报告 |
| 副作用操作 | 需要执行但不影响当前决策 | 发送通知邮件、记录审计日志 |
| 并行批处理 | 多个独立任务同时执行 | 同时分析 5 个竞品的产品页 |
| 不阻塞交互 | 希望用户继续与 Agent 对话 | "我帮你查着,你可以继续问别的" |
设计要点
# 伪代码:异步 Sub-Agent 实现
class AsyncSubAgent:
def dispatch(self, task: str, tools: list, callback_channel: str) -> str:
"""
关键设计:
1. Fire-and-forget,父 Agent 不等待
2. 指令偏向详尽(因为结果直接给用户看)
3. 完成后通过指定渠道通知用户
"""
sub_agent = create_agent(
system_prompt=f"""
你是一个独立的任务执行者。
完成任务后,生成详细、完整、对用户友好的报告。
用户不会看到你的执行过程,只能看到最终结果。
请确保结果自包含、易理解。
""",
tools=tools,
can_spawn_sub_agents=False
)
# 异步派发,立即返回
job_id = sub_agent.dispatch_async(
task=task,
callback=callback_channel, # Slack/邮件/WebSocket
timeout=300, # 异步任务可以给更长时间
max_steps=50
)
return f"任务已启动 (ID: {job_id}),完成后将通知你。"
关键区别:同步 vs 异步的指令差异
这是一个常被忽略但极其重要的设计点:
| 维度 | 同步 Sub-Agent | 异步 Sub-Agent |
|---|---|---|
| 输出风格 | 简洁、结构化 | 详尽、用户友好 |
| 原因 | 父 Agent 会整合和润色 | 结果直接面向用户 |
| 典型指令 | "返回关键数据点即可" | "生成完整报告,包含上下文解释" |
注意事项
- ⚠️ 结果不回流:父 Agent 无法获取异步 Sub-Agent 的结果,如果需要结果做判断,应该用同步模式
- ⚠️ 通知机制必备:必须有可靠的渠道将结果送达用户
- ⚠️ 幂等性设计:异步任务可能被重试,确保多次执行不会产生错误副作用
- 💡 Dan Farrelly 的建议:如果你不确定用哪种模式,默认选异步——成本更低,父 Agent 更精简
3.3 定时模式(Scheduled)
核心语义
"在未来某个时间点做这件事"
父 Agent 安排 Sub-Agent 在指定时间运行。执行时基于当时的最新数据做决策,而非预设响应。
架构图
┌─────────────┐ schedule ┌──────────────┐ at T ┌─────────────┐
│ 父 Agent │ ──────────────→ │ 调度系统 │ ──────────→ │ Sub-Agent │
│ (done) │ │ (waiting) │ │ (running) │
└─────────────┘ └──────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ 用户 │
└─────────────┘
与传统 Cron Job 的本质区别
传统 Cron Job:
"每天 9:00 发送昨日报表"
→ 固定脚本,固定数据源,固定格式
→ 无法适应变化
定时 Sub-Agent:
"明天 9:00 检查部署指标"
→ 在 9:00 基于实时数据判断
→ 可能发现异常 → 自动查日志 → 生成诊断报告
→ 也可能一切正常 → 发送简短确认
→ 具有上下文意识和自主决策能力
适用场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 智能跟进 | 基于时间点的上下文感知提醒 | "3 天后检查 PR 是否已合并" |
| 定期巡检 | 周期性的智能检查 | "每天 9 点检查线上服务健康度" |
| 延迟决策 | 需要等待条件成熟 | "1 小时后检查 A/B 测试数据是否达到统计显著性" |
| SLA 监控 | 超时自动升级 | "24 小时后检查工单是否已处理,否则升级" |
设计要点
# 伪代码:定时 Sub-Agent 实现
class ScheduledSubAgent:
def schedule(self, task: str, run_at: datetime, tools: list,
callback_channel: str) -> str:
"""
关键设计:
1. 本质是延迟触发的异步 Sub-Agent
2. 执行时获取最新数据(不是预存数据)
3. 需要持久化调度信息(数据库/消息队列)
"""
job = scheduler.create_job(
run_at=run_at,
agent_config={
"system_prompt": """
你是一个智能巡检 Agent。
检查当前系统状态,基于实时数据做出判断。
如果发现异常,详细说明问题并建议解决方案。
如果一切正常,简短确认即可。
""",
"tools": tools,
"task": task,
"callback": callback_channel
}
)
return f"已安排在 {run_at} 执行 (Job ID: {job.id})"
注意事项
- ⚠️ 持久化调度:定时任务必须持久化到可靠存储(数据库),不能只存内存
- ⚠️ 时区处理:明确时区,避免 "我说的9点和系统理解的9点不一样"
- ⚠️ 过期策略:定义任务过期窗口(如超过预定时间 1 小时则取消)
- ⚠️ 执行上下文:确保 Sub-Agent 在触发时能获取到所需的工具和权限
- ⚠️ 任务描述冻结(关键限制):排队时的任务描述是"快照"——在调度时刻被固定下来。如果从调度到实际执行之间经过了较长时间,任务描述中引用的上下文可能已经过时。例如:"检查 PR #123 的状态"在排队时有意义,但执行时该 PR 可能已被关闭。缓解策略:① 任务描述中尽量引用可查询的标识符(如 PR 编号),而非快照数据;② 通过句柄(handle)支持查询和取消已排队任务
- ⚠️ 排队后难以修改:一旦任务进入调度队列,修改或取消的成本较高。设计时应预留通过任务句柄(handle)查询状态和取消任务的能力
3.4 三种模式对比总结
| 维度 | 同步 | 异步 | 定时 |
|---|---|---|---|
| 父 Agent 行为 | 阻塞等待 | 立即继续 | 立即继续 |
| 结果流向 | 返回给父 Agent | 直接给用户 | 直接给用户 |
| 父上下文影响 | 增加(摘要级别) | 零增加 | 零增加 |
| 适合任务时长 | 秒级~分钟级 | 分钟级~小时级 | 未来任意时间 |
| 并行支持 | 不支持(阻塞中) | 天然支持 | 天然支持 |
| 典型超时设置 | 30-120 秒 | 5-30 分钟 | 取决于任务 |
| 复杂度 | 低 | 中 | 中-高 |
| 底层实现 | 函数调用 | 消息队列/事件 | 调度器+消息队列 |
3.5 决策框架:如何选择模式
Dan Farrelly 提供了一个简洁的决策树:
需要结果才能继续?
├── 是 → 同步模式
└── 否
├── 应该在未来某时间执行?
│ ├── 是 → 定时模式
│ └── 否
│ ├── 多个独立任务?→ 异步模式(并行)
│ └── 单个独立任务?→ 异步模式
└── 不确定?→ 默认选异步
核心原则:不确定时选异步。原因:
- 零协调开销
- 父 Agent 上下文保持精简
- 天然支持并行
- 成本最优
四、更多编排模式(广度篇)
除了 Dan Farrelly 的三种 Sub-Agent 模式,业界(特别是 Anthropic 官方指南)还总结了更多实用的编排模式。
4.1 提示链(Prompt Chaining)
核心思想
将复杂任务分解为线性的步骤序列,每一步的输出作为下一步的输入。可以在步骤之间加入代码检查点(Gate)。
架构图
Input → [Step 1] → Gate ✓ → [Step 2] → Gate ✓ → [Step 3] → Output
✗ ✗
↓ ↓
Error Error
适用场景
- 文档先写大纲,审核通过后再写正文
- 数据处理流水线:提取 → 清洗 → 转换 → 格式化
- 多语言翻译:先翻译,再审校,再本地化调整
代码示例
def prompt_chain(input_data: str, steps: list[str], gates: list[callable] = None) -> str:
"""
链式调用多个 LLM 步骤,可选地在步骤间加入质量门控。
"""
result = input_data
for i, step_prompt in enumerate(steps):
result = llm_call(f"{step_prompt}\nInput: {result}")
# 可选:步骤间的质量检查
if gates and i < len(gates) and gates[i]:
if not gates[i](result):
raise QualityGateError(f"Step {i+1} failed quality check")
return result
# 使用示例:生成技术文档
doc_steps = [
"根据以下需求,生成文档大纲(包含章节标题和要点)",
"基于以下大纲,为每个章节撰写详细内容",
"审核以下文档,修正技术错误并优化表达"
]
何时使用 / 何时不用
- ✅ 用:任务可以自然分解为有序步骤,后续步骤依赖前序输出
- ❌ 不用:步骤之间没有依赖关系(应该用并行化)
- ❌ 不用:需要动态决定执行路径(应该用路由或协调者模式)
4.2 路由(Routing)
核心思想
对输入进行分类,将其定向到不同的专门处理链路。适合输入多样、处理逻辑差异大的场景。
架构图
┌──→ [链路 A:账单问题处理]
Input → [分类器/路由器] ├──→ [链路 B:技术支持处理]
└──→ [链路 C:账户安全处理]
适用场景
- 客服系统:根据问题类型分流到不同处理链路
- 多模态输入:文本 / 图片 / 代码走不同处理管线
- 多租户系统:不同客户走不同的模型或策略
代码示例
def route(input_text: str, routes: dict[str, str]) -> str:
"""
路由器:先分类,再用对应的专门提示词处理。
"""
# Step 1: 分类
classifier_prompt = f"""
分析以下输入,选择最合适的处理团队:{list(routes.keys())}
<reasoning>你的分析过程</reasoning>
<selection>选择的团队名</selection>
Input: {input_text}
"""
response = llm_call(classifier_prompt)
selected_route = extract_xml(response, "selection").strip()
# Step 2: 使用对应链路处理
handler_prompt = routes[selected_route]
return llm_call(f"{handler_prompt}\nInput: {input_text}")
# 路由定义
support_routes = {
"billing": "你是账单专家。帮用户解决费用、退款、订阅问题...",
"technical": "你是技术支持工程师。帮用户排查技术故障...",
"security": "你是安全专家。帮用户处理账户安全、权限问题..."
}
关键设计决策
- LLM 路由 vs 规则路由:LLM 路由更灵活但有延迟开销,规则路由更快但不够灵活
- 兜底策略:必须有默认路由处理未匹配的输入
- 信心阈值:路由器信心不足时,走通用处理链路而非强行分类
4.3 并行化(Parallelization)
核心思想
同一任务由多个 LLM 同时处理,结果通过聚合策略合并。有两种子模式:
- 分片并行(Sectioning):任务被拆分为独立子任务,并行执行
- 投票并行(Voting):同一任务由多个实例处理,通过投票决定最终结果
架构图
分片并行: 投票并行:
┌→ [Worker A: 子任务1] ┌→ [Instance 1]
Input → ├→ [Worker B: 子任务2] → Merge Input → ├→ [Instance 2] → Vote → Output
└→ [Worker C: 子任务3] └→ [Instance 3]
代码示例
from concurrent.futures import ThreadPoolExecutor
def parallel_section(task_prompt: str, sub_tasks: list[str],
n_workers: int = 3) -> list[str]:
"""分片并行:不同子任务同时执行"""
with ThreadPoolExecutor(max_workers=n_workers) as executor:
futures = [
executor.submit(llm_call, f"{task_prompt}\n子任务: {sub}")
for sub in sub_tasks
]
return [f.result() for f in futures]
def parallel_vote(task: str, n_votes: int = 3) -> str:
"""投票并行:多个实例处理同一任务,取多数意见"""
with ThreadPoolExecutor(max_workers=n_votes) as executor:
futures = [executor.submit(llm_call, task) for _ in range(n_votes)]
results = [f.result() for f in futures]
# 简单多数投票(实际可以更复杂)
return most_common(results)
适用场景
- 分片:从不同角度分析同一个问题(客户/员工/投资者视角)
- 分片:同时处理多个独立的数据源
- 投票:安全敏感的判断(如内容审核,多实例投票降低误判率)
- 投票:代码审查(多个实例独立检查,汇总发现)
4.4 协调者-工作者模式(Orchestrator-Worker)
核心思想
中央协调者 LLM 动态分解任务,将子任务委派给 Worker LLMs,最后综合结果。与并行化的关键区别:子任务不是预定义的,而是由协调者根据输入动态生成的。
架构图
┌→ [Worker 1] → Result 1 ─┐
Input → [Orchestrator] ├→ [Worker 2] → Result 2 ─┼→ [Orchestrator] → Output
(分析&拆分) └→ [Worker N] → Result N ─┘ (综合&输出)
适用场景
- 大型代码重构:协调者分析影响范围,动态分配文件级修改任务
- 复杂报告生成:协调者拆分章节,Worker 并行撰写
- 多步骤数据分析:协调者规划分析路径,Worker 执行各步
与 Sub-Agent 同步模式的关系
协调者-工作者模式可以视为同步 Sub-Agent 的批量版:
Sub-Agent 同步模式:父 → 1 个 Sub-Agent → 等待结果
Orchestrator-Worker:协调者 → N 个 Worker → 等待所有结果 → 综合
4.5 评估者-优化者模式(Evaluator-Optimizer)
核心思想
一个 LLM 生成内容,另一个 LLM 评估并反馈,循环迭代直到满足质量标准。
架构图
┌──────────────────────────────┐
│ │
Task → [Generator] → Output → [Evaluator] → PASS? ─→ Final Output
↑ │
└── feedback ─────────────┘
FAIL
代码示例
def iterative_refinement(task: str, max_iterations: int = 5) -> str:
"""评估者-优化者循环"""
# 初始生成
result = llm_call(f"Generator prompt...\nTask: {task}")
for i in range(max_iterations):
# 评估
eval_response = llm_call(f"""
评估以下内容是否满足任务要求。
如果满足,回复 PASS。
如果不满足,提供具体的改进建议。
任务: {task}
内容: {result}
<evaluation>PASS 或 FAIL</evaluation>
<feedback>改进建议</feedback>
""")
evaluation = extract_xml(eval_response, "evaluation")
if evaluation.strip() == "PASS":
return result
feedback = extract_xml(eval_response, "feedback")
# 基于反馈重新生成
result = llm_call(f"""
根据反馈改进你的输出。
原始任务: {task}
上次输出: {result}
反馈: {feedback}
""")
return result # 达到最大迭代次数
适用场景
- 文学翻译:翻译 → 审校 → 修改 → 再审
- 代码生成:生成 → 测试 → 修复 → 再测试
- 提示词优化:生成 → 评估效果 → 调整 → 再评估
注意事项
- ⚠️ 必须设最大迭代次数:防止无限循环
- ⚠️ 评估标准要明确:模糊的评估标准会导致永远无法 PASS
- 💡 不同模型组合:可以用强模型评估、弱模型生成,优化成本
4.6 DAG 编排(有向无环图)
核心思想
将任务建模为有向无环图,节点是任务,边是依赖关系。系统自动解析依赖,最大化并行度。
架构图
[A] ──→ [C] ──→ [E]
↗ ↘
[Start] [End]
↘ ↗
[B] ──→ [D] ────
A 和 B 并行执行;C 依赖 A,D 依赖 B;E 依赖 C;最终汇聚。
适用场景
- 复杂的数据处理流水线(ETL)
- CI/CD 流水线中的 Agent 任务
- 多模态处理(图片、文本、音频各走一路,最后汇聚)
实现方式
通常借助现有的工作流引擎:
- Temporal:分布式持久执行引擎
- Prefect / Airflow:Python 生态的工作流编排
- Inngest:事件驱动的后台任务框架
- LangGraph:LangChain 生态的图状态机
# LangGraph 风格的 DAG 定义(概念示例)
from langgraph.graph import StateGraph
workflow = StateGraph(AgentState)
# 添加节点
workflow.add_node("research", research_agent)
workflow.add_node("analyze", analyze_agent)
workflow.add_node("write", write_agent)
workflow.add_node("review", review_agent)
# 定义依赖边
workflow.add_edge("research", "analyze")
workflow.add_edge("analyze", "write")
workflow.add_edge("write", "review")
# 条件分支
workflow.add_conditional_edges("review", quality_check, {
"pass": END,
"fail": "write" # 循环回写作节点
})
4.7 事件驱动模式(Event-Driven)
核心思想
Agent 的执行由事件触发,而非直接调用。事件可以来自用户操作、系统事件、外部 Webhook 等。
架构图
[Event Source] → [Event Bus] → [Event Handler/Agent]
↓
┌───────┼───────┐
↓ ↓ ↓
[Agent A] [Agent B] [Agent C]
(订阅事件X) (订阅事件Y) (订阅事件X+Y)
适用场景
- 代码合并后自动触发 Agent 做 Code Review
- 监控告警触发 Agent 做根因分析
- 用户注册后触发 Agent 做个性化引导
- 票据创建后触发 Agent 做自动分类和分配
与定时模式的关系
定时模式是事件驱动的特例——触发事件是"时间到了"。
4.8 层级委托模式(Hierarchical Delegation)
核心思想
多层 Agent 形成树状层级结构,每层负责不同粒度的决策和执行。
架构图
[总监 Agent]
↗ ↓ ↘
[经理 A] [经理 B] [经理 C]
↙ ↘ ↙ ↘ ↙ ↘
[工人1] [工人2] [工人3] [工人4] [工人5] [工人6]
适用场景
- 超大规模项目管理
- 企业级自动化(部门→团队→个人层级)
- 复杂的多步骤决策链
注意事项
- ⚠️ Dan Farrelly 建议限制深度为 1:即使业务需要层级,也建议从扁平开始
- ⚠️ 复杂度指数增长:每增加一层,调试和可观测性难度翻倍
- ⚠️ 成本风险:深层级意味着大量的 LLM 调用
4.9 编排模式全景对比
| 模式 | 复杂度 | 灵活性 | 可预测性 | 最佳场景 |
|---|---|---|---|---|
| 提示链 | ⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | 固定步骤的线性流程 |
| 路由 | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | 输入多样、处理分支明确 |
| 并行化 | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | 独立子任务并发加速 |
| 协调者-工作者 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | 子任务需动态拆分 |
| 评估者-优化者 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | 需要迭代优化的输出 |
| 同步 Sub-Agent | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | 需要结果才能继续 |
| 异步 Sub-Agent | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | 独立长任务 |
| 定时 Sub-Agent | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | 未来时间点的智能任务 |
| DAG 编排 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 复杂依赖关系的工作流 |
| 事件驱动 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | 响应式、解耦的系统 |
| 层级委托 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐ | 超大规模、多层决策 |
五、工具设计原则
Dan Farrelly 在原文中强调了两个关键的工具设计原则,这对系统的可靠性至关重要。
5.1 独立工具优于参数切换
反模式 ❌:
{
"name": "run_sub_agent",
"parameters": {
"task": "分析数据",
"mode": "sync | async | scheduled"
}
}
推荐做法 ✅:
原文中 Dan Farrelly 的设计是两个工具,而不是三个——定时模式是异步工具的扩展用法(通过添加 ts 时间戳参数实现),而非独立的第三个工具:
// 工具 1:同步委托(对应 Inngest 的 step.invoke())
{
"name": "delegateTaskTool",
"description": "启动一个子代理并等待结果。当你需要答案才能继续当前任务时使用。",
"parameters": {
"task": "任务描述",
"tools": ["可用工具列表"]
}
}
// 工具 2:异步委托(对应 Inngest 的 step.sendEvent())
// 定时模式 = 异步 + ts 时间戳参数
{
"name": "delegateAsyncTaskTool",
"description": "启动一个子代理在后台独立运行。当任务可以独立完成、不需要等待结果时使用。可选指定未来执行时间。",
"parameters": {
"task": "任务描述",
"tools": ["可用工具列表"],
"ts": "(可选) ISO 8601 时间戳,指定后变为定时模式"
}
}
设计理念:定时模式本质上是"带时间戳的异步",不需要作为独立工具存在。这样减少了工具数量,同时保持了语义清晰。
原因:
- LLM 擅长工具选择(从多个工具中选一个),不擅长参数优化(在一个工具中选参数值)
- 但对于"同步 vs 异步"这种核心行为差异,应该用独立工具区分
- 而"异步 vs 定时"只是一个可选时间参数的区别,合并在一个工具中更合理
- 独立工具带来更清晰的日志和更简单的调试
- 每个工具的 description 可以精确描述使用场景
5.2 不要为模型做选择
给 LLM 提供所有可用工具及其清晰描述,让模型自己决定用哪个。
❌ 你用 if-else 硬编码路由逻辑
✅ 你给模型三个工具,让它基于任务特征自行选择
5.3 统一的后端实现
三种模式在后端可以复用同一个核心函数,区别仅在于:
| 维度 | 同步 | 异步 | 定时 |
|---|---|---|---|
| 触发方式 | 同步调用 | 异步派发 | 调度器触发 |
| 结果路由 | 返回给调用者 | 发送给用户 | 发送给用户 |
| 输出指令 | "简洁返回" | "详尽报告" | "详尽报告" |
5.4 通道无关路由(Channel-Agnostic Routing)
这是原文中一个重要的实现架构细节,对多渠道 Agent 系统至关重要。
核心问题
当异步/定时 Sub-Agent 完成任务后,需要将结果发送给用户。但用户可能从 Telegram、Slack、Email、Web 等不同渠道发起请求。Sub-Agent 不应该关心消息是从哪个渠道来的。
解决方案:通道元数据
┌──────────────┐ ┌──────────────┐
│ Telegram │ ──→ ┌─────────┐ │ Sub-Agent │
│ Slack │ ──→ │ 父Agent │ ──→│ (不关心渠道) │
│ Email │ ──→ └─────────┘ └──────┬───────┘
│ Web │ ──→ │
└──────────────┘ 结果 + 渠道元数据
│
▼
┌───────────────┐
│ 通道路由器 │
│ 根据元数据 │
│ 发回对应渠道 │
└───────────────┘
父 Agent 在委托任务时,将渠道信息作为结构化元数据传递给 Sub-Agent:
# 通道元数据结构
channel_meta = {
"channel": "telegram", # 渠道类型
"destination": "chat_12345", # 目标标识(聊天 ID / 频道 / 邮箱)
"channelMeta": { # 渠道特有的附加信息
"message_thread_id": 789,
"reply_to_message_id": 456
}
}
设计原则
| 原则 | 说明 |
|---|---|
| Sub-Agent 不感知渠道 | Sub-Agent 只负责完成任务、生成结果,不关心结果去哪里 |
| 结构化的元数据传递 | 渠道信息以 {channel, destination, channelMeta} 结构随任务传递 |
| 统一的结果路由层 | 一个独立的通道路由器负责将结果发回正确的渠道 |
| 新增渠道零改动 | 添加新渠道(如 Discord)只需在路由器中注册,不影响任何 Sub-Agent |
价值:这个设计使得 Agent 系统的核心逻辑与消息渠道完全解耦。在多渠道客服、跨平台助手等场景中,是保持架构清洁的关键。
5.5 持久执行原语(Durable Execution Primitives)
原文的一个核心工程理念是持久执行(Durable Execution)——每次 LLM 调用都是一个可恢复的检查点。
Inngest 的原语映射
原文使用 Inngest 框架,其核心原语及其在三种模式中的映射关系如下:
| Inngest 原语 | 语义 | 对应模式 |
|---|---|---|
step.invoke() | 函数到函数的 RPC,具有持久性。调用另一个函数并等待结果 | 同步模式 |
step.sendEvent() | 发送事件,触发另一个函数运行,不等待结果 | 异步模式 |
step.sendEvent() + ts | 发送带时间戳的事件,在指定时间触发 | 定时模式 |
createFunction() | 定义一个可被触发的函数(Agent 的入口) | 所有模式的基础 |
step.run() | 持久化执行步骤,每次 LLM 调用都是一个检查点 | Agent 循环核心 |
step.sendEvent() (结果) | Sub-Agent 完成后发送结果事件 | 异步/定时结果报告 |
关键设计:LLM 调用作为检查点
// 原文使用 TypeScript/Inngest,这是其核心设计理念
const agentFunction = inngest.createFunction(
{ id: "my-agent" },
{ event: "agent/run" },
async ({ event, step }) => {
// 每次 step.run() 都是一个持久化检查点
// 如果进程崩溃,恢复后会从最后一个成功的 step 继续
let messages = [{ role: "user", content: event.data.task }];
for (let i = 0; i < MAX_ITERATIONS; i++) {
// 这个 LLM 调用是持久化的——崩溃后可从此处恢复
const response = await step.run(`llm-call-${i}`, async () => {
return await llm.chat(messages);
});
// 工具调用也是持久化的
if (response.toolCalls) {
const toolResults = await step.run(`tools-${i}`, async () => {
return await executeTools(response.toolCalls);
});
messages.push(toolResults);
}
}
}
);
为什么持久执行很重要
| 问题 | 无持久执行 | 有持久执行 |
|---|---|---|
| 进程崩溃 | Agent 从头重新运行,浪费已完成的 LLM 调用 | 从最后一个检查点恢复 |
| 长时间运行 | 进程超时被杀 → 任务丢失 | 每个步骤持久化,可跨进程恢复 |
| 成本控制 | 崩溃重跑 = 双倍费用 | 只重跑未完成的部分 |
| 调试追踪 | 只有最终结果,过程不可见 | 每个检查点都有记录 |
子会话隔离(Sub-Session Key)
原文还提到了 subSessionKey 的设计——每个 Sub-Agent 有独立的会话标识,确保:
- 子代理的对话历史不会混入父代理
- 多个并行 Sub-Agent 之间完全隔离
- 便于独立追踪和调试每个 Sub-Agent 的执行过程
注:本文其他章节的代码示例使用 Python 伪代码以面向更广泛的读者,但原文使用的是 TypeScript + Inngest 框架。上述 Inngest 原语和持久执行机制是原文的核心工程基石,任何实际构建系统时都建议参考这些持久化设计理念,不论使用什么语言和框架。
六、通用 Agent vs. 专业化 Agent
这是一个架构师必须做出的关键决策。Dan Farrelly 给出了明确的建议。
6.1 强烈建议:从通用 Agent 开始
不要过早构建专业化 Agent(如邮件 Agent、数据 Agent、日程 Agent)。
这一观点也得到了业界顶尖团队的验证。Cursor 团队(当前最成功的 AI 编码工具之一)在实践中使用的是"大多是通用的任务接口"(mostly-generic task interface)。他们的经验表明:Sub-Agent 首先是上下文压缩边界(context-compression boundary),其次才是并行性(parallelism)工具。这意味着拆分 Sub-Agent 的首要动机应该是管理上下文复杂性,而不是追求领域专业化。
6.2 反对过早专业化的四个理由
理由 1:路由悖论
你需要一个"超级 Agent"来决定交给哪个"专家 Agent"
如果这个超级 Agent 足够聪明能做出正确路由……
那它为什么不能直接完成任务?
如果它不够聪明……
那路由错误造成的问题比直接处理更严重。
理由 2:工具重叠
Email Agent → 需要搜索工具 + 日历查询
Calendar Agent → 需要搜索工具 + 联系人查询
Contact Agent → 需要搜索工具 + 邮件查询
→ 大量工具重叠 → 维护成本 × N
→ 通用 Agent 用一套工具就能覆盖
理由 3:错误 Agent 问题
路由错误时,"专家 Agent"会在不完整的能力下尝试任务,产生微妙的、难以检测的错误——比直接失败更危险。
理由 4:评估复杂度爆炸
N 个专业 Agent 需要:
- N 套独立评估
- 1 套路由评估
- O(N²) 的交互测试
= 维护噩梦
6.3 何时应该专业化
只有在以下场景,专业化才有价值:
| 场景 | 原因 | 示例 |
|---|---|---|
| 不同模型要求 | 不同任务需要不同模型能力 | 视觉识别用多模态模型,快速分类用小模型 |
| 安全边界 | 数据隔离是硬性要求 | 客户数据 Agent 和内部数据 Agent 必须隔离 |
| 监管要求 | 需要独立的审计管道 | 金融合规 Agent 需要完全独立的日志链路 |
| 评估证据 | 实测证明专业化确实更好 | A/B 测试显示专业 Agent 准确率高 15% |
6.4 一句话总结
先用一个通用 Agent + Sub-Agent 模式跑通全流程,有数据证明瓶颈后再考虑专业化。
七、生产环境落地指南(实战篇)
7.1 架构分层设计
生产级 Agent 系统应该遵循分层解耦的架构:
┌────────────────────────────────────────────────┐
│ 用户交互层 │
│ (多渠道接入、NLU、输入归一化) │
├────────────────────────────────────────────────┤
│ Agent 核心层 │
│ (意图分类、上下文管理、Sub-Agent 调度) │
├────────────────────────────────────────────────┤
│ 知识检索层 │
│ (向量数据库、混合检索、重排序) │
├────────────────────────────────────────────────┤
│ 决策推理层 │
│ (LLM 推理、任务规划、自我优化) │
├────────────────────────────────────────────────┤
│ 工具执行层 │
│ (API 编排、外部服务、自主执行) │
├────────────────────────────────────────────────┤
│ 安全合规层(贯穿全链路) │
│ (认证鉴权、内容审核、审计日志、异常检测) │
└────────────────────────────────────────────────┘
核心原则:各层职责单一,通过标准接口交互。更换 LLM 不应影响工具层;更换向量数据库不应影响核心层。
7.2 上下文管理策略
上下文管理是 Agent 系统质量的生命线。
分层记忆架构
短期记忆(会话级)
├── 当前对话历史(滑动窗口 + 摘要压缩)
├── 中间计算状态
└── Sub-Agent 返回的摘要
长期记忆(用户级)
├── 用户偏好和历史
├── 跨会话的关键信息
└── 学习到的用户习惯
知识库(系统级)
├── 向量数据库(语义检索)
├── 结构化知识图谱
└── 文档和 FAQ
Token 预算管理
class ContextManager:
MAX_CONTEXT_TOKENS = 8000 # 给模型留足够的生成空间
def manage(self, messages: list, tools_schema: str) -> list:
"""确保上下文不超预算"""
# 1. 工具 schema 是固定开销
fixed_cost = count_tokens(tools_schema) + count_tokens(system_prompt)
# 2. 计算可用预算
available = self.MAX_CONTEXT_TOKENS - fixed_cost
# 3. 按优先级分配
# - 最新 3 轮对话:必须保留
# - 更早的对话:压缩为摘要
# - Sub-Agent 结果:已经是摘要形式
return self.compress_to_budget(messages, available)
7.3 错误处理与容错机制
分层错误处理策略
Level 0:工具级
├── 单次工具调用失败 → 重试(指数退避)
├── 重试 3 次仍失败 → 返回错误描述给 Agent,让 Agent 决定替代方案
Level 1:Sub-Agent 级
├── Sub-Agent 超时 → 父 Agent 收到超时通知,触发降级策略
├── Sub-Agent 输出质量低 → 父 Agent 评估后决定重试或换方案
Level 2:任务级
├── 整个任务链路失败 → 保存进度,通知用户,等待人工介入
├── 部分子任务失败 → 返回已完成部分 + 失败说明
Level 3:系统级
├── LLM 服务不可用 → 切换备用模型或排队等待
├── 成本超限 → 立即终止,通知管理员
熔断机制
class CircuitBreaker:
"""Agent 熔断器:防止 Agent 陷入无限循环"""
def __init__(self, max_steps=30, max_cost_usd=1.0, max_time_sec=300):
self.max_steps = max_steps
self.max_cost = max_cost_usd
self.max_time = max_time_sec
def check(self, current_state: AgentState) -> bool:
"""每次工具调用前检查"""
if current_state.step_count >= self.max_steps:
raise CircuitBreakerError("达到最大步数限制")
if current_state.total_cost >= self.max_cost:
raise CircuitBreakerError("达到成本上限")
if current_state.elapsed_time >= self.max_time:
raise CircuitBreakerError("达到时间上限")
# 检测重复行为(同一个工具调用相同参数 ≥ 3 次)
if self.detect_loop(current_state):
raise CircuitBreakerError("检测到循环行为")
return True
7.4 可观测性体系
生产环境的 Agent 系统必须具备完整的可观测性,否则就是在裸奔。
三大支柱
| 支柱 | 内容 | 工具选型 |
|---|---|---|
| 日志(Logs) | 每次 LLM 调用的输入/输出/耗时/Token 数 | ELK、Loki |
| 指标(Metrics) | 成功率、延迟 P99、Token 消耗、成本 | Prometheus、Grafana |
| 追踪(Traces) | 端到端链路追踪,串联父 Agent → Sub-Agent → 工具调用 | LangSmith、Jaeger |
关键监控指标
# Agent 系统核心监控指标
metrics:
# 质量指标
- name: task_success_rate
description: 任务完成成功率
alert_threshold: < 85%
- name: tool_call_accuracy
description: 工具调用参数正确率
alert_threshold: < 90%
# 性能指标
- name: e2e_latency_p99
description: 端到端延迟 P99
alert_threshold: > 30s
- name: sub_agent_latency_p95
description: Sub-Agent 执行延迟 P95
alert_threshold: > 60s
# 成本指标
- name: cost_per_task
description: 单任务平均成本(USD)
alert_threshold: > 0.50
- name: daily_total_cost
description: 日总成本(USD)
alert_threshold: > 100
# 安全指标
- name: circuit_breaker_triggers
description: 熔断器触发次数
alert_threshold: > 5/hour
- name: loop_detection_count
description: 循环检测触发次数
alert_threshold: > 3/hour
7.5 安全与合规
核心原则:安全前置,而非后补
❌ "先把功能做完,安全以后再加"
✅ "安全是第一天就要设计进去的"
安全清单
| 维度 | 要求 | 实现方式 |
|---|---|---|
| 认证鉴权 | 每个请求必须经过身份验证 | OAuth 2.0 / API Key + RBAC |
| 权限最小化 | Agent 只能访问完成任务所需的最小资源集 | 细粒度的工具权限配置 |
| 输入过滤 | 防御 Prompt Injection 攻击 | 输入内容审核 + 指令隔离 |
| 输出审核 | 防止泄露敏感信息 | 输出过滤器 + PII 检测 |
| 审计日志 | 全链路不可篡改的操作记录 | 结构化日志 + 只追加存储 |
| 高风险审批 | 关键操作需人工确认 | Human-in-the-loop 设计 |
Prompt Injection 防御
def sanitize_input(user_input: str) -> str:
"""基本的 Prompt Injection 防御"""
# 1. 检测已知的注入模式
injection_patterns = [
r"ignore (?:previous|above|all) instructions",
r"you are now",
r"new system prompt",
r"forget (?:everything|your instructions)",
]
for pattern in injection_patterns:
if re.search(pattern, user_input, re.IGNORECASE):
raise SecurityError("Potential prompt injection detected")
# 2. 使用分隔符隔离用户输入
# 在 System Prompt 中明确标记用户输入的边界
return f"<user_input>{user_input}</user_input>"
7.6 成本控制
成本模型
单次任务成本 = Σ(每次 LLM 调用的 input_tokens × 输入价格 + output_tokens × 输出价格)
+ 工具调用成本(API 费用等)
+ 基础设施成本(计算、存储、网络)
优化策略
| 策略 | 预期节省 | 说明 |
|---|---|---|
| Sub-Agent 上下文压缩 | 60-90% | 核心策略,减少父 Agent 的 token 消耗 |
| 模型分级 | 30-50% | 简单任务用小模型,复杂任务用强模型 |
| 缓存 | 20-40% | 相同输入的 LLM 调用结果缓存 |
| 默认异步 | 10-30% | 异步 Sub-Agent 不占父 Agent 上下文预算 |
| 提前终止 | 变动大 | 检测到答案后立即停止,不执行剩余步骤 |
class CostAwareAgent:
"""成本感知的 Agent"""
def select_model(self, task_complexity: str) -> str:
"""根据任务复杂度选择模型"""
model_map = {
"simple": "claude-3-haiku", # $0.25/$1.25 per MTok
"medium": "claude-3-5-sonnet", # $3/$15 per MTok
"complex": "claude-3-opus", # $15/$75 per MTok
}
return model_map.get(task_complexity, "claude-3-5-sonnet")
def estimate_cost(self, task: str) -> float:
"""执行前预估成本"""
estimated_turns = self.estimate_turns(task)
estimated_tokens = estimated_turns * 2000 # 平均每轮 2K tokens
return estimated_tokens * self.price_per_token
八、常见陷阱与反模式
陷阱 1:过度工程化
❌ 项目第一天就设计 5 层 Agent 架构 + 3 种专业 Agent + 完整可观测性
✅ 从单个 Agent + 工具开始
→ 遇到上下文瓶颈 → 引入 Sub-Agent
→ 遇到延迟问题 → 引入异步模式
→ 遇到定期需求 → 引入定时模式
→ 遇到质量问题 → 引入评估者-优化者
陷阱 2:递归 Sub-Agent
❌ Sub-Agent 可以再生成 Sub-Agent(无限套娃)
→ 成本指数增长
→ 调试不可能
→ 容易死循环
✅ 严格限制深度 1:Sub-Agent 只能调用工具,不能生成新 Agent
陷阱 3:万物皆同步
❌ 所有 Sub-Agent 都用同步模式
→ 父 Agent 串行等待每个子任务
→ 总延迟 = 所有子任务延迟之和
→ 上下文不断膨胀
✅ 默认异步,只在"必须等结果才能继续"时用同步
陷阱 4:忽视评估
❌ "Agent 跑起来了就行"
→ 不知道成功率是多少
→ 不知道哪些场景会失败
→ 不知道质量趋势
✅ 构建系统化的评估体系
→ 定义成功标准
→ 收集测试用例集
→ 定期回归测试
→ 监控质量趋势
陷阱 5:上下文污染
❌ 所有信息不加区分地塞入上下文
→ 干扰模型注意力
→ 前面的关键指令被稀释
→ 幻觉率上升
✅ 精心管理上下文
→ Sub-Agent 返回摘要而非全文
→ 滑动窗口管理对话历史
→ 按相关性过滤检索结果
陷阱 6:没有熔断机制
❌ Agent 自由运行,没有任何限制
→ 可能无限循环调用工具
→ 可能产生天价 API 账单
→ 可能执行不可逆的危险操作
✅ 多维度熔断
→ 最大步数限制
→ 最大成本限制
→ 最大时间限制
→ 循环检测
→ 高风险操作人工确认
陷阱 7:为了架构美学而专业化
❌ "微服务架构好,所以我也要把 Agent 拆成专业化微服务"
→ Email Agent、Calendar Agent、Search Agent...
→ 需要路由器决定找谁
→ 路由错误产生微妙 bug
→ 评估成本 O(N²)
✅ Dan Farrelly 的建议
→ 从通用 Agent 开始
→ 用 Sub-Agent 做上下文隔离
→ 只在有数据证明的情况下才专业化
九、选型决策树
面对一个具体的 Agent 系统需求,按以下路径做技术选型:
1. 任务是否有固定的步骤流程?
├── 是 → 步骤之间是否有依赖?
│ ├── 全部有依赖 → 提示链
│ ├── 部分有依赖 → DAG 编排
│ └── 无依赖 → 并行化
└── 否 → 继续 ↓
2. 输入是否需要先分类再处理?
├── 是 → 路由
└── 否 → 继续 ↓
3. 是否需要迭代优化输出质量?
├── 是 → 评估者-优化者
└── 否 → 继续 ↓
4. 是否需要拆分为子任务?
├── 是 → 子任务是否预先确定?
│ ├── 是 → 并行化
│ └── 否 → 协调者-工作者
└── 否 → 继续 ↓
5. 子任务的结果是否需要反馈到父流程?
├── 是 → 同步 Sub-Agent
└── 否 → 是否需要延迟执行?
├── 是 → 定时 Sub-Agent
└── 否 → 异步 Sub-Agent
6. 系统是否需要响应外部事件?
├── 是 → 事件驱动模式
└── 否 → 以上模式按需组合
💡 不确定?从最简单的模式开始,遇到瓶颈再演进。
十、总结与行动建议
核心认知
- AI Agent 系统的本质是管理复杂性——编排模式是核心工具
- 上下文压缩比大上下文窗口更可持续——Sub-Agent 模式能减少 90%+ 的上下文 token
- 简单性是美德——从通用 Agent 开始,用数据驱动架构演进
三步行动建议
Step 1:MVP(1-2 周)
单个 Agent + 基础工具
├── 不用 Sub-Agent,先验证核心业务逻辑
├── 实现基本的错误处理和日志
└── 收集基线数据(成功率、延迟、成本)
Step 2:引入编排(2-4 周)
根据 MVP 阶段暴露的问题引入合适的模式
├── 上下文爆炸 → 引入 Sub-Agent(优先异步)
├── 长任务延迟 → 引入异步 Sub-Agent
├── 需要定期执行 → 引入定时 Sub-Agent
├── 质量不达标 → 引入评估者-优化者
└── 构建可观测性体系
Step 3:生产加固(4-8 周)
面向生产环境的工程化加固
├── 完善熔断机制(步数/成本/时间/循环检测)
├── 完善安全体系(认证/权限/审计/注入防护)
├── 建立评估体系(测试集/回归测试/质量监控)
├── 优化成本(模型分级/缓存/上下文压缩)
└── 只在有数据支撑时才考虑专业化 Agent
最后一句话
不要为了技术美感而架构,为了解决实际问题而架构。
最好的 Agent 系统不是最复杂的,而是恰好满足需求的最简单系统。
十一、未来探索方向
Dan Farrelly 在原文末尾提出了三个值得探索的前沿方向,这些方向代表了 Agent 编排系统的演进趋势。
11.1 自我迭代代理(Self-Iterating Agents)
利用定时模式,Agent 可以安排自己在未来某个时间重新检查和迭代自己的工作。
场景:Agent 撰写了一份分析报告
→ 完成后,安排自己 24 小时后回来
→ 24 小时后,基于新数据重新审视报告
→ 如果发现需要更新 → 自动修订
→ 如果无需更新 → 结束
这使得 Agent 从"一次性执行"进化为"持续关注"的模式——Agent 可以给自己排任务。
11.2 编排感知:从循环到网状(Orchestration-Aware Patterns)
目前的主流编排模式是循环 + 扇出(loop + fan-out):一个协调者循环决策,必要时扇出多个子代理。
未来的方向是网状结构(mesh):Agent 之间可以直接通信和协调,而非必须通过中心协调者。这需要更复杂的消息传递和状态同步机制,但能解锁更灵活的协作模式。
当前模式:星型拓扑 未来方向:网状拓扑
[协调者] [Agent A] ←→ [Agent B]
↙ ↓ ↘ ↕ ✕ ↕
[A] [B] [C] [Agent C] ←→ [Agent D]
11.3 回调与通用子代理系统(Callbacks and Generic Sub-Agent Systems)
建立一个通用的 Sub-Agent 回调系统——当子代理完成工作后,不仅可以通知用户,还可以触发父代理或其他系统组件的回调。这为构建更复杂的多代理工作流奠定基础。
核心趋势:Agent 系统正在从"工具式"(完成一个任务就结束)向"生态式"(Agent 之间持续协作、自我进化)演进。
参考资料
- Dan Farrelly (Inngest):《Three sub-agent patterns you need for your agentic system》— 本文的核心参考,提出同步/异步/定时三模式框架、两工具设计、通道无关路由、持久执行原语等关键概念
- Anthropic:《Building effective agents》(官方 Agent 构建指南,2024.12)
- Anthropic:Agent 架构模式详解(提示链、路由、并行化、协调者-工作者、评估者-优化者)
- Cursor 团队:关于"大多是通用的任务接口"和"Sub-Agent 首先是上下文压缩边界"的实践分享(在 Dan Farrelly 原文中引用)
- Inngest:事件驱动的持久执行后台任务框架,提供
step.invoke()、step.sendEvent()、step.run()等持久化原语 - LangChain / LangGraph:Agent 编排框架实现参考
- 2026 年企业级 AI Agent 终极架构蓝图:六大核心模块 + 生产环境部署指南