Storybook Agent 技能解析:逐条交互式处理 PR 评审评论的 handle-pr-comments 工作流
Storybook 仓库在 .claude/skills/ 与 .agents/skills/ 目录下沉淀了一套面向编码 Agent 的技能(Skill)定义,其中 handle-pr-comments 技能定义了如何对 PR 上的评审评论进行"逐条、交互式"的分诊与解决。本篇技术文章完整解析该技能文件的工作流设计——从 PR 定位、GraphQL 评审线程拉取,到逐条评论的"总结—建议—询问—执行—提交—解决"交互循环——并结合仓库中的技能族与 Agent 指令文件,说明这套人机协作(human-in-the-loop)模式的工程考量,帮助读者理解如何为大型 TypeScript monorepo 编写可复用的 Agent 技能。
技能文件的位置与结构
handle-pr-comments 技能的正本位于 .agents/skills/handle-pr-comments/SKILL.md,而 .claude/skills/handle-pr-comments/SKILL.md 仅是一个指向该文件的引用(内容为一行相对路径 @../../../.agents/skills/handle-pr-comments/SKILL.md)。这种"薄引用"布局与仓库 Agent 指令体系保持一致:AGENTS.md 明确要求"Keep CLAUDE.md and other agent entrypoints as thin references to AGENTS.md",即各 Agent 入口文件不复制内容、只指向唯一的事实来源,避免指令漂移。
技能文件采用标准的 SKILL.md 格式,由两部分构成:
- YAML frontmatter——机器可读的元数据:
---
name: handle-pr-comments
description: Triage and resolve GitHub PR review comments one by one, interactively. Use when the user asks to handle, address, respond to, or resolve PR review comments, or mentions reviewer feedback on a pull request.
---
description 字段承担"触发器"职责:它不仅概括了技能的能力(逐条、交互式地分诊并解决 PR 评审评论),还列举了用户可能使用的意图关键词(handle / address / respond to / resolve),供 Agent 在判断何时调用该技能时匹配。
- Markdown 正文——面向 Agent 执行的自然语言工作流指令,包含一个四步编号流程和一个"Notes"补充规则小节。
值得注意的是,与仓库中声明了 allowed-tools: Bash, Read(如 pr 技能)或 allowed-tools: Bash, Read, AskQuestion(如 open-pr 技能)的技能不同,handle-pr-comments 未显式声明工具白名单。从技能文本的实际流程看,它依赖三类能力:shell 命令(gh 与 GraphQL 请求)、文件读取(查看评论对应的代码上下文)以及交互式提问(AskQuestion)。
工作流第一步:定位 PR
技能的第一步是确定要处理的 PR:
- 如果用户给出了 PR 编号或 URL,直接使用;
- 否则从当前分支推导(
gh pr view); - 若不存在 PR,立即停止。
"无 PR 即停止"是一个显式的失败退出条件。这一点在 Agent 工作流中很重要:它阻止 Agent 在缺乏目标的情况下臆造 PR 或误操作其他 PR,与 AGENTS.md 中"Verify environment assumptions empirically before encoding them"的实证风格一脉相承。
工作流第二步:用 GraphQL 拉取未解决的评审线程
技能明确要求通过 GraphQL 的 reviewThreads 字段获取 Pull Request 上的评审线程,并指定了每个线程需要请求的字段:
| 字段 | 用途 |
|---|---|
id |
线程唯一标识,后续 resolveReviewThread mutation 的入参 |
isResolved |
过滤条件:仅保留 isResolved == false 的线程 |
isOutdated |
标记因代码变更而失效的评论,帮助判断是否需要重新处理 |
path / line |
评论定位到的文件与行号,用于读取上下文 |
| comments | 线程内的评论正文 |
文档特别强调了一个实现层面的技术选型理由(见 SKILL.md 第 12 行):
Use GraphQL, not REST: only it exposes thread
ids and resolution state.
即:必须使用 GraphQL 而非 REST,因为只有 GitHub GraphQL API 才暴露评审线程的 id 与"已解决/未解决"状态。这是一个很典型的 Agent 技能写作细节——把 API 选型的约束直接写进指令,可以避免 Agent 在运行时尝试 REST 端点后卡住或走偏。
工作流第三步:逐条评论的交互循环
这是技能的核心。它对每一个未解决线程执行一个六环节的子循环(见 SKILL.md 第 14 行):
- (a) 总结反馈 + 文件/行号:读取评论指向位置的周边代码("reading surrounding code"),形成对该条意见的准确理解,而不是只看评论文字;
- (b) 给出具体的修复建议:必须是可落地的修复方案,而非泛泛回应;
- (c) 通过 AskQuestion 向用户提问,并给出四个选项——apply suggested fix(应用建议的修复)/ apply a different fix(采用另一种修复)/ reply only(仅回复评论)/ skip(跳过该条);
- (d) 应用用户的选择;
- (e) 如适用,总结改动内容并询问是否提交;
- (f) 通过 GraphQL 的
resolveReviewThreadmutation 解决该线程。
这个循环的设计意图是一次只处理一条评论("Loop one comment at a time")。对于 Agent 自动化而言,批量自动解决所有评论看似高效,但风险很高:评论可能包含歧义、需要产品判断、或用户只希望部分采纳。逐条处理 + 每条一次 AskQuestion,把决策权交还给人类,Agent 只负责信息收集与执行。这与仓库内其他技能的交互风格一致,例如 update-pr-description 技能 要求"Ask the user one change at a time",open-pr 技能 在标签选择处同样使用 AskQuestion。
其中 resolveReviewThread 与第二步的线程 id 呼应:正因为第二步通过 GraphQL 拿到了 id,最后才能把线程标记为已解决——如果当初用 REST 拉取,这个闭环是走不通的。
工作流第四步:生成处理报告
全部处理完毕后,技能要求输出一份总览报告,涵盖:改动了什么、提交了什么、回复了什么、跳过了什么、解决了什么,并附上链接。
这一步保证了整个交互过程对人类可审计:用户在 Agent 执行完一批操作后,无需逐条回翻对话即可核对结果。
补充规则:优先级、去重与提交粒度
技能正文末尾的 Notes 小节(见 SKILL.md 第 18–22 行)给出三条执行规则:
- 真人评论优先于 AI Agent 评论。仓库的评审流中同时存在人类评审者与 AI 评审方(GitHub Copilot、CodeRabbit),当两者都留下未解决线程时,先处理真人意见。这一优先级设定反映了评审意见的权重差异——AI 生成的评论通常是可批量裁决的,而真人评论更可能承载业务判断。
- 重复合并处理。当同一或相似问题被多次提出时,分组后一次性处理,避免重复修改同一处代码。
- 每个变更单独提交("Make a separate commit for each change")。这与仓库 AGENTS.md 的质量流程相衔接:AGENTS.md 要求改动后执行
yarn fmt:write(oxfmt 格式化)、针对文件的 lint(yarn --cwd code lint:js:cmd <file> --fix)以及相应测试,且 pre-commit hook 会检测 AI Agent 并自动进入自动修复模式。按变更粒度拆分提交,使得每个 commit 都能独立通过这套格式、lint 与测试校验,也便于维护者在评审时按条核对。
与仓库 PR 技能族的协作关系
handle-pr-comments 并非孤立存在,它与 .agents/skills/ 下的其他技能共同覆盖了 Storybook 的 PR 全生命周期:
| 技能 | 职责 | 所处阶段 |
|---|---|---|
| pr | PR 约定:标题格式 [Area]: [Description]、类别/CI/QA 三组标签体系 |
约定层 |
| open-pr | 从当前分支创建 draft PR,含基线分支探测脚本 | 创建 PR |
| update-pr-description | 对照 commits 与 diff 校正 PR 标题/描述 | 描述维护 |
| handle-pr-comments | 逐条交互式处理评审评论 | 评审响应 |
| fix-linting-types-on-pr | 修复 PR 上的 lint/类型错误 | CI 修复 |
| canary | 为 PR 发布 canary 版本 | 发布验证 |
其中 pr 技能 定义了本仓库 PR 的标题与标签规范(类别标签如 bug、documentation,CI 标签如 ci:normal、ci:docs,QA 标签如 qa:needed),并要求 PR 正文严格按 .github/PULL_REQUEST_TEMPLATE.md 模板填写。处理完评审意见后若需调整 PR 描述,可衔接 update-pr-description 技能——该技能同样以"证据优先"为原则:先用 gh pr view --json title,body、--json commits 和 gh pr diff 收集证据,再迭代式地征得用户同意后修改。可以看到,"先取证、再行动、逐条确认"是这一技能族共享的方法论。
小结:一个可复用的 Agent 技能写作范式
从 handle-pr-comments 中可以提炼出编写编码 Agent 技能的几个通用做法,均能在 Storybook 仓库的 .agents/skills/ 目录中得到印证:
- frontmatter 的 description 即触发条件:写明技能能力 + 用户意图关键词,让 Agent 能自动匹配何时调用;
- 显式失败退出条件:如"无 PR 则停止",防止 Agent 在无目标时臆造行动;
- 把 API 选型的硬约束写进指令:如"用 GraphQL 而不是 REST,因为只有前者暴露 thread id 与解决状态",并说明原因,使 Agent 知其然亦知其所以然;
- 逐条处理 + 选项式交互:将高风险决策(改不改、怎么改、提不提交)以带选项的 AskQuestion 交还人类,Agent 只负责总结、建议与执行;
- Notes 小节承载裁决规则:评论优先级、重复合并、提交粒度等无法机械执行的策略,用简短规则表达;
- 入口薄引用:
.claude/skills/下的技能文件只是指向.agents/skills/本本的引用,保证单一事实来源。
这套模式使 Storybook 这类大型 monorepo 中的 Agent 工作既可交互可控,又可跨 Claude Code、Codex 等多种 Agent 入口复用同一份技能定义。
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 StartedRust0627
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