Storybook 的 Agent 技能实战:用 update-pr-description 技能让 AI 帮你校准 PR 标题与描述
本篇围绕 Storybook 仓库中的 Agent 技能文件 update-pr-description/SKILL.md 展开,讲解该技能如何通过一套标准化的六步工作流,让编码 Agent 对比 PR 的标题/描述与其实际实现(commit + diff),逐条提出并应用修正。读完后你将掌握:如何在自己的大型仓库中组织"一个核心技能文件 + 多工具镜像"的 Agent 技能体系,以及 PR 描述与 CI 流水线之间如何通过 HTML 注释锚点(如 canary 区段)形成机器可读的协作契约。
技能体系定位:.claude/skills 是指向 .agents/skills 的镜像层
在 Storybook 仓库中,AI 编码 Agent 的指令体系遵循 AGENTS.md 中声明的原则:"This file is the canonical instruction source for coding agents. Files like CLAUDE.md should point here instead of duplicating instructions"——即指令以单一来源为准,其余文件只做指针。
技能(skill)文件同样采用这一"单一来源 + 镜像"策略:
- 核心技能定义位于 .agents/skills/ 目录下,例如 .agents/skills/update-pr-description/SKILL.md;
- 而 .claude/skills/ 目录下的同名文件仅是一行内容,直接引用核心文件。以本文主角为例,.claude/skills/update-pr-description/SKILL.md 的全部内容就是
@../../../.agents/skills/update-pr-description/SKILL.md,即通过相对路径@引用把 Claude Code 的技能入口指向.agents中的权威版本(.claude/skills/下的canary、pr、docs-review等 12 个技能目录全部采用相同模式)。
这样做的收益是:技能逻辑只在 .agents/skills/ 中维护一份,Claude Code 与通用 Agent 规范共用同一份定义,避免两处文档漂移——而 PR 描述本身漂移,恰恰是这个技能要解决的问题。
Frontmatter:技能的元数据与触发语义
每个 SKILL.md 以 YAML frontmatter 开头,声明技能的名称与触发条件:
---
name: update-pr-description
description: Evaluate a PR's title and description against its actual
implementation, then iteratively suggest and apply updates. Use when the
user asks to check, fix, or update a PR title or description.
---
其中 description 承担了"触发词"的角色:当用户要求"检查、修正或更新 PR 标题/描述"时,Agent 应加载该技能。作为对照,同目录下的 canary 技能 还额外声明了 allowed-tools: Bash,说明 frontmatter 也用于约束技能可用的工具集。
六步工作流:从证据采集到代用户提交
技能的主体是一个明确的六步流程,核心思想是先取证、再比对、最后才动笔,全程以 gh CLI 为操作界面。
第 1 步:定位目标 PR(Resolve)
优先使用用户提供的 PR 编号或 URL;若用户未提供,则用 gh pr view 反查当前分支对应的 PR:
gh pr view # 查看当前分支关联的 PR
这一步保证了后续所有操作都锚定在正确的 PR 上,而不是凭分支名猜测。
第 2 步:采集三类证据(Gather evidence)
技能要求并行采集三类相互独立的证据,分别回答"声称了什么"与"实际做了什么":
# 1) PR 的标题与正文(声称的内容)
gh pr view <pr> --json title,body
# 2) 提交历史(实际做了什么,按 commit 粒度)
gh pr view <pr> --json commits
# 3) 相对 base 分支的完整 diff(实际改动的最终形态)
gh pr diff <pr>
值得注意的是技能特意区分了 commits 与 diff:commit message 反映的是开发过程中的阶段性意图(可能包含已被推翻的中间提交),而 gh pr diff 给出的是合并前相对 base 的最终净改动。只比对其中任何一个都可能误判。
第 3 步:评估实质性偏差(Evaluate divergence)
这是技能中最体现判断力的步骤。技能明确区分了"值得标记的偏差"与"应忽略的差异":
- 只标记 meaningful divergence(实质性偏差):范围错误(wrong scope)、遗漏重大改动(missing major changes)、过时陈述(stale claims)、不准确的总结(inaccurate summary);
- 忽略 trivial wording(琐碎措辞差异):措辞风格、形容词选择等不影响信息准确性的差异不触发修改。
这一条实际上是在给 Agent 设定"克制度":PR 描述是写给维护者看的工程沟通文档,不是文学文本,Agent 的职责是纠正事实性偏差,而非润色文风。
第 4 步:汇报并设置"早退"出口(Report)
技能要求 Agent 先向用户报告"标题和/或描述是否存在实质性偏差",并且如果没有偏差,到此为止(If not, stop here)。这个显式的早退分支很关键——它防止 Agent 为了"展示能力"而对一个本来准确的描述强行重写,与后文 Notes 中的"Do not rewrite a description that's already accurate"形成呼应。
第 5 步:逐条迭代建议(Suggest iteratively)
若存在偏差,技能规定采用"一次只提一个变更"的协商节奏:
- 提出具体的更新后标题/描述(Propose concrete updated title/description);
- 每次只就一处变更征询用户是否应用(Ask the user one change at a time);
- 接受用户的编辑与进一步修正,循环精炼(accept edits, and refine)。
这种"小步提交式"的对话设计,使每次修改都可被独立审查,避免 Agent 一次性提交整篇重写稿让用户难以逐条判断。
第 6 步:代用户应用(Apply)
用户同意后,Agent 通过单条 gh 命令完成提交:
gh pr edit <pr> --title ... --body ...
至此,技能闭环完成:读取(gh pr view/gh pr diff)→ 判断 → 协商 → 写回(gh pr edit)。
Notes 四原则:与 Storybook PR 模板的机器级契约
技能末尾的 Notes 部分看似简短,实则是与仓库 PR 模板 深度耦合的约束规则,逐条展开:
1. 匹配仓库既有模板风格(Match the repository's existing PR template/style)
Storybook 的 PR 模板 .github/PULL_REQUEST_TEMPLATE.md 有严格的分区结构:
Closes #:关联 issue 编号,多 issue 需拆分列出;## What I did:变更摘要;- Checklist for Contributors:自动化测试覆盖(stories / unit tests / integration tests / end-to-end tests 四个复选框)+ 强制性的 Manual testing 小节(模板明确标注 "This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly");
- Checklist for Maintainers:CI 沙箱标签(
ci:normal/ci:merged/ci:daily,对应沙箱集合定义在 code/lib/cli-storybook/src/sandbox-templates.ts)、QA 标签(qa:needed/qa:skip)、以及必选的类别标签(bug、maintenance、dependencies、build、cleanup、documentation、feature request、BREAKING CHANGE、other); - Canary release 区段 与 Benchmark 区段(由 HTML 注释锚点占位)。
Agent 在重写描述时必须保留这套骨架,只填充内容,不改变结构。
2. 不重写已经准确的描述(Don't rewrite a description that's already accurate)
与第 4 步的"早退"逻辑一致,属于幂等性约束:技能的最终状态是"描述与实现一致",而非"描述被我改过"。
3. 同步更新复选框状态(Update the state of checkboxes where appropriate)
PR 正文中的 - [ ] 复选框是维护者流程的输入。典型场景:PR 初开时 Manual testing 步骤缺失,Agent 补齐步骤的同时把对应的测试覆盖项从 - [ ] 改为 - [x]。这与模板"填写章节、保留注释"的约定("put an 'x' inside the '[ ]'")配合。
4. 填充章节时删除占位符/提示注释(Remove section placeholders/reminders when filling out a section)
模板中大量使用 HTML 注释作为给人类贡献者的填写提示,例如 <!-- Briefly describe what your PR does -->。当 Agent 实际填写了某章节后,应删去对应提示注释,避免"说明文字"与"填写内容"并存的冗余。
5. 绝不删除 canary release 区段(Do not remove the canary release section)
这是 Notes 中唯一一条"禁止性"规则,其背后有直接的工程原因,可以从发布流水线得到验证。
在 publish.yml 的 publish-canary 作业中,canary 发布完成后有一个 "Replace Pull Request Body" 步骤,使用 ivangabriele/find-and-replace-pull-request-body 动作,以 CANARY_RELEASE_SECTION 这一 HTML 注释作为定位锚点,把整个区段替换为发布结果(见 publish.yml#L380-L405):
- name: Replace Pull Request Body
uses: ivangabriele/find-and-replace-pull-request-body@...
with:
githubToken: ${{ secrets.GH_TOKEN }}
prNumber: ...
find: 'CANARY_RELEASE_SECTION'
isHtmlCommentTag: true
replace: |
This pull request has been released as version `0.0.0-pr-<PR_NUMBER>-sha-<SHORT_SHA>`.
Try it out in a new sandbox by running `npx storybook@<VERSION> sandbox` ...
也就是说,模板中包裹 canary 说明的成对注释:
<!-- CANARY_RELEASE_SECTION -->
...
<!-- CANARY_RELEASE_SECTION -->
是 CI 与 PR 正文之间的机器可读接口:流水线发布 canary 版本(版本号格式为 0.0.0-pr-<PR_NUMBER>-sha-<SHORT_SHA>,由 publish.yml#L365-L372 的 yarn release:version --exact 步骤确定)后,依赖这对锚点原地注入版本号与 npx storybook@<VERSION> sandbox / upgrade 的试用命令。一旦 Agent 在校准描述时把这个区段当作"未填写的模板残留"删掉,后续所有 canary 发布的 PR 回写都会静默失效。同理,模板末尾的 BENCHMARK_SECTION 注释也承担类似职责。
这条规则因此可以从一般性的"谨慎编辑"升格理解为:PR 描述不仅是给人读的文档,还是 CI 系统写入结果的挂载点,编辑 Agent 必须把锚点注释视为不可变结构。
兄弟技能:update-pr-description 在 PR 生命周期中的位置
从 .agents/skills/ 下的技能集合看,update-pr-description 并非孤立工具,而是 Storybook PR 流水线上"开 PR → 发 canary → 校准描述"链条的一环:
| 技能 | 文件 | 职责 | 与本文技能的关系 |
|---|---|---|---|
pr |
SKILL.md | 定义 PR 标题格式 [Area]: [Description](如 CSFFactories: Fix type export)与三类必选标签(category/CI/QA),要求逐字复制模板并保留全部 HTML 注释 |
定义了"描述应该长什么样",是本文技能评估偏差时的风格基准 |
open-pr |
SKILL.md | 从当前分支开 draft PR:自动探测 base 分支(支持 stacked PR)、按模板填正文、创建后主动询问是否发 canary | 开 PR 阶段产出"第一版描述",后续可能随代码演进偏离实现 |
canary |
SKILL.md | 通过 gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER> 触发 canary 发布,并说明如何从 PR 正文中读取发布版本号 |
canary 发布会改写 PR 正文的 canary 区段,进一步提高了"编辑描述不得破坏锚点"的必要性 |
update-pr-description |
SKILL.md | 本文主角:比对标题/描述与实际实现,迭代式修正 | 在 PR 生命周期中任何时点(新增 commit、rebase、拆分合并后)都可触发,兜底保证描述不失真 |
可以看到一个清晰的设计思路:pr/open-pr 技能保证开 PR 时描述是模板化、准确的;canary 技能保证发布时CI 能写回 PR;而 update-pr-description 保证整个迭代过程中描述持续与实现同步。三者共同依赖同一个契约文件 .github/PULL_REQUEST_TEMPLATE.md,以及其中不可删除的注释锚点。
可迁移的实践要点
从这个技能中可以提炼出若干对任意大型仓库都有参考价值的做法:
- 技能文件用"证据驱动"而非"模板驱动"编写。流程的每一步都绑定可执行命令(
gh pr view --json、gh pr diff、gh pr edit),Agent 的每个判断都有数据源,而不是凭上下文"感觉"PR 描述写得对不对; - 显式定义"不做什么"。"只标记实质性偏差""已准确则不重写""逐条协商"等约束,比步骤本身更能决定技能的实际效果,它们共同压制了 LLM 常见的过度改写倾向;
- 单一来源 + 镜像引用(
.claude/skills/一行指针 →.agents/skills/权威文件)让多 Agent 工具生态共享同一份技能定义; - 文档锚点即接口:
CANARY_RELEASE_SECTION这类 HTML 注释让 PR 正文同时服务人类阅读与 CI 程序化改写,任何自动编辑流程都必须把"保留锚点"列为硬约束——这正是 update-pr-description/SKILL.md 最后一条 Note 存在的原因。
综合来看,update-pr-description 技能 的价值不仅在于"帮人改 PR 描述",更在于它示范了如何在 Storybook 这样的工程仓库中,把 PR 正文当作一份同时面向人与 CI 的可执行文档来治理:模板定义结构,注释锚点定义机器接口,Agent 技能则负责在整个 PR 生命周期内维持内容与实现的持续一致。
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 StartedRust0623
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