1. 从 Hacker News 登顶说起:这个项目到底解决了什么痛点
如果你最近在折腾 AI Agent,大概率会有一种感觉:框架多到用不过来。LangChain、AutoGPT、CrewAI、AutoGen、MetaGPT……每隔几周就冒出一个新东西,每个都号称能帮你编排多个 Agent 协同干活。但真正上手之后你会发现一个很尴尬的问题——Agent 之间的通信、任务分发、状态管理,几乎全靠自己手搓。你写一个 Agent 负责查资料,再写一个负责写代码,第三个负责审核,然后你发现它们之间怎么传消息、怎么保证不重复劳动、怎么在某个 Agent 挂掉之后自动恢复,这些破事全得自己处理。
这就是 Google 这次开源的Agent Development Kit(ADK)想要解决的问题。项目在 Hacker News 上登顶那天我正好在刷,看到标题里写着 “Apache 2.0 全开源”,第一反应是:Google 终于舍得把内部用的 Agent 编排框架拿出来了。要知道 Google 内部做 Agent 相关的东西已经有好几年了,从最早的 Dialogflow 到后来的 Vertex AI Agent Builder,积累了不少工程经验。这次开源的 ADK 不是那种“演示级”的玩具,而是一个面向生产环境的 Agent 开发框架,支持多 Agent 编排、工具调用、状态管理、可观测性等一整套东西。
简单来说,ADK 能帮你做这几件事:定义单个 Agent 的能力边界(它能用什么工具、能访问什么数据);把多个 Agent 组织成一个工作流(谁先谁后、谁给谁传数据);管理整个执行过程的状态(某个步骤失败了怎么重试、上下文怎么传递);以及提供调试和监控的手段(每个 Agent 到底干了什么、花了多少 token)。适合谁用?如果你正在做 AI 应用开发,尤其是那种需要多个 Agent 协作完成复杂任务的场景,比如自动客服、代码生成流水线、数据分析管道,那 ADK 值得你花时间研究一下。哪怕你只是好奇 Google 内部是怎么做 Agent 编排的,这个项目的源码也很有参考价值。
2. 核心设计思路拆解:为什么 Google 要造这个轮子
2.1 现有 Agent 框架的三大痛点
在 ADK 出现之前,市面上主流的 Agent 框架大致分两类。一类是通用编排框架,比如 LangChain 的 Agent 模块和 AutoGen,它们提供了很灵活的抽象,但灵活意味着你需要自己定义很多东西。另一类是垂直场景框架,比如专门做代码生成的 MetaGPT,开箱即用但很难定制。这两类框架在实际生产环境中都会遇到问题。
第一个痛点是状态管理混乱。多 Agent 协作时,每个 Agent 都有自己的上下文,这些上下文怎么共享、怎么隔离、怎么在 Agent 之间传递,大部分框架没有给出标准答案。你经常需要自己写一个全局的 state 对象,然后在各个 Agent 之间手动传递,代码写起来又臭又长。
第二个痛点是错误处理缺失。单个 Agent 调用 LLM 可能会超时、可能会返回格式错误的结果、可能会陷入死循环。多 Agent 场景下,一个 Agent 挂掉可能导致整个工作流卡死。大部分框架只提供了最基础的 try-catch,没有内置重试、降级、回滚机制。
第三个痛点是可观测性差。当你有五六个 Agent 协同工作时,出了问题你根本不知道是哪个环节出了错。是第一个 Agent 没查到资料?还是第二个 Agent 解析错了?还是第三个 Agent 调用工具时参数传错了?没有详细的日志和追踪,调试全靠 print。
2.2 ADK 的解题思路:分层抽象 + 显式编排
Google 的 ADK 选择了一条中间路线。它不像 LangChain 那么底层,也不像 MetaGPT 那么高层,而是把 Agent 开发拆成了几个清晰的概念层。
最底层是Agent 定义层。每个 Agent 是一个独立的单元,你定义它的名称、描述、使用的模型、可调用的工具列表、以及它的指令(instruction)。这个指令就是告诉 Agent “你是谁、你能干什么、你不能干什么”。ADK 支持多种类型的 Agent,包括基于 LLM 的 Agent、基于规则的 Agent、以及自定义的 Agent。
中间层是编排层。ADK 提供了几种编排模式:顺序执行(Sequential)、并行执行(Parallel)、循环执行(Loop)、以及条件分支(Conditional)。你可以把这些模式组合起来,形成一个有向图或者工作流。每个节点是一个 Agent,边定义了数据流向和执行顺序。这种显式编排的好处是,整个流程一目了然,不像某些框架那样把逻辑藏在 Agent 的 prompt 里。
最上层是运行时层。ADK 内置了一个执行引擎,负责调度 Agent、管理状态、处理错误、记录日志。这个执行引擎支持同步和异步两种模式,也支持流式输出。你可以把它理解为一个专门为 Agent 工作流设计的轻量级运行时。
2.3 为什么选择 Apache 2.0 全开源
Google 这次选择 Apache 2.0 许可证,而不是像某些项目那样搞“开源核心 + 商业插件”的模式,这个决策很值得玩味。Apache 2.0 意味着你可以自由地商用、修改、分发,甚至把它集成到自己的闭源产品里。这对于企业用户来说非常重要——很多公司不愿意把核心业务逻辑绑定在一个许可证不清晰或者有商业限制的框架上。
从生态角度看,Apache 2.0 也更容易吸引社区贡献。开发者可以放心地提交 PR、写扩展、做集成,不用担心自己的贡献被某家公司独占。Google 显然是想把 ADK 做成一个中立的、社区驱动的标准,而不是一个只服务于自家云平台的工具。这一点从项目结构也能看出来:ADK 的核心代码不依赖任何 Google 特有的服务,你可以用 OpenAI 的模型、Anthropic 的模型、或者本地部署的开源模型来跑。
3. 核心概念与实操要点:从零搭建一个多 Agent 工作流
3.1 环境准备与安装
ADK 是一个 Python 库,目前要求 Python 3.10 以上。安装方式很简单,直接 pip 安装即可。不过要注意,ADK 的依赖比较多,建议在虚拟环境里安装,避免和系统里的其他包冲突。
python -m venv adk-env source adk-env/bin/activate # Windows 用 adk-env\Scripts\activate pip install google-adk安装完成后,你可以用adk --version来验证是否安装成功。如果遇到依赖冲突,最常见的问题是 protobuf 版本不兼容。ADK 依赖较新的 protobuf,如果你之前装过 TensorFlow 之类的库,可能需要先升级 protobuf。
提示:如果你在国内网络环境下安装,可能会遇到下载速度慢的问题。可以临时指定镜像源,比如
pip install google-adk -i https://pypi.tuna.tsinghua.edu.cn/simple。这个镜像源是清华大学的开源镜像,稳定性和速度都不错。
3.2 定义第一个 Agent
ADK 里定义一个 Agent 非常直观。你只需要创建一个LlmAgent对象,指定模型、名称、指令和工具列表。下面是一个最简单的例子,一个能查天气的 Agent:
from google.adk.agents import LlmAgent from google.adk.tools import FunctionTool def get_weather(city: str) -> dict: """查询指定城市的天气""" # 实际项目中这里会调用天气 API return {"city": city, "temperature": "25°C", "condition": "晴"} weather_tool = FunctionTool(func=get_weather) weather_agent = LlmAgent( name="weather_agent", model="gemini-2.0-flash", instruction="你是一个天气助手,用户问你天气时,调用 get_weather 工具查询。", tools=[weather_tool] )这里有几个关键点需要注意。instruction参数是 Agent 的“人设”,它决定了 Agent 的行为方式。写 instruction 的时候要尽量具体,告诉 Agent 什么情况下该调用工具、什么情况下该直接回答。tools参数是一个列表,里面可以放多个工具。ADK 会自动把工具的签名和文档字符串转换成 LLM 能理解的格式,所以你的工具函数一定要写清楚参数类型和 docstring。
3.3 多 Agent 编排的三种模式
单个 Agent 只能做简单任务,真正体现 ADK 价值的是多 Agent 编排。ADK 提供了三种基本的编排模式,你可以根据任务特点来选择。
顺序模式适合有明确先后依赖的任务。比如一个“写报告”的工作流:先让 research_agent 查资料,再把资料传给 writing_agent 写初稿,最后让 review_agent 审核。用 ADK 的SequentialAgent可以这样写:
from google.adk.agents import SequentialAgent report_workflow = SequentialAgent( name="report_workflow", sub_agents=[research_agent, writing_agent, review_agent] )并行模式适合可以同时进行的子任务。比如你要分析一份数据,可以同时让 statistical_agent 做统计分析、visualization_agent 做图表、summary_agent 写摘要,最后汇总。用ParallelAgent来实现:
from google.adk.agents import ParallelAgent analysis_workflow = ParallelAgent( name="analysis_workflow", sub_agents=[statistical_agent, visualization_agent, summary_agent] )循环模式适合需要反复迭代的任务。比如代码生成场景,你让 code_agent 写代码,test_agent 跑测试,如果测试不通过就把错误信息传回 code_agent 重新写,直到通过或者达到最大迭代次数。用LoopAgent来实现:
from google.adk.agents import LoopAgent code_workflow = LoopAgent( name="code_workflow", sub_agents=[code_agent, test_agent], max_iterations=5 )这三种模式可以嵌套使用。比如你可以在一个 SequentialAgent 里面放一个 ParallelAgent 作为其中一个步骤,实现更复杂的编排逻辑。
3.4 状态管理与上下文传递
多 Agent 协作时,状态管理是最容易出问题的地方。ADK 的做法是引入一个Session概念。每个工作流执行时都会创建一个 Session,Session 里有一个state字典,所有 Agent 都可以读写这个字典。Agent 之间传递数据就是通过往 state 里写 key-value 来实现的。
# 在 research_agent 里写入数据 def save_research_result(session, result): session.state["research_result"] = result # 在 writing_agent 里读取数据 def get_research_result(session): return session.state.get("research_result", "")这种设计的好处是解耦了 Agent 之间的直接依赖。research_agent 不需要知道谁会消费它的输出,writing_agent 也不需要知道数据是谁生产的。但要注意,state 是全局共享的,如果多个 Agent 同时写同一个 key,可能会产生竞态条件。ADK 在并行模式下会为每个子 Agent 创建独立的 state 副本,最后再合并,避免这个问题。
注意:state 里不要放太大的对象,比如整个 DataFrame 或者大段文本。ADK 会把 state 序列化后存储,太大的对象会影响性能。如果确实需要传递大数据,建议存到外部存储(比如文件或数据库),state 里只放引用路径。
4. 完整实操流程:搭建一个自动代码审查工作流
4.1 场景定义与架构设计
光说不练假把式。我们用一个完整的例子来演示 ADK 的实际用法:自动代码审查工作流。这个工作流的输入是一个 GitHub PR 的 diff,输出是一份审查报告,包含代码风格问题、潜在 bug、以及改进建议。
整个工作流分四个步骤。第一步,diff_parser_agent解析 diff,提取出变更的文件和代码片段。第二步,style_checker_agent检查代码风格,比如命名规范、缩进、注释完整性。第三步,bug_hunter_agent分析潜在 bug,比如空指针、资源泄漏、边界条件。第四步,report_writer_agent汇总前两步的结果,生成最终的审查报告。
这个场景很适合用 ADK 来实现,因为四个步骤有明确的先后顺序,而且每个步骤都可以独立测试和替换。比如你以后想加一个安全检查步骤,只需要在 style_checker 和 bug_hunter 之间插入一个 security_agent 就行,不需要改动其他部分。
4.2 工具定义与参数计算
每个 Agent 都需要一些工具来完成任务。diff_parser_agent 需要一个解析 diff 的工具,style_checker_agent 需要一个检查命名规范的工具,bug_hunter_agent 需要一个静态分析的工具。这些工具可以用 Python 函数来实现,也可以用现成的库。
以命名规范检查为例,我们可以写一个简单的工具函数:
import re def check_naming_convention(code: str, language: str = "python") -> list: """检查代码中的命名规范问题""" issues = [] if language == "python": # 检查函数名是否使用 snake_case func_pattern = r"def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(" for match in re.finditer(func_pattern, code): func_name = match.group(1) if not re.match(r"^[a-z_][a-z0-9_]*$", func_name): issues.append(f"函数名 '{func_name}' 不符合 snake_case 规范") # 检查类名是否使用 PascalCase class_pattern = r"class\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[:\(]" for match in re.finditer(class_pattern, code): class_name = match.group(1) if not re.match(r"^[A-Z][a-zA-Z0-9]*$", class_name): issues.append(f"类名 '{class_name}' 不符合 PascalCase 规范") return issues这个工具函数虽然简单,但已经能覆盖大部分命名规范问题。在实际项目中,你可以把它替换成 pylint 或者 flake8 的封装,获得更全面的检查能力。
4.3 工作流组装与执行
定义好各个 Agent 和工具之后,就可以组装工作流了。我们用 SequentialAgent 把四个 Agent 串起来:
from google.adk.agents import LlmAgent, SequentialAgent from google.adk.tools import FunctionTool # 定义工具 parse_diff_tool = FunctionTool(func=parse_diff) check_naming_tool = FunctionTool(func=check_naming_convention) static_analysis_tool = FunctionTool(func=run_static_analysis) # 定义 Agent diff_parser = LlmAgent( name="diff_parser", model="gemini-2.0-flash", instruction="你是一个 diff 解析器。用户会给你一个 PR 的 diff,你需要调用 parse_diff 工具提取变更的文件和代码片段。", tools=[parse_diff_tool] ) style_checker = LlmAgent( name="style_checker", model="gemini-2.0-flash", instruction="你是一个代码风格检查器。根据上一步提取的代码片段,调用 check_naming 工具检查命名规范,并给出风格改进建议。", tools=[check_naming_tool] ) bug_hunter = LlmAgent( name="bug_hunter", model="gemini-2.0-flash", instruction="你是一个 bug 猎手。根据代码片段,调用 static_analysis 工具进行静态分析,找出潜在的 bug 和边界条件问题。", tools=[static_analysis_tool] ) report_writer = LlmAgent( name="report_writer", model="gemini-2.0-flash", instruction="你是一个报告撰写者。汇总前面步骤的风格问题和 bug 问题,生成一份结构清晰的审查报告,包含问题列表和改进建议。" ) # 组装工作流 code_review_workflow = SequentialAgent( name="code_review_workflow", sub_agents=[diff_parser, style_checker, bug_hunter, report_writer] )执行这个工作流只需要调用run方法,传入初始输入:
result = code_review_workflow.run( input="请审查这个 PR:\n```diff\n+def BadName():\n+ pass\n```" ) print(result.output)ADK 会自动按顺序执行四个 Agent,把前一个的输出作为后一个的输入,最后返回 report_writer 的输出。
4.4 调试与可观测性配置
工作流跑起来之后,你肯定想知道每个 Agent 到底干了什么。ADK 内置了日志和追踪功能,可以通过配置来开启。最简单的方式是设置环境变量ADK_LOG_LEVEL=DEBUG,这样执行过程中会打印详细的日志,包括每个 Agent 的输入输出、工具调用参数和返回值。
如果你想要更结构化的追踪数据,ADK 支持导出 OpenTelemetry 格式的 trace。你可以在代码里配置一个 exporter,把 trace 数据发到 Jaeger 或者 Zipkin 之类的追踪系统里。这样你就能看到一个完整的调用链路:哪个 Agent 花了多长时间、调用了哪些工具、消耗了多少 token。
from google.adk.tracing import setup_tracing setup_tracing( exporter="otlp", endpoint="http://localhost:4317", service_name="code-review-workflow" )提示:在开发阶段,建议把
ADK_LOG_LEVEL设为 DEBUG,方便排查问题。但在生产环境,建议设为 INFO 或 WARN,避免日志量过大影响性能。另外,ADK 的日志里可能会包含代码片段等敏感信息,上线前记得检查一下日志脱敏配置。
5. 常见问题与排查技巧实录
5.1 Agent 不调用工具怎么办
这是新手最常遇到的问题。你定义了一个工具,但 Agent 就是不用,直接用自己的知识回答了。原因通常有三个。第一,instruction 写得不够明确,没有告诉 Agent 什么情况下必须调用工具。第二,工具的 docstring 写得太模糊,LLM 不理解这个工具是干什么的。第三,模型本身的能力问题,一些小模型对工具调用的支持不好。
解决办法:在 instruction 里明确写“当用户询问 X 时,必须调用 Y 工具,不要自己编造答案”。工具的 docstring 要写清楚功能、参数含义、返回值格式。如果还是不行,换一个工具调用能力更强的模型试试。
5.2 工作流执行到一半卡住了
多 Agent 工作流卡住的原因比较多。最常见的是某个 Agent 陷入了循环,一直在调用同一个工具。ADK 的 LoopAgent 有max_iterations参数可以限制循环次数,但 SequentialAgent 里的单个 Agent 如果自己陷入循环,就需要在 Agent 层面设置超时。
agent = LlmAgent( name="my_agent", model="gemini-2.0-flash", instruction="...", tools=[...], max_tool_calls=10 # 限制单个 Agent 最多调用 10 次工具 )另一个常见原因是工具函数抛出了未捕获的异常。ADK 默认会把异常包装成错误信息返回给 Agent,但如果异常信息太长或者格式不对,Agent 可能会无法理解。建议在工具函数里做好异常处理,返回结构化的错误信息。
5.3 状态数据丢失或错乱
前面提到过,并行模式下 ADK 会为每个子 Agent 创建独立的 state 副本。但如果你在并行 Agent 里直接修改了全局变量,而不是通过 session.state 来传递数据,就会出现数据错乱。记住一个原则:所有跨 Agent 的数据传递都必须通过 session.state,不要用全局变量或者闭包。
另外,state 的 key 命名要有规范,建议用{agent_name}_{data_name}的格式,比如research_agent_result、writing_agent_draft。这样可以避免不同 Agent 之间的 key 冲突。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Agent 不调用工具 | instruction 不明确 / docstring 模糊 | 查看 DEBUG 日志中 Agent 的决策过程 | 明确 instruction,完善工具文档 |
| 工作流卡住 | Agent 循环调用 / 工具异常 | 检查日志中是否有重复的工具调用 | 设置 max_tool_calls 和超时 |
| 状态数据丢失 | 未使用 session.state / key 冲突 | 打印 state 字典查看内容 | 统一通过 session.state 传递,规范 key 命名 |
| 输出格式错误 | instruction 未指定格式 / 模型能力不足 | 查看 Agent 原始输出 | 在 instruction 中给出输出格式示例 |
| 执行速度慢 | 串行执行 / 模型响应慢 | 查看各步骤耗时 | 改用并行模式,或换更快的模型 |
| Token 消耗过大 | 上下文过长 / 重复调用 | 查看 token 统计 | 精简 instruction,设置上下文窗口限制 |
5.5 几个踩过的坑
第一个坑是模型选择。ADK 默认用 Gemini 模型,但如果你用 OpenAI 的模型,需要额外配置 adapter。我试过用 GPT-4 跑同样的工作流,工具调用的成功率明显低于 Gemini,尤其是在多轮工具调用的场景下。所以如果你要用非 Gemini 模型,建议先做一轮小规模测试。
第二个坑是工具函数的参数类型。ADK 会根据函数签名生成工具的 JSON Schema,如果参数类型是复杂的嵌套对象,生成的 Schema 可能会让 LLM 困惑。建议工具参数尽量用简单类型:字符串、数字、布尔值、简单的列表和字典。
第三个坑是日志里的敏感信息。ADK 的 DEBUG 日志会打印完整的输入输出,如果你的工作流处理的是用户数据或者代码,这些信息可能会泄露到日志里。上线前一定要配置日志脱敏,或者把日志级别调到 INFO 以上。
6. 这个项目后续还能怎么玩
ADK 目前还在快速迭代中,社区贡献的插件和扩展也越来越多。我个人比较看好的几个方向:一是与现有 CI/CD 系统集成,把代码审查工作流嵌入到 GitHub Actions 或者 GitLab CI 里,每次 PR 自动触发;二是多模态 Agent,ADK 已经开始支持图像和音频输入,你可以做一个能看设计稿、听会议录音的 Agent;三是本地模型支持,随着 Ollama 和 vLLM 的成熟,用本地模型跑 ADK 工作流完全可行,这对数据敏感的场景很有吸引力。
我在实际使用中的体会是,ADK 最大的价值不在于它提供了多少现成的功能,而在于它把 Agent 开发中的那些“脏活累活”标准化了。状态管理、错误处理、可观测性,这些在 demo 阶段可以忽略的东西,在生产环境里一个都不能少。Google 把自己踩过的坑变成了框架的一部分,这对整个生态来说都是好事。如果你正在做 Agent 相关的项目,建议花一个下午把 ADK 的官方示例跑一遍,感受一下它的设计思路,哪怕最后不用它,也能帮你理清很多概念。