Gemini CLI 的 pr-address-comments 技能解析:把 GitHub PR 评论处理流程变成可复用的 Agent 工作流
本文以 Gemini CLI 仓库自带的 pr-address-comments 工作区技能(workspace skill)为核心,完整还原其“获取 PR 上下文 → 汇总评论状态 → 交由用户决策”的三步处理流程,并深入解析其配套脚本 fetch-pr-info.js 的实现细节。读完本文,你将理解 Gemini CLI 如何通过 Agent Skills 机制把一个团队协作流程固化为可发现、可激活、可执行的标准工作流,并掌握复刻同类“PR 协作技能”的方法。
技能定位:它解决什么问题
pr-address-comments 是 Gemini CLI 开发团队放在仓库内部(.gemini/skills/pr-address-comments/)的一个技能,用于帮助用户处理当前分支 Pull Request 上的评论——这些评论可能来自自动化评审机器人,也可能来自团队成员。技能的完整定义见 SKILL.md,其 YAML frontmatter 声明了触发条件:
---
name: pr-address-comments
description: Use this skill if the user asks you to help them address
GitHub PR comments for their current branch of the Gemini CLI.
Requires `gh` CLI tool.
---
从描述可以确认两个关键前提:
- 作用域:针对“当前分支”的 Gemini CLI PR,而不是任意仓库的任意分支;
- 外部依赖:需要本机已安装并登录
ghCLI(GitHub 官方命令行工具),脚本中的所有 GitHub 数据均通过gh获取。
技能的目录结构非常精简,是一个典型的“指令 + 确定性脚本”组合:
.gemini/skills/pr-address-comments/
├── SKILL.md # 技能说明与处理流程(注入给模型的正文)
└── scripts/
└── fetch-pr-info.js # 拉取 PR 信息的 Node.js 脚本
这种结构的意义在于:把“从 GitHub 拉取数据”这类需要确定性结果的操作交给脚本,把“理解、归纳、与用户沟通”这类需要推理的操作交给模型,各取所长。
Agent Skills 机制:技能如何被发现与激活
理解这个技能之前,先了解 Gemini CLI 的 Agent Skills 生命周期(详见 docs/cli/skills.md):
- 发现(Discovery):会话开始时,CLI 扫描各发现层级,把所有已启用技能的
name和description注入系统提示词。.gemini/skills/下的技能属于 Workspace 层级(优先级最高档之一),随版本库共享给团队——pr-address-comments正是靠这一机制被仓库内所有协作者自动发现。 - 激活(Activation):当用户请求匹配技能描述时(例如“帮我处理一下这个 PR 的评论”),模型调用
activate_skill工具。 - 确认(Consent):UI 展示技能名称、用途及其将获得访问权限的目录路径,等待用户批准。从 activate-skill.ts 的源码可以看到,该工具通过
getConfirmationDetails返回确认细节,getDescription会拼出"技能名": 描述供用户判断,确认逻辑由消息总线(MessageBus)驱动。 - 注入(Injection):批准后,
SKILL.md正文与目录结构被追加到对话历史,技能目录被加入 Agent 的允许文件路径,模型因此有权读取scripts/下的脚本。 - 执行(Execution):模型按照注入的流程指导继续工作。
也就是说,用户只需要说一句“帮我处理 PR 评论”,模型即可在用户授权后获得这份 13 行流程指令 + 配套脚本的全部权限,而无需在每次对话中手工粘贴说明。
核心流程:三步处理 PR 评论
SKILL.md 的正文是写给模型的操作规程,目标明确:“Help the user review and address comments on their PR.”(帮助用户评审并处理 PR 上的评论)。其流程共三步,每一步都有严格的约束:
第 1 步:运行 fetch-pr-info.js 获取 PR 全量上下文
原文要求:运行
scripts/fetch-pr-info.js获取 PR 信息与状态,即使输出被截断也必须完整读取整个输出。
这条约束是实战经验的直接体现:脚本一次性输出 diff、提交日志、全部评论,信息量可能超出单次工具调用的展示上限;如果模型只看了截断后的前段就开始分析,会漏掉后半段的评论线程。因此指令强制要求模型分页/滚动读完整输出,才能进入归纳环节。
第 2 步:交叉比对,归纳评论状态
模型需要同时分析 diff、提交日志和评论,判断每条评论是否已经在新提交的代码中被解决(例如评审人指出第 40 行有问题,而 diff 显示该行已修改),并按统一格式输出汇总:
- 已解决(resolved)的线程:压缩为一行,以 ✅ 标记;
- 未解决(open)的线程:赋予引用编号(如
[1])并附上评论内容,方便用户后续指代。
特别注意指令中要求的“Pay attention to the current user's comments”——即优先关注当前用户本人的评论(比如自己在 PR 上留下的待办性评论),这决定了汇总的重点排序。
第 3 步:呈现汇总,等待用户决策
原文要求:呈现反馈与当前状态的汇总,由用户决定“修哪些 / 处理哪些 / 跳过哪些”。不要自动开始修复(DO NOT begin fixing issues automatically)。
这是整个技能最重要的安全护栏:技能的价值止于“信息整理与决策支持”,实际改代码的启动权交还给人。这种 human-in-the-loop 设计避免了 Agent 在未确认的情况下批量改动评审意见对应的代码。
脚本深度解析:fetch-pr-info.js 如何一次性喂饱模型
fetch-pr-info.js 是整个技能的数据底座,全文约 160 行,纯 Node.js 实现(仅依赖 node:child_process 与 node:util,无第三方包),值得逐段拆解。
统一封装的 shell 调用:run()
脚本顶部封装了一个容错的 run() 函数(第 16–26 行):
async function run(cmd) {
try {
const { stdout } = await execAsync(cmd, {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'ignore'], // 丢弃 stderr
});
return stdout.trim();
} catch {
return null; // 命令失败时返回 null,由调用方判空
}
}
两个设计点:stdio 第三个位置设为 'ignore',避免各命令的 stderr 噪声污染输出;失败时返回 null 而非抛错,把“哪个数据源失败”的决策留给上层逻辑。
四路并发数据采集
main() 首先确认当前 git 分支(拿不到直接以退出码 1 结束),随后用 Promise.all 并发执行四个数据源(第 48–55 行):
| 数据源 | 命令 | 用途 |
|---|---|---|
| GitHub 身份 | gh auth status -a |
确认当前操作者(“current user's comments”的判定基准) |
| PR diff | gh pr diff |
判断评论是否已被最新代码修复 |
| 提交历史 | git fetch && git log origin/main..origin/<branch> |
了解分支上已有哪些提交,辅助判断评论状态 |
| PR 元数据 | gh api graphql -F branch="..." -f query='...' |
拉取评论、评审、线程结构 |
注意提交历史用的是 origin/main..origin/branch 区间并先 git fetch,保证比较基准是远端最新的 main,而不是本地可能落后的引用。若 gh pr diff 返回空,脚本判定当前分支没有活跃 PR 并退出——这与技能“面向当前分支 PR”的定位一致。
GraphQL 查询:精确圈定要哪些字段
脚本内嵌的 GraphQL 查询(第 46 行)结构如下:
repository(name: "gemini-cli", owner: "google-gemini")
└── pullRequests(headRefName: $branch, first: 100)
├── state
├── comments(first: 100) # PR 级通用评论
│ └── createdAt, isMinimized, minimizedReason, author, body, url, authorAssociation
└── reviews(first: 100) # 评审及行内评论
├── state, author, createdAt, body
└── comments(first: 30)
└── replyTo{id}, path, line, startLine, originalLine, originalStartLine
几个值得注意的点:
- 按 head 分支反查 PR:
headRefName: $branch让脚本无需用户提供 PR 编号即可定位 PR; - 分页上限显式化:PR、通用评论各取 100 条,每个 review 的行内评论取 30 条——对单条 PR 足够,且避免无界拉取;
replyTo{id}是关键:行内评论的replyTo字段是后续构建“线程树”的依据;originalLine/originalStartLine:GitHub 在代码行被后续 push 修改后会让line失效,originalLine提供定位兜底,脚本在输出行号时做了c.startLine || c.originalStartLine的降级处理。
需要说明的限制:查询中 owner: "google-gemini"、name: "gemini-cli" 是硬编码的,因此这个脚本只适用于 Gemini CLI 仓库本身。这是它与技能描述中“for their current branch of the Gemini CLI”完全对应的——它是一个仓库专属的工作区技能,而非通用 PR 工具。
噪声过滤:IGNORE_MESSAGES 白名单
评审机器人会产生大量模板化留言,脚本用一个精确匹配表把它们从输出中剔除(第 28–37 行):
const IGNORE_MESSAGES = [
'thank you so much for your contribution to Gemini CLI!',
"I'm currently reviewing this pull request and will post my feedback shortly.",
'This pull request is being closed because it is not currently linked to an issue.',
];
被过滤的是致谢、评审进度提示、关闭通知这类“流程性”留言。用 body.includes(msg) 做包含匹配而非全等匹配,可以容忍模板前后附加内容。这个过滤对模型侧同样重要:减少无信息量的输入,降低“把机器人寒暄当成待处理评论”的概率。
输出结构:为 LLM 阅读优化的分段排版
脚本的最终输出分为四个 Markdown 式区块,顺序即模型消费顺序:
# Current GitHub user info:gh auth status -a的结果,用于识别“当前用户是谁”;# PR diff for current branch: <branch>:完整 diff,包在代码围栏内;# Commit history (origin/main..origin/<branch>):提交日志;# PR Feedback:分三个子块——- General Comments:过滤后的 PR 级评论,每条标注
[时间] [作者],被最小化(minimized)的评论会附(Minimized: 原因); - Review 摘要:每个有正文的评审一行呈现,
APPROVED状态用 ✅ 前缀,其余用 💬; - Code Reviews & Inline Threads:先输出所有评审摘要,再重建行内评论线程。
- General Comments:过滤后的 PR 级评论,每条标注
线程重建逻辑是脚本的精华(第 120–152 行):
const topLevelThreads = filteredInlines.filter((c) => !c.replyTo);
const printThread = (parentId, depth = 1) => {
const indent = ' '.repeat(depth);
filteredInlines
.filter((c) => c.replyTo?.id === parentId)
.forEach((reply) => {
console.log(`${indent}↳ [${reply.createdAt}] ${reply.author.login}${minimized}: ${reply.body}`);
printThread(reply.id, depth + 1); // 递归打印更深层回复
});
};
以 replyTo 为边,把扁平的评论列表还原成带缩进的树形线程:顶层评论打印 作者 | 时间 | (文件路径:行号范围) 定位信息,其下所有回复以 ↳ 加两格递增缩进递归展开。行号范围按 startLine/line 计算,起止不同则输出 start-end 区间,否则只输出单行号。这样模型(和用户)看到的不再是离散的评论碎片,而是“某个文件某几行处的一整段讨论”,与 SKILL.md 第 2 步“按线程归纳状态”的要求严丝合缝。
失败路径一览
脚本对三种失败情形都有显式出口,全部 process.exit(1) 并在 stderr 给出可读原因:
- 无法确定当前 git 分支(detached HEAD 等非分支状态);
gh pr diff为空(当前分支无活跃 PR,或未登录gh);- GraphQL 结果中找不到 PR 节点。
这些“快速失败”很重要:技能第 1 步之后的一切归纳都建立在完整数据之上,数据缺失时直接终止比让模型基于残缺输出猜测要安全得多。
流程设计哲学:值得借鉴的三点
这个技能虽然只有 13 行指令,但体现了三条在 Agent 技能设计中普遍适用的原则:
- 确定性数据交给脚本,模糊判断交给模型。拉 diff、解析 GraphQL、重建线程树,这些用代码做是稳定且可重复的;而“这条评论是否已被新提交解决”需要理解代码语义,交给模型交叉比对。脚本输出的排版(分段标题、线程缩进、行号定位)本身就是为下游 LLM 消费设计的。
- 强制完整性读取。第 1 步的“即使截断也要读完整个输出”是对长工具输出的显式防御,属于把已知失败模式写进规程的典型案例。
- 决策权留在人手里。“DO NOT begin fixing issues automatically”把 Agent 的角色锚定在“分析与建议”,执行动作必须经用户逐条确认——这与仓库中其他协作技能形成呼应:pr-creator 负责按模板创建 PR(并强调“绝不 push 到 main”),async-pr-review 处理异步评审,
pr-address-comments则专注“收到反馈之后”的环节,三者覆盖了 PR 生命周期中可自动化的不同切面。
如何复刻一个类似的协作技能
如果你想在自己的团队仓库中做一个“PR 协作”类技能,可以参照本技能的结构与 创建技能指南:
mkdir -p .gemini/skills/my-skill/scripts建立工作区技能目录;- 在
SKILL.md中写清 frontmatter(name+ 描述触发场景)和分步流程,用明确的 MUST/DO NOT 句式划定模型行为边界(例如“先汇总、后等指令、不自动修改”); - 把需要确定性结果的部分(数据拉取、格式化)写成
scripts/下的自包含脚本,失败时显式exit(1); - 提交到仓库后,技能即随
.gemini/skills/工作区层级对全团队生效;用/skills list或gemini skills list --all验证是否被发现(见 skills 文档)。
最后再次强调适用前提:pr-address-comments 的脚本硬编码了 google-gemini/gemini-cli 仓库,并依赖已登录的 gh CLI 与可用的 git fetch;直接复用其流程模板是合理的,直接运行其脚本则仅在本仓库内有效。
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 StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00