用 Google ADK 编排多 Agent 工作流:Sequential、Loop、Parallel 实战拆解
单个 LLM Agent 能干的事有限。让它写代码还行,让它写完代码再审查再重构,一个 Agent 就容易乱。Google 在 2025 年开源了 Agent Development Kit(ADK),专门解决多 Agent 编排问题。核心思路:把复杂任务拆成多个专职 Agent,用工作流 Agent 控制执行顺序。
ADK 提供了三种工作流 Agent:SequentialAgent(顺序执行)、LoopAgent(循环迭代)、ParallelAgent(并行执行)。这三个东西本身不调用 LLM,只负责调度。具体的 LLM 调用在子 Agent 里完成。
我用 ADK 搭了几个实际场景,记录下代码和踩过的坑。
安装和基础设置
bash
pip install google-adk
ADK 依赖 Gemini 模型,需要一个 Google AI API Key:
bash
export GOOGLE_API_KEY="your-key-here"
基础结构很简单------定义 LlmAgent,指定 model 和 instruction,用 output_key 把结果存到 state 里。state 是所有子 Agent 共享的字典,这是多 Agent 之间传数据的唯一通道。
python
from google.adk.agents import LlmAgent
agent = LlmAgent(
name="MyAgent",
model="gemini-2.5-flash",
instruction="你是一个Python开发者,根据需求写代码。",
output_key="result" # 结果存到 state["result"]
)
SequentialAgent:流水线模式
最直观的模式。三个 Agent 按顺序跑:A 跑完 → B 拿 A 的结果跑 → C 拿 B 的结果跑。
实际场景:代码生成流水线。写代码 → 审查 → 重构,三步串联。
python
from google.adk.agents import SequentialAgent, LlmAgent
MODEL = "gemini-2.5-flash"
writer = LlmAgent(
name="CodeWriter",
model=MODEL,
instruction="""根据用户需求写Python代码。
只输出代码块,不要解释。""",
output_key="draft_code"
)
reviewer = LlmAgent(
name="CodeReviewer",
model=MODEL,
instruction="""审查以下代码,找出bug和改进点。
代码:
```python
{draft_code}
逐条列出问题,不要重写代码。""", output_key="review_notes" )
refactorer = LlmAgent( name="CodeRefactorer", model=MODEL, instruction="""根据审查意见重构代码。
原始代码:
python
{draft_code}
审查意见: {review_notes}
输出重构后的完整代码。""", output_key="final_code" )
pipeline = SequentialAgent( name="CodePipeline", sub_agents=[writer, reviewer, refactorer] )
ini
数据流走 state:writer 的输出写入 `state["draft_code"]`,reviewer 的 instruction 里用 `{draft_code}` 引用这个值,它的输出再写入 `state["review_notes"]`,refactorer 同时引用两个 state key。
**踩坑记录:**
1. `output_key` 不能省。不设 output_key,下游 Agent 拿不到上游的结果,instruction 里的 `{draft_code}` 会被渲染成空字符串。这个错误不报错,只是结果不对,排查起来很痛苦。
2. instruction 里的变量名必须跟 output_key 完全一致。写成 `{code}` 但 output_key 是 `"draft_code"`,不会报错,渲染出来也是空的。
3. 子 Agent 的 `include_contents` 参数默认会把整个对话历史塞给每个子 Agent。在流水线模式下建议设成 `'none'`,只通过 state 传数据,不然 reviewer 收到一堆不相关的历史消息。
```python
reviewer = LlmAgent(
name="CodeReviewer",
model=MODEL,
include_contents='none', # 只看 state,不看历史
instruction="...",
output_key="review_notes"
)
LoopAgent:迭代改进模式
有些任务不是跑一次就够的。比如写文章------初稿写完,修改,再看,再改,直到满意为止。LoopAgent 就是干这个的。
它会反复执行子 Agent 列表,直到达到最大迭代次数,或者某个子 Agent 主动调用 escalate 退出循环。
场景:迭代式文档改进。Writer 写草稿,Critic 评审,如果没问题就退出,有问题就继续改。
python
from google.adk.agents import LoopAgent, LlmAgent
from google.adk.tools.tool_context import ToolContext
def exit_loop(tool_context: ToolContext):
"""文档质量达标时调用此函数退出循环。"""
tool_context.actions.escalate = True
return {}
writer = LlmAgent(
name="Writer",
model="gemini-2.5-flash",
include_contents='none',
instruction="""你是技术文档作者。
当前文档:
{current_doc}
评审意见:
{critic_feedback}
根据评审意见改进文档。如果没有评审意见,根据主题 {topic} 写初稿。
只输出文档内容。""",
output_key="current_doc"
)
critic = LlmAgent(
name="Critic",
model="gemini-2.5-flash",
include_contents='none',
instruction="""审查这份文档:
{current_doc}
检查标准:
1. 至少包含3个代码示例
2. 有清晰的章节结构
3. 没有事实错误
如果全部达标,调用 exit_loop 函数。
否则列出需要改进的地方。""",
tools=[exit_loop],
output_key="critic_feedback"
)
loop = LoopAgent(
name="DocRefiner",
sub_agents=[writer, critic],
max_iterations=5
)
关键细节:
退出循环靠 tool_context.actions.escalate = True。这不是一个配置项,而是要定义一个 tool 函数,让 Critic Agent 在满意时主动调用它。escalate 的意思是"把控制权交回上层",LoopAgent 收到这个信号就停止循环。
max_iterations 是安全网。LLM 做判断有概率出错------Critic 可能永远不满意,也可能忘记调用 exit_loop。设个上限防止死循环。实测下来,大多数文档改进任务 3-4 轮就收敛了,5 轮足够。
还有一个坑:Writer 第一轮跑的时候,{critic_feedback} 在 state 里不存在。ADK 的处理方式是把它渲染成空字符串,不会报错。所以 instruction 里要写清楚"如果没有评审意见就写初稿",不然 Writer 可能对着空的反馈发呆。
ParallelAgent:并行加速
独立任务并行跑,最直接的提速手段。三个调研任务串行要 30 秒,并行可能 12 秒搞定。
场景:竞品分析。同时调研三个竞品,最后汇总。
python
from google.adk.agents import ParallelAgent, SequentialAgent, LlmAgent
from google.adk.tools import google_search
MODEL = "gemini-2.5-flash"
def make_researcher(name, topic, output_key):
return LlmAgent(
name=name,
model=MODEL,
instruction=f"""调研 {topic} 的最新动态。
用 Google Search 搜索,总结关键发现。
输出2-3句话的摘要。""",
tools=[google_search],
output_key=output_key
)
r1 = make_researcher("LangChainResearch", "LangChain", "langchain_result")
r2 = make_researcher("CrewAIResearch", "CrewAI", "crewai_result")
r3 = make_researcher("AutoGenResearch", "AutoGen", "autogen_result")
parallel = ParallelAgent(
name="CompetitorResearch",
sub_agents=[r1, r2, r3]
)
merger = LlmAgent(
name="ReportMerger",
model=MODEL,
instruction="""根据以下三份调研结果写竞品分析报告:
LangChain: {langchain_result}
CrewAI: {crewai_result}
AutoGen: {autogen_result}
对比优劣势,给出推荐。""",
output_key="final_report"
)
# 先并行调研,再串联汇总
full_pipeline = SequentialAgent(
name="CompetitorAnalysis",
sub_agents=[parallel, merger]
)
注意事项:
-
并行 Agent 之间不共享 state。每个分支独立跑,各自往 state 里写结果。如果两个并行 Agent 写同一个 output_key,结果不可预测------谁后写谁赢。所以每个并行子 Agent 的 output_key 必须不同。
-
ParallelAgent 等所有子 Agent 跑完才返回。如果一个子 Agent 卡住了(比如 API 超时),整个 ParallelAgent 都会等。生产环境建议给每个子 Agent 加超时控制。
-
并行 + 串行组合是最常见的用法。上面的例子就是:先 ParallelAgent 并行调研,结果存到 state,再用 SequentialAgent 串联一个 Merger 做汇总。这个模式可以套很多场景------并行爬数据 → 串行汇总报告、并行翻译多语言 → 串行质检。
三种模式怎么选
不用纠结,根据任务依赖关系选:
- 上游输出是下游输入 → SequentialAgent
- 需要反复改进直到达标 → LoopAgent
- 多个独立任务互不依赖 → ParallelAgent
实际项目里经常混合用。上面竞品分析的例子就是 Parallel 嵌套在 Sequential 里。再复杂一点,可以在 Sequential 的某一步放一个 LoopAgent:先并行采集 → 串行清洗 → 循环迭代优化 → 最终输出。
ADK 的工作流 Agent 本身不跑 LLM,只是调度器。开销很小,嵌套也不会增加额外的 API 调用。
实测数据
我用竞品分析场景跑了对比(3 个调研 + 1 个汇总):
| 模式 | 耗时 | API 调用次数 |
|---|---|---|
| 全部 Sequential | 38s | 4 |
| Parallel + Sequential | 15s | 4 |
API 调用次数一样,但并行模式快了 60%。调研任务越多,并行的优势越大。
代码生成流水线(SequentialAgent,3 步)平均耗时 12 秒,相当于 3 次 Gemini 调用的时间。没有额外开销。
LoopAgent 的文档迭代场景,平均 3.2 轮收敛,耗时 25 秒。设 max_iterations=5 从来没跑满过。
几个实用建议
调试用 include_contents='none'。 默认情况下每个子 Agent 能看到完整对话历史,排查问题时很难分清 Agent 到底在看什么输入。设成 none,强制所有数据走 state,数据流一目了然。
别在 instruction 里硬编码模型特定的格式。 比如 "输出 JSON 格式"------如果下游 Agent 期望的是纯文本,中间就要加解析逻辑。让 Agent 输出自然语言,需要结构化数据时用 Pydantic + output_schema。
state key 用有意义的名字。 别用 result1、result2。当 Agent 数量超过 5 个,state 里有十几个 key 的时候,命名不清晰会直接影响可维护性。
生产环境加监控。 ADK 的 before_agent_callback 和 after_agent_callback 可以在每个 Agent 执行前后插入逻辑------记日志、统计耗时、检查 state 完整性。
python
from google.adk.agents.callback_context import CallbackContext
def log_agent_start(callback_context: CallbackContext):
print(f"[{callback_context.agent_name}] 开始执行")
# 也可以记录 state 快照
pipeline = SequentialAgent(
name="Pipeline",
sub_agents=[writer, reviewer],
before_agent_callback=log_agent_start
)
ADK 的文档在 adk.dev,GitHub 仓库是 google/adk-python。三种工作流 Agent 加起来覆盖了大部分多 Agent 编排需求。比自己用 asyncio 手搓调度逻辑要省事很多。