
ADK SkillRegistry 深度指南为 Agent 打造运行时技能发现目录【免费下载链接】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-pythonSkillRegistry是 ADKAgent Development Kit中一个可搜索技能目录的抽象接口它把“技能发现”从构建期搬到运行期Agent 不再需要在上线前拿到全部技能清单而是通过工具调用按需检索和加载。本篇指南以官方文档为核心结合仓库源码skill_registry.py、skill_toolset.py、gcp_skill_registry.py与真实示例agent.py带你实现自己的技能注册表、理解与SkillToolset的协作机制并掌握 ADK 内置的GCPSkillRegistry用法。SkillRegistry 要解决的问题SkillToolset默认携带一份固定的技能列表这些技能是在构建 Agent 时从磁盘加载的。当 Agent 只拥有自己的技能时这种“一次加载、始终可用”的方式是合理的。但它会在以下三种场景中迅速变得力不从心目录被多个 Agent 共享同一个技能库要为成百上千个 Agent 服务每个 Agent 都预加载全部技能会造成巨大浪费目录需要持续增长新增技能不应要求任何人重新部署 Agent目录规模过大把所有技能的 description 全部塞进 prompt在大多数轮次里都是对上下文预算的浪费。SkillRegistry通过“把发现变成一次工具调用”来同时绕开这三个问题给SkillToolset挂上一个 registry 后Agent 会获得一个search_skills工具而原有的load_skill工具也能拉取到构造时列表里从未出现过的技能——这正是 ADK 技能渐进式披露progressive disclosure机制在运行期的延伸。SkillRegistry是一个抽象基类ABC包含两个抽象方法和一个可选方法。ADK 自带一个官方实现GCPSkillRegistry底层对接 Google Cloud 的 Agent Registry API当你的技能存放在别处数据库、内部服务、带自有索引的存储桶时实现这个接口即可接入。from google.adk.skills import SkillRegistry快速上手最小的可用注册表下面这个注册表是基于内存字典的最简实现它能真正运行并完成有用的事。注意两个方法之间的“不对称性”get_skill返回完整的Skill对象而search_skills只返回Frontmatter技能的名字和描述——正是这种“只返回轻量元数据”的设计让发现过程保持廉价。import pathlib from google.adk import Agent from google.adk.skills import Frontmatter from google.adk.skills import Skill from google.adk.skills import SkillRegistry from google.adk.skills import load_skills_from_dir from google.adk.tools.skill_toolset import SkillToolset catalog { skill.name: skill for skill in load_skills_from_dir(pathlib.Path(__file__).parent / skills) } class DictSkillRegistry(SkillRegistry): Serves skills out of a dictionary keyed by skill name. def __init__(self, skills: dict[str, Skill]): self._skills skills async def get_skill(self, *, name: str) - Skill: if name not in self._skills: raise KeyError(fNo skill named {name!r}) return self._skills[name] async def search_skills(self, *, query: str) - list[Frontmatter]: terms query.lower().split() return [ skill.frontmatter for skill in self._skills.values() if any(t in skill.description.lower() for t in terms) ] root_agent Agent( namesupport_agent, descriptionAnswers customer questions., instructionSearch for a skill before answering an unfamiliar question., tools[SkillToolset(skills[], registryDictSkillRegistry(catalog))], )两个方法都是异步的且都只接受 keyword-only 参数源码中 skill_registry.py 的定义可以印证。skills[]与 registry 同时出现完全合理Agent 以空的本地列表启动所有用到的技能都通过搜索获得。加载本地技能目录的细节示例中的load_skills_from_dir会把指定目录下符合 skills 规范的技能目录逐一解析成Skill对象。技能目录的核心是SKILL.md文件其 frontmatterYAML 头被解析为Frontmatter模型正文成为instructions随附的 references / assets / scripts 目录则进入Resources。数据模型定义见 models.py 与 models.py。工作原理拉取式目录与三层调用链注册表本身从不“推送”任何内容它只被“拉取”。SkillToolset会在模型发起工具调用时按以下顺序访问它search_skills当模型调用search_skills工具时触发。该工具只有在配置了 registry 时才会存在——在 skill_toolset.py 中可以看到SkillToolset初始化时只有self._registry非空才会追加SearchSkillsTool。它接收模型的查询字符串返回Frontmatter对象列表。模型看到的是技能的名字和描述这是 Skill 指南所描述的渐进式披露的第 1 层。get_skill当模型对不在 toolset 本地列表中的名字调用load_skill时触发。完整Skill被返回其 instructions 进入模型上下文。抓取到的技能定义会按轮次缓存最多 16 轮因此同一场对话中模型反复加载同一技能时注册表只会被命中一次。这条缓存路径实现在SkillToolset._get_or_fetch_skillskill_toolset.py缓存容器是带 FIFO 淘汰的OrderedDict上限_max_cache_turns 16skill_toolset.py。同时源码还做了“同轮内并发去重”同一轮次中多个并发加载请求会共享同一个asyncio.Future避免重复请求。Resources随后从已抓取的Skill对象提供服务而不再经过注册表。load_skill_resource与run_skill_script读取的都是skill.resources所以get_skill返回什么就决定了模型能触达的完整边界。从源码看search_skills工具返回给模型的还有一层保护若搜索结果与本地技能重名该条目会被过滤并记录 warning见SearchSkillsTool.run_async中的冲突检查skill_toolset.py确保注册表条目不会覆盖 Agent 自带的技能。你要交回的东西Skill 契约最关键的契约是你返回的对象。get_skill必须产出一个通过校验的Skill它由三部分组成一个Frontmatter其中name必须符合 kebab-case 规则instructions字符串SKILL.md 正文一个Resources持有 references、assets 与 scripts。你可以直接构造这些模型如果你的存储按技能目录组织也可以先解包目录再构造同样的对象。校验规则在 models.py 中有完整实现name长度不超过 64 字符、必须是全小写 kebab-case形如^[a-z0-9](-[a-z0-9])*$开启SNAKE_CASE_SKILL_NAME特性后也接受 snake_casedescription非空且不超过 1024 字符compatibility不超过 500 字符。两个方法的错误契约截然不同get_skill应该大声失败它被请求的是一个由模型选择的具体名字因此名字不存在时抛出异常是正确行为这正是抽象方法 docstring 要求的Raises: Exception: If the skill with the specified name does not exist.。search_skills必须宽容调用方无法控制你的目录里有什么一条畸形条目不能毁掉整个目录的发现能力。官方 GCP 实现会对未通过 frontmatter 校验的结果记录 warning 并跳过gcp_skill_registry.py你的实现应当保持一致。配置选项接口的完整方法清单SkillRegistry本身不引入任何配置项——它是一个纯粹的接口全部“配置”就是下面三个方法MethodRequiredSignatureReturnsget_skillyesasync (*, name: str)Skillsearch_skillsyesasync (*, query: str)list[Frontmatter]search_tool_descriptionno(self)str \| Nonesearch_tool_description是一个普通的同步方法而非抽象方法默认返回None见 skill_registry.py。覆写它可以用自定义描述替换模型看到的search_skills工具描述——当你的搜索有特殊语法时非常值得做例如“仅匹配整词”或“支持标签过滤”。默认描述无法表达这些细节模型猜错查询语言就会白白浪费一轮去搜索空结果。在SearchSkillsTool的构造中registry.search_tool_description()的返回值会直接作为工具描述skill_toolset.py。由于两个必需方法都是abstractmethod只实现其中一个的子类根本无法实例化Python 会在构造时抛出TypeError让你立刻发现问题而不是等到第一次调用。高级应用内置实现与设计决策ADK 官方实现GCPSkillRegistry解决的问题你的技能发布在 Google Cloud Agent Registry 上希望 Agent 无需本地副本即可发现它们。实现方式用 project 和 location 构造它再作为 toolset 的 registry 传入from google.adk.integrations.skill_registry import GCPSkillRegistry from google.adk.tools.skill_toolset import SkillToolset registry GCPSkillRegistry(project_idmy-project, locationus-central1) skill_toolset SkillToolset(skills[], registryregistry)结合源码gcp_skill_registry.py可以确认以下行为细节三个参数project_id、location、credentials均为 keyword-onlyproject_id与location缺省时回退到GOOGLE_CLOUD_PROJECT和GOOGLE_CLOUD_LOCATION环境变量两者都缺失时构造器抛出ValueErrorcredentials默认为应用默认凭据application default credentials并且是在首次请求时才惰性解析而非构造时API 端点通过AGENT_REGISTRY_ENDPOINT环境变量覆盖默认指向https://agentregistry.googleapis.com/v1alpha启用 mTLS 时切换到 mtls 端点当检测到需要客户端证书时会尝试加载默认 mTLS 客户端证书构建自定义 SSL 上下文。get_skill的实现值得留意它先请求技能的元数据取出defaultRevision无默认修订版本则抛ValueError再以altmedia参数直接下载该修订版本的 zip 压缩包最后解包成Skill——因此一次get_skill的成本超过一次网络往返。由于name直接来自模型发出的工具调用它在拼入 URL 之前会先用命名模式校验_SNAKE_OR_KEBAB_NAME_PATTERN防止把不可信字符串插进请求路径gcp_skill_registry.py。同时凭据刷新是阻塞调用被放到独立线程中执行以免卡住事件循环。search_skills调用 Agent Registry 的skills:search端点search_string参数为每个结果构造Frontmatter逐条跳过并记录未通过校验的条目HTTP 失败统一包装为携带状态码与响应体的RuntimeErrorgcp_skill_registry.py。仓库中有一个完整的可运行示例agent.py它把GCPSkillRegistry与空本地技能列表的SkillToolset组装成Agent并在 instruction 中明确指示模型“用search_skills找技能必要时用load_skill加载”。设计决策search_skills应该返回什么要解决的问题面对大型目录搜索结果本身可能吃掉整个 prompt 预算。返回太多条目就等于重新制造了“技能机制想要避免的那个问题”。实现建议只返回最有可能回答查询的少量条目并把“何时该用此技能”的信息写进每个description——因为这是模型做选择时唯一能看到的内容。返回类型不携带相关性分数也没有分页机制这意味着排序和截断完全由你这一侧决定。限制与边界搜索结果没有分数、没有游标list[Frontmatter]是全部契约。模型按你返回的顺序看到结果无法感知置信度也无法请求第二页。注册表无法列出整个目录接口没有list_skills模型的list_skills工具只报告 toolset 的本地技能。一个从不主动搜索的模型永远不会知道注册表的存在。从源码看ListSkillsTool.run_async调用的确实是_list_skills()只返回本地字典而系统指令中那句“本地技能不足时可以使用search_skills发现更多技能”的提示skill_toolset.py是引导模型走上搜索路径的关键。技能名必须满足 frontmatter 规则键不是 kebab-case 的存储无法通过Frontmatter校验往返请在边界处做映射。缓存归 toolset 所有抓取到的技能在SkillToolset内按轮次缓存 16 轮。注册表无法使该缓存失效因此对话中途被编辑的技能可能要过一段时间才会被感知。GCPSkillRegistry需要一个真实存在的端点它面向 Agent Registry APIHTTP 调用失败会以携带状态码和响应体的RuntimeError呈现。实验性skills 包被标记为实验性且处于积极开发中接口未来可能变化。相关示例与延伸阅读GCP Skill Registry agent把官方注册表接入一个本地技能列表为空的SkillToolset是端到端最小集成范例。Skills处于同一权衡的另一端——不经过注册表直接把技能提供给 Agent。Skill详细说明Skill、Frontmatter与Resources究竟包含什么这正是get_skill必须产出的东西。SkillToolset是消费方它决定了配置 registry 后模型最终拿到哪些工具list_skills、load_skill、load_skill_resource、run_skill_script以及注册表存在时的search_skills。对实现者而言动手前通读 skill_registry.py接口契约、skill_toolset.py 的_get_or_fetch_skill与工具实现缓存、冲突处理、错误码再对照 gcp_skill_registry.py 的完整实现即可在数小时内写出生产可用的自定义技能注册表。【免费下载链接】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),仅供参考