让仓库自我维护:goose 如何用 GitHub Actions 把 Issue 变成可合入的 PR
本文源文档:documentation/blog/2025-12-28-goose-maintains-goose/index.md
开源项目 goose 是一个开源、可扩展的 AI Agent,它不仅能写代码,还能安装、执行、编辑与测试代码。当 AI Agent 让更多人能轻松参与开源贡献时,维护者面对的问题也随之增多。goose 团队给出了一种自我消化的解法:把 goose 直接嵌入 GitHub Actions,让维护者在 Issue 下评论一条 /goose ...,goose 便会在容器中读取 Issue、探索代码库、运行验证,最终提交一份 draft PR。阅读本文后,你将理解这套"Issue → PR"自动化流水线的完整设计:触发方式、recipe 的分阶段约束、TODO 扩展如何充当外部记忆,以及维护者角色如何从"实现者"转变为"审阅者"。
背景:维护者为什么需要让 AI 处理自己的 backlog
随着 AI Agent 能力增强,越来越多人愿意写代码并贡献开源。这对生态是净收益,却也改变了维护者的日常现实——goose 团队正面对数量增长远超处理速度的 PR 与 Issue。与其抗拒,不如拥抱:
- 人工排查一个用户反馈通常横跨数小时甚至数天;
- 低优先级问题常常在 Discord 或 GitHub 评论区滚动消失,用户会以为没人倾听;
- 即便是修复,也常因"谁有足够上下文并抽出时间写这段代码"而卡住。
开源真正的瓶颈往往不是"能否有人写出这段代码",而是"能否有掌握足够上下文的人抽出时间写这段代码"。goose 团队的答案是把 goose 用在自己的维护工作上,做自己的第一个用户。
事实上,goose 在 1.0 发布前就借助 goose 本身完成了从 Python CLI 到 Rust、Electron 与 MCP-native 架构的迁移。将这种能力延伸到 Issue 分类与变更审查,成为很自然的下一步——于是他们把 goose 直接嵌入了 GitHub Actions 工作流(即上游仓库中公开的 goose-issue-solver.yml 工作流,最初由 Tyler Longwell 构建,将团队手动探索的想法变成任何维护者都能用一条评论触发的机制)。
第一阶段:本地会话中的"单次对话修复"
在 GitHub Action 出现之前,goose 团队已经用本地 goose 加速 Issue 工作流。原文记录了一个真实案例:
- 一位用户在 Discord 反馈:某个 Ollama 模型在 chat 模式下抛错,原因不明;
- 维护者没有亲自翻代码,而是让 goose 探索代码、定位根因并解释回来;
- 随后让 goose 借助 GitHub CLI(
gh)打开了一个 Issue; - 同一会话中,goose 表示有 95% 的把握修复该问题。由于改动很小,维护者让 goose 直接打开一个 PR,当天即被合并。
对比传统路径,问题报告的处理是碎片化的:先澄清问题、在 GitHub 搜索相关 Issue、拉最新代码、grep 文件、阅读逻辑、形成假设——要么写成详细 Issue 塞进开发者的 backlog(后来者还需重新切换上下文),要么亲自动手修复(常常耗费更多时间并在 review 阶段反复往返)。而用 goose,整个过程坍缩为一次对话。
但本地工作流仍有局限:问题仍由维护者驱动——需要停下来、开启会话、粘贴 Issue 上下文、引导 goose 修复、跑测试、开 PR。
第二阶段:把整条链路压缩进一条评论
GitHub Action 将上述全部步骤压缩为一条评论。团队成员看到某个 Issue,评论 /goose 后就可以继续做别的事;goose 在容器中启动,读取 Issue,探索代码库,运行验证,然后打开一个 draft PR。维护者回来时面对的是一个"已给出的解决方案",而不是一块空白的编辑器。
原文记录了三个可复现的实例:
| 案例 | 现象 | 触发 | 结果 |
|---|---|---|---|
| 时间问题 | goose 总是默认使用 2024 年,尽管上下文里已有正确日期(Issue 搁置两天) | 维护者凌晨 1:59 评论 /goose solve this minimally |
14 分钟后 goose 打开了对应的 PR(#6101) |
| 社区修复 | 用户反馈 slash commands 不能正确处理可选参数 | 维护者评论 /goose can you fix this |
一小时内出现带修复与 4 个新测试的 draft PR |
| Ollama 报错 | 用户报告模型在 chat 模式抛错 | 本地会话驱动 | 当天开 PR 并合并 |
这背后的价值在于可扩展性:手动分类无法规模化,而 backlog 同时混杂着功能请求、复杂 Bug 和快速修复。Action 允许你指向某个 Issue 说"试试这个",而无需押上整个下午——goose 失败只损失几分钟算力,成功则节省数小时。对贡献者而言,响应速度改变一切:即便 PR 不完美、需要调整,贡献者也能看到项目的动能。
Under the Hood:六阶段 recipe 约束 Agent 行为
表面上,触发方式只是类似 /goose fix this 的评论;实际上工作流内部定义了远超一条简单提示的约束。核心机制是一个 recipe(配方),它用阶段(phase)确保 goose 真正完成任务,且不做超出要求的事:
| 阶段 | goose 做什么 | 为什么重要 |
|---|---|---|
| Understand | 阅读 Issue,把所有需求提取到文件中 | 迫使 AI 在写代码前先明确"完成"长什么样 |
| Research | 用搜索与分析工具探索代码库 | 防止对陌生代码盲目修改 |
| Plan | 决定实现方案 | 在动手前先拦截架构性错误 |
| Implement | 严格按需求做最小改动 | "需求里没有的,就不要加" |
| Verify | 运行测试与 linter | 在人类看到 PR 前拦截明显失败 |
| Confirm | 重读原始 Issue 与需求 | 防止 AI 宣布胜利却漏掉一半任务 |
recipe 还授予 goose 对 TODO 扩展的访问权限——这是一个内置工具,充当外部记忆。简单说:phase 告诉 goose 该做什么,TODO 帮 goose 记住自己在做什么。当 goose 在代码库中逐步构建解决方案时,上下文窗口会被填满,早期指令可能被压缩或丢失;TODO 持久存在,让 goose 随时可以核对已完成与未完成项。
从仓库源码可以印证这一机制的设计:
- TODO 扩展是 goose 内置的 platform extension,实现位于 crates/goose/src/agents/platform_extensions/todo.rs。其官方文档 documentation/docs/mcp/todo-mcp.md 说明:当任务涉及多文件/多组件或范围不确定时,goose 会自动创建内部清单,边工作边读取和更新进度,并在最后核对全部任务是否完成;你也可以随时让 goose "show me the current todo list"。
- recipe 是 goose 的一等公民能力。配方文件支持 YAML 与 JSON 两种格式,见 crates/goose/src/recipe/mod.rs 中
RECIPE_FILE_EXTENSIONS = &["yaml", "json"],Recipe结构体(同文件 L42 附近)定义了 title、description、instructions、extensions 等字段。
可落地的 recipe 文件长什么样
本仓库自带一份真实可运行的 recipe,位于 workflow_recipes/release_risk_check/recipe.yaml。它演示了"分段式 instructions + 声明式扩展 + 参数"的组合:
version: 1.0.0
title: "Release Change Risk Check"
description: "Create a report to assess the change in an upcoming release"
instructions: |
## Step 1: Generate the heuristic report
Run the script to collect PR data and do initial risk scoring:
{{recipe_dir}}/release_risk_report.py --version {{version}} -o /tmp/release_report.md
...
## Step 2: AI review of MEDIUM and HIGH risk PRs
...
## Step 3: Generate the final report
...
prompt: follow the instructions to generate the final report
parameters:
- key: "version"
input_type: string
requirement: required
description: "release version"
extensions:
- type: platform
name: developer
对照 documentation/docs/guides/recipes/session-recipes.md 中完整的字段参考,recipe 还支持更多配置项:activities(桌面端可点击的示例任务)、settings(goose_provider / goose_model / temperature)、retry(自动重试与成功校验)、response(结构化 JSON 输出)、以及通过 {{ variable_name }} 模板变量 + parameters 声明实现参数化运行。
在 CLI 中使用 recipe 的常用命令包括:
# 在会话内由当前会话生成 recipe
/recipe
/recipe my-custom-recipe.yaml
# 运行本地 recipe
goose run --recipe recipe.yaml
goose run --recipe ./recipes/my-recipe.yaml --interactive
goose run --recipe recipe.yaml --params language=Python
# 校验 recipe 是否完整、参数格式是否正确、引用的扩展是否存在
goose recipe validate recipe.yaml
recipe 既可以存放在本地目录,也可以存放在 GitHub 仓库中统一共享:在 crates/goose-cli/src/recipes/github_recipe.rs 中可以读到完整检索逻辑——配置 GOOSE_RECIPE_GITHUB_REPO(owner/repo)后,goose 会用 gh 克隆配方仓库、用 git archive 抽取对应目录并解析其中的 recipe.yaml/recipe.json(从源码结构看,检索过程还包含了认证检查与临时目录清理等防护逻辑)。这正是 issue-solver 工作流能按 recipe 名调度修复流程的基础设施。
护栏:谁可以触发、能碰哪些文件、谁来做最终裁决
工作流同时强制了几层护栏:
- 触发权限:限制谁可以调用
/goose,避免任意用户消耗 CI 算力或滥用 Agent 能力; - 文件作用域:限定 goose 允许触碰的文件范围,防止修复扩散到无关模块;
- 人工审阅:要求维护者对每个 PR 进行 review 与 approve,Agent 只提交 draft PR,不直接合并。
在仓库侧的 recipe 执行栈里也能看到配套的安全设计:例如 crates/goose/src/recipe/read_recipe_file_content.rs 中的测试明确覆盖了"拒绝读取符号链接指向的外部文件",防止恶意 recipe 借路径逃逸读取任意位置;read_recipe_file 对超出默认工具响应阈值的超大 recipe 内容也做了放行处理,说明配方内容会被完整注入、而读取路径被严格收敛。这正是把"AI 能碰什么"做成硬约束的工程体现。
为什么"goose 维护 goose"是一件值得坚持的事
用 goose 维护 goose 看起来有些奇妙,但它让团队保持诚实:我们是自己的第一个客户——如果 Agent 在这里产不出可合入的 PR,团队会立刻感受到。
原文点出了这套实践面向的未来:并非 AI 取代维护者,而是维护者可以指向一个问题说"试试这个",然后回到一个具体的提案面前,而非一块空白编辑器。如果这成为常态,开源将以不同的方式规模化。
这套 GitHub Actions 工作流在源项目中是公开的,任何维护者都可以在自己的 CI 流水线中参考该模式。对于本镜像仓库的读者,若要复现类似实践,可以从以上仓库内的 recipe 规范、本地 recipe 示例(workflow_recipes/release_risk_check/recipe.yaml)与 TODO 扩展文档出发,先本地验证"分阶段约束 + 外部记忆 + 最小改动"的写法,再移植到自己的 CI 工作流中。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00