TL;DR:结合 adk-python 源码,讨论下 Google Cloud Tech 发布的 5 类 Agent Skill 设计模式。
1. Agent Skill 设计讨论
2026 年 3 月 18 日,Google Cloud Tech 官方推文发布了一篇长文: "5 Agent Skill design patterns every ADK developer should know" ,by @Saboo_Shubham_ 和 @lavinigam。48 小时内收获 99 万+查看、2800+ 点赞。文章提出了五种设计模式:
-
Tool Wrapper:让 Agent 按需加载某个库的最佳实践 -
Generator:用模板驱动结构化文档生成 -
Reviewer:用外部 checklist 对代码进行分级评审 -
Inversion:Agent 先采访你,再执行 -
Pipeline:多步骤工作流 + 检查点

这五个模式确实概括了 SKILL.md 的主流用法。但一个关键问题被忽略了:这些"模式"在 adk-python 的源码中到底实现了多少?哪些有框架级保障,哪些只是靠提示词"许愿"?
2. SkillToolset 的三层按需加载架构
ADK 的 skills 体系围绕 SkillToolset 类构建,实现了一套三层 progressive disclosure(渐进式披露):
|
|
|
|
|
|---|---|---|---|
|
|
list_skills |
|
|
|
|
load_skill |
|
|
|
|
load_skill_resource |
|
|

核心初始化代码(skill_toolset.py):
class SkillToolset(BaseToolset):
def __init__(
self,
skills: list[models.Skill],
*,
code_executor: Optional[BaseCodeExecutor] = None,
script_timeout: int = 300,
additional_tools: list[ToolUnion] | None = None,
):
# 去重校验:同名 skill 直接 raise ValueError
self._skills = {skill.name: skill for skill in skills}
process_llm_request 方法在每次大模型调用前注入 system instruction 和可用 skills 的 XML 摘要:
async def process_llm_request(self, tool_context, llm_request):
# 注入默认 system instruction(告知大模型如何使用 skills)
llm_request.config.system_instruction += _DEFAULT_SKILL_SYSTEM_INSTRUCTION
# 注入 <available_skills> XML(仅 name + description)
llm_request.config.system_instruction += format_skills_as_xml(skills)
这套设计的核心价值在于:Agent 在对话开始时只"看到" skill 的名字和描述(L1),只有当大模型判断需要某个 skill 时才加载完整指令(L2),再按需加载具体资源(L3)。这确实能有效控制上下文消耗。
3. 逐一拆解:5 个 Pattern 的源码

3.1 Tool Wrapper:references/ 按需加载——最扎实的 Pattern ✅
Tool Wrapper 是五个模式中实现最完整的。它的核心是 LoadSkillResourceTool:
# skill_toolset.py - LoadSkillResourceTool.run_async()
async def run_async(self, *, args, tool_context):
path = args["path"]
if path.startswith("references/"):
content = skill.resources.get_reference(rel_path)
elif path.startswith("assets/"):
content = skill.resources.get_asset(rel_path)
elif path.startswith("scripts/"):
content = skill.resources.get_script(rel_path)
当 SKILL.md 中写着 Load 'references/conventions.md',LLM 会调用 load_skill_resource(skill_name="api-expert", path="references/conventions.md"),触发上述代码加载对应文件。
官方示例 contributing/samples/skills_agent/ 中的 weather-skill 完整演示了这个模式:
skills/weather-skill/
├── SKILL.md # 指令:按步骤加载 references 和执行 scripts
├── references/
│ └── weather_info.md # 天气相关的知识文档
└── scripts/
└── get_humidity.py # 可执行脚本
评价:有框架级代码支撑。references/ 的按需加载是 LoadSkillResourceTool 的核心路径,经过测试。企业可以放心使用。
3.2 Generator:assets/ 模板引擎——有基础,缺校验 ✅
Generator 模式在源码中同样由 LoadSkillResourceTool 支撑——path.startswith("assets/") 分发到 get_asset()。SKILL.md 中写 Load 'assets/report-template.md',Agent 就能拿到模板。
但关键差距在于:模板"填充"逻辑完全由大模型执行 instructions 完成。框架不会校验输出是否包含模板中的所有 section,也不会检查格式一致性。
SKILL.md 写的: "Every section in the template must be present"
源码做的: 把 assets/report-template.md 的文本返回给 LLM,然后...就没了
评价:资源加载有代码支撑,但输出质量保障依赖大模型的指令遵循度。对于格式要求严格的企业场景(如合规报告),建议在 skill 外层加 Pydantic 校验。
3.3 Reviewer:checklist 即 references——同一机制的语义变体 ✅
Reviewer 模式在源码层面与 Tool Wrapper 完全共享同一套代码。references/review-checklist.md 被加载的方式和 references/conventions.md 一模一样。
区别仅在于 SKILL.md 的 instructions 赋予了不同语义:
-
Tool Wrapper:「按 conventions 写代码」 -
Reviewer:「按 checklist 逐条评分」
评分逻辑(error/warning/info 分级)、结构化输出(Summary/Findings/Score)——这些全部由 instructions 中的自然语言驱动,源码不参与。
评价:和 Tool Wrapper 一样可靠。但企业若需要稳定的 JSON 格式评审结果,请在 Agent 外层加 output parser。
3.4 Inversion:多轮门控——框架级空白 ⚠️
这是第一个需要警惕的 Pattern。
文章中 Inversion 的核心是"阶段门控":
"DO NOT start building until all phases are complete"
但在 adk-python 源码中,没有任何代码实现阶段状态管理或门控逻辑。我们搜索了整个 skills 模块和 SkillToolset:
-
无 phase或stage状态 -
无 "gate" 或 "checkpoint" 拦截器 -
tool_context.state中唯一与 skill 相关的 key 是_adk_activated_skill_{agent_name},仅记录哪个 skill 被激活,不记录执行到哪个阶段
这意味着 "DO NOT proceed" 的约束完全依赖大模型对自然语言的遵守。实测表明:
-
GPT-4 级别模型在 3-5 轮内通常能遵守 -
但 5-10 轮后注意力稀释,可能跳过阶段 -
上下文窗口越长,风险越大
不过值得注意的是,ADK 的 before_tool_callback 机制提供了一条补救路径:你可以在回调中检查 tool_context.state 中的阶段标记,在大模型试图跳阶段时拦截工具调用。但这需要开发者自行实现,SkillToolset 本身不会自动解析 SKILL.md 中的阶段约束。
评价:SkillToolset 层面仅约定,无保障。但 ADK 的 callback 机制提供了自定义门控的扩展点。在 PoC 演示中可以直接用 SKILL.md 约定,在生产中必须用 before_tool_callback + session state 补位。
3.5 Pipeline:多步检查点——最大的承诺与最弱的保障 ⚠️
Pipeline 是五个 Pattern 中承诺最大("严格多步骤工作流 + 检查点")但实现最弱的。
文章的示例要求:
"Do NOT proceed to Step 3 until the user confirms."
源码中:SkillToolset 本身没有 checkpoint 机制、没有步骤状态机、没有步骤校验。
但 DeepWiki 的交叉验证揭示了一个关键补充:ADK 的 FunctionTool 支持 require_confirmation=True 参数,配合 SequentialAgent 可以实现人机交互检查点。当工具被标记为需要确认时,Runner 会暂停执行并发送确认请求,等用户响应后才继续。这意味着你可以把 Pipeline 的每个步骤拆成独立的 Agent + Tool,用 SequentialAgent 编排,在关键步骤插入 require_confirmation 门控。
但这不是 SkillToolset 的能力,而是 ADK Agent 编排层的能力。 SKILL.md 中写的 "Do NOT proceed to Step 3 until the user confirms" 仍然只是自然语言约定,SkillToolset 不会解析这段话并自动挂载确认门控。
与成熟的工作流编排框架对比(修正版):
|
|
|
|
|
|
|
|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
有
require_confirmation) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
评价:SkillToolset 层面仅约定。但 ADK 自身的 Agent 编排层(SequentialAgent + require_confirmation)已经具备了 Pipeline 的基本能力。务实做法是:不要在单个 SKILL.md 中定义 Pipeline,而是把每个步骤拆成独立 Skill,用 SequentialAgent 编排 + require_confirmation 做门控。
4. 去魅:被高估的三个点
4.1 "Progressive Disclosure" 的隐藏成本
三层按需加载是个好设计,但实现中有性能问题:
-
无缓存:每次 load_skill_resource都从skill.resources读取,同一个 reference 被多次加载时无 LRU 缓存 -
GCS 串行遍历: _list_skills_in_gcs_dir逐个下载每个 skill 的 SKILL.md,10 个 skill 就是 10 次串行网络请求 -
16MB 上限: RunSkillScriptTool将整个 skill 的 references/assets/scripts 打包进执行环境,大型 skill 可能触发_MAX_SKILL_PAYLOAD_BYTES限制
4.2 "实验性" 标记的含义
adk-python 的 src/google/adk/skills/README.md 明确写着:
"This is experimental"
此外:
-
SkillToolset未出现在tools/__init__.py的 lazy mapping 中 -
llms.txt(ADK 的官方能力描述文件)完全没有提及 skills -
skills 模块未出现在 AGENTS.md 的源码结构说明中
这些信号表明:skills 目前在 ADK 中是二等公民。API 可能在后续版本发生 breaking change。
4.3 SKILL.md 的 "描述优先" 陷阱
社区反馈的一个共识:SKILL.md 的 description 字段比 instructions 更重要。因为 description 决定了大模型是否会激活这个 skill——如果 description 写得不够精准,skill 可能永远不会被调用,或者被错误地调用。
这带来一个反直觉的问题:你花 80% 精力写的 instructions 可能白费,因为前置的 description 就没写对。
5. 企业级落地修正建议

5.1 Inversion 的修正:显式状态机
不要依赖 "DO NOT proceed" 的自然语言约束。ADK 原生的 before_tool_callback 是最佳扩展点:
PHASES = ["discovery", "constraints", "synthesis"]
async def phase_gate_callback(tool, args, tool_context):
"""before_tool_callback:在工具执行前检查阶段门控"""
required_phase = args.get("_required_phase")
if not required_phase:
return None # 非门控工具,放行
current = tool_context.state.get("_skill_phase", "discovery")
if PHASES.index(current) < PHASES.index(required_phase):
# 返回字典会直接作为工具结果,阻止实际执行
return {"error": f"阶段 {required_phase} 未解锁。当前:{current}"}
return None # 放行
agent = Agent(
model="gemini-2.5-flash",
tools=[my_skill_toolset],
before_tool_callback=phase_gate_callback,
)
核心思路:利用 ADK 原生的 before_tool_callback 而非继承 SkillToolset,在 tool_context.state 中维护 _skill_phase,每个阶段完成后显式推进。这把"约定"变成了"代码",且不需要侵入 SkillToolset 内部。
5.2 Pipeline 的修正:外部编排 + Skill 组合
有两条路径,根据团队技术栈选择:
路径 A:ADK 原生方案(可优先尝试)
利用 SequentialAgent + require_confirmation 实现 Pipeline,不引入外部框架:
from google.adk import Agent
from google.adk.agents import SequentialAgent
from google.adk.tools import FunctionTool
# 每个步骤是独立 Agent + Skill
step1_agent = Agent(name="parser", tools=[skill_toolset_step1])
step2_agent = Agent(name="docgen", tools=[skill_toolset_step2])
# 关键步骤的工具标记 require_confirmation
confirm_tool = FunctionTool(
func=submit_docstrings,
require_confirmation=True # Runner 会暂停,等用户确认
)
step2_agent_with_gate = Agent(name="docgen_gated", tools=[skill_toolset_step2, confirm_tool])
# SequentialAgent 编排
pipeline = SequentialAgent(
name="doc-pipeline",
sub_agents=[step1_agent, step2_agent_with_gate, step3_agent]
)
路径 B:外部编排框架(复杂 Pipeline 场景)
当 Pipeline 需要条件分支、并行扇出/扇入、跨服务编排时,引入 LangGraph / Prefect / Temporal:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
核心原则不变:ADK Skill 定义知识,编排机制(无论原生还是外部)控制流程。
5.3 缓存与性能修正
在 SkillToolset 外层加缓存,避免重复加载:
from functools import lru_cache
class CachedSkillToolset(SkillToolset):
@lru_cache(maxsize=64)
def _cached_resource(self, skill_name: str, path: str) -> str:
skill = self._get_skill(skill_name)
if path.startswith("references/"):
return skill.resources.get_reference(path.removeprefix("references/"))
elif path.startswith("assets/"):
return skill.resources.get_asset(path.removeprefix("assets/"))
GCS 模式建议用 asyncio.gather 并发拉取,替代当前的串行遍历。
5.4 输出校验修正
对 Generator 和 Reviewer 的输出加结构化校验:
from pydantic import BaseModel
class ReviewOutput(BaseModel):
summary: str
findings: list[dict] # {severity, location, message, fix}
score: int # 1-10
top_recommendations: list[str]
# 在 Agent 回复后校验
try:
result = ReviewOutput.model_validate_json(agent_response)
except ValidationError as e:
# 要求 Agent 重新格式化
...
5.5 技能治理:企业级 Skill Registry
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deprecated: true,list_skills 时标记 |

6. 总结
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
before_tool_callback + state 门控 |
|
|
|
|
|
SequentialAgent + require_confirmation |
核心建议:
-
ADK Skill 定义知识,ADK Agent 编排层(或外部框架)编排流程。这是最务实的分工。 -
不要把 SKILL.md 当万能药。它是一种优秀的知识封装格式,但不是工作流引擎。 -
Inversion 和 Pipeline 不是"不能用",而是"不能只用 SKILL.md" 。结合 before_tool_callback、SequentialAgent、require_confirmation,ADK 自身就能补位。 -
skills 模块仍是实验性的。在选型时要有 Plan B,密切关注 API 变更。 -
description 写得比 instructions 重要。企业应该建立 skill 的 description 评审机制。

