作者:David Pine & Peli de Halleux
排版:Alan Wang
探索 Aspire 团队如何将已合并的产品变更自动转化为由 SME 审核的文档 Pull Request,弥合产品发布与文档更新之间的时间差。

在这里插入图片描述

“文档在哪里?”这是产品团队里没人愿意回答的问题。最诚实的回答通常都是“还没跟上”。技术写作者正盯着一个已经关闭的 Pull Request,试图逆向推断到底改了什么;而 Pull Request 的作者早已投入到下一项工作。等到文档真正发布时,功能早已上线,有时甚至已经发布过不止一个版本。

这曾经也是 Aspire 团队的真实写照(我们是一个仅有 10 人的小团队,专注于构建分布式应用开发工具)。几个月前,我们一直在思考如何将 AI 安全地引入那些已经值得信赖的自动化流程。就在那时,我们发现了 GitHub Agentic Workflows,于是我开始将各种原型逐步集成到 microsoft/aspire 项目中。

最终带来的效果,可以直接用 GitHub 上的数据说明:在 Aspire 13.3 和 13.4 两个版本中,共有 82 个功能文档 Pull Request 被成功合并,从产品 Pull Request 合并到文档 Pull Request 合并的中位耗时仅为 44.8 小时,而且每一个文档 Pull Request 都由交付该功能的工程师完成审核。无需增加人手,无需重新培训流程,只是换了一种回答“谁来写这份文档?”的方式。

🔒 约束条件:跨仓库自动化才是真正的难点

我们的产品代码位于 microsoft/aspire,而文档网站位于 microsoft/aspire.dev——这是两个不同的仓库,也拥有各自独立的部署目标和审核流程。大多数团队很快就能实现同一仓库内的自动化,但真正困难的是跨仓库自动化。拥有整个仓库写权限的广域 Token 早就该进博物馆了,任何负责任的安全策略(包括我们的)都会严格限制它们。这是一件好事,但当文档仓库和代码仓库分离时,它也确实成为了一个现实的瓶颈。

多年以来,默认的工作流程一直是这样的:

  1. 工程师在 microsoft/aspire 中完成一个功能并合并。

  2. 几周之后,文档作者才注意到这个功能。

  3. 文档作者打开 Pull Request,阅读代码差异,并联系工程师确认到底发生了哪些变化。

  4. 工程师已经开始开发下一个功能,只能依稀记得一些细节,回复的信息往往并不完整。

  5. 文档草稿最终发布时,对应的产品版本有时已经正式上线。

这就是所谓的“逆向理解成本”。我们需要一种能够跨仓库工作的自动化方案,同时又不需要赋予智能体一个拥有全仓库写权限的 Token。而 GitHub Agentic Workflows 正好解决了这个问题。

🤖 为什么选择 GitHub Agentic Workflows

GitHub Agentic Workflows 是 GitHub Next 团队推出的一个项目。我经常向别人这样介绍它:“它就像 GitHub Actions,只不过把模型作为工作项处理器,同时提供能够通过安全审查的防护机制。”这种说法虽然有些简化,但已经非常接近它的本质。

它的整体工作方式如下:

  • 你只需使用一个 Markdown 文件.github/workflows/my-thing.md)来编写工作流。文件顶部使用类似 YAML 的 Frontmatter 元数据,下面则直接使用自然语言编写提示词。

  • 随后运行 GitHub Agentic Workflows compile,它会自动生成一个对应的 .lock.yml 文件(本质上是一个标准的 GitHub Actions 工作流),并与 Markdown 文件一起提交到仓库。

  • 运行时,整个工作流会使用受限工具集启动一个智能体,根据提示词执行任务。

  • 最关键的是,智能体不会直接向 GitHub 写入任何内容。它只会输出自己的意图(一个描述需要创建哪些 Pull Request、Issue 和评论的 JSON 对象),随后再由另一个权限严格受限的任务(safe-outputs handler)根据这些意图,通过专属于该工作流的 GitHub App 将其真正执行。

最后这一点,正是整个方案的关键所在。智能体只拥有读取权限和提示词;所有写操作都会经过一条权限极小、可验证且拥有明确允许列表的安全流水线。安全团队认可,我们也能够放心上线。

💚 一个小插曲:理念相通的技术栈

我一直很喜欢这样一种体验:你用来构建产品的工具,本身也是用同样的工具构建出来的。GitHub Agentic Workflows 官方文档采用 Astro 和 Starlight 构建,而 aspire.dev 同样如此——基于 Astro 与 Starlight,并结合了整个 Starlight 插件生态(包括 astro-mermaid、starlight-llms-txt、starlight-sidebar-topics、starlight-image-zoom、精美的 @catppuccin/starlight 主题等等)。特别感谢 Chris Swithinbank 以及所有 Starlight 维护者,整个生态都能让人感受到开发者对产品的用心。

这种一致性让人感觉十分自然。我们用来自动生成文档的工具,与文档网站本身建立在同样的技术基础之上。这也带来了一个很方便的结果:下一节中的 Mermaid 时序图,在两个环境中的渲染效果完全一致。

端到端工作流

下面就是我们最终采用的完整流程。整个流程的主角,是位于 microsoft/aspire 仓库中的一个工作流:pr-docs-check.md
在这里插入图片描述

整个流程会在 pull_request: closed 事件触发,并且目标分支为 mainrelease/*,同时要求 merged == true。随后,在智能体真正开始工作之前,工作流会首先通过一个纯 Bash 编写的确定性目标分支解析器来确定目标分支:

  1. 首先检查 Pull Request 所属的里程碑名称(例如 13.4,对应 aspire.dev 中的 release/13.4)。

  2. 然后检查关联 Issue 的里程碑名称(解析 Pull Request 描述中的 FixesClosesResolves #N,获取对应 Issue,并使用第一个非空的里程碑)。

  3. 接着检查 Pull Request 的基础分支,如果符合 release/X.Y[.Z] 格式,则直接使用。

  4. 如果以上都不满足,则默认回退到 main 分支。

这是整个流程最关键的一环。产品仓库中的里程碑能够自然映射到文档仓库中的发布分支。因此,当智能体真正开始工作时,它已经能够准确知道文档应该提交到哪个目标分支,而无需对目标分支进行任何猜测或“自由发挥”。

智能体随后读取代码差异,扫描关联 Issue,并判断:“这个功能是否需要更新文档?”如果答案是肯定的,它便会在已检出的 microsoft/aspire.dev 工作区中,根据我们现有的文档编写规范(包括写作风格、MDX 规范以及 Starlight 组件等)生成完整的文档内容,然后输出一个 create_pull_request safe-output,并将流程交给下一阶段。

随后,safe-outputs handler 接管整个流程:

  • Pull Request 标题统一添加 [docs] 前缀。

  • 自动添加 docs-from-code 标签。

  • 创建为草稿 Pull Request(draft: true),我们绝不会自动合并。

  • 目标分支由智能体提供,但仅允许为 mainrelease/*

  • 目标仓库固定为 microsoft/aspire.dev

  • 审核人则自动指定为源 Pull Request 中识别出的 SME,也就是当初审核并批准该产品功能的人——现在,他也会被邀请审核与该功能对应的文档。

与此同时,另一个配套任务还会回到源 Pull Request,发布一条包含文档 Pull Request 链接的标记评论;如果工作流重新运行,则会自动折叠之前由 pr-docs-check 创建的旧评论。刚刚点击 Merge 的工程师,几分钟后便会收到一条通知:“这是对应的文档草稿,方便帮忙看一下吗?”

🔐 Safe Outputs 合约

整个安全机制的核心,都归结于 Frontmatter 中一小段看似平凡的配置:

tools: 
  github: 
    toolsets: [repos, issues, pull_requests] 
    min-integrity: approved          # only run pinned, integrity-checked actions 
    allowed-repos: 
      - microsoft/* 
    github-app: 
      app-id: ${{ secrets.ASPIRE_BOT_APP_ID }} 
      private-key: ${{ secrets.ASPIRE_BOT_PRIVATE_KEY }} 
      owner: "microsoft" 
      repositories: ["aspire.dev", "aspire"] 

safe-outputs: 
  create-pull-request: 
    title-prefix: "[docs] " 
    labels: [docs-from-code] 
    draft: true                      # human-in-the-loop, always 
    base-branch: main 
    allowed-base-branches: [main, release/*] 
    target-repo: "microsoft/aspire.dev" 
    protected-files: blocked         # AGENTS.md, manifests, security config: hands off 
    fallback-as-issue: true

这段配置清晰地定义了整个安全边界。智能体获得的是一个 GitHub App Token,而该应用的安装范围仅限于两个仓库——产品仓库和文档仓库——组织中的其他任何仓库都无法访问。它只能向 mainrelease/* 分支发起 Pull Request。按照策略,AGENTS.md、依赖清单文件以及安全配置文件均禁止修改。如果创建 Pull Request 失败(例如网络抖动、代码冲突等),框架会自动退回到创建 Issue 的方式,确保任何工作都不会被悄无声息地丢弃。

这正是安全审查团队最认可的部分。智能体的推理过程可以具有一定的不确定性,但它能够执行的操作范围却是严格受控、可验证的。

📊 数据统计

下面的数据统计来自一个连续 30 天的时间窗口(2026 年 5 月 3 日至 6 月 2 日),覆盖了 Aspire 13.3 发布的后半程以及 13.4 发布前的准备阶段:

指标 数值
microsoft/aspire 中已合并的产品 Pull Request 396(338 个合并到 main、50 个合并到 release/13.3、8 个合并到 release/13.2
pr-docs-check 工作流运行次数 396
microsoft/aspire.dev 创建的文档 Draft Pull Request 82
- 已合并 82(100%)
- 未合并即关闭 0
- 仍处于打开状态 0
文档 Pull Request 的目标分支 52 → release/13.3,27 → release/13.4,3 → main
文档 Pull Request 合并中位耗时 44.8 小时
24 小时内 / 7 天内完成合并 38% / 96%

注:以上数据统计于本文撰写时获取。由于这些工作流仍在持续运行,因此所有统计数字只会不断增长。

其中,有几个数字尤其值得关注:

  • 396 次工作流运行仅生成了 82 个 Pull Request,这并不是缺陷。工作流会在每一个已合并的 Pull Request 上触发,而其中绝大多数只是内部重构、测试修复或依赖升级,并不会影响用户。因此,智能体能够 300 多次准确判断“无需更新文档”,本身就是这套系统成功的体现。

  • 100% 的合并率说明,智能体对于哪些变更需要编写文档的判断是准确的。我们在第一版出现误判之后优化了 Prompt,如今已经取得了明显成效。

✅ 哪些有效,❌ 哪些没有

哪些有效

  • Milestone → 发布分支映射。 这是整个方案中收益最高的一项设计。开发者本来就会为 Pull Request 和 Issue 设置 Milestone,因此我们几乎“免费”获得了准确的目标分支路由能力。

  • 始终创建 Draft Pull Request,并由 SME 担任 Reviewer。 智能体永远不会自动合并文档。真正发布功能的工程师负责确认文档是否准确。我们不再需要在文档阶段反向推导功能细节,而是让工程师直接在他们熟悉的地方告诉文档应该写什么。

  • 为每个工作流配置独立、权限受限的 GitHub App。 每个工作流都拥有自己的 GitHub App Token,并且仓库访问范围和权限都经过精确限定。这一点不仅顺利通过了安全审查,也让我们在第一次轮换密钥时切身体会到了它的价值。

  • protected-files: blocked 智能体无法修改 AGENTS.md、依赖清单或仓库安全配置。任何情况下都不允许。

哪些一开始没有做好

  • 智能体对于“是否值得编写文档”的判断过于宽松。 第一版会为一些纯内部改动(例如 CI 调整或日志重构)创建文档 Pull Request。最终,在 69 个 Pull Request 中有 9 个被关闭(约占 13%)。随后,我们收紧了 Prompt 中关于“面向用户变更”的定义,并增加了明确的反例(CI、内部辅助代码、仅测试修改等),如今误判率已经持续下降。

  • 跨仓库创建 Pull Request 需要一种官方文档并未明确说明的“双重检出”模式。 智能体在一个仓库中工作,而 Safe Outputs 又需要定位目标仓库来推送分支。我们的解决方案是将 microsoft/aspire.dev 检出两次:一次作为当前工作目录,一次放在 _repos/aspire.dev 下,使 Safe Outputs 能够稳定、确定地找到目标仓库。

  • 大型代码差异会迅速耗尽 Prompt Token。 因此,我们在 pre-agent-steps 的 Bash 脚本中预先提取 Pull Request 元数据(关联 Issue、Milestone、Base 分支等),让智能体获得的是一份小巧、结构化的摘要,而不是一个庞大的输入。这也是 GitHub Agentic Workflows 官方推荐的设计模式,并且实践证明效果很好。

总结

这套改造彻底改变了我们的思维方式。现在,一个功能只有在对应文档完成之后,才算真正完成。文档不再像拖在功能后面的一只空罐子,而是与功能同步推进。工程师的审核成为最终的质量关口,而机器人则负责完成大部分重复性的写作工作。

更重要的是,这并不是要取代技术文档作者,而是让他们摆脱重复劳动。过去,他们的大部分时间都花在反向推导功能细节上;如今,他们终于可以把精力投入到只有人类才能做好、也更有价值的工作中,例如撰写叙述性内容、示例程序、概念讲解以及各种无法仅凭代码差异自动生成的文档。而机器人则负责那些机械性的工作,例如“新增了一个配置项,因此需要更新参考文档”,这种原本没人真正喜欢做的事情。

特别感谢 GitHub Next 团队带来的 GitHub Agentic Workflows(尤其是将 Safe Outputs 设计为一等公民能力),也感谢 Chris Swithinbank 以及 Starlight 维护团队打造了我们所依赖的文档平台。同时,也真诚感谢安全团队提出的各种限制条件,正是这些护栏促使我们第一次就设计出了正确的方案。优秀自动化系统最朴素的秘密在于:严格的安全约束,反而能够让整个系统更加值得信赖,也更加可靠。

如果你的产品代码位于一个仓库,而文档又位于另一个仓库——尤其是在需要满足较严格安全要求的企业环境中——那么 GitHub Agentic Workflows 非常值得认真尝试。不妨从一个像 pr-docs-check 这样的工作流开始,然后观察你的文档交付周期会发生怎样的变化。

🔗 其他工作流

pr-docs-check 是本文重点介绍的工作流,但它并不是唯一运行中的自动化流程。如果你对其他工作流感兴趣,所有源码都已公开:

  • milestone-changelog.md:每两小时运行一次,收集当前 Milestone 中新近合并的 Pull Request,持续维护 13.x-Change-log Wiki 页面(包括新功能、功能改进和重要 Bug 修复),并同步创建一个配套的编辑反馈 Issue。目前已运行 346 次

  • release-update-support-mdx.md:当 Aspire 发布稳定版本后,自动在 aspire.dev 创建一个带有 [support] 前缀的 Pull Request,用于更新支持策略页面,包括提升新版本状态、下调上一版本状态以及更新“Last updated”标识。

  • update-integration-data.md:位于文档仓库中,每天执行一次 pnpm update:all,刷新 NuGet 元数据、GitHub 统计信息以及示例数据,并创建一个标题为 chore: Update integration data 的 Pull Request,同时支持自动替换并关闭过期运行。目前已运行 27 次,并完成了 8 个 Pull Request 的合并

  • repo-pulse.md:一个持续滚动更新的三天仓库状态面板,固定维护在同一个 Issue 中,实时展示最近合并记录、等待审核的 Pull Request、新增 Issue 以及 Discussion 活动。始终只有一个 Issue,但内容持续保持最新。

祝大家自动化开发愉快!🤖🚀

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐