ADK Python 示例注入指南:用 ExampleTool 为 Agent 构建动态 Few-shot 学习
2026/9/14 5:13:54 网站建设 项目流程

ADK Python 示例注入指南:用 ExampleTool 为 Agent 构建动态 Few-shot 学习

【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python

ADK Python 的ExampleBaseExampleProviderExampleTool三件套,用于把「输入-输出」示例以系统指令文本的形式注入到每个回合的 LLM 请求中,让模型在真实调用前先掌握正确的回答形态。本文以 Example and ExampleTool 官方指南 为主线,结合源码与仓库示例,讲解静态示例列表、按查询动态选取示例、与上下文缓存的相互作用,以及 YAML 配置、A2A Agent 卡片发布等进阶用法。

为什么需要独立的示例注入机制

如果示例只是一小段固定文本,直接拼进instruction字符串就足够了。ADK 专门提供这套包,是为了覆盖两种固定字符串解决不了的场景:

  1. 示例是结构化数据而非散文:示例中要包含工具调用(function call)和工具响应(function response)。这些内容需要 ADK 按目标模型期望的格式渲染,手工拼字符串容易出错且难以跨模型复用。
  2. 示例取决于当前回合的用户提问:合适的示例必须根据「这一轮用户问了什么」动态查询,而不是构建 Agent 时一次性写死。

整套机制由三个部件组成(定义见 src/google/adk/examples/ 与 src/google/adk/tools/example_tool.py):

  • Example:一个 Pydantic 模型,只有两个字段——input: types.Content(一轮用户输入)和output: list[types.Content](应当紧随其后的模型回合)。定义见 example.py。
  • BaseExampleProvider:一个单方法抽象接口get_examples(query: str) -> list[Example],负责按查询取回示例。ADK 自带一个实现VertexAiExampleStore。定义见 base_example_provider.py 与 vertex_ai_example_store.py。
  • ExampleTool:把以上两者接到请求管线上的「接线员」。它是一个不向模型声明任何 function 的BaseTool,唯一职责是在请求发出前改写请求。

关键事实:LlmAgent没有examples=参数

在 ADK Python 中,LlmAgent不存在examples=字段。示例要到达 Agent,唯一途径是把ExampleTool放进它的tools列表。如果你正在找一个字段却找不到,原因就在这里。这一点可以从源码得到印证:example_tool.py 中ExampleTool继承自BaseTool,而Agenttools参数接受BaseTool实例。

与上下文缓存(context cache)的相互作用

添加ExampleTool会改变系统指令,而系统指令正是上下文缓存命中的关键依据。该工具在每个回合都会把渲染好的示例块追加到系统指令末尾,因此:

  • 固定列表:每个回合渲染出的字符串完全一致,缓存前缀始终匹配,缓存持续生效。
  • 编辑列表:下一次运行会生成新的系统指令,缓存失效,重新计费。
  • Provider 动态选取:这是最需要警惕的情况——Provider 根据当前用户查询重建示例块,一旦选中的示例与构建缓存时不同,系统指令就与缓存不匹配,该回合需要为整个前缀支付全价,而不是缓存价。

缓存的开关通过App.context_cache_config配置,详见 App 指南。

快速开始:在代码中构建示例

最基本的用法是在代码里构建示例列表,传给tools中的ExampleTool

from google.adk.agents import Agent from google.adk.examples import Example from google.adk.tools import ExampleTool from google.genai import types example_tool = ExampleTool([ Example( input=types.UserContent(parts=[types.Part(text="Where is order 4417?")]), output=[ types.ModelContent( parts=[types.Part.from_function_call( name="check_order_status", args={"order_id": "4417"} )] ), types.ModelContent( parts=[types.Part(text="Order 4417 shipped on Tuesday and arrives Friday.")] ), ], ), Example( input=types.UserContent(parts=[types.Part(text="I want a refund.")]), output=[ types.ModelContent( parts=[types.Part(text="Sure — which order number is that for?")] ) ], ), ]) agent = Agent( name="support_agent", instruction="Help the user with their order.", tools=[check_order_status, issue_refund, example_tool], )

注意:这些示例不是模型可以调用的工具。它们以系统指令文本的形式,在每一回合与你的instruction一起送达模型。

字典简写形式

当示例全是纯文本时,ExampleTool也接受普通字典,并通过TypeAdapter(list[Example])校验转换为Example对象(见 example_tool.py 的validate_python调用)。代码更短:

example_tool = ExampleTool([ { "input": {"role": "user", "parts": [{"text": "Is 7 a prime number?"}]}, "output": [{"role": "model", "parts": [{"text": "Yes, 7 is a prime number."}]}], }, ])

推荐:从代码内列表开始

建议优先使用代码内静态列表。这是仓库中所有示例采用的路线,也是 A2A Agent 卡片唯一能发布的路线,并且运行时零开销。只有当下述情况成立时才考虑 Provider:示例必须按查询逐个挑选——意味着存储规模达数千条,或示例内容需要在不重新部署的情况下变更。

工作原理:每个回合发生了什么

ExampleTool的全部工作发生在一个每回合都会执行的钩子里,唯一的区别只是示例来自你构建的列表还是每次询问的 Provider。

它实现了process_llm_request——这是BaseTool提供的、在请求构建完成后、发送之前运行的钩子(源码见 example_tool.py)。每个回合它会:

  1. 读取tool_context.user_content.parts[0].text,即当前用户消息的文本。如果没有 parts,或第一个 part 不是文本,直接返回、不追加任何内容。
  2. 解析示例:列表原样使用;BaseExampleProvider则用这段文本调用get_examples(query)
  3. 把示例渲染成一个字符串,追加到请求的系统指令中。

渲染后的块带有明确的分隔标记和自描述文本,模型可以区分示例与你的指令:

<EXAMPLES> Begin few-shot The following are examples of user queries and model responses using the available tools. EXAMPLE 1: Begin example [user] Where is order 4417? [model] ``` check_order_status(order_id='4417') ``` Order 4417 shipped on Tuesday and arrives Friday. End example End few-shot <EXAMPLES>

渲染细节与模型分支

渲染逻辑实现在 example_util.py 的convert_examples_to_text中:

  • 函数调用渲染为 Python 风格调用语法(字符串参数加引号,其余按字面量),函数响应渲染为 dict 形式(源码中实际是part.function_response.__dict__的字符串化结果,example_util.py)。
  • 代码围栏(fence)取决于请求中的模型名:模型名包含gemini-2,或没有模型名,使用普通的三反引号围栏;其他任何模型名使用```tool_code```tool_outputs围栏。这个判定是 Gemini 1.5 到 2.0 过渡时期写的,之后未更新——Gemini 3 模型会走 pre-2.0 分支(example_util.py 中的gemini2 = model is None or "gemini-2" in model)。

从工具角度看,ExampleTool不声明任何 function,因此它永远不会出现在模型的工具列表中、永不可被调用、也永远不会产生 function response——它只是在tools中占一个槽位,别无他用。

Provider 路由

BaseExampleProvider在异步请求路径内部每回合同步调用一次,以用户文本作为查询。get_examples里的任何耗时操作都会阻塞本次调用,而且没有缓存、没有超时、没有错误处理——异常会直接向上传播,导致该回合失败。

VertexAiExampleStore是对 Vertex AI Example Store 的实现。构造时传入 store 资源名,每次调用对该查询文本做相似度检索,丢弃相似度低于0.5的结果,把剩余结果转换为Example对象。top_k=10与 0.5 阈值都是硬编码常量(见 vertex_ai_example_store.py 中的请求构造与过滤逻辑):

example_tool = ExampleTool( VertexAiExampleStore( "projects/my-project/locations/us-central1/exampleStores/my-store" ) )

该类在未安装任何 Vertex AI 包时也能正常 import——它的依赖在get_examples内部才 import(vertex_ai_example_store.py),因此缺失安装会在第一个回合ModuleNotFoundError形式暴露,而不是在构造时。

配置参数

ExampleTool只接受一个参数,位置传参或examples=关键字均可:

OptionTypeDefaultDescription
exampleslist[Example] \| BaseExampleProviderrequired示例列表,或按查询取示例的 Provider。
  • 列表通过TypeAdapter(list[Example])校验,形状正确的字典会被接受并转换;
  • Provider 实例原样存储,每回合被查询;
  • 工具的namedescription被固定为"example_tool""example tool"(example_tool.py),因为它们永远不会被发送给模型,工具本身不向模型声明。

Example恰好只有两个字段:

OptionTypeDefaultDescription
inputtypes.Contentrequired示例演示的用户回合。
outputlist[types.Content]required应当紧随其后的回合。

output是列表,因为一次完整交互往往需要多个回合:先是一次函数调用,再是使用其结果的回答。请给每个Content都带上role,因为渲染器根据它切换[user]/[model]前缀;types.UserContenttypes.ModelContent已经替你设好了 role。

进阶应用

代码内列表已经覆盖大多数 Agent。以下小节分别处理它覆盖不到的场景。

不用 Vertex Store 也能按查询选示例

当示例按意图分组成几十条时,值得做筛选——每回合全量发送会消耗上下文,并稀释真正匹配那几条示例的信号。实现你自己的BaseExampleProvider即可。该方法同步执行且只收到原始用户文本,请保持为内存内选择,不要做网络调用

class IntentExampleProvider(BaseExampleProvider): def __init__(self, examples_by_intent: dict[str, list[Example]]): self._examples_by_intent = examples_by_intent def get_examples(self, query: str) -> list[Example]: for intent, examples in self._examples_by_intent.items(): if intent in query.lower(): return examples return []

返回空列表是安全的。块仍然会被追加——只含头部和尾部、中间没有示例——模型会看到一段略显奇怪但无害的前缀。

在 Agent 配置文件(YAML)中声明示例

用 YAML 而非 Python 定义的 Agent 同样能获得示例。ExampleTool.from_config(example_tool.py)接受两种形式:

  • 内联示例列表:直接在配置中写list[Example]
  • Provider 全限定名:一个字符串,指向你代码中定义的BaseExampleProvider实例。

校验规则:名字无法解析时抛出ValueError;解析成功但对象不是BaseExampleProvider时抛出ToolExecutionError(错误类型为BAD_REQUEST,见 example_tool.py)。

在 A2A Agent 卡片上发布示例

远程调用方在调用前往往想知道你的 Agent 接受什么样的输入,而示例是表达这一点最清晰的方式,且无需额外配置:Agent 卡片构建器会在 Agent 的工具列表中查找ExampleTool,把它的示例复制进卡片的 skill examples。只有列表形式会被发布;Provider 会被跳过并打一条 debug 日志,因为构建器没有查询可用来调用它(见 agent_card_builder.py,其中_convert_example_tool_examples明确跳过动态 Provider)。

限制与注意事项

  • 非文本回合下工具静默无效:音频、图片或空的用户内容意味着第一个 part 没有textprocess_llm_request直接返回、不追加任何内容,也不记录任何警告。在语音或多模态 Agent 中,示例可能实际上永远不会生效。
  • Gemini 3 上的围栏启发式判断失效:渲染器只检测模型名中是否含"gemini-2",因此 Gemini 3 模型会收到本为 Gemini 1.5 设计的```tool_code格式。
  • 渲染出的函数响应包含空字段:响应 part 被逐字段字符串化,未设置的 genai 字段会以{'will_continue': None, 'scheduling': None, 'parts': None, 'id': None, 'name': ..., 'response': ...}形式出现在提示词中,模型需要略过这些噪音。
  • Provider 每个回合都被调用:同步、无缓存。成本按回合计而非按会话计;且因为它们渲染的块进入系统指令,返回不同示例的 Provider 也会使该回合的上下文缓存失效。
  • VertexAiExampleStore不可配置:10 条结果上限与 0.5 相似度下限是源码中的常量。
  • 示例是追加而非合并:一个 Agent 添加两个ExampleTool,会产生两个独立的<EXAMPLES>块,而不是合并成一组。
  • 无法从 Agent 侧查看渲染后的块:要检查模型实际收到什么,请直接调用google.adk.examples.example_util.convert_examples_to_text(examples, model)(该函数的完整实现与常量前缀见 example_util.py)。

仓库中的相关示例

  • hello_world_ma 的 agent.py:多 Agent 架构示例,根 Agent 携带一个由Example对象(UserContent+ModelContent)构建的ExampleTool,演示掷骰子与素数判断两个子 Agent 的委派流程。
  • a2a_basic 的 agent.py:同样的示例换成字典形式,Agent 通过 A2A 对外提供服务,因此这些示例最终也会出现在 Agent 卡片上。

相关指南

  • BaseTool:覆盖了ExampleTool所依赖的process_llm_request钩子,以及如何编写另一个只改写请求的工具。
  • AgentCardBuilder:列表形式示例如何在 A2A Agent 卡片中呈现,对应实现见 agent_card_builder.py。
  • App 指南:上下文缓存配置(App.context_cache_config)与示例注入的联动。

【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询