news 2026/9/7 4:57:04

goose 文档自动化管线:用「确定性脚本 + AI Recipe」让 CLI 参考文档与代码自动同步

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
goose 文档自动化管线:用「确定性脚本 + AI Recipe」让 CLI 参考文档与代码自动同步

goose 文档自动化管线:用「确定性脚本 + AI Recipe」让 CLI 参考文档与代码自动同步

【免费下载链接】goosean open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM项目地址: https://gitcode.com/GitHub_Trending/goose3/goose

本文基于 goose 仓库 documentation/automation/ 目录的官方文档与配套脚本源码,讲解 goose 如何构建一套文档自动化管线:在新版本发布时,通过「构建二进制 → 解析--help→ 确定性 diff → AI 合成变更说明 → AI 外科手术式更新文档」五步流程,自动检测 CLI 命令与选项的变更,并把 CLI Commands Guide 保持与代码一致。读完后你可以完整理解这套「脚本负责确定性、AI 负责语义合成」的混合管线设计,并能在本地或 GitHub Actions 中复现整条管线。

文档自动化的总体设计

goose 的 documentation/automation/ 目录存放一组自动化管线,目标是「让 goose 文档与代码变更保持同步」。每个自动化项目追踪特定类型的代码变更,并更新对应的文档:

项目状态追踪对象更新对象
cli-command-trackingPlannedCLI 命令与选项CLI 文档
provider-trackingPlanned受支持的 AI ProviderProvider 文档
extension-trackingPlanned内置扩展扩展文档

目前仓库中真正落地的完整案例是cli-command-tracking,其余项目仍在规划中。所有自动化项目遵循统一的标准目录结构:

project-name/ ├── README.md # 项目专属文档 ├── TESTING.md # 该自动化如何测试 ├── config/ # 配置文件 ├── scripts/ # 确定性的抽取/diff 脚本 └── recipes/ # AI 驱动的合成/更新 recipe

其设计原则可以概括为四点:模块化(每个项目自包含)、可测试(每阶段输入/输出清晰)、透明(中间文件可人工检查)、可复用(跨项目共用同一模式)。而贯穿所有项目的核心手法是混合架构(Hybrid Approach):

  • Shell/Python 脚本:负责确定性的抽取与比对——构建二进制、运行--help、解析输出、JSON 结构比对,全程不做任何解释与推断;
  • AI Recipe:负责语义合成与文档更新——解释变更影响、生成迁移指引、以正确的格式更新文档。

之所以这样划分,是因为「抽取什么变了」必须是可复现的事实问题,而「这变更对用户意味着什么、文档该怎么改」是需要语言理解的能力问题。两者通过 JSON/Markdown 中间文件解耦,每一步都可以单独重跑、单独检查。

CLI 命令追踪管线的架构

cli-command-tracking/README.md 描述了管线的四阶段流水线,目标是让 CLI Commands Guide 始终与代码同步:

┌─────────────────────────────────────────────────────────────────┐ │ EXTRACTION (确定性) │ ├─────────────────────────────────────────────────────────────────┤ │ extract-cli-structure.sh → extract-cli-structure.py │ │ ↓ │ │ cli-structure.json (commands, options, subcommands, aliases) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ DIFFING (确定性) │ ├─────────────────────────────────────────────────────────────────┤ │ diff-cli-structures.py │ │ ↓ │ │ cli-changes.json (added, removed, modified commands/options) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ SYNTHESIS (AI 驱动) │ ├─────────────────────────────────────────────────────────────────┤ │ synthesize-cli-changes.yaml │ │ ↓ │ │ cli-changes.md (人类可读的变更文档) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ UPDATE (AI 驱动) │ ├─────────────────────────────────────────────────────────────────┤ │ update-cli-commands.yaml │ │ ↓ │ │ goose-cli-commands.md (已更新) + update-summary.md │ └─────────────────────────────────────────────────────────────────┘

各阶段之间全部通过output/目录下的 JSON/Markdown 文件通信,这是整个管线「透明、可测试」的关键:

文件生产者消费者用途
old-cli-structure.jsonextract-cli-structure.shdiff-cli-structures.py旧版本 CLI 结构
new-cli-structure.jsonextract-cli-structure.shdiff-cli-structures.py新版本 CLI 结构
cli-changes.jsondiff-cli-structures.pysynthesize-cli-changes.yaml检测到的变更(结构化)
cli-changes.mdsynthesize-cli-changes.yamlupdate-cli-commands.yaml人类可读的变更文档
update-summary.mdupdate-cli-commands.yaml人工审查文档更新摘要

版本如何被确定

管线支持自动版本检测,逻辑实现在 run-pipeline.sh 中:

  • 旧版本:通过gh release list取最近第二个 release tag;gh不可用时回退到git tag --sort=-v:refname取第二个匹配vX.Y.Z的 tag;
  • 新版本:取最近的 release tag,或读取 CI 注入的RELEASE_TAG环境变量;
  • 测试未发布变更:显式传HEAD即可从当前代码构建二进制。

实操:本地运行整条管线

环境变量

变量是否必需默认值说明
GOOSE_REPO本地运行时必需无(脚本默认$HOME/Development/goosegoose 仓库根目录路径
CLI_COMMANDS_PATH$GOOSE_REPO/documentation/docs/guides/goose-cli-commands.md目标文档文件完整路径
RELEASE_TAGGitHub Actions 指定新版本时使用

前置条件(来自 TESTING.md):Python 3.7+、Rust 工具链(构建 goose 时)、jq(JSON 处理)、已安装 goose CLI(运行 recipe)、可访问 goose 仓库的 Git。

一键运行

# 设置 goose 仓库路径 export GOOSE_REPO=/path/to/goose # 自动检测版本,跑完整管线 ./scripts/run-pipeline.sh # 或显式指定新旧版本 ./scripts/run-pipeline.sh v1.17.0 v1.19.0 # 测试未发布的变更 ./scripts/run-pipeline.sh v1.19.0 HEAD

run-pipeline.sh 的执行流程是:先对旧版本、新版本分别抽取结构并统计命令数(jq '.commands | length'),再运行 diff 脚本得到cli-changes.json只有当has_changestrue才继续执行两个 AI recipe 阶段,否则直接输出「No Changes Detected」并结束。AI 阶段调用goose run --recipe ...时,脚本会用sed/grep过滤掉 ANSI 转义和会话日志行(starting sessionsession id:text_editor等),避免噪声混入输出;同时用PIPESTATUS[0]检查 goose 进程本身是否失败,而不是被grep的退出码误导。

分步手动执行

# 1. 抽取 CLI 结构 ./scripts/extract-cli-structure.sh v1.17.0 > output/old-cli-structure.json ./scripts/extract-cli-structure.sh v1.19.0 > output/new-cli-structure.json # 2. 检测变更 python3 scripts/diff-cli-structures.py output/old-cli-structure.json \ output/new-cli-structure.json \ > output/cli-changes.json # 3. 生成人类可读的变更文档 cd output && goose run --recipe ../recipes/synthesize-cli-changes.yaml # 4. 更新 goose-cli-commands.md cd output && goose run --recipe ../recipes/update-cli-commands.yaml

跳过命令配置

有些命令被有意排除在抽取与文档追踪之外,配置在 skip-commands.json:

{ "description": "Commands to skip during extraction (not documented intentionally)", "skip_commands": [ { "name": "term", "reason": "Terminal integration documented via @goose/@g aliases" } ] }

增删跳过命令只需编辑该配置,无需改代码——抽取脚本启动时通过load_skip_commands()读取该文件并据此跳过对应子命令(见 extract-cli-structure.py)。

确定性抽取阶段:脚本在做什么

获取指定版本的二进制

extract-cli-structure.sh 是抽取阶段的入口,按版本类型走两条路:

  • release tag(vX.Y.Z格式):通过官方download_cli.sh下载对应版本预构建二进制(is_release_tag()用正则^v[0-9]+\.[0-9]+\.[0-9]+$判断),省去编译时间;
  • HEAD 或其他 git ref:在GOOSE_REPO中构建。其中非 HEAD 的 ref 会先用git rev-parse校验版本存在,再通过git worktree add把该版本检出到临时目录执行cargo build --release,构建完成后把二进制拷出并移除 worktree,保证不污染主工作区。

拿到二进制后,脚本打印--version输出确认版本,最后调用python3 extract-cli-structure.py <binary> <version>完成真正的解析。

解析--help输出为命令树

extract-cli-structure.py 是抽取的核心:它递归地对命令树中每个节点执行<command> --help,用正则把 clap 风格的帮助文本解析为结构化 JSON。关键解析函数包括:

  • parse_about():取Usage:行之前的第一行作为命令描述;
  • parse_aliases():匹配[aliases: x, y]模式提取别名(从帮助文本前 500 字符中找);
  • parse_options():定位Options:段,按「行首为-的缩进行」切分选项块;parse_option_block()再对每个块提取短标志-f)、长标志--format)、值名<FORMAT>)、帮助文本(含首行内联帮助)、默认值[default: ...])、可选值[possible values: ...])六个字段;
  • parse_subcommands():解析Commands:段中的子命令名与别名,自动跳过 clap 生成的help子命令;
  • extract_command_structure():递归入口,对每个子命令先检查是否在SKIP_COMMANDS列表中,再深入其子树。

最终输出的 JSON 顶层结构为{version, source_version, extracted_at, binary_path, commands: [...]},每个命令节点包含name / about / aliases / usage / options / subcommands。所有--help调用带 10 秒超时,超时只会告警并返回空串而不中断整个抽取。

确定性 diff:变更如何被分类

diff-cli-structures.py 的算法分为三步:

  1. 展平flatten_commands()把嵌套的命令树按全路径(如session list)展开为字典,便于按路径逐一对比;
  2. 逐字段比对compare_commands()对同一路径的旧新命令比较aboutaliasesusage,选项层面由compare_options()按「长标志优先、否则短标志」作为键,逐一比对short / long / value_name / help / default / possible_values六个字段,任一字段不同即记入modified
  3. 破坏性变更归类categorize_breaking_changes()将变更打上类型与严重级别标签。
变更类型严重级别判定逻辑
command_removedhigh命令路径从新版本消失
option_removedhigh选项标志键从选项中消失
option_renamedhigh短/长标志发生变化
default_changedmedium默认值改变(行为可能隐性变化)
enum_values_removedhigh[possible values]中出现值被移除
alias_removedmedium命令别名被移除(可能破坏用户肌肉记忆)

输出 JSON 包含has_changes布尔值、summary(各计数,其中 breaking 只统计 high 级别)、changes.commands.{added,removed,modified}breaking_changes数组。summary.breaking_changes的计数口径在 main() 中可以确认:只统计severity == 'high'的条目。

AI 阶段:两个 goose Recipe

管线的后两步用 goose 的 recipe 机制实现,recipe 文件即提示词工程——instructions定义系统级约束,prompt触发执行,均依赖内置的developer扩展(提供text_editor等工具)。

synthesize-cli-changes.yaml:生成变更说明

synthesize-cli-changes.yaml 读取三份输入(cli-changes.json变更 diff + 新旧两份结构 JSON 作上下文),产出cli-changes.md。recipe 指令中规定输出必须包含:摘要统计、Breaking Changes(每条附影响说明与迁移指引,破坏性变更永远排在最前)、New Commands(含用途与关键选项)、Removed Commands(含替代方案)、Modified Commands(描述/选项/别名的逐项对比)、Non-Breaking Changes。

它的分析准则也很具体:从用户影响而非实现细节的角度解释变更;为破坏性变更给出新旧用法对照示例;利用命令名与选项名推断变更意图;跳过纯排版级的帮助文本微调;对空类别整节跳过。

update-cli-commands.yaml:外科手术式更新目标文档

update-cli-commands.yaml 是整条管线中约束最严格的一步。它的核心目标是:文档永远描述 CLI 的当前状态,而不是变更历史——选项被删就从文档删掉,选项被加就补上,绝不写「已移除」「已新增」这类措辞。为此 recipe 列出了一组硬性禁令(ABSOLUTE PROHIBITIONS):

  1. 只有当cli-changes.md明确写「命令 X 被移除」时才删除整节命令;
  2. 绝不改分区标题(如### Task Execution### Session Management);
  3. 绝不在没有明确记录的情况下重命名选项;
  4. 绝不重复创建已存在的分区,只原地更新;
  5. 绝不删除分区之间的水平线---
  6. 绝不重写示例,只更新实际变化的那个标志/选项。

更新策略上,它要求按「读cli-changes.md识别全部变更 → 逐条施加最小化编辑(surgical edits,用str_replace精确匹配)→ 保持目标文档既有风格(####命令标题、加粗选项名的 bullet 列表、带语言标识的代码块、admonition 提示框)→ 自查确认」的顺序执行,并在完成后额外生成update-summary.md供人工审查(含「已应用变更清单 + 更新分区 + 验证清单」)。目标文档路径优先取CLI_COMMANDS_PATH环境变量,否则回退为$GOOSE_REPO/documentation/docs/guides/goose-cli-commands.md

从源码结构看,run-pipeline.sh在调用该 recipe 前会显式export CLI_COMMANDS_PATH="${GOOSE_REPO}/documentation/docs/guides/goose-cli-commands.md",保证 CI 与本地行为一致。

追踪范围与 GitHub Actions 集成

追踪什么

该管线检测的完整范围(来自 cli-command-tracking/README.md):

命令层面:新增/删除命令、描述变更、别名增删、子命令增删。选项层面:新增/删除选项、帮助文本变更、默认值变更、可选值(枚举)变更、短/长标志变更。破坏性变更:按上文严重级别表自动归类。

GitHub Actions 工作流

自动化通过 docs-update-cli-ref.yml 接入 GitHub Actions:

  • 触发:新版本发布时自动触发,或手动触发用于测试;
  • 流程:为两个版本分别构建 goose、抽取 CLI 结构、检测变更、更新文档;
  • 输出:检测到变更时创建一个包含更新后goose-cli-commands.md的 PR;
  • 工件old-cli-structure.jsonnew-cli-structure.jsoncli-changes.jsoncli-changes.mdpipeline.log都会作为 artifacts 上传,可下载检查。

工作流支持三个输入参数:

输入说明默认值
old_version旧版本 tag从 release 自动检测
new_version新版本 tagHEAD
dry_run只生成文件、不创建 PRtrue

在 fork 中测试时,需要在 fork 的 Actions 设置里配置ANTHROPIC_API_KEYsecret,可选配置GOOSE_PROVIDER(默认 anthropic)与GOOSE_MODEL变量;手动触发时建议dry_run: true,跑完后从 workflow run 页面下载 artifacts ZIP 检查中间产物。

用已知变更做回归验证

TESTING.md 给出了三种典型测试用例,可直接复用:

# 用例 1:版本间新增了命令 ./scripts/run-pipeline.sh v1.13.0 v1.14.0 jq '.changes.commands.added' output/cli-changes.json # 用例 2:版本间修改了选项 ./scripts/run-pipeline.sh v1.14.0 v1.15.0 jq '.changes.commands.modified' output/cli-changes.json # 用例 3:同版本对比(应无变更) ./scripts/run-pipeline.sh v1.14.0 v1.14.0 jq '.has_changes' output/cli-changes.json # 期望输出 false

抽取阶段的验证则用jq直接抽查结构文件:jq '.commands[] | select(.name == "session")' output/test-extraction.json检查特定命令、jq '.commands[].name' | grep -v term确认跳过命令已排除。

常见问题排查

TESTING.md 沉淀了几个典型故障的排查路径:

  • macOS Keychain 提示:goose 启动时可能尝试读取已存凭据而触发钥匙串访问提示;CI runner 没有 keychain,可能需通过keyring: false之类的配置或环境变量禁用凭据加载(文档中标注为待调查项);
  • 旧版本构建失败:先git tag确认版本存在,再手动git worktree add /tmp/goose-test v1.14.0 && cargo build --release定位依赖问题;
  • 抽取超时:调大 extract-cli-structure.py 中run_help_commandtimeout=10
  • diff 出现意外变更:可能是帮助文本排版格式变了,直接对两版二进制的原始--help输出做diff对比;
  • AI recipe 失败:先用ls -lhjq empty确认三份输入 JSON 存在且格式合法。

扩展指南与维护建议

按照总 README 的约定,新增一个自动化项目只需四步:创建documentation/automation/your-project/子目录、遵循标准结构(README、TESTING、config、scripts、recipes)、按需创建 GitHub Actions 工作流、最后更新 automation 总 README 的项目表格。

维护cli-command-tracking本身时,README 给出的纪律是:先用测试版本本地跑./scripts/run-pipeline.sh;将生成文件与实际 CLI 变更逐一核对;在 fork 中用 dry-run 模式验证工作流;把设计决策回写进 README。此外 TESTING.md 还建议保存「已知正确」的输出作为回归测试数据(如test-data/v1.14.0-to-v1.15.0-changes.json),防止后续脚本改动破坏既有行为。

这套管线值得借鉴的地方在于:它没有把「文档更新」整体交给 AI,而是把可复现的事实提取(构建、--help、解析、diff)留给脚本、把需要语言判断的合成与编辑留给 AI,中间用可检查的 JSON/Markdown 文件交接——每一步失败都可以定位到具体阶段,每一步的产物都可以人工复核,这正是它能在发布流程中无人值守运行的前提。

【免费下载链接】goosean open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM项目地址: https://gitcode.com/GitHub_Trending/goose3/goose

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 4:56:37

局域网NTP时间同步服务器搭建:chrony配置与客户端接入指南

简介&#xff1a;这是一套面向小型局域网的时间同步工具&#xff0c;包含服务器端与客户端程序&#xff0c;适合需要在无外部NTP条件下自行校准设备时钟的IT运维或开发人员。方案以Visual C编写客户端&#xff0c;通过自定义协议与指定服务器通信&#xff0c;完成时间请求、响应…

作者头像 李华
网站建设 2026/9/7 4:55:21

重复文件清理软件推荐:Czkawka 免费去重工具 5 步上手全解

重复文件清理软件推荐&#xff1a;Czkawka 免费去重工具 5 步上手全解 【免费下载链接】czkawka Multi functional app to find duplicates, empty folders, similar images etc. 项目地址: https://gitcode.com/GitHub_Trending/cz/czkawka 如果你正在找一款重复文件清…

作者头像 李华
网站建设 2026/9/7 4:52:48

告别低速爬行与振荡:台达ASD驱动DD马达调试要点全解析

简介&#xff1a;面向运动控制工程师与自动化调试人员的ASD驱动器简易调试手册&#xff0c;围绕ASD与DD马达联调场景&#xff0c;覆盖面板语言设置、软件中英文切换、USB通讯连接、电机参数配置与方向确认等完整流程。文档基于Akribis Motion Gallery界面&#xff0c;偏重实操路…

作者头像 李华
网站建设 2026/9/7 4:52:46

Vibe Coding实战:做好这四件事,AI编程才不会翻车

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华