谷歌开源ADK:解锁多智能体协作开发新范式
1. 谷歌ADK是什么?为什么说它改变了游戏规则
最近在AI开发者圈子里,谷歌开源的Agent Development Kit(ADK)成了热门话题。我作为一个在智能体领域折腾了挺久的老兵,看到这个框架的第一反应是:终于有人把多智能体协作这摊“脏活累活”给系统化地收拾明白了。你可能听说过LangChain、CrewAI这些工具,它们让单个智能体的构建变得简单,但一旦涉及到多个智能体之间如何分工、如何对话、如何避免“踢皮球”,事情就变得异常复杂。ADK的出现,正是瞄准了这个痛点。
简单来说,ADK是一个专门为构建和编排多智能体系统而生的开源框架。你可以把它想象成一个智能体团队的“总导演”和“调度中心”。它不关心你团队里的每个“演员”(单个智能体)是用Gemini还是Claude,也不限制你从哪里找“道具”(工具),它的核心职责是定义一套清晰的规则,让这些智能体知道什么时候该自己上场,什么时候该把任务交给更专业的队友,并且确保整个演出(任务执行)流畅、可控。
为什么这很重要?我举个自己踩过的坑。去年我尝试用几个开源模型搭建一个客服系统,里面包含一个理解用户意图的“路由”智能体、一个处理业务咨询的“专家”智能体,还有一个负责生成友好结束语的“告别”智能体。光是让它们三个之间顺畅地传递上下文、避免循环调用,就写了几百行胶水代码,调试起来简直是噩梦。而ADK的核心理念,就是把这种层级化的代理组合和动态的任务委派机制,变成了框架内置的一等公民。你只需要定义好每个智能体的职责和它们之间的关系,剩下的编排工作,ADK帮你搞定。
更吸引人的是,ADK并非一个实验室玩具。它是支撑谷歌内部如Agentspace和客户互动套件(CES)的底层框架。这意味着它经过了大规模、高并发生产环境的考验。谷歌这次把它开源,相当于把自家“武器库”里的重型装备拿了出来,让普通开发者也能用上企业级的智能体编排能力。这对于我们这些想要构建复杂AI应用,但又受限于工程化能力的中小团队来说,无疑是个巨大的福音。
2. 庖丁解牛:ADK的模块化架构如何工作
要理解ADK的威力,我们得钻进它的肚子里看看。它的设计哲学非常清晰:模块化和关注点分离。整个框架可以拆解成几个核心的“乐高积木”,让你能够像搭积木一样构建应用。
首先是智能体(Agent)本身。在ADK里,一个智能体不再是一个黑盒。它由几个明确的模块构成:一个大脑(模型),比如Gemini或Claude;一套工具(Tools),比如搜索、代码执行或调用某个API;一份清晰的职责说明书(Instruction & Description),告诉它“你是谁,该做什么”;以及一个可选的子智能体列表(Sub-agents),定义了它可以向谁求助或委派任务。这种结构化的定义,让智能体的行为变得可预测、可调试。
其次是工具生态。ADK对工具的包容性极强。你可以直接用它预置的搜索、代码执行工具,可以集成Model Context Protocol(MCP)工具来连接外部数据源,甚至可以无缝使用LangChain或LlamaIndex生态里成千上万的工具。最酷的是,在ADK里,一个智能体本身也可以作为另一个智能体的工具。这意味着你可以把一套复杂的、由多个智能体组成的子系统,封装成一个“超级工具”,供上层智能体调用。这种递归的设计,为构建极其复杂的系统提供了可能。
最后是编排层(Orchestration)。这是ADK的灵魂所在。它提供了两种主要的编排模式:一种是流程代理(Flow Agents),比如顺序执行(Sequential)、并行执行(Parallel)或循环执行(Loop)。这适合那些步骤固定、逻辑明确的任务流。另一种是LLM驱动的动态路由(Dynamic Routing)。这才是处理开放域对话和复杂协作的杀手锏。系统不会硬编码路由逻辑,而是将当前对话状态、所有可用智能体的描述,一起交给一个“路由”LLM(或者就是主智能体自己)去判断:“嘿,现在这个问题,我们团队里谁最适合处理?” 这种基于语义理解的动态委派,让多智能体系统真正拥有了灵活应变的能力。
这种模块化架构带来的直接好处是可维护性和可测试性的大幅提升。每个智能体都可以独立开发、独立测试。你可以单独评估一个天气查询工具是否准确,也可以单独测试问候智能体是否足够友好,最后再通过ADK的编排层把它们组装起来。这种开发体验,比起过去把所有逻辑揉在一个巨型Prompt里,要清爽和可靠得多。
3. 手把手实战:用ADK构建一个天气查询协作系统
光说不练假把式,我们直接动手,用ADK重现并扩展一下官方文档里的天气查询案例。这个案例虽然简单,但完美展示了多智能体协作的核心流程:任务识别、智能路由、工具执行、结果返回。我会在官方例子的基础上,增加一些更贴近真实场景的细节和错误处理。
3.1 环境搭建与项目初始化
首先,确保你的Python环境在3.10以上。安装ADK非常简单,一行命令搞定:
pip install google-adk
安装完成后,我强烈建议你先跑一下 adk --help 看看所有命令。ADK配套了一个强大的CLI工具和一个Web UI,这对于本地开发和调试来说简直是神器。你可以用 adk web 命令启动本地Web界面,实时观察智能体的思考过程、工具调用和状态流转,这对理解系统行为至关重要。
创建一个新的项目目录,比如 weather_agent_system。ADK没有严格的脚手架要求,但一个好的实践是创建一个 agents 目录来存放各个智能体的定义文件,一个 tools 目录存放工具函数。
3.2 定义工具:模拟与真实API的桥梁
工具是智能体作用于世界的“手”。我们先在 tools/weather_tool.py 里定义一个天气查询工具。官方例子用了模拟数据,我们稍微升级一下,加入缓存和更健壮的错误处理。
# tools/weather_tool.py
import requests
from typing import Dict
import hashlib
import time
# 一个简单的内存缓存,避免频繁调用(实际项目可用Redis)
_weather_cache = {}
CACHE_TTL = 300 # 缓存5分钟
def get_weather(city: str, country_code: str = None) -> Dict:
"""
查询城市天气。
参数:
city: 城市名,如 'Beijing'
country_code: 国家代码(可选),如 'CN',用于消除歧义
返回:
包含状态和报告/错误信息的字典
"""
# 生成缓存键
cache_key = hashlib.md5(f"{city}_{country_code}".encode()).hexdigest()
current_time = time.time()
# 检查缓存
if cache_key in _weather_cache:
cached_data, timestamp = _weather_cache[cache_key]
if current_time - timestamp < CACHE_TTL:
print(f"[Cache Hit] Returning cached weather for {city}")
return cached_data
print(f"--- Tool Called: get_weather for city: {city} ---")
# 这里是模拟逻辑。真实场景中,你会在这里调用如OpenWeatherMap的API
# 例如:response = requests.get(f"https://api.openweathermap.org/...&q={city}")
# 模拟一个外部API调用可能出现的错误
if "error" in city.lower():
return {
"status": "error",
"error_message": f"模拟外部API调用失败,无法获取 '{city}' 的天气信息。",
"suggestion": "请检查城市名拼写,或稍后重试。"
}
# 模拟数据库查询
city_normalized = city.lower().replace(" ", "")
mock_weather_db = {
"newyork": {"status": "success", "report": "纽约天气晴朗,气温25°C,微风。", "temp_c": 25},
"london": {"status": "success", "report": "伦敦多云,气温15°C,湿度较高。", "temp_c": 15},
"tokyo": {"status": "success", "report": "东京有小雨,气温18°C,记得带伞。", "temp_c": 18},
"beijing": {"status": "success", "report": "北京今日晴转多云,气温22°C,空气质量良。", "temp_c": 22},
"shanghai": {"status": "success", "report": "上海阴天,气温20°C,东南风3-4级。", "temp_c": 20},
}
if city_normalized in mock_weather_db:
result = mock_weather_db[city_normalized]
# 存入缓存
_weather_cache[cache_key] = (result, current_time)
return result
else:
error_result = {
"status": "error",
"error_message": f"抱歉,未找到城市 '{city}' 的天气信息。",
"suggestion": "您是指 {list(mock_weather_db.keys())[0]} 吗?或者请提供更具体的城市名。"
}
_weather_cache[cache_key] = (error_result, current_time)
return error_result
这个工具比基础版本更实用。它引入了简单的缓存机制来模拟优化API调用,加入了更详细的错误信息和用户建议,返回的数据结构也更丰富(比如包含了温度数值)。在实际项目中,你只需要替换掉模拟数据库的部分,接入真实的天气API即可。
3.3 构建智能体团队:明确分工与协作规则
接下来是重头戏:定义我们的智能体团队。我们设计一个包含三个角色的团队:
- 主协调员(Weather Coordinator):负责理解用户整体意图,并决定将任务分派给谁。
- 天气专家(Weather Expert):专门负责调用天气工具,处理具体的天气查询。
- 社交专员(Social Specialist):包含问候和告别两个子职能,处理所有社交礼仪。
我们在 agents/__init__.py 中定义它们:
# agents/__init__.py
from google.adk.agents import Agent
from litellm import completion
from ..tools.weather_tool import get_weather
# 1. 社交专员 - 问候子智能体
greeting_agent = Agent(
name="greeting_agent",
description="专门处理简单问候的智能体。你的唯一任务就是热情、友好地回应用户的‘你好’、‘嗨’等开场白。不要回答任何其他问题。",
instruction="你是一个友好的问候机器人。当用户说‘Hi’, ‘Hello’, ‘你好’, ‘早上好’等问候语时,请回复一句热情、自然的欢迎语,并可以简单询问对方今天需要什么帮助。绝对不要处理天气查询或其他任务。",
# 使用LiteLLM兼容的模型,这里用Claude 3 Sonnet为例
model=lambda prompt: completion(model="claude-3-sonnet-20240229", messages=[{"role": "user", "content": prompt}]),
)
# 2. 社交专员 - 告别子智能体
farewell_agent = Agent(
name="farewell_agent",
description="专门处理告别和结束语的智能体。你的唯一任务就是礼貌地结束对话。",
instruction="你是一个礼貌的告别机器人。当用户表示对话结束,如说‘Bye’, ‘再见’, ‘谢谢,没事了’时,请回复一句友好、温暖的结束语,并表达期待再次为您服务。不要进行其他对话。",
model=lambda prompt: completion(model="claude-3-sonnet-20240229", messages=[{"role": "user", "content": prompt}]),
)
# 3. 天气专家智能体
weather_expert_agent = Agent(
name="weather_expert",
description="专业的天气查询智能体。你拥有查询天气的工具,并且精通如何解读和呈现天气信息。",
instruction="你是一个天气专家。当用户询问某个城市的天气时,你需要调用`get_weather`工具来获取信息。工具会返回一个包含‘status’和‘report’字段的结果。如果status是‘success’,请将report中的信息用友好、易懂的方式组织成一段话告诉用户。如果status是‘error’,请根据error_message和suggestion,委婉地告诉用户查询失败,并给出建议。你只处理与天气直接相关的问题。",
tools=[get_weather], # 将工具赋予这个智能体
model="gemini-2.0-flash-exp", # 直接使用Gemini模型
)
# 4. 主协调员智能体(根智能体)
root_agent = Agent(
name="weather_coordinator",
description="你是用户对话的总协调员。你需要分析用户输入,并决定由哪个专业智能体来完成任务。",
instruction="""
你是这个智能体团队的调度中心。请严格遵循以下规则:
1. **任务分派**:仔细分析用户的输入。
- 如果用户是在打招呼(如:Hi, Hello, 你好,早上好),立即将任务委派给 `greeting_agent`。
- 如果用户是在告别或结束对话(如:Bye, 再见,谢谢,不问了),立即将任务委派给 `farewell_agent`。
- 如果用户是在询问天气(包含‘天气’、‘气温’、‘下雨吗’、‘city名+天气’等关键词),将任务委派给 `weather_expert`。
2. **其他情况**:对于无法识别的请求(如问时间、讲笑话),请直接、礼貌地告知用户你目前只擅长处理问候、告别和天气查询。
3. **不要越权**:你自己不要尝试去回答问候、告别或查询天气,你的核心工作是准确分派。
请根据以上规则,输出你的决策。只需输出你要委派的智能体名称,或者‘无法处理’。
""",
model="gemini-2.0-flash-exp",
# 关键:指定你的下属团队
sub_agents=[greeting_agent, farewell_agent, weather_expert_agent]
)
这段代码是ADK多智能体协作的核心。注意看root_agent的instruction,它本质上是一个路由逻辑的Prompt。我们在这里用自然语言清晰地定义了协作规则。sub_agents参数则建立了清晰的层级关系。当root_agent运行时,ADK框架会将它自身的描述(description)以及所有sub_agents的描述一起提供给LLM,帮助LLM做出更准确的路由决策。这种基于描述(description)的匹配,比硬编码的关键词匹配要灵活和智能得多。
3.4 运行与调试:观察协作流程
定义好之后,我们可以写一个简单的 run.py 来测试这个系统:
# run.py
import asyncio
from agents import root_agent
from google.adk.runners import AgentRunner
async def main():
runner = AgentRunner(root_agent)
test_messages = [
"你好!",
"今天北京天气怎么样?",
"那上海呢?",
"帮我查一下ErrorCity的天气",
"谢谢,再见!",
"给我讲个笑话吧。"
]
for msg in test_messages:
print(f"\n{'='*50}")
print(f"[用户]: {msg}")
print(f"{'-'*50}")
response = await runner.run(msg)
print(f"[系统回复]: {response}")
print(f"{'='*50}")
if __name__ == "__main__":
asyncio.run(main())
运行这个脚本,你会看到清晰的执行日志。更棒的是,如果你同时运行着 adk web,可以在浏览器中打开Web UI(通常是 http://localhost:8080),这里会以时间线或流程图的形式,可视化地展示整个对话过程:用户输入如何进入root_agent,root_agent的LLM如何思考并做出“委派给weather_expert”的决策,weather_expert又如何调用get_weather工具,工具返回结果后,weather_expert如何生成最终回复。整个链条一目了然,这对于调试复杂协作中的逻辑错误至关重要。
4. 超越案例:ADK在复杂场景下的高级玩法
上面的天气案例展示了基础协作。但ADK的能力远不止于此。在实际项目中,你会遇到更复杂的场景,这时候就需要用到ADK提供的高级特性。
场景一:并行处理与信息聚合
假设用户问:“同时对比一下北京、上海和广州明天的天气。” 这是一个典型的可以并行执行的任务。你可以在weather_expert内部,或者再创建一个WeatherParallelAgent,利用ADK的Parallel流程代理。这个代理会同时发起对三个城市的天气查询(调用三次工具或并行调用一个支持批量查询的API),然后等待所有结果返回,再聚合生成一个对比报告。这能极大缩短响应时间,提升用户体验。
场景二:循环与条件判断
考虑一个订餐助理:用户说“我想吃披萨”。智能体需要先调用“餐厅搜索”工具,返回一个列表,然后问用户“这里有A、B、C三家,您想看哪家的详情?”。根据用户选择(比如“A”),再调用“菜单查询”工具获取A的菜单,接着可能继续循环,让用户选择菜品、确认口味等等。这种多轮、有条件分支的对话,可以用ADK的Loop代理配合LLM的动态判断来实现。LLM在每一轮都会根据当前状态(已选餐厅、已选菜品)和用户新输入,决定下一步是调用“加料”工具,还是进入“结算”流程。
场景三:智能体即工具(Agent-as-a-Tool)
这是构建复杂系统的关键模式。比如,你可以将上面整个“天气查询协作系统”打包成一个weather_agent_tool。然后,在一个更大的“个人旅行助手”智能体中,将这个weather_agent_tool作为其可用工具之一。当旅行助手规划行程时,它可以自主调用这个工具来获取目的地天气,作为行程建议的参考。这种层层封装和复用,让系统架构变得非常清晰和可扩展。
场景四:基于状态的持久化会话
真实的对话应用需要记忆。ADK提供了状态管理机制。你可以在智能体定义中声明需要持久化的状态变量(例如conversation_history, user_preferences)。ADK框架会负责在每次对话轮次中维护和传递这个状态。这样,你的天气智能体可以记住用户上次查询的城市,并主动问:“还是查询北京的天气吗?”,从而提供更连贯的对话体验。
要实现这些高级玩法,你需要更深入地使用ADK的Flow类(用于定义顺序、并行、循环流程)和更精细地设计智能体的instruction以及工具返回的结构。官方文档提供了这些高级模式的示例,但核心思想不变:用清晰的模块定义智能体,用灵活的编排规则描述协作。
5. 从开发到生产:ADK的完整生命周期支持
构建一个能跑的智能体demo只是第一步,把它变成一个稳定、可评估、可部署的生产级服务,才是真正的挑战。ADK在这条链路上提供了堪称“保姆级”的支持。
内建评估框架:告别“感觉还行” 以前评估智能体,我们往往手动问几个问题,看看回答“感觉还行”就过了。ADK内置的评估功能让你能进行系统化测试。你可以在代码中定义测试套件:
from google.adk.evaluation import AgentEvaluator, TestCase
# 定义测试用例
test_cases = [
TestCase(
input="你好",
expected_agent_invocation="greeting_agent", # 期望被调用的智能体
expected_response_contains=["欢迎"] # 期望回复中包含的关键词
),
TestCase(
input="北京天气",
expected_tool_calls=[("get_weather", {"city": "北京"})], # 期望被调用的工具及参数
expected_response_contains=["北京", "天气", "22°C"]
),
TestCase(
input="不存在的城市天气",
expected_response_contains=["抱歉", "未找到"] # 期望的错误处理回复
),
]
evaluator = AgentEvaluator(root_agent)
results = evaluator.evaluate(test_cases)
然后通过 adk eval 命令行或Web UI运行评估。ADK会自动化执行所有测试用例,并生成详细的报告:哪些通过了,哪些失败了,失败的原因是路由错了、工具调用参数不对,还是最终回复不符合预期。这为持续集成(CI)和回归测试提供了基础。
一站式部署:从本地到云端 当你对智能体的表现满意后,ADK可以让部署变得非常简单。它支持将你的整个多智能体应用容器化。基本上,你只需要一个简单的Dockerfile,ADK的CLI工具能帮你处理好依赖打包。生成容器镜像后,你可以部署到任何支持容器的环境:你自己的Kubernetes集群、云服务器,或者谷歌云的Cloud Run、Kubernetes Engine。
如果你在谷歌云生态内,体验会更丝滑。ADK与Vertex AI Agent Engine深度集成。你可以直接将智能体部署到Agent Engine上,它是一个全托管的智能体服务平台,自动处理扩缩容、监控、日志和安全性。对于需要快速上线、不想管理基础设施的团队来说,这是最省心的选择。
与Genkit如何选择? 谷歌内部其实有两个相关的框架:ADK和Genkit。简单区分一下:
- ADK:专为多智能体协作而生。如果你的应用核心是多个智能体之间的复杂交互、任务分派和协调,ADK是你的不二之选。它的整个架构都是围绕“代理”和“编排”设计的。
- Genkit:更偏向于构建通用的AI体验和插件。它提供了丰富的插件生态和快速构建AI功能(如文本生成、分类、摘要)的能力,但在多智能体编排上的抽象不如ADK深入。
所以,选择很简单:要做多智能体系统,用ADK;要做灵活的、插件化的单智能体或AI功能,用Genkit。
6. 个人经验与避坑指南
最后,结合我这段时间的实践,分享几点可能对你有用的经验和容易踩的坑。
第一,智能体的“职责描述”是关键中的关键。 ADK的动态路由严重依赖LLM对智能体description和instruction的理解。你的描述必须清晰、无歧义、互斥。比如,greeting_agent的描述要强调“只处理问候”,weather_expert要强调“只处理天气查询”。如果描述模糊重叠,LLM就可能出现路由混乱,把天气问题丢给问候智能体。我建议在描述中使用“Your ONLY task is to...”、“Do NOT...”这样非常绝对的措辞。
第二,工具设计要“健壮”且“信息丰富”。 工具是智能体感知世界的窗口。工具函数的返回结构应该标准化,比如始终包含一个status字段(success/error),并在出错时提供结构化的error_message和可选的suggestion。这能让调用它的智能体更容易编写稳定的处理逻辑。避免工具函数直接返回纯文本或复杂嵌套的JSON,最好返回一个字典,包含明确的状态、数据和建议。
第三,充分利用Web UI进行调试。 在开发初期,不要只盯着代码看。一定要打开 adk web 的界面。它能让你直观地看到每一次LLM的思考过程(推理内容)、状态的变化、工具调用的输入输出。很多路由逻辑的错误,在代码层面很难发现,但在可视化流程图中一目了然。这是ADK相比其他框架一个巨大的体验优势。
第四,从简单开始,逐步复杂化。 不要一开始就设计一个包含十几个智能体的庞大系统。先从两个智能体的简单协作开始(比如一个路由,一个专家),确保路由机制工作正常。然后逐步添加第三个、第四个智能体,并不断运行评估测试,确保新成员的加入不会破坏原有的协作逻辑。这种迭代式开发能帮你更好地控制复杂度。
第五,注意成本与延迟。 多智能体系统意味着多次LLM调用。每一次路由决策、每一个子智能体的处理,都可能是一次API调用。在设计流程时,要思考是否真的需要动态路由?有些步骤固定的流程,用Sequential流程代理可能更便宜、更快。同时,合理设置LLM调用的超时时间和重试策略,避免因为一个智能体的卡顿导致整个系统挂起。
谷歌ADK的开源,确实为多智能体开发带来了一个强大的、工程化程度很高的新选择。它可能不是最简单的入门工具,但绝对是当你需要构建严肃的、可维护的多智能体应用时,最值得深入研究的框架之一。它的模块化思想、对协作流程的抽象,以及从开发到部署的全套工具链,都在试图解决这个领域最棘手的问题。如果你正在或计划涉足多智能体应用,花点时间折腾一下ADK,很可能会有意想不到的收获。至少,它提供了一套清晰的最佳实践,让你在设计自己的系统时,知道路该怎么走。
更多推荐
所有评论(0)