第 7 章 Google ADKLiteLlmOllama

第 7 章 模型中立、开放生态与协议标准

第 7 章 模型中立、开放生态与协议标准

前六章我们一直在 ADK 这个"框架内部"工作。这一章,我们把镜头拉远,看 ADK 怎么和整个世界打交道。三个问题贯穿全章:ADK 能用哪些模型?ADK 怎么接入外部工具?ADK 怎么和别的 Agent 对话?答案分别是——模型中立、MCP、A2A。它们共同构成了 ADK 的开放生态。

7.1 模型中立:一个框架,所有模型

7.1.1 三种接入机制

ADK 的模型接入有三种机制,覆盖从"Google 深度集成"到"任意第三方"的全谱系:

机制一:直接字符串 / 注册表(Direct String / Registry)

针对与 Google Cloud 深度集成的模型——比如经 Google AI Studio 或 Agent Platform 访问的 Gemini、Claude。你只需要提供模型名(如 'gemini-flash-latest'),ADK 内部注册表会把它解析为对应的后端客户端。

from google.adk.agents import LlmAgent

# 直接用模型名字符串,注册表自动解析
agent_gemini = LlmAgent(
    model="gemini-flash-latest",
    name="gemini_flash_agent",
    instruction="You are a fast and helpful Gemini assistant.",
)

这是最省事的方式,适合 Gemini 用户。认证用 .env 里的 GOOGLE_API_KEY

机制二:模型连接器(Model Connectors)

针对 Google 生态之外的模型,或需要特定客户端配置的模型。你实例化一个包装类(如 LiteLlmApigeeLlm),把它作为 model 参数传给 LlmAgent。这是本节的重点。

机制三:模型路由(Model Routing)

在运行时用路由函数在多个模型之间动态选择,出错时自动故障转移(failover)。见 7.1.4。

机制 适用模型 代码形态
注册表 Gemini、Agent Platform 上的 Claude model="gemini-flash-latest"
连接器 OpenAI、Anthropic、Ollama、vLLM、本地 model=LiteLlm(model="openai/gpt-4o")
路由 任意多个模型,按需动态选 model=RoutedLlm(...)

7.1.2 LiteLlm:接 OpenAI、Anthropic 和 100+ 模型

LiteLLM 是一个 Python 库,充当"模型与模型托管服务的翻译层",向 100+ 个大模型提供标准化、OpenAI 兼容的接口。ADK 通过 LiteLLM 集成,可以访问 OpenAI、Anthropic、Ollama、Mistral、DeepSeek、Cohere 等提供方的模型。

安装(注意版本要求):

pip install "litellm>=1.84"

配置 API Key 作为环境变量:

export OPENAI_API_KEY="YOUR_OPENAI_API_KEY"
export ANTHROPIC_API_KEY="YOUR_ANTHROPIC_API_KEY"

准确 import 路径(这里容易写错,注意是 lite_llm 不是 litellm):

from google.adk.models.lite_llm import LiteLlm

用 LiteLlm 接 OpenAI 的 GPT-4o:

from google.adk.agents import LlmAgent
from google.adk.models.lite_llm import LiteLlm

agent_openai = LlmAgent(
    model=LiteLlm(model="openai/gpt-4o"),
    name="openai_agent",
    instruction="You are a helpful assistant powered by GPT-4o.",
)

接 Anthropic 的 Claude:

agent_claude = LlmAgent(
    model=LiteLlm(model="anthropic/claude-3-haiku-20240307"),
    name="claude_direct_agent",
    instruction="You are an assistant powered by Claude Haiku.",
)

关键点:模型字符串采用 LiteLLM 的 provider/model 格式("openai/gpt-4o""anthropic/claude-3-haiku-20240307")。这个统一的字符串格式,就是"模型中立"的底层机制——换模型,只是换一个字符串

补充一个细节:通过 LiteLlm 连接器使用 Anthropic Claude 时,ADK 自动支持其"思考块"(thinking blocks)特性——它会自动提取思考块及其签名,并在每次出站请求时重建,从而跨工具调用和多轮对话保留 Claude 的推理,无需你管理任何自定义状态。

安全提示:文档包含一则真实的安全公告——LiteLLM 1.82.7/1.82.8 曾在 PyPI 上被检出未经授权的代码(2026 年 3 月供应链事件)。使用 ADK Python 的用户应升级到最新版;此期间安装过 LiteLLM 的需轮换密钥。这提醒我们:第三方依赖也是供应链安全的一部分。

7.1.3 Ollama 本地模型:零成本跑起来

如果你没有云模型 API Key,或者想完全本地跑,Ollama 是最方便的选择。ADK 通过 LiteLLM 连接器接入 Ollama。

先装好 Ollama 并拉一个带工具能力的模型(比如 Gemma 3):

ollama pull gemma3

然后配置环境变量指向本地 Ollama 服务:

export OLLAMA_API_BASE="http://localhost:11434"

写 Agent:

from google.adk.agents import Agent
from google.adk.models.lite_llm import LiteLlm

root_agent = Agent(
    model=LiteLlm(model="ollama_chat/gemma3:latest"),
    name="local_agent",
    description="A local agent that can roll dice.",
    instruction="You roll dice and answer questions about the outcome of the dice rolls.",
    tools=[roll_die, check_prime],
)

三个必须记住的坑(官方文档明确警告):

  1. 必须用 ollama_chat 而不是 ollama。用 ollama 可能导致无限工具调用循环忽略之前上下文等意外行为。
  2. 务必设置 OLLAMA_API_BASE。虽然 LiteLLM 的 api_base 参数可用于生成,但自某版本起库依赖环境变量处理其他 API 调用。
  3. 选带工具支持的模型。依赖工具时,必须选 Ollama 上带工具能力的模型。用 ollama show mistral-small3.1 检查 Capabilities 下是否列出 tools

如果 Ollama 模型没有原生工具支持,还有一个替代方案:用 openai provider 对接 Ollama 的 OpenAI 兼容端点:

export OPENAI_API_BASE=http://localhost:11434/v1
export OPENAI_API_KEY=anything
root_agent = Agent(
    model=LiteLlm(model="openai/mistral-small3.1"),
    ...
)

7.1.4 模型路由:运行时动态选模型 + 故障转移

有些场景,一个 Agent 需要在运行时动态选择模型。ADK 的模型路由(Model Routing)正是为此设计,它支持:

  • 故障转移:主模型挂了自动切到备选模型
  • A/B 测试:按比例把请求分给不同模型
  • 按复杂度路由:简单请求用便宜模型,复杂请求用强模型

RoutedLlm 是核心 API。路由函数在 errorContext(记录失败模型)的配合下决定下一个模型:

from google.adk.agents import LlmAgent
from google.adk.models import RoutedLlm, Gemini

primary_model = Gemini(model="gemini-flash-latest")
fallback_model = Gemini(model="gemini-pro-latest")

def router(models, request, error_context=None):
    if not error_context:
        return "primary"  # 先试主模型
    if "primary" in error_context.failed_keys:
        return "fallback"  # 主模型失败,切备选
    return None  # 没有更多选项,传播错误

routed_llm = RoutedLlm(
    models={"primary": primary_model, "fallback": fallback_model},
    router=router,
)

agent = LlmAgent(
    name="my_agent",
    model=routed_llm,
    instruction="You are a helpful assistant.",
)

路由的故障转移规则很清晰:

  • 路由函数首次调用不带 errorContext,做初始选择
  • 若所选模型在产出任何响应之前失败,则带着 errorContext(含 failedKeyslastError)再次调用
  • 已尝试过的模型不能被重新选择;返回 undefined 时停止重试并传播错误

注意:模型路由目前是实验性特性,官方文档以 TypeScript 呈现为主。如果你需要同时切换 instructions、tools 或子 Agent,用 Agent 级路由(RoutedAgent)而不是 RoutedLlm

7.1.5 vLLM:自托管高吞吐模型

如果你的团队要自托管开源模型做高吞吐推理,vLLM 是常见选择。vLLM 把模型托管为 OpenAI 兼容的 API 端点,ADK 通过 LiteLlm 接入:

agent_vllm = LlmAgent(
    model=LiteLlm(
        model="your-model-name",
        api_base="https://your-vllm-endpoint",
        extra_headers=auth_headers,
    ),
    name="vllm_agent",
    instruction="You are a helpful assistant running on a self-hosted vLLM endpoint.",
)

注意:部署 vLLM 时必须开启 OpenAI 兼容的工具/函数调用(如 --enable-auto-tool-choice),否则 ADK 的工具无法工作。

7.1.6 主线项目:把云销客服切到本地模型

验证一下"模型中立"。把第 2 章的云销客服从 Gemini 切到本地 Ollama,只需要改一行

from google.adk.models.lite_llm import LiteLlm

yunxiao_agent = Agent(
    model=LiteLlm(model="ollama_chat/gemma3:latest"),  # 只改这一行
    name="yunxiao_cs_agent",
    ...
)

工具、指令、Session 全部不变。这就是"代码零改动换模型"——模型中立不是口号,是架构

7.2 技能(Skills)深入:可复用的能力包

第 3 章我们入门了 Skills。这一节深入它的完整设计。

7.2.1 为什么 Skills 比 Tools 更高层

Skill 是 ADK agent 可用于执行特定任务的自包含功能单元,封装了任务所需的指令、资源和工具,基于 Agent Skill 规范(agentskills.io)

它和 Tool 的区别在于:

  • Tool:一个可执行函数,只有"做什么"
  • Skill:指令 + 资源 + 工具 的打包,同时告诉 Agent"做什么"和"怎么做"

Skill 最大的卖点是增量加载——L1 只用于发现,L2 在触发时才加载,L3 资源按需读取。这最小化了对 Agent 上下文窗口的占用。

7.2.2 三层结构

my_agent/
    agent.py
    .env
    skills/
        example-skill/          # Skill
            SKILL.md            # main instructions (required)
            references/         # 附加 Markdown 参考
                REFERENCE.md
            assets/             # 模板、图片、数据
            scripts/            # 可执行脚本
                *.py
  • L1 元数据SKILL.md 的 frontmatter(namedescription),用于技能发现。name ≤64 字符、小写 kebab-case;description 非空、≤1024 字符。
  • L2 指令SKILL.md 正文,Agent 触发技能时加载。
  • L3 资源references/(附加文档)、assets/(素材)、scripts/(脚本),按需加载。

整个目录只有 SKILL.md 是必需文件

7.2.3 加载与挂载

从文件系统加载:

import pathlib
from google.adk.skills import load_skill_from_dir
from google.adk.tools import skill_toolset

weather_skill = load_skill_from_dir(
    pathlib.Path(__file__).parent / "skills" / "weather_skill"
)

my_skill_toolset = skill_toolset.SkillToolset(
    skills=[weather_skill],
    additional_tools=[get_weather_tool],  # 可附带额外工具
)

root_agent = Agent(
    model="gemini-flash-latest",
    name="skill_user_agent",
    description="An agent that can use specialized skills.",
    instruction="You are a helpful assistant that can leverage skills to perform tasks.",
    tools=[my_skill_toolset],
)

SkillToolset 会给 Agent 注入一套默认系统指令,规定它如何与 Skill 交互:

  • 必须先用 load_skill 工具读取技能的指令才能使用它
  • 必须严格遵循技能定义中的指令
  • 必须用 load_skill_resource 查看技能目录内的文件
  • 必须用 run_skill_script 运行 scripts/ 目录中的脚本

7.2.4 代码内联定义 Skill

除了从文件系统加载,还可以在代码里定义 Skill(内联 Skill):

from google.adk.skills import models

greeting_skill = models.Skill(
    frontmatter=models.Frontmatter(
        name="greeting-skill",
        description="A friendly greeting skill that can say hello to a specific person.",
    ),
    instructions=(
        "Step 1: Read the 'references/hello_world.txt' file to understand how"
        " to greet the user. Step 2: Return a greeting based on the reference."
    ),
    resources=models.Resources(
        references={
            "hello_world.txt": "Hello! So glad to have you here!",
        },
    ),
)

内联定义的价值:Source 接口可被任意数据存储(如数据库)支撑,支持实时更新和个性化等动态用例——技能可以"动态生成",而不只是静态文件。

7.2.5 Skills 与 MCP 的对比

Skills 和 MCP 都是"能力打包",但有本质区别:

维度 Skills MCP 工具
粒度 指令 + 资源 + 工具 工具
加载方式 增量加载(省上下文) 一次性工具清单
面向 Agent 内部能力组织 Agent 外部工具接入
规范 agentskills.io modelcontextprotocol.io

Skills 管"内部组织",MCP 管"外部连接",两者互补。

7.3 协议标准:连接一切

7.3.1 协议全景图

ADK 的开放生态建立在几大协议之上。这一节我们逐个讲清楚:

协议 全称 解决什么问题
MCP Model Context Protocol Agent 接入外部工具 工具层
A2A Agent2Agent Agent 与 Agent 通信 Agent 间
Grounding Agent 接入实时/私有知识 知识层
AGENTS.md 给编码 Agent 的项目说明 项目上下文
AG-UI Agent-User Interaction Agent 与前端界面交互 人机界面

7.3.2 MCP:Agent 接入整个工具生态

MCP(Model Context Protocol) 是一个开放标准,用于标准化 LLM 与外部应用、数据源、工具之间的通信。它采用客户端-服务器架构,定义了数据(resources)、交互模板(prompts)、可执行函数(tools)如何被 MCP server 暴露、被 MCP client 消费。

ADK 在 MCP 生态里扮演双重角色

角色一:作为 MCP 客户端,消费外部 MCP server 的工具。

这是最常见的模式。ADK agent 通过 McpToolset 使用外部 MCP server 提供的工具。第 3 章已经演示过 stdio 连接,这里再展示远程 HTTP 连接:

import os
from google.adk.agents.llm_agent import Agent
from google.adk.tools.mcp_tool import McpToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams

root_agent = Agent(
    model='gemini-flash-latest',
    name='travel_planner_agent',
    description='A helpful assistant for planning travel routes.',
    tools=[
        McpToolset(
            connection_params=StreamableHTTPConnectionParams(
                url="https://your-mcp-server.example.com/mcp",
                headers={"Authorization": "Bearer YOUR_TOKEN"},
            )
        )
    ]
)

McpToolset 初始化时自动完成:建立/管理连接、发现工具(list_tools)、把 MCP 工具的 schema 转成 ADK 兼容的 BaseTool 实例、代理调用。tool_filter 参数可以筛选只暴露部分工具——这是生产环境安全过滤的关键手段。

角色二:作为 MCP server,暴露 ADK 工具给其他客户端。

反过来,你可以把 ADK 工具包装成 MCP server,让任何 MCP 客户端(其他 Agent 框架、应用)都能调用。ADK 使用 FastMCP 处理 MCP 协议细节——"high-level and Pythonic,大多数情况下装饰一个函数就够了"。

7.3.3 A2A:让 Agent 与 Agent 对话

A2A(Agent2Agent) 是让不同 Agent 互相通信的标准协议。它和本地子 Agent 有本质区别:

维度 本地 sub-agent 远程 Agent(A2A)
进程 同一进程 独立服务
通信 内存中,极快 网络
维护 同一团队 可以是不同团队/组织
语言 同语言 可跨语言/跨框架

什么时候该用 A2A:对方是独立部署的服务、由不同团队维护、跨语言/框架、需要强形式化契约。什么时候不该用:纯内部代码组织、性能关键的高频操作、需要共享内存/上下文。

A2A 的三大核心能力:

  • Reasoning:消息在 Agent 间传递时保留模型的推理痕迹
  • Long-Running Tools:跟踪超过标准响应时长的工具调用
  • Artifacts:在 Agent 间传递文件产物

暴露(Exposing):把已有 ADK agent 变成可在网络上接收请求的服务。最简方式:

from google.adk.a2a.utils.agent_to_a2a import to_a2a

a2a_app = to_a2a(root_agent, port=8001)

to_a2a() 自动生成 agent card(描述这个 agent 能力/技能的元数据),经 uvicorn 服务后即可在 .well-known 端点提供。安装依赖 pip install google-adk[a2a]

消费(Consuming):在另一个 agent 中使用 RemoteA2aAgent 调用远程 agent:

from google.adk.agents.remote_a2a_agent import RemoteA2aAgent, AGENT_CARD_WELL_KNOWN_PATH

prime_agent = RemoteA2aAgent(
    name="prime_agent",
    description="Agent that handles checking if numbers are prime.",
    agent_card=(
        f"http://localhost:8001/a2a/check_prime_agent{AGENT_CARD_WELL_KNOWN_PATH}"
    ),
)

然后把它作为 sub-agent 挂进根 agent,和本地子 Agent 一起编排:

root_agent = Agent(
    model="gemini-flash-latest",
    name="root_agent",
    sub_agents=[local_roll_agent, prime_agent],  # 本地 + 远程 混合编排
)

调用远程 agent 就像调用本地工具一样——ADK 把网络通信、认证、数据格式化的全部复杂性都抽象掉了。

7.3.4 Grounding:接入实时与私有知识

Grounding(接地)是将 AI agent 连接到外部信息来源的过程,使响应更准确、最新、可验证,从而减少幻觉。ADK 支持多种 grounding 方式。

Google Search Grounding(实时网络信息):

from google.adk.agents import Agent
from google.adk.tools import google_search

root_agent = Agent(
    name="google_search_agent",
    model="gemini-flash-latest",
    instruction="Answer questions using Google Search when needed. Always cite sources.",
    description="Professional search assistant with Google Search capabilities",
    tools=[google_search]
)

它的数据流是:用户查询 → LLM 决定调用 google_search 工具 → grounding 服务向 Google Search 发查询 → 检索结果注入模型上下文 → 生成带引用的 grounded 响应。响应会带 groundingChunks(模型查阅的网页列表)和 groundingSupports(把回答的句子关联回 chunks)。

Grounding with Search(企业私有文档):

from google.adk.agents import Agent
from google.adk.tools import VertexAiSearchTool

DATASTORE_ID = "projects/YOUR_PROJECT_ID/locations/global/collections/default_collection/dataStores/YOUR_DATASTORE_ID"

root_agent = Agent(
    name="vertex_search_agent",
    model="gemini-flash-latest",
    instruction="Answer questions using Agent Search to find information from internal documents. Always cite sources when available.",
    tools=[VertexAiSearchTool(data_store_id=DATASTORE_ID)]
)

这用于查询组织私有文档和企业数据。注意:这需要连接 Google Cloud 项目认证(GOOGLE_GENAI_USE_ENTERPRISE=TRUE 等),不能用 AI Studio 的 API Key。

7.3.5 AGENTS.md 与 AG-UI:生态的另外两块拼图

AGENTS.md:给编码 Agent 看的项目说明文件,跨工具通用的开放格式(由 Linux 基金会维护,已被 6 万+ 开源项目采用)。它相当于"给 Agent 的 README"——把构建步骤、测试命令、代码约定放在固定位置,让编码 Agent 自动读取。本书的 books/adk/AGENTS.md 就是它的应用。

AG-UI(Agent-User Interaction):由 CopilotKit 提出的协议,目标是"把 Agent 带进前端应用",标准化 Agent 与用户界面交互。

这些协议标准共同构成了 Agent 生态的"通用语言":MCP 让 Agent 用任何工具,A2A 让 Agent 连任何 Agent,AGENTS.md 让 Agent 理解任何项目,AG-UI 让 Agent 出现在任何界面。ADK 对这些协议的支持,就是"开放生态"的具体含义。

主线项目:给云销客服封装技能

这一节把 Skills 用到主线项目。给云销客服封装一个"退款处理技能":

skills/
  refund-handling/
    SKILL.md
    references/
      refund-policy.md

SKILL.md

---
name: refund-handling
description: 处理售后退款咨询和退款申请,包括政策查询、资格判断、流程引导。
---

# 退款处理技能

1. 用户咨询退款时,先加载 references/refund-policy.md 了解当前政策
2. 按政策判断用户订单是否符合退款条件
3. 引导用户提供订单号和商品类别
4. 如需要人工审批(金额超阈值),转交人工

挂载到云销客服:

import pathlib
from google.adk.skills import load_skill_from_dir
from google.adk.tools import skill_toolset

refund_skill = load_skill_from_dir(
    pathlib.Path(__file__).parent / "skills" / "refund-handling"
)

yunxiao_agent = Agent(
    model="gemini-flash-latest",
    name="yunxiao_cs_agent",
    tools=[skill_toolset.SkillToolset(skills=[refund_skill])],
    ...
)

退款处理的知识和流程被封装成了一个可复用的能力包——这正是 Skills 的价值:把 Agent 的专业知识从代码里解放出来,变成可管理、可复用、可跨 Agent 共享的资产

「为什么 ADK 这样设计」:开放协议赢,封闭生态输

这一章讲了很多协议。为什么 ADK 要把自己押在"开放"上?

回顾第 1 章的框架版图:Agent 框架分"官方阵营"和"中立阵营"。官方阵营有个天然诱惑——把开发者绑在自己生态里(只用自己的模型、只用自己的云)。但 ADK 选择了反方向:

  1. 模型中立:不要求你用 Gemini。因为 Google 明白,开发者选框架的第一考量是"不被绑架"。ADK 赌的是"开放协议赢,不是自家模型赢"——只要你用 ADK,哪怕用 Claude,你也在 Google 的生态引力范围内。

  2. 工具中立(MCP):不要求你只用 Google 的工具。MCP 是 Anthropic 提出的协议,ADK 全力支持。因为"支持 MCP"不是支持 Anthropic,而是支持"工具互联"这个必然趋势。

  3. Agent 中立(A2A):不要求你的 Agent 只和 ADK Agent 对话。A2A 让跨框架、跨组织的 Agent 能协作。这是"Agent 互联网"的雏形。

这一整套开放策略,本质是一个判断:Agent 生态的未来不属于任何单一公司,而属于开放的协议。谁先支持开放的协议,谁就在未来的 Agent 互联网里占据中心位置。ADK 的选择是——不做围墙,做连接器。

本章小结

  • 三种模型接入机制:注册表(Gemini 直连)、连接器(LiteLlm 接 100+ 模型)、路由(运行时动态选)
  • LiteLlm 是模型中立的钥匙from google.adk.models.lite_llm import LiteLlmprovider/model 字符串格式,换模型只换字符串
  • Ollama 本地跑:用 ollama_chat 前缀、设 OLLAMA_API_BASE、选带工具支持的模型(三个坑)
  • 模型路由RoutedLlm + 路由函数,支持故障转移、A/B 测试、按复杂度路由
  • Skills 是能力包:指令 + 资源 + 工具,三层结构(L1 元数据/L2 指令/L3 资源),增量加载省上下文
  • MCP 双角色:作为客户端消费外部工具(McpToolset),作为 server 暴露工具(FastMCP)
  • A2A 让 Agent 互联to_a2a 暴露 + RemoteA2aAgent 消费,本地/远程 Agent 混合编排
  • Grounding 接地:Google Search(实时网络)+ VertexAiSearchTool(私有文档),减少幻觉
  • 开放哲学:ADK 赌"开放协议赢",做连接器不做围墙

练习

  1. 换模型实战:把你的云销客服分别用 Gemini、OpenAI(如果有关键)、Ollama 本地跑一遍,对比响应质量和成本。
  2. 接一个 MCP server:找一个公开的 MCP server(比如 GitHub MCP),用 McpToolset 接到你的 Agent,让 Agent 能用 GitHub 工具。
  3. 封装一个技能:给云销客服做一个"物流查询技能"(SKILL.md + references),验证增量加载和指令遵循。
  4. A2A 实验:如果你有第二个 Agent,用 to_a2a 暴露它,再用 RemoteA2aAgent 从主 Agent 调用它,体验跨 Agent 通信。

下一章预告:第 8 章,我们把这些能力送上生产。从本地到云端——Cloud Run、GKE、自有服务器,ADK 的部署路径怎么选,怎么把云销客服真正跑在线上。