从零开始使用ADK开发agent(1)-单agent开发
目前市场上有很多的agent 框架,如 autogen,langgraph,crewai,agno 等等,不同的框架对于agent 的实现有些差异,有的框架侧重点在流程编排如 langGraph,有的框架提供很多内置的工具如crewai,但总体而言,这些agent 框架的基本能力包含工具调用,agent 协同。
前段时间 google 提出了A2A 协议,意在统一这些框架直接的交互方式。本系列我准备从零开始学习一下agent 的开发。
框架我选择了google 的 adk,一来这个框架使用起来比较简单,二来这个框架由google开发维护,对于A2A 协议有着很好的支持。
ADK 介绍¶
ADK (Agent Development Kit), 是谷歌在2025年4月正式发布的一款开源智能体开发框架,旨在简化复杂Agent应用程序的整个端到端开发生命周期。该框架与谷歌自身产品中用于支持Agent的框架相同,现在可供各地的开发人员使用。
主要功能¶
- 多智能体架构:支持构建由多个专业智能体组成的层次化应用,实现复杂的协调和委派。开发者可定义不同层级的智能体,每个专注特定任务,提高系统整体效率和可扩展性。
- 丰富的工具生态系统:提供预构建工具(如搜索、代码执行)、自定义函数和第三方库集成,开发者能轻松扩展智能体能力,满足多样化需求。
- 灵活的编排:内置多种工作流智能体(如SequentialAgent、ParallelAgent、LoopAgent),支持LLM驱动的动态路由,可灵活定义复杂工作流程,满足不同场景任务需求。
- 集成开发工具:提供命令行界面(CLI)和开发者UI,支持运行智能体、检查执行步骤、调试交互和可视化智能体定义,帮助开发者快速开发、调试和优化智能体。
- 原生流式支持:支持双向流式交互(文本和音频),与底层能力(如Gemini Developer API)无缝集成,使智能体能实时响应用户输入,提供更流畅交互体验。
- 广泛的LLM支持:虽与谷歌的Gemini模型深度集成,但通过BaseLlm接口,也支持与各种LLM(如OpenAI、Anthropic、Meta、Mistral AI等)集成,为开发者提供更多选择和灵活性。
废话不多说,接下来从头开始跟着官方文档来一起学习如何使用ADK 吧。
初始化项目¶
这里我使用uv 来管理项目,当然也可以使用pip,差异不大。
我这里创建子项目的原因是想以后每个章节使用独立的子项目,这样每个子项目可以公用一套虚拟环境,关于uv 中使用 workspace 的教程可以参考我之前的文章,如果只是想跑demo,完全可以不用workspce,单纯的再根目录下运行。
此时的目录结构为
创建智能体¶
作为ADK系列的第一篇,我们准备实现一个可以在线查询天气的agent,这个也是众多教程中都快包浆了的工具,虽然很多教程都在使用,包括官方的文档中也是用的天气查询,但是作为学习的工具,我们只需要了解它的运行机制,工作原理就行,之后我们可以在工作中实现真实的agent。
官方文档使用的是fake 数据,只是模拟了天气查询,我这里使用高德地图的接口,真实的进行天气查询。
创建工具函数¶
[!NOTE] 注意 以下创建的文件名和类名,名字不能变
在 adk_starter 目录下,先创建 agent_tool,py 写入一下代码
这里定义了两个工具函数:
- get_weather, 调用高德天气查询接口获取天气信息
- get_current_time,返回当前的时间
具体的函数实现很简单,就不用讲解了,只要记住,这就是两个普通的函数。
每个函数在注释部分写了函数的描述,以及参数和返回值说明,这个非常重要!大模型之后分析解析参数是否准确依赖于这里的说明是否清晰明确。
创建智能体¶
在 adk_starter 目录下新建 agent.py 文件
这个文件名**必须** 叫 agent.py ,里面的Agent 实例也必须叫 root_agent,这个先这样写,因为之后在使用官方的测试工具进行测试的时候,调用的对象都是固定写死的。
Agent 类有很多初始化参数,这里只使用了几个重要参数
name: 智能体的名字,这个理论上叫什么都学,甚至用个随机字符串都行,但是为了可读性,还是起个有意义的名字把。
model: 这个参数非常重要,是大模型agent 的核心,这个参数可以是 str 或者 BaseLlm,如果是str, 如 gemini-1.5-flash, 那么会用到 google 供应商,这个也是ADK 深度整合的,但是由于一些众所周知的原因是访问不了的,所以这里我使用了阿里的qwen 模型,需要在阿里云申请key, 然后使用 litellm 进行代理, 需要安装 litellm 和 openai
litellm 初始化时model 参数为 供应商/模型名
的形式, 如这里的 openai/qwen-plus
。
description: 这个参数是描述智能体能做什么,需要根据智能体的能力来定义。
instruction: 这个参数是用法说明,可以指导agent 在什么情况下调用哪个工具,具体的工作流程是什么样的。
tools: 工具列表,定义这个agent 可以调用哪些工具,这里填写上面的两个函数名称。
[!important] 注意 description 和 instruction 非常重要,决定这智能体是否可以正常工作,以及工作的效果的好坏
关于agent 类型¶
上面示例中使用的是 google.adk.agents.Agent
, 这个agent 是 LlmAgent 的别名,是一个基于大模型的agent,除了这个agent,adk 中还定义了很多实用的 agent :
- SequentialAgent: 顺序执行子agent 的 agent
- LangGraphAgent: 基于langgraph 实现的agent
- LoopAgent: 循环执行的 agent
- ParallelAgent: 并行执行的 agent
我们还可以自定义agent ,这个在之后用到的时候再详细记录下,目前我们只需要使用 LlmAgent 即可。
编辑 __init__.py
文件¶
编辑 adk_starter 下的 __init__.py
, 如果没有这个文件,需要新创建一个,这一步也是为了之后使用官方测试工具而准备的,后期我们在开发自己的agent 平台时是不需要的
创建环境变量文件 .env¶
在 adk_starter 目录下创建 .env
文件, 将agent 运行时需要的环境变量写进去,上面使用的是openai 兼容格式的供应商,所以这里还需要配置以下 OPENAI_API_KEY, 高德天气查询函数里需要 GAODE_KEY
经过上面的步骤以后,一个最简单的agent 就完成了,我们来测试一下。
测试¶
有多种测试方法,官方sdk中提供三种,我们都来实验一下。
webui¶
返回到 adk_starter 上层目录,也就是 adk_project 根目录, 运行
webui 的方式我认为是最友好的,用户可以在浏览器上与agent 进行交互
打开 http://127.0.0.1:8000
这个页面主要分为两部分,左边为agent 运行时的状态信息,右边是一个对话页面。让我们来问几个问题。
这个对话框会将agent 运行过程中调用了哪个工具以及执行结果详细的展示出来,并且将最终的结果返回。
点击工具调用的按钮,左边就会展示出工具的名称和入参,这个很方便的进行调试
我问了一个问题
现在几点了? 我明天要去上海,那边的天气怎么样?
可以看出,这个agent 分别调用了两个工具函数。
api_server¶
api_server 与 webui 的方式差不多,只是它不提供web 页面,而是通过http 接口调用
启动命令
启动成功以后,也是使用 8000 端口对外提供服务, 使用curl 命令或者postman 发送请求
session_id 是记录对话上下文的,同一个用户通过不同的 seseion_id 来管理对话,在使用webui 进行测试是,页面顶端会显示当前的session_id,这个看起来像是uuid生成的,如果刷新页面或者点击 New Session,这个id 就会变,将开启新的一轮对话。
cli¶
最后是在shell 中运行命令,在shell 中进行交互
我觉得从交互体验上来讲 webui>api_server>cli, 你们也可以选择自己喜欢的方式。
手搓 runner¶
上面有提到,agent.py 和 agent.py 里的 root_agent 都是固定的,需要写死,这个只是为了配合官方的测试工具,我们也可以手动的运行agent,这样就不用受限于文件名称和类对象名称。
修改 adk_project 下的main.py,注意这个是adk_project 根目录下的 main.py, 不是 adk_starter 目录下的main.py。
agent 在执行过程中,需要使用Runner 对象,这个对象可以帮助保存对话内容,执行工具调用等,这个Runner 对象非常重要。接下来看下执行过程分析:
session_service = InMemorySessionService()
初始化一个内存类型的SessionService,这类型的SessionService 是将与用户交互的上下文保存到内存中,如果重启了就没了,后面我们尝试使用外部存储如mysql。runner = Runner()
初始化一个Runner 对象,之后主要就是使用这个对象进行调用runner.run_async()
异步进行agent 调用。
未来我们会大量使用手搓Runner 调用。
注意¶
上面的代码有几点注意
- 工具函数的定义从一开始就需要考虑异步,尽量使用
async def
定义,无论是官方的测试webui 还是未来我们自己用fastAPI写后台服务,都要求写异步函数 - Runner 类本身有 run 和 run_async 方法,run 方法用于同步调用,官方也不推荐使用,还是使用 run_async 方法吧
之前的工具使用async def
定义的异步函数
这个函数没有什么IO操作,所以也看不出来什么问题,但是如果改成下面的代码:
首先把 async def
改为的 def
, 并且在函数体添加了阻塞的 time.sleep(30)
来模拟阻塞式 IO 操作,如果 agent 执行到了这段代码,则会将整个系统卡住,无论是web 还是api 访问,都将阻塞住。这个问题之前我也写个多次了。
与MCP的区别¶
上面的代码看上去和MCP 差不多,MCP 也是定义工具,绑定服务运行,有大模型来决定使用哪个工具,这里的agent 和MCP 有哪些区别呢?
首先,从这个简单的demo 来看,确实实现的功能和MCP 差不多的,但是这个是由于这个agent 实在是太简单了,没有体现出agent 的优势,上面有提到,定义agent 的description 和 instruction 非常重要,可以通过instruction 来指导agent 行动,之后我们会实现多agent示例,agent 完成的是一个task, agent 在完成task 时可能还会调用别的agent,完成的工作单元是 task,而mcp 完成的是一个个工具调用,完成的工作单元是tool。
这个在之后介绍复杂的多agent 时会有明显的体现。
总结¶
本文通过 adk 实现了最最基础的单 agent 多工具的调用,主要学习adk 创建agent 以及agent 如何运行,有了简单初步的认识
创建agent 的流程为
- 定义工具执行函数,注意要使用异步的,在函数注释中写清楚函数的作用,以及参数的含义,这样大模型才能分析出是否要调用该工具
- 定义agent,本文使用 LlmAgent 类
- 运行测试,官方提供三种测试工具,主要以 webui 测试为主,交互比较好
后面会使用多agent 进行自主规划, 不同agent 执行模式,以及A2A协议的实践。
代码已经放到github, 有兴趣可以查看一下
https://github.com/kevinkelin/a2a_demo
