YouTube视频总结

视频信息

开篇引入

“嘿,伙计们!” 伴随着Brandon Hancock那充满活力的开场白,他向我们揭示了Google最新发布且人气“爆炸”的智能体框架——Agent Development Kit (ADK)。Brandon,一位已经帮助数十万开发者通过Langchain和Crew AI等工具构建AI智能体的资深导师,满怀信心地承诺,这门“ADK速成课程”将带领所有学员从初学者一跃成为专家。他深知初学者的痛点,因此课程设计得极其友好,不仅将手把手地带领大家走过12个精心设计的示例,从最基础的个体智能体构建,逐步深入到多智能体协作、工具调用、记忆管理乃至于复杂的并行、顺序和循环工作流。

这不仅仅是一次技术教学,更是一场沉浸式的AI智能体构建之旅。Brandon不仅会揭示ADK的核心原理和最佳实践,更会分享他在实践中遇到的“坑”和解决之道,让读者能够真正“超越”视频内容,获得更深刻的理解和实战经验。他希望通过这种方式,让大家亲身感受到使用ADK创建AI智能体是“多么的简单”,并免费提供所有示例的源代码,旨在“尽快帮助你掌握ADK”。这门课程的核心价值在于,它不仅仅传授技术,更引导我们思考如何利用AI智能体自动化工作流,并将AI智能体整合到我们自己的应用程序中,为未来的AI应用开发奠定坚实基础。

详细内容

1. 初识ADK:构建你的第一个智能体 [00:02:29-00:11:43]

核心观点
初探ADK,首先要掌握构建单个AI智能体的核心原则,这包括理解其关键属性、遵循特定的文件夹结构、正确安装依赖项、获取并配置API密钥,最终成功运行并与你的第一个智能体进行交互。

深度阐述
Brandon将构建第一个智能体视为ADK学习的“绝对基础”。他强调,这是一个从零到一的过程,旨在让学习者理解创建智能体的“核心原理”。

  1. 核心属性(Core Attributes)

    • 根智能体(Root Agent):ADK框架要求每个项目至少有一个“根智能体”。它是所有请求的“入口点”,后续所有发送给智能体的请求都将通过它进入系统。
    • 名称(Name):每个智能体都需要一个唯一的名称。这个名称不仅在运行时用于标识智能体,更重要的是,它必须与其在文件系统中的文件夹名称“完全匹配”。Brandon强调,如果名称不匹配,ADK会“发脾气”,抛出“不识别”的错误。
    • 模型(Model):智能体需要一个底层大型语言模型(LLM)来驱动其智能。ADK的设计非常灵活,允许集成来自OpenAI、Anthropic等框架的任何模型,而不仅仅局限于Google自家的Gemini模型。在本次课程中,Brandon主要使用Gemini 2.0 No Flash模型。
      • Gemini系列模型:Google提供了多种Gemini模型,包括:
        • Gemini 2.5 Pro:被描述为“最智能、最强大”的模型。
        • Gemini 2.0 Flash:性能稍逊但“速度真的很快”。
        • Gemini 2.0 No Flash:具备多模态特性,支持图像、音频等,是课程中主要使用的模型。
      • 成本效益:Brandon特别指出Gemini 2.0 No Flash的定价“非常便宜”,输入价格约为每百万token 10美分,输出价格为40美分,并且拥有100万token的上下文窗口,这意味着可以处理“大量信息”。
    • 描述(Description):这是一个“高级别的职责概述”。虽然在单个智能体中作用不明显,但在多智能体解决方案中至关重要。它帮助根智能体判断“哪个其他智能体能更好地完成这项任务”。例如,如果一个请求是关于写作,根智能体可以通过描述找到“擅长写作”的智能体进行任务委托。
    • 指令(Instructions):这是“最重要”的属性,它直接告诉智能体“应该做什么以及如何做”。Brandon强调,即使是“非常复杂的指令”,Gemini 2.0 Flash也能“完美处理”。
  2. 文件夹结构(Folder Structure)

    • 组织原则:每个智能体项目都应将其智能体放在独立的文件夹中。
    • 核心组件:每个智能体文件夹内必须包含:
      • init.py:此文件告诉Python该文件夹内包含需要导入的“重要信息”,对于ADK智能体而言,它指示Python导入当前文件夹中的智能体。
      • .env:用于存储环境变量,尤其是API密钥。Brandon强调,整个项目只需要一个.env文件,且必须放置在“根智能体”的目录下。在多智能体解决方案中,子智能体无需各自拥有.env文件。他甚至提供了一个.env.example文件,方便用户复制并重命名为.env来粘贴API密钥。
      • agent.py:智能体的实际代码文件。再次强调agent.py的文件名必须与包含它的文件夹名称“一对一匹配”,否则会引发错误。
  3. 安装依赖项(Installing Dependencies)

    • requirements.txt:这个文件列出了所有需要安装的Python包,其中“最重要的是Google ADK”。
    • 虚拟环境(Virtual Environment):Brandon强烈建议为每个Python项目创建独立的虚拟环境。这能有效避免不同项目间的依赖冲突,确保项目运行的稳定性和隔离性。
    • 安装步骤
      1. 创建虚拟环境:python -m venv venv
      2. 激活虚拟环境(Mac/Linux):source venv/bin/activate。Windows用户有不同的命令。
      3. 安装依赖:pip install -r requirements.txt。这将安装所有必需的包,包括Google ADK和其他未来会用到的库。
  4. 获取API密钥(Accessing API Key)

    • Google Cloud Platform:需要前往Google Cloud创建一个账户。新用户可能获得免费积分。
    • 创建项目:在Google Cloud控制台中创建一个新项目,例如“YouTube ADK crash course”。需要绑定一个计费账户,尽管运行示例的费用可能“只有一分钱”。
    • 创建API密钥:访问Google AI Studio,点击“创建API密钥”按钮,并输入项目名称。
    • 配置.env文件:复制生成的API密钥,并将其粘贴到根智能体文件夹内的.env文件中。
  5. 运行智能体(Running Agents)

    • 进入目录:在终端中,使用cd命令进入根智能体所在的文件夹(例如basic_agent/greeting_agent)。
    • ADK CLI工具:ADK提供了一个命令行界面(CLI)工具。
      • adk:显示所有可用选项。
      • adk api-server:创建API端点,允许通过HTTP请求与智能体交互。
      • adk create:创建智能体文件夹(本次课程中无需使用,因为已提供代码)。
      • adk deploy:将智能体部署到云端(超出本次课程范围)。
      • adk eval:对智能体进行测试(超出本次课程范围)。
      • adk run:在终端中直接与智能体聊天。
      • adk web课程中主要使用的方式,它会启动一个“非常漂亮的网站”,提供一个友好的用户界面来与智能体聊天,并实时查看智能体的底层事件和状态。
    • ADK Web界面:启动后,在浏览器中访问指定链接。
      • Agent选择:左上角可选择要对话的智能体(单智能体时会自动选择)。
      • 事件(Events):实时显示智能体运行过程中的所有事件,如消息传递、工具调用、响应生成等。这对于调试和理解智能体行为“超级互动和有帮助”。
      • 状态(State):用于存储智能体在会话中需要记忆的信息,如用户偏好、购买历史等。
      • 会话(Session):一系列用户与智能体之间的消息交流,可以创建多个会话。
    • 示例运行:以“Greeting Agent”为例,它被指令“问候用户,询问用户姓名,然后用姓名问候用户”。当输入“嘿,你好吗?”时,智能体将按照指令询问姓名,然后用提供的姓名进行问候。Brandon强调了“事件”功能的重要性,它能让你“深入”了解每次请求背后智能体内部发生了什么,包括初始指令、描述、用户消息以及智能体的响应。

个人感受
Brandon在讲解这一部分时充满了信心和热情,他反复强调了ADK的易用性和强大功能,并特别提到他自己在使用ADK时遇到的一些“坑”,如文件夹命名不匹配的问题,这让学习者感到他的教学是基于真实的实践经验。他对Gemini模型低廉的价格和强大的能力感到“疯狂”和“惊叹”。

精华收获
ADK提供了一个结构清晰、功能强大的框架来构建AI智能体。 理解根智能体、核心属性(名称、模型、描述、指令)和严格的文件结构是入门的关键。通过adk web命令提供的可视化界面,可以实时观察智能体内部的工作流程,这对于初学者来说是极大的便利。API密钥是连接Google AI服务的桥梁,而虚拟环境则是良好编程实践的基石。

2. 为智能体添加工具:扩展功能边界 [00:12:00-00:17:59]

核心观点
为ADK智能体集成工具是增强其功能和自动化复杂任务的关键步骤。ADK支持函数调用工具、内置工具和第三方工具,但使用时需注意一些限制和最佳实践,尤其是关于返回结果的格式和内置工具的兼容性。

深度阐述
Brandon指出,添加工具能够“超级强大”地提升智能体的功能。他详细介绍了ADK中可用的三种主要工具类型。

  1. 工具类型(Types of Tools)

    • 函数调用工具(Function Calling Tools):这是最常用(“99%的时间”)的工具类型。开发者可以创建自定义的Python函数,如查询天气、查找股票等,然后将其提供给智能体调用。Brandon强调,这是创建自定义功能的主要方式。
      • 代理作为工具(Agents as Tools):这是一个更复杂的概念,但在多智能体解决方案中会用到。它允许将一个智能体封装成另一个智能体的工具,实现更精细的任务分配。
      • 长时间运行函数工具(Long-running Function Tools):这类工具涉及更复杂的异步操作,超出了本次速成课程的范围,但Brandon指出其“可能性”。
    • 内置工具(Built-in Tools):Google ADK提供了一些开箱即用的强大内置工具。
      • Google Search(谷歌搜索):能够让智能体“开箱即用”地访问互联网信息,Brandon称其“超级强大”。
      • Vertex AI Search:用于检索增强生成(RAG)查询。
      • Code Execution Tool(代码执行工具):允许智能体执行代码。
      • 重要限制:Brandon强调,内置工具仅与Gemini模型兼容。如果使用OpenAI或Claude等模型,这些内置工具将无法工作。此外,一个智能体“一次只能传递一个内置工具”,例如,不能同时使用Vertex AI Search和代码执行工具。
    • 第三方工具(Third-party Tools):ADK旨在“尽可能开放”,允许轻松集成来自Langchain或Crew AI等框架的工具。尽管本次课程未深入讲解,但Brandon指出其“可行性”。
  2. 添加工具到智能体(Adding Tools to Agents)

    • 在智能体的配置中,添加一个名为tools的新属性,它是一个列表,用于列出所有要使用的工具。
    • 示例:Google Search工具:在tools列表中引用Google Search内置工具。
    • 示例:自定义Python函数工具
      • 定义函数:创建一个普通的Python函数,如get_current_time
      • 指定返回类型:清晰地定义函数的返回类型。
      • Docstring(文档字符串):这是“超级重要”的部分。Docstring用于描述函数的功能,智能体通过它来判断何时调用该工具。例如,如果函数Docstring说明它能“获取当前时间”,当用户请求时间时,智能体就能正确识别并调用它。
  3. 构建自定义工具的最佳实践和限制(Best Practices & Limitations for Custom Tools)

    • 返回结果的详细性:当工具返回结果给智能体时,务必“尽可能具体和具有指导性”。不要仅仅返回原始结果,而是将其封装在字典中,并提供明确的键值对。例如,不要只返回"Hello",而是返回{"result": "Hello"}或更好的{"current_time": "..."}。ADK会在底层尝试将非字典结果包装成字典,但最好由开发者明确提供。
    • 函数参数:当函数需要参数时,直接指定参数名和类型,例如format: str
    • 避免默认值:Brandon特别强调,在创建自定义工具函数时,“不要添加默认值”给函数参数。在ADK录制时,这样做会导致程序中断。
    • 内置工具与自定义工具的组合限制:Brandon指出一个重要的“痛点”:不能同时在同一个智能体中添加内置工具和自定义工具。如果尝试这样做,ADK会“崩溃”,抛出“不清晰的错误信息”。这是他自己踩过的“坑”,希望大家引以为戒。
  4. 运行带工具的智能体(Running Agents with Tools)

    • 准备:确保已激活虚拟环境,并在终端中cd到正确的智能体文件夹(例如tool_agent)。
    • 启动Web界面:运行adk web命令。
    • Google Search示例:当询问“特斯拉本周有什么新闻?”时,智能体将调用Google Search工具。在事件流中,可以看到工具被调用,查询内容是“Tesla news this week”,然后智能体获取了搜索结果,并生成了关于特斯拉Q1业绩等新闻的摘要。Brandon再次强调了事件视图的强大,它允许深入查看智能体如何调用工具并处理响应。
    • 自定义时间工具示例:切换到使用get_current_time工具。当询问“现在是什么时间?”时,智能体将调用自定义工具,获取当前时间,并将其格式化后返回。事件视图同样能显示工具调用和响应的详细过程。

个人感受
Brandon在讲解工具的限制时,语气中透露出他自己曾经为此“吃过苦头”的无奈和经验,这让他对学习者发出真诚的警告,希望大家能避免同样的“心痛”。他对Google Search开箱即用的强大功能感到“超级强大”。

精华收获
工具是智能体的“超级能力”,允许智能体执行超出其LLM原生能力的复杂任务,如实时信息检索和数据操作。理解并熟练运用函数调用工具是核心。务必注意ADK对内置工具和自定义工具组合使用的限制,并遵循工具返回结果的详细化最佳实践,这将极大地提升智能体的可靠性和可控性。

3. 连接到其他模型:打破模型限制 [00:18:00-00:21:59]

核心观点
ADK通过集成LightLLM和OpenRouter等技术,能够灵活地连接到OpenAI、Claude等多种大型语言模型,打破了仅限Gemini模型的限制,极大地扩展了智能体的能力和选择范围。

深度阐述
Brandon认为,能够引入其他模型是ADK的一个“超级激动人心”的功能。他解释了实现这一目标所需的两个核心技术。

  1. LightLLM

    • 功能:LightLLM是一个“免费库”,它负责处理与不同大型语言模型(如OpenAI、Claude、Llama)交互时的所有“复杂性”。它提供了一个统一的接口,让开发者可以“轻松”地与各种模型进行通信。
    • 工作原理:LightLLM的核心是将模型名称和消息传递给一个库,该库在底层处理与不同模型的连接细节和各种函数调用。Brandon指出,ADK已经集成了LightLLM,使得使用它变得“比你在这里看到的还要容易”。
  2. OpenRouter

    • 功能:OpenRouter是一个平台,它允许用户“购买可以用于任何模型的tokens”。它作为一个单一的工具,连接到OpenAI、Claude等不同的模型服务器。通过OpenRouter,用户可以访问并使用各种模型,例如OpenAI GPT-4 Mini等,并查看它们的性能和成本信息。
    • 成本与操作:OpenRouter不是免费工具,使用时会产生费用,并且在购买积分时会有少量(3%或5%)的额外费用。用户需要注册OpenRouter账户,购买积分,然后创建一个API密钥。这个API密钥是“通用密钥”,可以用于访问OpenRouter支持的所有模型。
    • 配置:将OpenRouter的API密钥复制并粘贴到项目根目录下的.env文件中。
  3. 配置智能体以使用其他模型(Configuring Agents for Other Models)

    • 导入LightLLM:需要在智能体代码中从Google ADK导入LightLLM。
    • 模型定义:使用LightLLM定义模型时,通常需要提供两个关键信息:模型和API密钥。
      • 提供者(Provider):首先定义模型提供者,例如openrouter
      • 模型系列(Model Family):指定模型所属的系列,例如openai(如果使用Claude,则为anthropic)。
      • 具体模型:最后指定要使用的具体模型,例如gpt-4-1
      • API密钥获取:通过操作系统环境变量os.getenv("OPEN_ROUTER_API_KEY")获取OpenRouter API密钥。
    • 将模型传递给智能体:将配置好的LightLLM模型对象直接传递给智能体的model属性即可。
    • 模型兼容性:Brandon还提供了一个链接,指向LightLLM文档,用户可以在其中查找OpenRouter兼容的所有模型列表,包括OpenAI和Claude模型,甚至可以查看OpenRouter上的模型排名来选择表现最佳的模型。他特别提醒,在使用OpenRouter提供的模型时,模型名称前需要加上openrouter/前缀。
  4. 示例运行(Example Run)

    • 场景:Brandon构建了一个“老爸笑话智能体”(Dad Joke Agent),它被指示“只使用get_dad_jokes工具来讲笑话”。
    • 自定义工具get_dad_jokes是一个简单的Python函数,从预定义列表中随机选择一个“敲门笑话”。
    • 运行:通过adk web启动Web界面,与“Dad Joke Agent”互动。当请求“请讲个笑话”时,智能体将调用自定义工具,获取笑话,并将其返回。最重要的是,这一切都是通过OpenAI模型(而非Gemini)实现的。

个人感受
Brandon对LightLLM和OpenRouter的组合评价极高,称之为“巨大作弊码”(huge cheat code),因为它们极大地简化了与各种模型接口的复杂性,为ADK智能体“真正扩展了能力”。他甚至在演示中展示了OpenRouter的仪表板,显示了其性能和成本,强调了其作为一站式解决方案的便捷性。

精华收获
ADK的开放性是其强大之处,通过集成LightLLM和OpenRouter,开发者不再受限于单一LLM供应商。LightLLM提供了统一的API接口,极大地简化了多模型集成,而OpenRouter则提供了一个便捷的Token购买和多模型访问平台。这两者结合,为智能体提供了访问广泛模型生态系统的能力,是构建更灵活、更强大AI应用的关键。

4. 结构化输出:确保数据格式的精准性 [00:22:00-00:26:49]

核心观点
在ADK中实现结构化输出对于构建复杂的多智能体工作流至关重要,它确保智能体生成特定格式的数据(如JSON),以便于与其他智能体、API或工具进行顺畅的数据交互。ADK主要通过output_schemaoutput_key来管理输出结构和状态存储,但使用时需注意一些限制和最佳实践。

深度阐述
Brandon强调,结构化输出在构建“越来越大”的智能体工作流时“超级重要”。它确保“智能体A以正确的格式生成正确的信息,以便传递给智能体B”,或者传递给其他API和工具。

  1. 结构化数据选项(Structured Data Options)

    • Input Schema(输入Schema):Brandon个人“不喜欢”这种方式,因为它“非常容易失败”。如果上一个智能体传递的信息与当前智能体期望的输入类型不匹配,就会导致程序中断。他认为这种方式“过于死板”,通常会避免使用。
    • Output Schema(输出Schema):这是“你将一直使用的”模式。它指示AI智能体“创建并生成一个符合特定类的输出”。
      • 定义:通过定义一个继承自Pydantic BaseModel的Python类来指定输出的结构。例如,一个Output类可以包含capital: str字段,并提供描述说明capital应是国家的首都。
      • 最佳实践:Brandon强调,为了获得最佳结果,必须在智能体的指令中明确定义期望的JSON结构。例如,如果Schema定义了subjectbody,指令中也需要明确说明“你需要返回匹配这种结构的JSON,包含主题和正文”。如果指令中没有明确告知,智能体在最终尝试将原始数据转换为Schema时可能会失败,导致崩溃。
      • 重要限制不能在有工具调用的智能体中直接使用output_schema,也不能在向其他智能体传输信息时使用output_schema。Brandon建议的“工作流”是,让第一个智能体(Agent 1)完成所有复杂的思考并传递“原始结果”,然后让第二个智能体(Agent 2)负责“确保输出Schema得到满足”。
    • Output Key(输出键):这是一个特殊的名称,用于指定智能体生成的所有信息应该存储在“状态”(State)的哪个特定位置。
      • 状态(State):可以理解为智能体共享的“内存”。通过output_key,智能体可以将生成的结果保存到state.found_capital这样的位置。
      • 共享与访问:其他智能体可以访问这个共享的状态信息。例如,Agent 2可以查询state.found_capital来获取Agent 1生成的结果。Brandon认为这是“让所有信息共享”并确保智能体总能访问所需信息的“最佳方式”。
  2. 代码示例:邮件生成智能体(Email Generation Agent)

    • 目标:构建一个智能体,将输入的文本转换为包含subject(主题行)和body(正文)两个属性的结构化邮件。
    • Output Schema定义:定义一个EmailContent类,包含subject: strbody: str
    • 智能体指令:明确告诉智能体它是一个“邮件生成助手”,总是根据用户请求撰写专业邮件,并“必须”返回JSON结构,包含subjectbody,并遵循简洁、相关主题行、专业正文、商务正式语调等指南。
    • Output Key配置:设置output_keyemail。这意味着智能体生成的邮件将以email为键存储在状态中。
    • 运行与状态观察
      • 启动adk web
      • 输入请求,例如“请给我妻子Carly写封邮件,询问她明天早上是否有空喝咖啡”。
      • 智能体返回包含主题和正文的结构化输出。
      • 观察State:在ADK Web界面的“State”部分,可以看到生成的邮件以email为键被保存,其值是包含subjectbody的JSON对象。Brandon解释说,每次生成新邮件,都会覆盖email键下的旧值。

个人感受
Brandon在讲解结构化输出时,能够感受到他对此功能的重视和兴奋,认为这是构建“更大更复杂工作流”的“强大且重要”组成部分。他通过具体的“坑”(不告知智能体期望的JSON结构会导致失败)来强调最佳实践,让学习者深感其经验的价值。

精华收获
结构化输出是实现AI智能体间高效数据流的关键。通过output_schema精确定义输出格式,结合output_key将结果存储在共享状态中,可以确保信息的准确传递和复用。提前在指令中明确输出结构是保证成功率的重要提示。虽然存在工具和智能体间传递的限制,但通过合理的智能体编排(如由第二个智能体负责结构化),可以有效规避。

5. 会话、状态与执行器:掌握智能体的记忆和生命周期 [00:26:50-00:35:59]

核心观点
在ADK中,会话(Session)、状态(State)和执行器(Runner)是驱动智能体持续交互和记忆的核心组件。会话封装了状态和事件历史,执行器则连接智能体和会话,处理用户的请求并更新会话,它们共同构成了智能体持续对话和解决问题的能力。

深度阐述
Brandon指出,虽然在ADK Web中这些组件的复杂性被隐藏了,但理解它们对于将智能体集成到自己的应用程序中“超级重要”。他将这部分内容分为三个阶段讲解:理论讲解、代码实现和实际运行。

  1. 理论讲解(Whiteboard Time)

    • 会话(Session):Brandon将“会话”定义为“有状态的消息历史”(stateful chat history),这是最核心的理解。每个会话包含两个主要部分:
      • 状态(State):一个字典结构,用于存储键值对信息,如username: Brandon。这是智能体共享的“内存”区域。
      • 事件(Events):不仅仅是用户与智能体之间的消息历史,还包括工具调用和智能体响应。它是一个记录所有交互的列表,允许智能体回顾之前的对话,从而理解上下文并提供相关信息。
      • 其他属性:会话还包括ID、应用程序名称(app name)、用户ID(user ID)和最后更新时间(last update time)。这些属性对于在大规模应用中查找和管理特定用户的对话历史至关重要。
    • 会话类型(Types of Sessions)
      • 内存会话(In-memory Session):所有对话历史都保存在内存中。一旦应用程序关闭,所有数据都会“消失”。
      • 数据库会话(Database Session):将对话历史保存到数据库中。即使应用程序关闭并重新打开,信息仍然保留。这将在下一个示例中详细探讨。
      • Vertex AI会话:将对话历史存储在Google Cloud的AI平台Vertex AI中。这是一种将数据存储在云端而非本地的方式,但超出了本次课程范围。
    • 执行器(Runner):Brandon将执行器描述为连接所有组件的“核心”,它是一个“原材料的集合”,用于“为用户生成响应”。执行器需要提供两部分核心信息:
      • 智能体(Agents):执行器需要知道它可以使用哪些智能体来处理请求。
      • 会话(Sessions):执行器需要一个会话来存储消息历史和状态。
    • 运行器的工作流(Runner Workflow)
      1. 用户请求:用户向执行器发送请求(例如“我的退货政策是什么?”)。
      2. 会话查找:执行器根据用户ID查找现有会话,提取其消息历史和状态。
      3. 上下文传递:将所有上下文信息传递给相关的智能体。
      4. 智能体处理:智能体根据上下文和指令决定如何响应。如果存在多个智能体,它会选择最合适的那个。
      5. 工具调用(可选):如果智能体配置了工具,它可能会进行工具调用(例如查询数据库或互联网)。
      6. LLM交互:将工具调用的结果和所有上下文传递给大型语言模型(LLM,如Gemini)生成响应。
      7. 会话更新:在返回响应给用户之前,执行器会将会话更新,添加新的事件(包括智能体的响应)到事件历史中。
      8. 返回结果:执行器将最终结果返回给用户。
        Brandon称这是“核心循环”,是所有核心概念协同工作的过程。
  2. 代码实现(Code Example)

    • 核心理念:这部分代码演示了如何“手动构建ADK Web命令通常为我们处理的所有核心功能”。当你想将智能体嵌入到自己的应用程序中时,你需要自己管理内存、会话和执行器。
    • 加载环境变量:由于不再依赖ADK Web自动加载,环境变量文件(.env)需要放置在项目的“根目录”。
    • 选择内存服务:这里使用InMemorySessionService,因为是本地测试。
    • 创建初始状态:定义一个字典作为初始状态,例如usernameuser_preferences。Brandon构建了一个名为“Brandonbot”的问答智能体,旨在回答关于他自己的问题,因此初始状态中包含了这些信息。
    • 创建会话:通过内存服务创建会话,需要传入app_nameuser_id和唯一的session_id(通常使用UUID生成)。同时传入之前定义的initial_state,为智能体提供额外上下文。
    • 加载智能体:指定智能体代码的路径。Brandon展示了一个名为“问答智能体”的例子,其指令是如何“回答关于用户偏好的问题”。
      • 访问状态:智能体可以通过“字符串插值”(string interpolation)的方式访问状态中的数据,即在指令中将状态键用括号括起来,例如{username},智能体就能自动从状态中拉取对应的值。
    • 创建消息:以“原始方式”创建消息,使用google.generativeai.types.Content。消息包含role(用户或智能体)和parts(消息文本)。
    • 运行执行器:调用runner.run()方法,传入user_idsession_id和新创建的消息。执行器将执行上述工作流,处理请求。
    • 处理响应:从会话中提取最终的智能体响应并打印出来。
  3. 实际运行(Running the Code)

    • 准备:确保在正确的文件夹(example_number_five)中,并激活了Python虚拟环境。
    • 执行:运行python main.py(或类似命令)。
    • 输出:程序会创建一个新的会话ID,并根据初始状态中存储的信息(例如,Brandon最喜欢的电视节目是《权力的游戏》)回答问题。Brandon展示了如何检查会话的最终状态,确认初始状态中的信息是如何被智能体访问和使用的。

个人感受
Brandon坦言,这部分内容“比运行ADK Web要代码密集得多”,但对于希望将智能体集成到自己的应用程序中的开发者来说,理解这些“核心组件”是“超级重要”的。他通过将这些概念与ADK Web的自动化功能进行对比,清晰地揭示了手动管理这些组件的必要性和复杂性,让学习者体会到掌握底层原理的价值。

精华收获
会话(Session)是智能体的记忆单元,它储存了状态(State)和完整的事件历史,使得智能体能够进行有上下文的、连贯的对话。执行器(Runner)是智能体的大脑,它协调智能体与会话之间的交互,处理用户的请求并管理智能体的生命周期。理解和手动管理这些组件,是将ADK智能体嵌入自定义应用程序的基础,从而摆脱对ADK Web界面的依赖,实现更灵活的部署和集成。

6. 会话与记忆的持久化:将数据保存到数据库 [00:36:00-00:43:59]

核心观点
为了实现智能体记忆的持久化,ADK允许将会话和状态数据保存到本地或云端数据库,确保应用程序关闭后,用户的对话历史和相关信息不会丢失,从而可以在任何时候“从上次中断的地方继续对话”。

深度阐述
Brandon认为,这是整个速成课程中“最喜欢”的例子之一,因为它是“所有东西都应该点击并理解如何协同工作的地方”。他将其分为代码回顾和实际运行两部分。

  1. 代码回顾(Code Review)

    • 数据库选择:示例中选择使用SQLite文件来保存数据,因为它“非常容易使用”,本质上是一个SQL数据库。Brandon指定了数据库文件的名称,例如my_agent_data.db
    • 数据库会话服务
      • 不再使用InMemorySessionService,而是切换到DatabaseSessionService
      • 灵活性:Brandon提到,这些数据库会话可以保存在本地,也可以指向云端托管的数据库服务(如Google Cloud Platform上的数据库)。
    • 初始状态(Initial State)
      • 此示例旨在构建一个“提醒智能体”(Reminder Agent),它将保存、查看、更新和删除用户的提醒。
      • 因此,初始状态被更新为包含用户名和一个空的提醒列表。
    • 处理现有会话或创建新会话
      • 场景:当用户开始与智能体对话时,程序需要判断是加载已存在的会话,还是创建一个新会话。
      • 查找现有会话:通过session_service.list_sessions()方法,传入app_nameuser_id,来查找与特定应用程序和用户相关的现有会话。
      • 逻辑判断:如果找到了现有会话(len > 0),则从中提取session_id
      • 创建新会话:如果不存在现有会话,则调用session_service.create_session(),传入app_nameuser_idinitial_state来创建一个新会话。
      • 结果:无论哪种情况,最终都会得到一个有效的session_id供智能体通信。
    • 设置执行器(Runner Setup)
      • 与之前示例类似,执行器需要传入两个核心成分:根智能体(处理所有请求、指令、工具等)和会话服务(之前设置的DatabaseSessionService)。
    • 交互式对话循环(Interactive Conversation Loop)
      • 代码设置了一个循环,允许用户持续与智能体聊天,直到输入“exit”或“quit”。
      • call_agent_async函数:这个辅助函数封装了处理用户输入的逻辑。
        • 将查询转换为内容:将用户输入的原始文本转换为Content对象。
        • 运行执行器:调用runner.run_async()。Brandon强调,run_async是ADK推荐用于“任何真实世界应用程序”的方式,而runner.run仅用于本地测试。run_async会传入user_idsession中的previous_messagesnew_message
        • 处理智能体响应:执行器会遍历其生命周期,处理请求,最终生成响应。call_agent_async会提取并打印最终的响应文本。
    • 智能体内部对状态的操作
      • 提醒智能体的指令和能力:智能体被指示为“友好的提醒助手”,可以访问共享状态中的usernamereminders列表。它拥有添加、查看、更新、删除提醒以及更新用户名的功能。
      • 通过工具修改状态:Brandon揭示了智能体如何实际更新状态:通过工具调用。
        • tool_context参数:所有工具函数都需要一个tool_context参数。这个tool_context提供了对“所有不同属性”的访问,最重要的是,它可以访问到当前的state对象。
        • 添加提醒:在add_reminder工具中,首先从tool_context.state获取当前的reminders列表。然后向列表中添加新的提醒。关键一步:将更新后的列表重新赋值给state中对应的键(例如state["reminders"] = new_list),从而将更改保存回共享状态。
        • 工具返回:工具的返回结果应该尽可能信息丰富,包括执行的操作、提醒内容和成功消息。
        • 其他操作:查看、更新、删除提醒等操作都遵循类似的模式:从tool_context获取状态,执行相应修改,然后保存回状态。
  2. 实际运行(Running the Example)

    • 初始状态:删除现有的数据库文件以确保从“干净状态”开始。
    • 第一次运行:运行python main.py。ADK会自动创建my_agent_data.db文件。
    • 添加提醒
      • 输入“请给我设置一个明天下午5点扔垃圾的提醒”。
      • 智能体响应“我已添加您的提醒…”。
      • 观察状态:在每次请求前后,Brandon都打印了状态,显示在处理消息前提醒列表为空,处理后则包含了新的提醒。这证明了状态的实时更新。
    • 添加更多提醒:再次添加提醒,例如“提醒我周末要修剪草坪”。状态中的提醒列表会再次更新。
    • 退出并重启:输入“quit”结束对话。由于使用了数据库会话服务,所有数据都已自动保存到数据库中,无需额外操作。
    • 检查数据库文件:Brandon使用Cursor工具展示了.db文件中的内容,可以看到sessionsapp_stateraw_eventsuser_state等表。打开sessions表,可以看到会话的状态,其中包含了用户的username更新后的提醒列表(扔垃圾和修剪草坪)。events表则记录了所有原始事件,包括用户请求、功能调用和功能响应,详细展示了工具如何被调用并返回结果。
    • 第二次运行(验证持久化):再次运行python main.py。当询问“我当前的提醒是什么?”时,智能体能够准确地列出之前保存的提醒,即使应用程序已经关闭并重新启动。这证明了会话和状态的成功持久化。

个人感受
Brandon在看到数据成功保存并能在重启后恢复时,表达出由衷的兴奋,他说:“我希望你们都像我一样激动,感觉自己理解了会话如何工作,执行器做什么,以及会话如何保存到内存或数据库,并看到它们如何协同工作。” 这种激动人心的语气充分传达了功能实现的巨大成就感。他承认在讲解过程中“有点跑题”提到了工具上下文,但认为它“超级有帮助”。

精华收获
会话持久化是构建真正有用、有记忆的AI智能体的基石。通过将智能体的会话和状态数据存储到数据库(无论是本地SQLite还是云端服务),可以确保用户体验的连续性和数据完整性。核心机制在于通过工具函数的tool_context参数访问并修改共享状态,然后将这些修改保存回数据库。这使得智能体不仅能记忆当前对话,还能在长期内保留重要信息,为更复杂的应用场景打下基础。

7. 多智能体解决方案(基础):委托与单次响应 [00:44:00-00:53:59]

核心观点
ADK中的多智能体系统核心在于委托(delegation)快速单次响应(single answer)。一个根智能体负责将任务委托给最合适的子智能体,然后由该子智能体独立完成任务并生成最终响应,而根智能体通常不再参与后续处理。这与Crew AI等框架中多智能体协作、迭代完成任务的模式有所不同,并且子智能体在使用内置工具时存在特殊限制。

深度阐述
Brandon将多智能体系统视为ADK学习的“高级阶段”。他首先在白板上详细解释了ADK多智能体的工作原理,并与读者可能熟悉的Crew AI等框架进行对比,以避免混淆。

  1. ADK多智能体的工作原理(How Multi-agent Systems Work in ADK)

    • 根智能体(Root Agent):在ADK设置中,始终存在一个根智能体。它通常扮演“委托者”(delegator)或“管理者”(manager)的角色,是应用程序的“入口点”。
    • 委托模式:当请求进入框架并发送给根智能体时,根智能体的目标是“尽快回答查询”。
      • 子智能体(Sub-agents):根智能体通过查看其所有子智能体的“描述”(description),来判断“哪个最适合回答查询”。
      • 完全转移责任:一旦根智能体确定了合适的子智能体,它会“将所有责任委托给该子智能体”,然后“退出”。被委托的子智能体将完全掌控任务,进行必要的工具调用,并生成“最终响应”。
    • 与Crew AI等框架的区别:Brandon特别强调,这与Crew AI等框架的工作方式“完全不同”。
      • 在Crew AI中,通常是“一个任务,多个智能体共同努力”来解决,它们之间会进行“多次迭代”和“协作”。
      • 而在ADK的基础示例中,多智能体系统是关于“委托和立即回答问题”,没有“多重迭代”、“多次尝试”或“循环”。智能体一旦被委托,就会“尽快回答问题”。
    • 核心要点
      • ADK专注于委托
      • 由最适合回答问题的智能体来完成工作并生成结果。
      • 在基本示例中,没有多轮迭代或协作。
  2. 核心限制与解决方案(Core Limitations & Workarounds)

    • 内置工具与子智能体:ADK的一个“核心限制”是:不能在子智能体中使用内置工具。Brandon表示,这曾让他“困惑不已”,并指出错误信息通常“不清晰”。例如,如果一个搜索智能体作为子智能体尝试调用内置的Google Search工具,它会报错。
    • agent_as_tool的变通方案:如果确实需要在子智能体中使用内置工具(如Google Search、Vertex AI Search、代码执行工具),唯一的“变通方法”是使用agent_as_tool包装器。
      • 工作原理:当根智能体将子智能体作为工具调用时,子智能体的结果会“传递回父智能体”,然后父智能体使用该答案来生成响应。这与直接委托(责任完全转移给子智能体)不同。
      • 导入agent_as_tool可以从google_adk.tools.agent_tool导入。
    • 工作流代理(Workflow Agents):Brandon提到,如果想要实现类似Crew AI中多轮协作的行为,需要使用ADK中更高级的“工作流代理”,如并行(Parallel)、顺序(Sequential)和循环(Loop)代理,这些将在后续示例中介绍。
  3. 代码示例:多智能体管理器(Multi-Agent Manager)

    • 根智能体:被定义为“管理器智能体”(Manager Agent),其职责是“委托任务给适当的智能体”。
    • 指令:明确列出管理器可以委托的任务及其对应的子智能体,例如:
      • “股票分析智能体”(Stock Analysis Agent):用于查找股票价格。
      • “搞笑极客智能体”(Funny Nerd Agent):用于讲笑话。
    • 子智能体属性:根智能体通过sub_agent属性(一个列表)来指定其可用的子智能体。
    • 子智能体导入:子智能体(如funny_nerdstock_analyst)被组织在独立的子文件夹中,并从这些文件夹中导入到根智能体。
    • 子智能体的指令:子智能体(如Stock Agent和Funny Nerd)的描述对于根智能体进行委托至关重要。描述清晰地说明了每个子智能体的核心功能,例如“这个智能体擅长查询股票价格”。
    • News Analyst作为agent_as_tool:Brandon展示了一个News Analyst智能体,它使用了内置的Google Search工具。因为这个限制,它不能作为常规子智能体被直接委托,而必须被包装成agent_as_tool
  4. 运行与调试(Running and Debugging)

    • 正常运行
      • 启动adk web
      • 当输入“请讲个有趣的笑话”时,管理器智能体通过查看描述,识别出“搞笑极客智能体”最合适,然后将其“委托”给它,搞笑极客智能体则会生成一个笑话。Brandon在事件视图中展示了“委托给代理”(transfer to agent)的事件。
      • 当询问“告诉我微软的当前股价”时,系统会将任务委托给“股票分析智能体”,后者会调用工具并返回股价。
    • 委托陷阱:Brandon在演示中遇到一个小“怪癖”:如果当前已委托到某个智能体(例如搞笑极客),并且后续请求属于另一个智能体(例如新闻),当前智能体可能不会自动重新委托回管理器。他建议,可以通过在指令中明确提示“委托给根智能体”来解决这个问题,或者改进提示,让子智能体在无法处理请求时自动委托回父级。
    • 故意制造错误(Built-in Tool Limitation):为了演示“不能在子智能体中使用内置工具”的限制,Brandon故意将news_analyst智能体(包含Google Search工具)从agent_as_tool中取出,直接作为子智能体添加到根智能体中。
      • 重现错误:重新运行服务器,当尝试询问“今天有什么新闻?”时,系统会立即崩溃,并抛出错误信息:“Oops, this tool is being used with function calling that’s unsupported.”。Brandon指出,这个错误信息“不清晰”,很难直接看出问题所在。通过这次演示,他希望学习者能识别出这种错误,并知道需要使用agent_as_tool作为解决方案。

个人感受
Brandon在讲解多智能体时,坦诚地分享了他自己初次接触ADK时对委托模式的“困惑”,这增强了他的教学的真实性。他在演示中故意制造错误,并解释了不清晰的错误信息,体现了他对学习者可能遇到问题的深切理解和帮助意愿。在遇到委托不畅的“怪癖”时,他立即反思是“弱提示”的问题,并给出了改进建议,展现了作为专业人士的调试思路。

精华收获
ADK多智能体系统的核心在于“委托”:根智能体识别并移交任务给最专长的子智能体进行“单次响应”。这与迭代协作模式不同。关键限制在于子智能体无法直接使用内置工具;若要使用,必须通过agent_as_tool进行包装。通过清晰的智能体描述和指令可以有效管理委托流程,即使遇到委托陷阱,也能通过优化提示或显式地“委托回根智能体”来解决。

8. 多智能体解决方案(进阶):共享状态与智能行为 [00:54:00-01:04:49]

核心观点
通过在多智能体系统中共享状态,智能体能够访问和修改彼此间的信息,从而实现更复杂的智能行为和基于上下文的动态响应。这使得智能体能够根据用户和应用程序的当前状态做出决策,例如判断用户是否已购买课程、是否符合退款条件,从而提供高度个性化的客户服务体验。

深度阐述
Brandon对此示例“超级兴奋”,认为这是“增加额外复杂性并真正让智能体解决复杂问题”的地方。他构建了一个客户服务智能体来演示多智能体系统如何与共享状态交互。

  1. 系统设计:客户服务智能体(Customer Service Agent)

    • 根智能体Customer Service Root Agent
    • 子智能体(Sub-agents):根智能体拥有四个专业的子智能体。
      • 政策智能体(Policy Agent):提供关于课程政策和退款等通用信息。
      • 销售智能体(Sales Agent):负责课程销售,介绍课程内容,并在用户购买后更新状态。
      • 课程支持智能体(Course Support Agent):在用户购买课程后,提供课程内部模块的详细解答。
      • 订单智能体(Order Agent):处理购买历史查询和退款请求。
    • 共享状态(Shared State):这是系统的核心。状态中包含三个关键信息:
      • username:用户姓名。
      • purchased_courses:用户已购买的课程列表,每个课程包含course_idpurchase_datepurchase_date对于判断是否符合30天退款政策至关重要。
      • conversation_history:用户与智能体的对话历史。
    • 智能体行为如何受状态影响
      • 销售智能体:如果状态显示用户未购买课程,则允许购买并更新状态;如果已购买,则拒绝再次购买。
      • 课程支持智能体:仅当状态显示用户已购买特定课程时才回答课程内容问题;否则,会引导用户去销售智能体处购买。
      • 订单智能体:检查状态以验证用户是否拥有课程,并根据purchase_date判断是否在30天内退款;退款成功后,将课程从状态中移除。
  2. 代码实现:状态交互细节(State Interaction Details)

    • main.py的设置
      • 使用InMemorySessionService进行本地测试。
      • 创建初始状态,包含username和空的purchased_courses列表。
      • 创建会话(app_namecustomer_supportuser_idinitial_state)。
      • 创建执行器(传入根智能体和会话服务)。
      • 设置用户交互循环,主要用于捕获用户输入并将其传递给执行器运行,并显示日志。
    • 根客户服务智能体(Root Customer Service Agent)
      • 描述与指令:明确其作为根客户服务智能体的职责,并引导用户到合适的专业智能体。
      • 访问状态:指令中明确说明了智能体可以访问的状态值(usernamepurchased_coursesinteraction_history)。
      • 委托逻辑:指令详细解释了何时将请求路由到哪个子智能体。例如,只有当用户已购买课程时,才将他们导向课程支持智能体。
    • 销售智能体(Sales Agent)——状态更新的核心
      • 指令:详细说明了销售课程、检查用户是否已拥有课程、并根据情况更新状态的逻辑。
      • purchase_course工具:这是关键的工具,它负责更新purchased_courses状态。
        • tool_context:再次强调,通过tool_context访问state
        • 读取状态:获取当前的purchased_courses列表,并设置一个空列表作为默认值,以防状态中没有该键。
        • 业务逻辑:检查用户是否已拥有该课程ID。如果已拥有,则返回“你已拥有此课程”。
        • 更新状态:如果未拥有,则创建一个新的列表,包含所有现有课程和新购买的课程(附带course_idpurchase_date)。
        • 保存状态:将更新后的列表重新赋值给tool_context.state["purchased_courses"],这会将更改保存回共享状态
        • 更新交互历史:同时将购买事件(包含时间戳)添加到interaction_history中,供其他智能体查看。
        • 工具返回:返回详细的状态信息,说明购买成功。
    • 课程支持智能体(Course Support Agent)
      • 根据purchased_courses状态判断是否提供课程内容支持。如果未购买,则引导至销售智能体。
    • 订单智能体(Order Agent)
      • 同样访问purchased_courses状态,处理退款请求。
      • refund_course工具会从purchased_courses列表中移除相应的课程,并更新状态和交互历史。
  3. 实际运行(Running the Demo)

    • 开始:运行python main.py
    • 查询课程:“你有什么课程出售?” -> 客户服务智能体响应课程信息。
    • 购买课程:“是的,我想购买那门课程” -> 转移到销售智能体 -> “是的,请购买课程”。
      • 状态变化:在购买请求前后,日志显示before statepurchased_courses为空,after state中则成功添加了已购买的课程。这直观展示了智能体通过工具如何改变共享状态。
    • 查询课程内容:“程序中所有模块是什么?” -> 转移到课程支持智能体。
      • 基于状态的响应:由于状态显示用户已购买课程,课程支持智能体提供了课程模块的详细信息。
    • 申请退款:“不,我不想再要这门课程了,给我全额退款。” -> 转移到订单智能体。
      • 状态变化before state显示课程存在,after state则显示课程已被移除。订单智能体成功通过工具从状态中移除了课程。

个人感受
Brandon在看到智能体成功地根据共享状态做出判断和操作时,再次表现出极大的兴奋和满足,认为这是“多么的棒”。他通过这个复杂的客户服务场景,将前面所有学习到的概念(根智能体、子智能体、工具、状态、持久化)完美地整合在一起,让学习者看到了它们协同工作的巨大潜力。

精华收获
共享状态是多智能体系统实现高级智能和复杂任务协作的强大基石。通过让不同职能的智能体访问和修改同一份共享数据,可以实现基于用户历史、偏好和当前情境的动态行为。理解工具如何通过tool_context读取、更新和持久化状态,是构建能够“记住”和“适应”用户需求的AI应用的关键。这使得智能体不仅能处理单个请求,还能管理用户在复杂流程中的完整生命周期。

9. 回调函数:精细控制智能体的生命周期 [00:56:50-01:15:59]

核心观点
ADK中的回调函数(Callbacks)提供了一种强大的机制,允许开发者在智能体工作流的“每个部分”进行干预和控制。通过在智能体运行前、后、模型调用前、后以及工具调用前、后插入自定义逻辑,可以实现资源设置、状态修改、请求验证、响应格式化或审查等高级功能,从而对智能体的行为拥有“完全的控制”。

深度阐述
Brandon将回调函数视为“最强大的工作流工具”之一。他介绍了六种不同类型的回调函数,并深入探讨了它们的使用场景和最佳实践。

  1. 回调函数的类型与触发时机(Types and Triggers)

    • Before/After Agent Callback(智能体前/后回调)
      • 触发时机:在AI解决方案的任何逻辑发生“之前”触发(before),或在智能体完成所有处理“之后”触发(after)。
      • Before Agent Callback:Brandon认为这是他“使用最多”且最可能被广泛使用的回调。主要用于设置资源和状态。例如,在智能体运行前,获取用户订单历史或订阅信息,并将其“注入”到智能体状态中,以便智能体运行时拥有所需的所有上下文。也可用于日志记录。
      • After Agent Callback:在所有LLM请求和工具调用完成后触发。主要用于后执行验证和日志记录。例如,在生产环境中记录智能体返回给用户的信息,或修改状态(如跟踪用户请求次数)。
    • Before/After Model Callback(模型前/后回调)
      • 触发时机:在将请求发送给LLM(Gemini, OpenAI, Claude)“之前”触发(before),或在LLM给出响应“之后”触发(after)。
      • Before Model Callback:在向LLM发送请求之前触发。ADK建议用于添加动态指令、基于状态注入示例或模型配置。Brandon主要用它来添加守卫(guard rails),例如,检查用户请求是否包含不当言论。如果检测到不当内容,可以“跳过”LLM调用,直接返回错误消息。
      • After Model Callback:在LLM返回答案后触发。主要用于重新格式化响应(例如替换不希望出现的词语)、日志记录审查/模糊化敏感信息(例如用户ID)。
    • Before/After Tool Callback(工具前/后回调)
      • 触发时机:在工具被调用“之前”触发(before),或在工具返回结果“之后”触发(after)。
      • Before Tool Callback:在工具实际执行前触发。主要用于检查和修改工具参数或执行授权检查。例如,确保请求工具的用户ID与当前账户匹配,防止未经授权的工具调用。可以拦截工具调用并直接返回结果,而不是实际调用工具。
      • After Tool Callback:在工具返回结果后触发。主要用于检查、修改和记录工具结果。也可以将信息保存到状态中。
  2. 回调函数的实现(Callback Implementation)

    • 在智能体中添加回调:在智能体定义中,使用before_agent_callbackafter_agent_callbackbefore_model_callbackafter_model_callbackbefore_tool_callbackafter_tool_callback等属性,并指向相应的回调函数。
    • 回调函数签名
      • Agent Callbacks:回调函数必须接收callback_context参数。callback_context提供对staterunner等信息的访问。返回类型应为Optional[Content],返回None表示继续正常流程,返回Content则表示要中断流程并返回该消息。
      • Model Callbacks:需要接收callback_contextllm_request(包含发送给LLM的消息)。
      • Tool Callbacks:需要接收tool(被调用的工具)、arguments(传递给工具的参数)和tool_contextAfter Tool Callback额外接收tool_response
  3. 代码示例与运行(Code Examples and Runs)

    • Before/After Agent Callback
      • 功能:演示如何利用before_agent_callback在智能体运行前初始化状态(如agent_namerequest_counterrequest_start_time)和日志。after_agent_callback则用于计算请求耗时并记录。
      • 运行:在ADK Web中与智能体交互。在State中可以看到初始化的状态值,日志中则会显示每次请求的耗时。
    • Before/After Model Callback
      • 功能before_model_callback用于内容过滤。如果用户消息包含“sucks”等“脏话”,它会中断LLM调用,直接返回“我无法回应包含不当语言的消息”。after_model_callback用于响应后处理,例如,将LLM响应中的“problem”或“difficult”替换为“challenge”或“complex”。
      • 运行
        • 输入“This website sucks can you help me fix it?” -> 智能体立即回复过滤消息。
        • 输入“What’s the biggest problem with machine learning today?” -> 智能体回复中“problem”被替换为“challenge”,验证了after_model_callback的修改。
    • Before/After Tool Callback
      • 功能:智能体旨在查找国家首都。before_tool_callback演示了两种用途。
        • 参数修改:如果用户输入“America”,将其修改为“United States”再传递给工具。
        • 工具调用拦截:如果用户输入“restricted”,它会拦截工具调用,不实际查找首都,而是直接返回“不能处理该请求”。
        • after_tool_callback用于修改工具的原始响应。例如,当获取“Washington DC”时,在结果末尾添加一个特殊的表情符号和注释。
      • 运行
        • 输入“What is the capital of America?” -> 返回“Washington DC”。在终端日志中可以看到,工具参数从“America”被修改为“United States”。
        • 输入“What is the capital of restricted?” -> 智能体立即返回被拦截后的消息。
        • 输入“What is the capital of USA?” -> 返回“Washington DC”及额外添加的注释,验证了after_tool_callback的修改。

个人感受
Brandon对回调函数的功能感到“超级兴奋”,尤其强调了它们提供的“完全控制”能力。他通过演示故意输入“脏话”和“America”的例子,直观地展示了回调函数如何干预并改变智能体的行为,让人感到这些功能“非常酷”和“强大”。他特别指出before_agent_callback是“超级方便”的一个,未来会经常使用。

精华收获
回调函数是ADK的强大“钩子”,提供了对智能体生命周期无与伦比的控制。它们允许在关键节点(智能体运行、模型交互、工具调用)注入自定义逻辑,实现状态初始化、请求校验、响应审查和参数修改等高级功能。掌握这些回调函数,意味着开发者能够构建更健壮、更安全、更符合业务需求的AI智能体,真正实现“完全控制”。

10. 工作流:顺序代理(Sequential Agents) [01:16:00-01:20:59]

核心观点
顺序代理(Sequential Agents)是ADK中的一种工作流模式,允许智能体按照预定义的顺序一个接一个地执行任务。这种模式适用于任务之间存在明确依赖关系,必须按特定步骤完成的场景。虽然代理按顺序执行,但它们之间不会自动传递状态,需要通过共享状态(State)来显式传递信息。

深度阐述
Brandon将顺序代理定义为ADK工作流代理的“第一个类型”。他指出,这种工作流模式下,智能体“总是按特定顺序工作”。

  1. 工作原理与特性(How It Works & Characteristics)

    • 定义:顺序代理是一种工作流代理类型,意味着智能体将以特定模式执行任务。
    • 执行顺序:所有提供给根智能体的子智能体都将按照开发者指定的顺序执行。这意味着如果列表中是Agent 1、Agent 2、Agent 3,它们将始终按1、2、3的顺序运行。
    • 适用场景:适用于任务必须按步骤完成的场景。例如,一个“网页内容总结”的智能体,必须先“获取页面内容”,然后才能“总结页面”。这种依赖性使得顺序代理成为理想选择。
    • 状态共享:Brandon强调一个重要概念:顺序代理之间不会自动传递状态。如果Agent 2需要Agent 1生成的信息,Agent 1需要将该信息写入共享状态(State),然后Agent 2再从状态中读取。这是在ADK中实现智能体间协作的关键。
  2. 代码示例:潜在客户资格鉴定流程(Lead Qualification Pipeline)

    • 核心目标:创建一个潜在客户资格鉴定流程,通过多个顺序代理来评估一个潜在客户的质量,并给出后续建议。
    • 根智能体:导入SequentialAgent而不是常规的Agent
    • 子智能体顺序:根智能体中指定了三个子智能体的顺序:
      1. Lead Validator Agent(潜在客户验证智能体)
        • 功能:验证潜在客户信息是否完整合格。
        • 指令:要求它检查联系方式、兴趣、需求和公司信息。如果信息完整,返回“valid”(有效),否则返回“invalid”(无效)。
        • 状态存储:将结果保存到状态中的output_keyvalidation_status的键下。
      2. Lead Score Agent(潜在客户评分智能体)
        • 功能:根据验证结果和额外信息对潜在客户进行1到10的评分。
        • 指令:要求它根据问题的紧急性、是否是决策者、是否有时间和预算等因素进行评分。返回一个数值分数和一句话的理由。
        • 状态存储:将结果保存到状态中的output_keylead_score的键下。
      3. Action Recommender Agent(行动建议智能体)
        • 功能:根据前两步的验证状态和评分,给出下一步的行动建议。
        • 指令从状态中访问前两步保存的信息validation_statuslead_score)。如果潜在客户无效,则说明需要补充哪些信息。根据评分(差、好、极好)给出不同的行动建议。
  3. 运行示例(Running the Example)

    • 准备:进入正确的文件夹(sequential_agent_workflow)并运行adk web
    • 非合格潜在客户示例
      • 输入一个不合格的潜在客户信息(例如John Doe,信息不全)。
      • 执行流程:智能体按顺序启动:
        • Lead Validator Agent触发,验证结果为“invalid”并保存到状态。
        • Lead Score Agent触发,根据无效状态给出低分(例如3分)和理由,并保存到状态。
        • Action Recommender Agent触发,访问状态中的validation_statuslead_score,给出建议,例如“John Doe不适合合作,建议继续教育”。
      • 观察状态:在ADK Web界面的“State”中,可以看到每个智能体依次将结果写入状态,最终智能体利用这些累积的状态信息生成最终响应。
    • 合格潜在客户示例
      • 输入一个合格的潜在客户信息(例如Sarah,CTO,有预算和时间)。
      • 执行流程
        • Lead Validator Agent验证为“valid”。
        • Lead Score Agent给出高分(例如8分)和理由。
        • Action Recommender Agent访问状态,建议“与Sarah安排演示,并准备一份提案”。

个人感受
Brandon认为顺序工作流“很棒”,特别指出如果熟悉Crew AI,会发现这种模式“可能更接近你习惯的方式”,这表明他对不同框架间的异同有清晰的认知,并能帮助学习者建立联系。

精华收获
顺序代理是解决多步骤、有依赖性任务的理想选择。它确保智能体按照既定流程逐步推进,但关键在于智能体之间需要通过共享状态显式传递信息。通过合理地设计每个顺序代理的职责及其对状态的读写,可以构建出结构清晰、逻辑严谨的复杂工作流,实现分阶段的数据处理和决策。

11. 工作流:并行代理(Parallel Agents) [01:21:00-01:27:00]

核心观点
并行代理(Parallel Agents)是ADK中的一种工作流模式,旨在实现任务的“速度”和“效率”。多个子智能体同时独立执行任务,并将各自的结果保存到共享状态中,最后由一个最终智能体整合所有并行结果,生成一份综合报告。这种模式适用于那些任务之间没有严格顺序依赖,可以同时进行的场景。

深度阐述
Brandon将并行代理视为“另一种类型的工作流代理”,其核心关注点是“速度”。

  1. 工作原理与特性(How It Works & Characteristics)

    • 并行执行:与顺序代理不同,并行代理中的所有智能体将“并行生成和执行工作”,从而“大大加快速度”。
    • 结果聚合:一旦所有并行任务完成,通常会将它们各自保存到状态中的信息,由“最终智能体”来“整合所有原始信息并输出一份漂亮的报告”。
    • 适用场景:特别适用于需要“快速完成大量工作”的场景。例如,同时收集系统CPU、内存和磁盘信息,这些任务没有先后依赖,可以并行进行。
  2. 代码示例:计算机分析监控(Computer Analytics Monitoring)

    • 核心目标:监控计算机的CPU、内存和硬盘空间,并生成一份综合报告。
    • 混合工作流:此示例结合使用了顺序代理和并行代理。
      • 根智能体:实际上是一个顺序代理
      • 第一个子代理(并行代理):是一个**ParallelAgent**,命名为System Information Gatherer。它包含三个并行的子智能体:
        1. CPU Agent(CPU智能体):获取CPU数量、核心数、使用率等信息。
        2. Memory Agent(内存智能体):查看内存使用情况。
        3. Hard Drive Space Agent(硬盘空间智能体):检查磁盘使用情况。
        • 这三个代理都将使用psutil库来获取系统信息。
        • 每个并行代理都会将自己获取的信息保存到状态中(例如CPU informationmemory informationdisk information)。
        • Brandon强调,在大型智能体工作流中,将每个代理的工具(例如get_cpu_data)分解到独立的tools.py文件中是“最佳实践”,以保持代码的整洁和轻量。
      • 第二个子代理(常规代理):是一个System Report Synthesizer(系统报告合成器)。
        • 功能:这个代理会“访问”由并行代理保存到状态中的所有信息。
        • 指令:要求它使用状态中的CPU、内存和磁盘信息,生成一份“格式良好的报告”(通常是Markdown格式)。
        • 链式工作流:Brandon形象地指出,这种结构允许将并行代理嵌套在顺序代理中,“世界是你的牡蛎,你可以做任何你想做的事情”。即,顺序代理首先运行并行代理以收集所有数据,然后将这些数据传递给报告合成器代理进行最终报告生成。
  3. 运行示例(Running the Example)

    • 准备:进入正确的文件夹(parallel_agents)并运行adk web
    • 请求:“请获取我电脑的统计数据”。
    • 执行流程
      • 并行执行:CPU、内存和磁盘代理几乎同时开始工作。Brandon指出,在ADK Web界面中,可以看到所有工具几乎同时被调用,这展示了并行的“速度优势”,因为无需等待一个代理完成后再启动下一个。他甚至提到,“代理2可能在代理1之前完成”,因为并行执行不保证顺序。
      • 状态存储:每个并行代理都将其结果保存到共享状态中。
      • 报告合成:一旦所有并行任务完成,System Report Synthesizer作为顺序工作流的第二步,会从状态中访问所有收集到的CPU、内存和磁盘信息。它会将这些信息作为提示的一部分,传递给LLM,从而生成一份“超级漂亮的报告”。这份报告包含了计算机的详细统计数据。

个人感受
Brandon对并行代理的“速度”和“效率”感到非常兴奋,尤其是在看到多个任务几乎同时完成时,他感叹“太快了,很难跟上”。他对这种混合工作流(顺序嵌套并行)的灵活性和强大功能赞不绝口,认为它让开发者能够“做任何想做的事情”。

精华收获
并行代理是实现AI工作流“速度与效率”的关键,它允许多个任务同时执行,尤其适用于那些没有严格依赖关系的独立数据收集或处理任务。核心模式是并行执行、将结果保存到共享状态,再由一个聚合代理进行最终的综合报告生成。这种设计使得ADK能够构建出响应迅速、功能全面的复杂系统,并通过灵活组合顺序和并行工作流,满足更广泛的应用场景。

12. 工作流:循环代理(Loop Agents) [01:27:00-01:33:00]

核心观点
循环代理(Loop Agents)是ADK中最强大的工作流工具之一,它允许智能体“反复迭代”一个问题,直到达到预设的最大迭代次数或满足特定条件。这种“推理与行动”(React)模式使得智能体能够通过多次尝试和改进,逐步逼近或达成所需输出,是解决复杂或需要精炼的任务的理想选择。

深度阐述
Brandon将循环代理称为“最强大的工作流工具之一”,并将其与Crew AI和Langchain中的“推理与行动”(React)模式进行类比。

  1. 工作原理与特性(How It Works & Characteristics)

    • 本质:循环代理本质上是“加强版的顺序代理”。
    • 循环退出条件:循环代理将“持续运行”,直到满足以下两个条件之一:
      • 达到最大迭代次数(Max Iterations):例如,“只尝试解决这个问题五次”。
      • 满足特定条件(Specific Condition):例如,“请继续搜索互联网,直到找到五份可用于我的报告的资源”。
    • 迭代过程:在循环内部,子智能体将按照预设的顺序(Agent 1 -> Agent 2 -> …)反复执行,直到满足退出条件。
    • 适用场景:适用于需要“多次尝试和改进”才能达到理想结果的场景。例如,生成图像直到图像中的物体数量符合要求。
  2. 代码示例:LinkedIn帖子生成与优化(LinkedIn Post Generation and Refinement)

    • 核心目标:生成一个高质量的LinkedIn帖子,并根据反馈进行多轮优化,直到帖子符合所有要求。
    • 整体工作流:这是一个包含顺序工作流和循环工作流的复合示例。
      • 第一部分(顺序)Initial Post Generator(初始帖子生成器)。
        • 功能:根据用户要求生成LinkedIn帖子的“初始草稿”。
        • 指令:明确帖子主题(ADK教程)、内容要求(涵盖教程亮点)、风格要求(无表情符号、无话题标签)、以及输出格式(只返回帖子内容,无额外评论或格式标记)。
        • 状态存储:将生成的帖子保存到状态中的current_post键下。
      • 第二部分(循环)Loop Agent。包含两个子智能体,它们将循环工作:
        1. Post Reviewer(帖子评审器)
          • 功能:评审current_post是否符合要求。
          • 指令
            • 字符数检查:使用character_count工具检查帖子长度是否在1000到1500字符之间。如果不在范围内,要求进行另一次迭代。
            • 内容与风格检查:验证帖子是否提及Brandon的名字、是否有明确的行动号召、是否表达真诚的兴奋,以及是否符合无表情符号、无话题标签的风格要求。
            • 反馈机制:如果任何要求不通过,返回“简洁的指令”说明哪里出了问题。
            • 退出循环最关键的指令:如果所有要求都满足,“调用exit_loop函数”。
          • character_count工具:此工具接收文本和tool_context。它计算文本长度,如果过短或过长,返回失败消息,说明需要添加或删除多少字符,并更新review_status为“fail”。如果符合长度要求,则返回“pass”。
          • exit_loop函数:这是退出循环的“特殊情况”。它通过tool_context.actions.escalate()实现。escalate()会“退出当前循环”,然后函数返回None
        2. Post Refiner(帖子精炼器)
          • 功能:根据评审器的反馈,修改并改进LinkedIn帖子。
          • 指令:从状态中读取current_post和评审器提供的review_feedback。然后“恰当地应用反馈”来改进帖子,确保改动不大,并再次遵循风格要求。
          • 状态更新:精炼后的帖子会再次保存回状态中的current_post键下,以便在下一次循环中被评审器读取。
  3. 运行示例(Running the Demo)

    • 准备:进入正确的文件夹(loop_agents)并运行adk web
    • 生成初始帖子:“请生成一个帖子,说这是我看过的最好的ADK教程”。
    • 循环优化过程
      • 初始草稿Initial Post Generator生成第一版帖子。
      • 第一次评审与精炼
        • Post Reviewer触发,使用character_count工具检查长度。发现帖子“太短”,需要添加更多细节。
        • Post Refiner接收反馈,生成一个“更长”的帖子。
      • 第二次评审与精炼
        • Post Reviewer再次检查。发现帖子“太长”,需要移除几乎一半的字符。
        • Post Refiner接收反馈,生成一个“好得多”的帖子。
      • 第三次评审与退出
        • Post Reviewer再次检查。这次,字符数符合要求,并且内容和风格要求也都通过了。
        • 调用exit_loop:由于所有条件满足,Post Reviewer调用exit_loop函数,导致循环终止。
      • 最终状态:在ADK Web界面的“State”中,可以看到最终优化后的帖子,它符合所有字符数和内容/风格要求。

个人感受
Brandon对循环代理的强大功能感到“超级兴奋”,将其视为“最强大的功能”之一,因为它让智能体能够“不断地思考一个问题并反复处理它,直到得到一个答案”。他详细地解释了循环退出的两种方式,特别是exit_loop的机制,让复杂概念变得清晰。在演示中,当智能体通过多次迭代最终生成完美的LinkedIn帖子时,他显得非常满意。

精华收获
循环代理是ADK中实现AI“精益求精”能力的关键,它使智能体能够通过“反复迭代”解决复杂问题,直到达到预设条件或最大尝试次数。核心机制在于子智能体在循环中读写共享状态,并根据特定条件(如所有要求都满足)调用exit_loop函数来优雅地退出循环。这种模式为需要多次思考、反馈和改进的任务提供了强大的自动化解决方案。


精华收获(总体概括)

Brandon的ADK速成课程全面且深入地揭示了Agent Development Kit的强大功能和灵活性。从最基础的个体智能体构建到复杂的自动化工作流,课程的核心洞察和实用建议贯穿始终:

  1. AI智能体是未来自动化的核心:ADK提供了一套结构清晰、易于上手的框架,帮助开发者快速构建AI智能体,自动化复杂工作流,并将其集成到应用程序中。
  2. 根智能体与核心属性是基石:理解根智能体的作用及其核心属性(名称、模型、描述、指令)是入门的关键,它们定义了智能体的基本行为和功能。
  3. 工具是智能体的“超级能力”:通过集成自定义函数、内置工具(如Google Search)和第三方工具,智能体能够超越LLM本身的限制,执行实时检索、数据操作等复杂任务。但需注意内置工具与自定义工具共存的限制和工具返回结果的详细性最佳实践。
  4. 会话与状态是记忆和智能的载体:会话封装了智能体的记忆(状态和事件历史),使智能体能够进行有上下文的连贯对话。通过output_keytool_context对共享状态的读写操作,智能体能记住并利用历史信息做出智能决策。
  5. 会话持久化是构建有记忆应用的基石:将状态保存到数据库(本地或云端)是实现智能体记忆持久化的关键,确保用户数据在应用程序重启后依然存在,提供无缝的用户体验。
  6. 多智能体系统核心在于“委托”与“共享状态”:ADK的多智能体系统通过根智能体的“委托”机制,将任务分派给最专业的子智能体。通过共享状态,不同智能体能够协同工作,访问和修改共同信息,实现更复杂的业务逻辑和动态响应。
  7. 回调函数提供“完全控制”:六种不同类型的回调函数(Agent、Model、Tool的前/后回调)提供了在智能体生命周期中注入自定义逻辑的强大能力。它们可用于状态设置、请求验证、响应过滤/格式化等,是构建健壮、安全和可控智能体的关键。
  8. 工作流是编排智能体行为的利器
    • 顺序代理 (Sequential Agents):适用于有明确步骤依赖的任务,确保任务按序完成,需通过共享状态显式传递信息。
    • 并行代理 (Parallel Agents):专注于速度和效率,允许多个独立任务同时执行,并通过最终代理聚合结果。
    • 循环代理 (Loop Agents):最强大的工作流工具之一,使智能体能反复迭代,通过多次尝试和改进来解决复杂问题,直到满足特定条件或达到最大迭代次数,并能通过exit_loop优雅地退出。

通过掌握ADK的这些核心概念和实践技巧,开发者将能够构建出高度智能、自动化且具备记忆能力的AI应用,从而在AI Agent的开发之旅中从初学者迈向专业级水平。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐