ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

adk-samples 配方校验工具链:用 `uv run validate` 在提交 PR 前完成 recipe 自动化体检

adk-samples 配方校验工具链:用 `uv run validate` 在提交 PR 前完成 recipe 自动化体检 adk-samples 配方校验工具链用uv run validate在提交 PR 前完成 recipe 自动化体检【免费下载链接】adk-samplesA collection of sample agents built with Agent Development Kit (ADK)项目地址: https://gitcode.com/GitHub_Trending/ad/adk-samples本指南系统讲解 adk-samples 仓库tools/目录下的本地校验工具链如何在提交 PR 之前用一条uv run validate命令对核心core/与社区贡献contrib/的示例 Agent即 recipe做清单、结构、README 与目录层级四类自动化检查。读完本文你将掌握校验命令的参数语义与退出码约定、每个子命令背后的检查规则与判定依据、诊断信息的结构化输出模型以及如何按规范扩展一个全新的校验工具。工具链定位提交 PR 前的最后一道本地关卡tools/README.md对这套工具的定义非常明确Local developer tools for validating recipes before submitting a PR。也就是说它面向的是想要向 adk-samples 贡献示例 Agent 的开发者而不是面向 Agent 运行时本身。仓库把每个可独立运行的示例 Agent 称为 recipe并规定了统一的目录布局core/recipe或core/language/recipe官方精选配方contrib/recipe或contrib/language/recipe社区贡献配方skills/vertical/solution垂直领域技能retail/、hr/、finance/等为 vertical其下才是 solution。校验工具负责在合入之前确认这些 recipe 的manifest.yaml、目录结构、README 与放置层级都符合仓库政策从而把 CI 阶段才会暴露的问题提前拦截在本地。环境准备从仓库根目录安装依赖并注册命令在仓库根目录执行一次依赖安装即可使用这是 README 明确要求的唯一前置步骤uv syncuv sync做了什么从根目录的 pyproject.toml 可以看到项目名为adk-samples-tools要求 Python3.11运行时依赖只有两个pyyaml解析manifest.yaml与policy.yml和jsonschema校验清单格式通过[project.scripts]注册了控制台命令validate validate:main把tools/validate.py中的main()绑定为命令行入口测试依赖uv sync --dev包括pytest8等用于运行tools/tests/下的单元测试。因此uv run validate ...实际执行的入口是 tools/validate.py 中被guard()包裹的main()——这个包裹不是装饰性的它保证校验器自身崩溃时以CI 故障而非配方违规的形式退出详见后文退出码一节。命令总览uv run validate [subcommand] [scope]README 给出的用法是两个参数都可选直接uv run validate会以默认值运行全部检查。参数说明subcommand可选。要运行哪一类检查manifest或all默认all。结合源码实际注册的子命令还包括structure、readme、placement见下文子命令详解。scope可选。all默认、core、contrib或指向单个 recipe 的路径如core/rag-agent-search。官方示例命令来自 tools/README.mduv run validate # run all checks on all recipes uv run validate all core # run all checks on core/ only uv run validate manifest core/rag-agent-search # run manifest check on one recipe参数路由单参数时的智能判定validate.py并不使用argparse而是手写了一套极简路由逻辑tools/validate.py#L92-L147looks_like_scope(arg)只要参数含/或者是core/contrib/skills/all之一就判定为 scope 而非子命令零个参数等价于validate all全部子命令、全仓库一个参数若是 scope 则运行全部子命令如uv run validate core/rag-agent-search若是合法子命令则全仓库运行该子命令否则报错并提示--help两个参数第一个必须是合法子命令第二个是 scope三个及以上参数直接报 Too many arguments。这套路由在 tools/tests/test_validate.py 中有完整覆盖例如test_main_single_scope_arg_runs_all_with_scope验证[core]会以scopecore触发全部子命令test_main_unknown_single_arg_returns_one验证非法参数返回退出码 1。退出码约定0、1、2 三种语义tools/ci_message.py定义了三个区分明确的退出码tools/ci_message.py#L267-L269退出码常量含义0EXIT_OK所有检查通过1EXIT_VIOLATIONS存在配方违规需要贡献者修复2EXIT_CI_FAULT校验工具自身故障CI 基础设施问题与贡献者的改动无关run_all()的汇总逻辑tools/validate.py#L61-L89特意让 CI 故障优先于普通违规如果任一子命令返回 2整体返回 2。原因在注释里说得很清楚——如果把 2 折叠成 1工作流会告诉贡献者你的配方无效而实际是校验器自己崩溃了贡献者收到的修复建议针对的是一个他们无法修复的 bug。对应的回归测试是test_run_all_ci_fault_outranks_a_violation。子命令详解README 只列举了manifest与all两个子命令结合 tools/validate.py#L45-L55 的SUBCOMMANDS注册表实际可用的是manifest、structure、readme、placement四个真实检查器加all汇总器。manifest— 校验 manifest.yaml对应实现 tools/validate_manifest.py核心检查链如下存在性每个 recipe 目录必须有一个manifest.yaml——它是这个目录算一个 recipe的判定依据缺失时给出manifest-missing诊断YAML 语法解析失败给出带行列号的解析错误文件为空或只有注释也会被单独检出yaml.safe_load对两者都返回NoneSchema 合规用jsonschema.Draft7Validator对照.github/schemas/manifest-schema.json校验该路径由 tools/validate_manifest.py#L50 定义。schema 是封闭式的additionalProperties: false未知字段不会被忽略而是当作拼写错误或非法字段报出占位符检查ownership.team仍是TODO: Replace with your team name、ownership.poc仍是TODO: Replace with your GitHub user ID、description以TODO开头都会分别报出ownership-placeholder与description-placeholder诊断——占位符能通过 schema但意味着没人认领这个配方所以单独拦截非阻断提示status: inactive的配方只打印[NOTICE]提示Severity.WARNING不影响退出码——因为 inactive 是走向删除的中间态需要让运行校验的人知情但不应阻塞 PR。值得注意的实现细节jsonschema 的错误信息讲的是 JSONPath 词汇_schema_diagnostictools/validate_manifest.py#L310-L341把每条错误翻译成字段名 实际值 阈值 修复建议的人话甚至用difflib.get_close_matches提供Did you mean xxx?拼写建议。scope 还带一层空匹配保护如果显式指定的 scope如core/some-typo没有匹配到任何 recipeempty_scope_diagnostic会报错而不是打印一个虚假的[PASS] All 0 recipe(s)——绿色的通过结果对一次什么都没校验的运行是最糟糕的答案。structure— 校验配方目录结构对应实现 tools/validate_structure.py按顺序对每个 recipe 执行六项检查manifest.yaml存在性与 manifest 检查共用同一诊断文案见missing_manifest_diagnosticmanifest.yamlschema委托给validate_manifest文件夹命名目录名必须匹配^[a-z][a-z-]*$小写字母与连字符、以字母开头见 tools/validate_structure.py#L86长度不超过.github/policy.yml中recipe_naming.max_folder_name_length默认 30。理由很实际目录名会原样进入 URL、包名和 shell 命令下划线、数字、大写在不同平台行为不一违规时工具会用git mv给出重命名建议体积与文件数按policy.recipe_size_limits中core/contrib分层、再按manifest.large是否true区分default/large两档对max_size_mb与max_files做限制。统计时按policy.excluded_paths排除.venv/、__pycache__/、uv.lock、构建产物等超限时报出贡献最大的前几个文件/目录删掉恰好能过关为止而不是只给一个总数。若声明了large: true但该根没有 large 档会明确报错而不是静默回退到 default必需文件取policy.required_files中always、by_root[root]、by_language[manifest.language]三者的并集uv.lock、pyproject.toml、tests/test_runnability.py、README.md、AGENTS.md等每个条目都携带来源provenance回答为什么我的配方需要这个文件必需目录同一并集逻辑应用于policy.required_dirs垂直技能用它要求scripts/空目录也放行。关键设计命名匹配在Python 里按名称比较完成而不是委托给文件系统——macOS 默认大小写不敏感PyProject.toml在本机能满足pyproject.toml的要求却在 Linux CI 上失败按名称比较让本地与 CI 的判定完全一致。只有显式列入policy.case_insensitive_files的条目才允许大小写不敏感匹配_find_entry的实现见 tools/validate_structure.py#L616-L648。readme— 校验 README.md对应实现 tools/validate_readme.pyREADME 是 recipe 的门面检查项包括文件存在且非空编码必须是合法 UTF-8latin-1/cp1252 保存的 README 连 CI 和 GitHub 渲染器都读不了会给出iconv转码命令不含TODO:占位符最少 100 词MIN_WORD_COUNTtools/validate_readme.py#L43词数作为是否有真实描述的代理指标低于阈值时提示目标 200-300 词有 Setup 小节标题需命中setup/prerequisites/installation/requirements/getting started/environment等关键词之一词边界、大小写不敏感有 Run 小节标题需命中run/usage/quickstart/deploy/how to run/launch等关键词之一至少一个围栏代码块精确的可复制运行命令必须放在bash块里散文描述不算数。placement— 校验 recipe 的放置层级对应实现 tools/validate_placement.py。这个检查器回答的是recipe 是否放在正确文件夹这是其余按 recipe 逐个检查的工具看不到的盲区collector 只能收集到它已经发现的 recipe一个放错位置的 recipe 会被静默漏掉因此它作为 CI 的独立任务运行同时注册进validate让贡献者本地就能发现。它只约束NAMESPACE_REQUIRED_ROOTS中的根即skills/要求严格满足skills/vertical/solution/manifest.yaml 有效 skills/solution/manifest.yaml 太浅 —— 缺少 vertical skills/vertical/solution/x/manifest.yaml 太深verticalretail/、hr/、finance/是强制的因为它承载所有权信息让一个团队能一眼看到自己的全部表面。遍历时跳过.git、.venv、node_modules等目录。值得注意的是validate.py会把字面量all分类为 scope 而非子命令validate placement all会以scopeall到达这里代码里对此有专门处理tools/validate_placement.py#L163避免一个没实际运行的检查打印出[PASS]。all— 顺序执行并输出汇总run_all(scope)按注册顺序依次执行全部四个检查器每个检查器打印分隔横幅最后输出汇总表 Summary [PASS] Manifest validation [FAIL] Structure validation ...退出码遵循前文CI 故障优先的聚合规则。诊断信息模型每条错误都必须回答 what / why / how / doc这套工具链最值得借鉴的工程实践是 tools/ci_message.py 中统一的Diagnostic数据类。它存在的目的是杜绝一种特定的失败模式一条只说这里错了、让贡献者自己去翻.github/policy.yml逆向工程规则的报错。Diagnostic强制要求四个字段构造时校验非空字段回答的问题示例what问题是什么精确到文件/字段/值Field ownership.team is 10 characters long; the schema requires at least 3.why哪条规则触发、为何适用于这个recipe溯源而非复述Required because manifest.language is python (policy.required_files.by_language.python).how具体修复命令或要补充的精确文本git mv core/foo_bar core/foo-bardoc指向 docs/recipe-handbook/troubleshooting.md 的锚点docs/…troubleshooting.md#folder-name-too-long-or-invalid验收标准是一句很硬的话could a contributor who has never opened policy.yml fix this in one attempt, without asking anyone?Diagnostic提供两种渲染方式render_human()面向 job log 的多行块包含[check] what、Why:、Fix:、Docs:四段render_annotation()面向 PR Files 页的单行::error file...::...GitHub 注解换行符按%0A百分号编码让多行修复命令在注解里保持多行同时转义%和\r防止破坏转义序列。与Diagnostic相对的是InfraFaultinfra_fault()工厂当校验器自己崩溃时使用它不携带file字段注解打在检查器上而不是贡献者的文件上并明确说明这是仓库 CI 工具的问题与你的改动无关。所有入口都必须用guard()包裹tools/ci_message.py#L375-L394把未捕获异常转换为EXIT_CI_FAULTSystemExit与KeyboardInterrupt则放行这样--help和 Ctrl-C 不受影响。tools/tests/test_ci_message.py甚至断言Doc枚举里的每个锚点都真实存在于 troubleshooting 文档中防止贡献者屏幕上出现 404 链接。与 CI 联动的配套工具除validate命令外tools/下还有两个面向 CI 工作流的辅助脚本同样值得了解affected_recipes.py— 把改动文件映射到受影响的 recipegit diff --name-only输出的是文件路径CI 需要的是哪些 recipe 需要重新校验。affected_recipes.py从 stdin 读路径、向 stdout 写出去重排序后的 recipe 目录git diff --name-only origin/main...HEAD | uv run python tools/affected_recipes.py它纯字符串地解析三种布局core/recipe、core/language/recipe、skills/vertical/solution默认丢弃磁盘上不存在的候选过滤掉core/README.md这类顶层文件与已删除的 recipe还支持--language python只筛选指定语言的 recipe命名空间路径以路径段为准扁平配方以manifest.language为准。check_frozen_paths.py— 拦截对已退休目录的修改仓库早期 recipe 位于language/agents/recipe如python/agents/...这些根已关闭新改动应进入contrib/language/recipe。该脚本读取.github/policy.yml的frozen_paths按完整路径分量匹配python/agents不会误匹配python/agents-archive对落入退休目录的改动按 recipe 分组报错并给出git mv迁移建议。调用方只传 ADDED/MODIFIED 文件git diff --diff-filterAM刻意放行删除与重命名这样把 recipe 迁出退休目录本身不会被阻塞。如何扩展一个新的校验工具README 给出了明确的扩展契约Create a newvalidate_name.pyfile in this directory with amain(scope: str | None) - intfunction, then register it invalidate.pyunderSUBCOMMANDS.完整步骤可以总结为在tools/下新建validate_name.py实现main(scope: str | None) - intscope遵循既有约定None/all表示全量core/contrib表示单根core/recipe表示单个 recipe——直接复用validate_manifest.collect_recipe_dirs(scope)即可拿到 recipe 目录列表在 tools/validate.py#L45-L55 的SUBCOMMANDS字典里注册格式为name: (人类可读描述, validate_name.main)run_all会自动纳入汇总内部用ci_message.Diagnostic输出诊断而不是手写::error字符串返回EXIT_OK/EXIT_VIOLATIONS入口用guard()包裹在tools/tests/下补充测试——现有测试矩阵覆盖了路由、manifest、结构、README、放置、冻结路径、受影响 recipe 与 CI 消息八个方面tools/tests。测试保障与配套文档校验工具自身的正确性由tools/tests/下的 pytest 套件保障其中test_validate.py通过 monkeypatch 替换SUBCOMMANDS来隔离测试参数路由与汇总逻辑如CI 故障优先于违规、--help 返回 0、崩溃绝不指向贡献者文件test_validate_manifest.py、test_validate_structure.py等则分别验证各检查器的判定。运行时依赖pyyaml、jsonschema声明在根 pyproject.toml测试依赖通过uv sync --dev安装。进一步深入时仓库还提供了三份关键配套资料docs/recipe-handbook/README.md一个 recipe 应该如何组织docs/recipe-checklist.md提交 PR 前的检查清单docs/recipe-handbook/troubleshooting.md每条诊断doc锚点指向的故障排查手册。结合这些文档与tools/源码你既能跑通改配方 → 本地 validate → 提交 PR的完整闭环也能理解每条检查规则背后的仓库政策与工程动机。【免费下载链接】adk-samplesA collection of sample agents built with Agent Development Kit (ADK)项目地址: https://gitcode.com/GitHub_Trending/ad/adk-samples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表