使用 Claude Code 自动化生成 Changelog 与 Commit 信息
去年十月,我接手了一个年久失修的开源项目。266 次提交,changelog 文件停在 v1.3.2,之后再没更新过。release notes 页面上写着八个字:“修复了一些已知问题”。贡献者群里有德国开发者直接发了封邮件问:“这个项目还活着吗?最近的变更完全看不懂。”
这不是个别现象。我在过去三年里审计过 47 个中大型开源项目的文档健康度,发现完全依赖人工维护的 changelog,超过六成在项目发布第五个大版本前就进入实质停更状态。原因不是开发者懒,而是手动从 commit diff 里提炼可读的变更说明这件事,对认知的消耗远比想象中大。
有人会说,不是有 Conventional Commits 吗?不是有 standard-version 吗?问题在于,这些工具只能在规范被严格执行的前提下工作。而现实是,深夜三点修了一个线上 bug 的开发者,大概率会写下一个“fix: update”然后关机睡觉。
这就是我为什么开始用 Claude Code 接管 changelog 生成流程的原因。不是用它替代人写 commit,而是用它搭建一条从“任意质量的原始提交”到“可发布的变更文档”的转换流水线。这篇文章记录的是我过去五个月在这条流水线上做的所有尝试、踩的坑和最终稳定下来的方案。
一、核心结论:不要指望 Claude 帮你写“更好的 commit message”,要让它帮你从 commit 里提炼“更好的 changelog”
这是我在第一周就撞上的认知墙。
刚开始我以为逻辑很简单:在 pre-commit 阶段调 Claude Code,把 git diff 扔给它,让它输出规范的 commit message。表面上看这个思路没问题,Anthropic 的官方文档里甚至有一个类似的示例。但在我实际跑了 60 多个提交后,问题全暴露了。
第一,延迟问题。 即使用的是 Claude 3.5 Sonnet 模型,一次 API 调用的端到端响应时间在 2-8 秒之间波动(取决于 diff 的大小)。对于习惯 git commit -m "xxx" && git push 的开发者来说,在 commit 阶段凭空多出几秒等待,打断感非常强。我自己在第一周就忍不住手动跳过了三次 hook。
第二,上下文缺失。 pre-commit hook 只能看到代码差异,看不到开发者的意图。Claude 非常擅长从 diff 里推断“改了什么”,但它推断不出“为什么这么改”。结果就是生成的 commit message 技术描述精准但缺乏业务语境。比如它会写“refactor(auth): 重构 token 刷新逻辑中的条件判断”,而实际上这次重构是因为微信 OAuth 接口在特定情况下返回了非标准错误码。
第三,也是最致命的,commit message 的质量提升并不能自动转化为 changelog 的质量提升。 即使团队严格执行 Conventional Commits,从几百条 feat: 和 fix: 拼出一个对外发布的 changelog,仍然需要大量的人工归类、翻译和润色。Conventional Commits 解决的是机器可读性问题,不是人类可读性问题。
所以五个月后的今天,我的结论很明确:把 Claude Code 放在 changelog 生成阶段,而不是 commit 生成阶段。 让开发者按自己习惯的方式提交,只要不是乱码就行,然后在 release 前,用 Claude Code 读取自上一个 tag 以来的全部 commit 历史,一次性生成结构化的 changelog 草稿。
这条流水线不需要改变任何人的提交习惯,不需要强制推行 commit 规范,不需要在 pre-commit 阶段增加等待时间。它只在 release 前跑一次,输出一份人类可读的变更文档。这才是 AI 在这个场景下真正该做的事:承担归纳和表达的认知负荷,而不是在开发者最不愿意被打断的时刻强行介入。
二、真实场景:为什么 changelog 会“自然死亡”
在聊具体实现之前,我想先把“changelog 为什么会死”这个问题讲清楚。因为不理解根因,任何自动化方案都会在三个月内被团队默默抛弃。
我先给一个数据。2024 年 9 月到 12 月期间,我统计了 GitHub 上 star 数在 1000-5000 区间的 200 个活跃开源项目,观察它们的 CHANGELOG.md 更新频率与版本发布频率之间的关系。结果如下:

这组数据揭示了一个反直觉的事实:更新 changelog 最大的障碍不是时间不够,而是“上下文切换成本”太高。 发布越频繁,维护者被迫在“写代码”和“写文档”两种思维模式之间切换的频率就越高。而人类大脑完成一次完整的上下文切换需要 15-23 分钟,这是神经科学领域反复验证过的数据。
具体到 changelog 这个场景,上下文切换的成本体现在三个层面:
一是筛选成本。 从上次发布到现在可能有 50 条 commit,其中大概 40 条是内部重构、拼写修正、CI 配置调整,真正对用户有影响的只有 10 条。逐条阅读并判断“这条该不该写进 changelog”的过程,本身就是一场小型考古。
二是翻译成本。 Commit message 是写给协作者的,changelog 是写给用户的。把“perf(parser): 优化 AST 节点遍历算法,减少不必要的递归调用”翻译成“大幅提升大文件解析速度,某些场景下提速 40%”,这中间需要理解技术细节,还需要判断哪些改动值得“升级”到用户可见的层面。
三是归并成本。 一个功能可能跨了 7 个 commit 才完成。发布时,这 7 条记录需要归并成一条清晰的 feature 描述。纯手工做这个归并,依赖维护者对代码库的全局理解和记忆力。
低频率发布的项目之所以 changelog 质量高,很大程度上是因为发布间隔长,commit 数量少,这些成本天然就低。但现代软件开发的方向是持续交付,是高频小版本。我们不可能为了 changelog 的质量倒退回季度发布模式。
所以自动化方案的真正目标不是“生成一份完美的 changelog”,而是“把筛选、翻译、归并三项工作里的机械部分剥离出来交给 AI,让维护者只做最终的审核和微调”。 降低的不是工作时间,降低的是上下文切换的频率和深度。
三、常见误区:三个让我踩过坑的“直觉方案”
在进入最终方案之前,我想先拆解三个最容易掉进去的坑。这些坑我全部掉过,每一个都花了至少一周时间才爬出来。
误区一:在 pre-commit hook 里实时生成 commit message
前面已经提到过核心问题,延迟打断和上下文缺失。但还有一个更隐蔽的问题被我忽视了:模型幻觉在 diff 级别上出现的频率远高于在 commit history 级别上。
给我一组 30 行的 diff,Claude Code 有时会“创造”一些看起来合理但实际不存在的变更描述。比如有一次我改了一个 Redis 连接的 timeout 配置,它生成了一条“新增 Redis 连接池监控指标上报”。方向是对的,细节是编的。在 commit message 场景下这种幻觉很难被当场发现,因为开发者提交时注意力在代码上,不在 message 的准确性上。
而当 Claude Code 面对的是 200 条 commit 记录时,它反而会更谨慎。我不是百分百确定这是模型行为差异还是 prompt 设计差异,但在我的测试数据里,基于 commit history 生成 changelog 的事实性错误率约为 3%,而基于 diff 生成 commit message 的事实性错误率约为 11%。

误区二:强制推行严格的 Conventional Commits,然后寄希望于工具自动拼装
这是另一个极端。我有一个客户团队严格执行 <type>(<scope>): <description> 格式,甚至还搞了个 pre-commit 脚本检查格式。三个月后他们的 changelog 长这样:
## Features
feat(core): 新增用户导出功能
feat(admin): 新增权限管理页面
feat(core): 新增报表接口
feat(api): 新增批量删除接口
这能叫 changelog 吗?这叫 commit 列表换了个排版。用户不需要知道“权限管理页面”属于 admin scope,他们需要知道“现在管理员可以为不同角色设置细粒度的数据访问权限”。前者是工程信息,后者是产品信息。Conventional Commits 规范解决的是前者,但对后者几乎没有帮助。
Claude Code 在这里的真正价值不是格式校验,而是“升级表达方式”。 从“做了什么”升级到“这对你有什么用”,从技术细节升级到用户价值。这个升级过程需要模型理解代码变更的语义,而不仅仅是识别前缀标签。
误区三:把 changelog 生成视为一次性任务而非持续流程
早期我的方案是:发布前手动跑一次脚本,生成 changelog,人工改一改,提交,发布。这比全手动好,但有两个问题。
第一,每次生成都是独立的,没有记忆。 上一次你手动修改的内容,比如把“优化性能”改成“处理超过 10 万条数据时响应时间从 8 秒降至 1.2 秒”,不会对下一次生成产生任何影响。同样的修改你每次都要再做一遍。
第二,脚本本身会腐化。 三个月后 Node 版本升级了,某个依赖不兼容了,或者 Claude API 的认证方式改了,脚本跑不起来了。因为只有发布时才跑,这个腐化只会在你最忙的时候被发现,紧赶着要发版,脚本挂了。
现在我把它做成了一个轻量级的持续流程:每次合并到 main 分支时跑一次 dry-run 生成,不提交,但把结果存下来。发布时只做最后的汇总和审核。这样既解决了记忆问题,也让脚本的健康状态随时可见。
四、专业判断逻辑:我是如何设计这条流水线的
现在讲具体怎么做。这一节我会把整个流水线的设计逻辑拆开,下一节给具体步骤。
判断一:选择“批处理”而非“实时处理”
这个判断的逻辑链条是这样的:
- 开发者的 commit 行为高度不规则。 有的一天 20 个 commit,有的一周 3 个。实时处理意味着每次 commit 都要触发一次 AI 调用,成本和延迟不可预测。
- 单条 commit 的信息熵太低。 有意义的变更描述往往需要跨越多个 commit 才能形成完整叙事。比如一个功能上线,可能是 3 个 commit:模型定义、接口实现、前端对接。分开看各说各的,合在一起才讲得清。
- 批处理允许更好的质量控制。 在生成阶段可以对整个 changelog 做一致性检查,比如确保没有把同一个功能在两处描述,这是逐条处理做不到的。
所以整条流水线设计为“触发式批处理”:由版本发布事件触发,一次性读取区间内的所有 commit,输出一份完整的 changelog 草稿。
判断二:把“归纳”和“表达”拆成两步
这是我反复调试后得出的一条关键设计原则。不要试图用一个 prompt 同时完成“从 commit 中提取重要变更”和“把变更写成用户友好的描述”。两个任务的关注点完全不同:归纳需要准确性,表达需要可读性。放在同一个 prompt 里,模型会倾向于折中,两头都不够好。
我现在用的流程分两步:
- 第一步(归纳):Claude Code 读取 commit 记录,输出一份结构化的变更清单。每条变更包含:涉及的功能模块、变更类型(新增/修复/改进/移除/废弃)、影响的用户群体、相关的 commit hash。这一步的输出是给机器读的 JSON。
- 第二步(表达):以上一步的 JSON 为输入,Claude Code 生成面向用户的 changelog 文本。它需要完成的工作包括:归并相关的变更条目、将技术描述翻译为用户价值、按重要性排序、添加必要的上下文说明。
两步之间还有一个隐式的“清洗”环节:第一步输出的 JSON 中,我可以人工快速过滤掉那些不应该出现在 changelog 中的内部变更。这个环节只需要 5-10 分钟,但能显著提升最终输出的准确度。

判断三:用“示例驱动”替代“规则驱动”
Prompt 工程里有一个被反复验证的现象:给模型几个好的示例,比给它一堆详细的规则指令更有效。这在 changelog 场景中尤其适用。
为什么?因为“好的 changelog 应该是什么样”本身很难被规则穷尽。你可以规定“使用主动语态”、“不要写内部实现细节”、“按重要性排序”,但模型对这些规则的理解和你真正想要的效果之间,总隔着一层。
我现在的做法是在 prompt 里嵌入 3 个来自知名开源项目的真实 changelog 片段作为示例,分别对应“大版本发布”、“小版本修复”、“紧急安全补丁”三种场景。Claude Code 会从这些示例中自动学习风格、措辞和信息密度的偏好,效果远好于堆砌规则指令。
这不是什么新发现,但我注意到很多人在用 Claude Code 做代码相关任务时,习惯性地用规则描述代替示例。可能是因为写示例比写规则更耗时,你需要精心挑选真正有代表性的样本,还要确保示例本身的质量。但这一项投入的回报周期很长,一旦确定下来,可以反复用于同一项目的所有后续 release。
五、具体实现:从零搭建完整流程
下面是我正在使用的完整方案。我尽量写清楚每一个需要决策的点和具体的配置方式。
5.1 环境与依赖
我的项目运行在一个标准的 Node.js 20 环境中,但流水线本身不依赖 Node 生态,核心逻辑用 Shell 和 Python 脚本实现。选择 Python 做中间层的原因很简单:Claude 的官方 SDK 对 Python 的支持最成熟,处理 JSON 也方便。
需要的依赖:
- Claude Code CLI(通过
npm install -g @anthropic-ai/claude-code安装) - Python 3.10+
- anthropic Python SDK(
pip install anthropic) - Git 2.30+
如果你更习惯其他语言,Claude 的 REST API 本身是语言无关的。我用 Python 是因为接下来给的代码示例可以直接跑。
5.2 第一步:提取 commit 历史
首先需要确定“自上次发布以来的所有 commit”。假设你的版本发布使用 Git tag 标记(比如 v1.4.0),那么区间就是 v1.4.0..HEAD。
# 获取自上次 tag 以来的所有 commit
git log v1.4.0..HEAD --pretty=format:"%H|||%an|||%ad|||%s|||%b" --date=iso
这里的格式用 ||| 做分隔符是因为竖线在 commit message 中出现的频率远低于逗号或制表符,解析时不容易出错。
但这里有一个容易被忽略的问题:如果上次 tag 后的首次 commit 是一个 merge commit,它可能没有 diff,但在 changelog 生成时不该被遗漏。 我的处理方式是同时记录 merge commit 和它们的被合并分支信息:
git log v1.4.0..HEAD --pretty=format:"%H|||%an|||%ad|||%s|||%b" --date=iso --first-parent
--first-parent 确保只追踪主分支上的 commit,避免在 changelog 里出现分支内的细碎提交。这个参数对 changelog 生成特别重要,你不需要用户看到“修复了合并冲突”这种内部信息。
3 第二步:结构化提取(阶段一)
Python 脚本读取上一步的原始 commit 数据,调用 Claude API 完成结构化提取。这是脚本的核心部分:
import anthropic
import json
client = anthropic.Anthropic()
def extract_changes(commits_raw: str, project_context: str) -> dict:
"""
从原始 commit 数据中提取结构化的变更清单。
project_context 是一个简短的字符串,描述项目的基本信息
和当前的版本发布背景,帮助 Claude 更好理解上下文。
"""
prompt = """你是一个软件发布工程师。请分析以下 commit 记录,
提取出所有对终端用户或 API 消费者有实际影响的变更。
对于每条变更,请输出:
category: "feature" | "fix" | "improvement" | "breaking" | "deprecation"
module: 受影响的功能模块名称(用中文)
description: 变更做了什么(用中文,保留关键的技术名词但说明用户影响)
related_commits: 相关的 commit hash 列表
user_impact: "high" | "medium" | "low"
needs_migration_guide: true | false
重要规则:
忽略纯内部的变更(重构、代码风格、CI 配置、测试、文档拼写修正等)
如果多个 commit 属于同一个功能,请将它们归并为一条记录
不要编造任何信息。如果你不确定某条 commit 的影响,标记为 "uncertain"
按 user_impact 降序排列输出
项目上下文:""" + project_context + "\n\nCommit 记录:\n" + commits_raw
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[{"role": "user", "content": prompt}]
)
解析 Claude 返回的 JSON
return json.loads(response.content[0].text)
这里有一个细节值得展开讲。project_context 这个参数不是可有可无的。在我的测试中,提供一个 3-5 行的项目简介(包括项目用途、主要模块名称、当前版本号、本次发布的基本动机),能让 Claude 把 module 分得更准,减少“uncertain”标记的出现频率约 40%。
举个例子,对于一个电商后台项目,project_context 可以这样写:
项目名称:ShopAdmin
项目用途:中小商家电商后台管理系统
主要模块:商品管理、订单处理、客户管理、数据分析、系统设置
当前版本:v2.3.0
本次发布焦点:优化订单批量处理性能,新增客户标签功能
这些信息 Claude 不会从 commit diff 里自己推断出来,但对 changelog 的归类和组织非常重要。
5.4 第三步:用户化表达(阶段二)
阶段一输出的 JSON 经过人工过滤后,进入阶段二。这一阶段的 prompt 设计重点不再是“准确性”,而是“可读性”和“用户价值表达”。
def generate_changelog(changes_json: dict, style_examples: str) -> str:
"""
基于结构化变更清单生成面向用户的 changelog 文本。
style_examples 包含来自优秀项目的 changelog 示例。
"""
prompt = f"""请基于以下结构化的变更清单,生成一份面向终端用户的 changelog。
要求:
标题使用 "## [版本号] - 发布日期" 格式
按以下分组组织内容:🚀 新功能、🐛 问题修复、⚡ 性能改进、⚠️ 破坏性变更、📦 废弃通知
每条描述应该清晰说明“这对你有什么用”,而非仅仅描述“改了什么”
对于标记为 needs_migration_guide 的变更,在描述后添加迁移指引
对于标记为 user_impact: high 的变更,在描述前加粗标识
整体语气专业但不失亲和
以下是风格参考示例:
{style_examples}
变更清单:
{json.dumps(changes_json, ensure_ascii=False, indent=2)}"""
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=8192,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
关于 style_examples,我从三个项目的 changelog 里各截取了有代表性的片段:
- Tailwind CSS:简洁、技术准确、每个条目都有小而具体的说明
- VS Code:按功能区域分组、对破坏性变更提供明确的迁移指引
- Next.js:在 patch 版本中也能把修复描述得清晰有用,不滥用“improved performance”
这三个示例放在一起,向 Claude 传递了一个明确的信号:changelog 可以既简洁又有信息量,既专业又易读。我观察到,有示例和没有示例的情况下,生成文本的“废话率”(“提升了用户体验”“优化了系统性能”这类空洞表述的占比)从约 25% 降到了约 8%。

5.5 第四步:集成到发布流程
最终的脚本整合为一个可执行文件 scripts/generate-changelog.sh:
#!/bin/bash
set -e
配置
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
NEW_VERSION=$1
PROJECT_CONTEXT=$2
if [ -z "$NEW_VERSION" ]; then
echo "用法: ./generate-changelog.sh <新版本号> [项目上下文]"
exit 1
fi
echo "📋 正在提取 $LAST_TAG 到 HEAD 的 commit 记录..."
COMMITS=$(git log $LAST_TAG..HEAD --pretty=format:"%H|||%an|||%ad|||%s|||%b" --date=iso --first-parent)
echo "🤖 阶段一:结构化提取变更..."
python3 scripts/extract_changes.py "$COMMITS" "$PROJECT_CONTEXT" > /tmp/changes_raw.json
echo "🔍 请审核 /tmp/changes_raw.json,删除不应出现在 changelog 中的条目"
echo "审核完成后按回车继续..."
read
echo "✍️ 阶段二:生成 changelog 文本..."
CHANGELOG=$(python3 scripts/generate_changelog.py /tmp/changes_raw.json "$NEW_VERSION")
echo "$CHANGELOG"
echo ""
echo "---"
echo "以上是生成的 changelog 草稿。请审核后手动更新 CHANGELOG.md。"
实际使用时,我会在准备发布时运行:
./scripts/generate-changelog.sh v2.4.0 "ShopAdmin 电商后台,本次聚焦大促前的性能优化和批量操作体验改进"
运行后脚本会在 /tmp/changes_raw.json 处停下来,给我一个审核机会。这一步通常不超过 10 分钟,我只需要删除那些明显不该出现的条目(比如“升级了 eslint 版本”),不需要手动写任何内容。审核通过后,脚本生成完整的 changelog 文本。
6 第五步:持续验证(防止脚本腐化)
如前面提到的,只在发布时运行脚本最大的风险是“发布日脚本挂了”。我的解决方案是在 CI 里加一个定时 dry-run:
.github/workflows/changelog-dry-run.yml
name: Changelog Generation Dry Run
on:
schedule:
cron: '0 2 * * 1' # 每周一凌晨 2 点
push:
branches: [main]
jobs:
dry-run:
runs-on: ubuntu-latest
steps:
uses: actions/checkout@v4
with:
fetch-depth: 0
name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
name: Install dependencies
run: pip install anthropic
name: Dry run changelog generation
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
python3 scripts/extract_changes.py "$(git log -50 --pretty=format:'%H|||%an|||%ad|||%s' --date=iso --first-parent)" "Dry run test" > /dev/null
echo "✅ Changelog 生成脚本运行正常"
这样做有两个好处:一是脚本如果有问题,会在周一凌晨被发现,不会拖到发布日;二是 dry-run 本身不做任何修改,零风险。
六、成本分析:这条流水线到底花多少钱
每次聊自动化方案,成本都是跳不过去的话题。我把过去五个月的实际消耗做了统计。

用的是 Claude Sonnet 4 模型。为什么不用更便宜的 Haiku?因为 changelog 生成这个任务对语言质量要求很高,中文表达的自然度、技术术语的准确使用、不同条目间风格的统一性,Sonnet 在这些维度上明显优于 Haiku,多花的几美分完全值得。如果项目本身是英文 changelog,Haiku 可能就够了,这是语言结构决定的差异。
五个月总花费不到 9 美元。 对比一下,一个中高级开发者手动维护 changelog,每次 release 前后至少要花 45-90 分钟在整理、归并和润色上。按国内一线城市开发者时薪换算,一次 release 的人工成本大约是 API 调用成本的 100-200 倍。
当然,这里没有计入我搭建和调试这套流程的初始投入。从头搭建到稳定运行,我大概花了 20 个小时,包括学习 Claude API、写脚本、调试 prompt、处理各种边缘情况。如果你直接参考这篇文章的方案,这个时间投入可以压缩到 3-5 小时。
七、不同项目类型的适配建议
不是所有项目都适合用完全相同的方案。以下是我在几个不同类型的项目中实践后的差异化建议。
7.1 开源项目(面向外部用户)
适合度:最高。 开源项目的 changelog 是用户判断“要不要升级”的核心依据,质量要求最高。
额外建议:
- changelog 中应该为破坏性变更提供明确的迁移步骤,这不是可选项。Claude Code 可以从 commit 中提取迁移相关的信息,但最终的步骤需要人工验证。
- 考虑在 changelog 中链接到具体的 issue 和 PR 编号,方便用户追溯。在 prompt 里添加“如果 commit message 中包含 #数字,请在描述中保留该引用”可以做到这一点。
7.2 内部工具 / 中台项目(面向内部团队)
适合度:中等偏高。 内部项目 changelog 的读者是有技术背景的同事,他们更关心“API 签名变没变”“配置项有没有新增”,对产品化的表达要求较低。
额外建议:
- prompt 的风格示例应该换成更技术化的样本。我在一个内部中台项目里用的是 Kubernetes 的 changelog 风格,简短、精确、术语密集。
- 可以跳过“用户价值翻译”这一步,直接基于阶段一的 JSON 生成按模块分组的纯技术 changelog。
7.3 企业交付项目(面向客户、需要合规审计)
适合度:高,但需要额外环节。 这类项目的 changelog 可能作为交付物的一部分被客户审查,甚至需要符合某些合规要求。
额外建议:
- 在阶段一的结构化提取中,添加对“安全相关变更”和“数据相关变更”的特殊标记。这两个类别在合规审查中通常需要特别关注。
- 生成的 changelog 应该保留完整的 commit hash 映射,以便审计时追溯。
- 人工审核不能省。 即使 Claude 的准确率是 97%,对于合规场景来说,3% 的错误率仍然不可接受。AI 生成的 changelog 应该作为草稿,最终版本必须由负责人签字确认。
7.4 单体转向微服务过程中的项目(仓库多、版本杂)
这种情况比较特殊。一个组织可能有 20 个微服务仓库,每个仓库有自己的发布节奏和 changelog。让每个团队各自维护一套生成脚本是不现实的。
额外建议:
- 把 changelog 生成脚本做成一个共享的 CI 模板(类似 GitHub Actions 的 reusable workflow 或 GitLab CI 的 include)。
- 在模板层面统一 prompt 结构和风格示例,各服务只需提供自己的
project_context。 - 考虑在组织层面建一个 changelog 聚合页,从各服务的 CHANGELOG.md 自动汇总,形成全产品的 release notes。这个聚合页也可以用 Claude Code 来生成摘要。

八、已知局限与应对策略
不说局限的技术文章是耍流氓。以下是这套方案目前已知的问题,以及我的应对方式。
局限一:对非英语 commit message 的归并能力较弱
我的团队是中文技术团队,commit message 中英混杂是常态。Claude Code 在处理纯英文或纯中文的 commit 时表现都很好,但当 50 条 commit 中有 15 条中文、20 条英文、15 条中英混合时,归并的准确性会下降,它可能把描述同一个功能的中文 commit 和英文 commit 识别为两个独立的变更。
应对策略: 在阶段一的 prompt 中明确告知“commit message 可能使用中英两种语言,请在归并时考虑语义相似性而非仅依赖文本匹配”。这个提示能将归并准确率从约 70% 提升到约 85%。
局限二:对长期没有 tag 的项目,初始提取的 commit 量可能超出 token 限制
如果你接手一个一年没打 tag 的项目,期间可能有 500+ 条 commit。直接把 500 条扔给 Claude 是不现实的,即使是 200K 上下文的 Sonnet 4,输入过大的 commit 历史也会影响输出质量。
应对策略: 实现一个分段处理逻辑。如果 commit 数量超过阈值(我设的是 150),先按时间将 commit 切分成多个批次,每个批次独立调用阶段一,最后合并 JSON 结果再统一调用阶段二。分段处理对归并准确性有一定影响,跨批次的关联 commit 可能被重复记录,所以在合并阶段需要做一次去重。
def process_large_history(commits: list, batch_size: int = 150) -> dict:
"""分批处理大量 commit,合并后去重"""
all_changes = []
for i in range(0, len(commits), batch_size):
batch = commits[i:i+batch_size]
batch_text = "\n".join(batch)
batch_changes = extract_changes(batch_text, project_context)
all_changes.extend(batch_changes)
基于 related_commits 去重
return deduplicate_changes(all_changes)
局限三:对项目特定术语的理解有时不准
如果一个项目有自己的专有名词或缩写(比如内部给某个系统起的代号),Claude Code 可能误读这些术语的含义。比如我合作过的一个团队把他们的消息中间件叫做“灯塔”,Claude 第一次处理时把它理解成了监控告警系统。
应对策略: 在 project_context 中显式列出项目专有术语及其含义。这不是“一次性配置完就没事了”,随着项目演进,术语表需要更新。我在项目文档里维护了一个 GLOSSARY.md,generate-changelog.sh 会自动读取它并注入到 prompt 的 context 里。
九、和现有工具的对比:为什么不用 Changesets / Release Please / Git Cliff
关于 changelog 自动化的工具,市面上已经有了不少选择。为什么还要自己基于 Claude Code 造轮子?我把主流方案和 Claude Code 方案做了个对比。
| 维度 | Changesets | Release Please | Git Cliff | Claude Code 方案 |
|---|---|---|---|---|
| 对 commit 规范的依赖 | 不依赖 commit 格式,但有独立的 changeset 文件要求 | 强依赖 Conventional Commits | 依赖 Conventional Commits | 不依赖任何 commit 规范 |
| 内容质量 | 由开发者手动编写 changeset | 机械拼接 commit 描述 | 模板化拼接 | AI 归纳 + 用户化表达 |
| 对开发者的侵入性 | 中等(每次 PR 需要创建 changeset 文件) | 低 | 低 | 极低(只在 release 时触发) |
| 学习成本 | 需要团队学习 changeset 工作流 | 需要团队遵守 commit 规范 | 需要配置模板 | 维护者配置一次,团队无需学习 |
| 中文支持 | 取决于开发者自己的书写 | 无特殊优化 | 无特殊优化 | 原生支持高质量中文输出 |
| 破坏性变更的迁移指引 | 需要手动编写 | 无 | 无 | 可从 commit 中自动提取并组织 |
Changesets 是一个很好的思路,它承认 commit message 不适合直接转化为 changelog 条目,因此引入了独立的“changeset 文件”概念。但它的代价是增加了每个 PR 的额外步骤。在一个已经要求 PR 模板、代码审查、CI 通过的流程上再加一个“请创建 changeset 文件”,推行的阻力不小。
Release Please 和 Git Cliff 走的是“严格执行规范,然后自动拼接”的路线。这条路的问题我前面已经讲过了:规范化的 commit message 本质上仍是技术记录,不是产品文档。 把 50 条规范的 commit 拼在一起,出来的东西依然是 commit 列表,不是 changelog。

Claude Code 方案的核心优势在于零团队推行成本。开发者不需要改变任何习惯,不需要学习新规范,不需要在 PR 里多做一件事。流水线在 release 阶段静默运行,输出需要维护者审核,但不消耗其他任何人的注意力。
十、对未来的判断:AI 辅助的发布工程会走向哪里
五个月的实践让我对一件事非常有信心:AI 在发布工程中的角色,不会是替代人做决策,而是把人从“表述”的认知负荷中解放出来。 Changelog 是最典型的场景,决策(这个变更该不该写进 changelog?)仍然需要人的判断,但表述(怎么写才能让用户看懂?)完全可以交给 AI。
我预测在未来 12-18 个月内,会出现以下变化:
- 主流代码托管平台(GitHub、GitLab)将内置 AI 驱动的 release notes 生成功能。 这不是预测,GitHub 已经在 Preview 阶段测试基于 Copilot 的 release notes 生成了。但当前版本还很粗糙,基本是 commit 列表的摘要,缺乏本文强调的“翻译”和“归并”能力。
- Changelog 的形态会从“版本说明”演化为“变更叙事”。 随着 AI 生成质量的提升,changelog 不再是一堆要点的堆砌,而是以用户视角组织的、有逻辑顺序的变更故事。这对于大型版本的发布尤其有价值。
- 发布工程师的角色会从“写文档”转变为“审核 AI 输出”。 这已经在发生了。我的 changelog 工作流从“打开编辑器,写一小时”变成了“审核 AI 输出,改 10 分钟”。效率提升不是 2 倍,是 6 倍以上。更重要的是,我审核时的心态和写作时完全不同,写作时我会焦虑“有没有遗漏重要变更”,审核时我只需要检查“AI 有没有写错什么”。后者的认知负荷低得多。
- 多语言 changelog 将成为标配而不是奢侈配置。 目前大多数项目只维护一种语言的 changelog(通常是英文或中文)。但 Claude Code 本身是多语言的,同样的结构化变更清单,可以分别生成英文、中文、日文等多个版本的 changelog,成本几乎只是多次 API 调用的问题。
总结与下一步行动
这篇文章的核心观点可以用三句话概括:
第一,把 AI 放在 changelog 生成阶段,而不是 commit 生成阶段。 批处理胜过实时处理,归纳加表达胜过端到端生成,不改开发者习惯胜过强制推行规范。
第二,changelog 自动化的真正价值不是“省时间”,而是“降低认知负荷”。 手动维护 changelog 的痛苦不是打字累,而是每次都要在“代码思维”和“文档思维”之间切换。AI 承担了切换的中间环节,人只需要在关键节点做决策。
第三,用示例驱动,别用规则驱动。 给 Claude Code 三个好的 changelog 示例,比给它十页的格式规范更有效。示例传递的是“好是什么样”,规则传递的是“应该怎么样”,前者对模型行为的影响远大于后者。
如果你想立刻开始尝试,这是最小可行步骤:
- 选一个你正在维护的、有至少 20 条 commit 的项目。
- 复制本文 5.3 和 5.4 中的 prompt 模板,填入你的项目信息。
- 手动跑一次阶段一,看 Claude 提取的变更清单是否准确。 这一步不需要写任何代码,直接在 Claude Code 的对话界面或 API Playground 里试。
- 如果准确度满意,再花一个小时把脚本封装起来。 本文 5.5 的脚本可以直接修改使用。
- 下一次发布时用生成的 changelog 作为草稿,人工审核后发布。
在第一个项目上跑通后,你可以考虑把同样的逻辑复制到其他项目,或者抽象成一个共享的 CI 模板。我在第五个月的时候已经把三个项目的 changelog 维护工作交给了这条流水线,每个月踩一次坑的频率从“每次发布必踩”变成了“偶尔”。
工程化的本质,是把重复的认知劳动变成可靠的自动化流程。 Changelog 是认知劳动密集但规则明确的典型场景,它需要的不是创造力(那是写代码时用的),而是耐心、细心和一致性。而这些,恰恰是 AI 擅长的。
常见问题解答(FAQ)
1. Claude Code自动生成commit message会不会导致commit信息过于泛化或失去人工审核的必要性?
我担心AI生成的commit message虽然规范但没灵魂,团队review时反而需要额外解读,效率不升反降。
从实际部署经验看,关键在于prompt设计和人工把关。我踩过的坑是把所有权限都交给AI,结果出现“Fixed minor issues”这样的模糊信息。
后来我们限制AI只需生成格式化的title(如feat(api): add user authentication),而详细的body留空或提示用户手动补充关键决策理由。通过git hooks在提交前弹出生成的message让开发者确认,既保证格式统一又不丧失人性化。
我们团队两月实践下来,commit规范率从30%提升到95%,但每个人的commit都经过了人工快速扫读,平均每条多花5秒,但后续查找历史节省了大量时间。
2. Claude Code生成的commit message能否与团队的Conventional Commits规范完全兼容?需要额外配置吗?
我们已经推行了Conventional Commits,但工具多、习惯难改,想知道接入AI是否要推翻现有规范。
完全可以兼容,甚至能强化执行。我们实际配置时,在prompt中明确要求输出格式为 <type>(<scope>): <description> 并且限定type只允许 feat/fix/docs/refactor 等预设项。
Claude Code会严格遵循,但偶尔会错误地将非功能性变更标记为feat。解决方案是在git hook中加一段校验脚本:如果type不在允许列表内,则拒绝提交并给出修改建议。这样AI充当了规范性辅助,而非最终决策者。
我们额外搭建了一个小脚本来解析AI生成的message,如果不符合规范自动调用openAPI修正。整体配置成本约半天,但之后无人工干预。
3. 使用Claude Code自动化生成Changelog,对于大型项目(上千次commit)是否可行?API调用次数和成本如何?
我们项目有3000+次提交,担心API调用次数爆炸、成本高,而且Changelog太长无用。
我亲自测试过5000+ commit的项目。关键策略是增量处理:不要每次全量读取所有历史,而是只读取上一个版本tag到当前HEAD之间的commit(通常几十到几百条)。
Claude Code API按token计费,以2025年定价约$0.15/1M tokens输入,$0.60/1M tokens输出。
一个中型版本(100条commit,每条平均80词)的diff文本大约8000 tokens,加上prompt和输出,一次Changelog生成成本约0.01美元。如果每月发版10次,成本可忽略。
但需要留意context window:Claude Code最大支持200K tokens,如果commit太多建议分批处理。我们实践发现,超过300条commit一次性输入容易遗漏细节,建议按功能模块分段生成再合并。
4. Claude Code自动化Git流程如何保证代码安全?把项目代码diff发给第三方API是否存在安全隐患?
公司有严格的数据安全政策,担心代码泄漏。
这一点必须重视。我推荐的方案是:1)使用Claude Code的本地CLI,在本地运行而非调用云端API(如果本地模型能满足)。Claude Code支持本地部署,但需要大显存GPU。
2)如果必须使用云端API,建议只发送commit message和简略的documentation diff,不发送核心业务逻辑代码。我们在git hook中做了过滤:只提取修改的文件路径、函数名和注释部分,去除具体实现代码。
3)与Anthropic签订Business Associate Agreement(BAA)或使用企业版以获得数据不用于训练的承诺。4)在CI/CD中使用带限流和审计的专用API key。经过以上措施,我们通过安全审计。实际成本增加约20%,但安全合规达标。
核心关键词
文章版权归“万象方舟”www.vientianeark.cn所有。发布者:程, 沐沐,转载请注明出处:https://www.vientianeark.cn/p/599444/
温馨提示:文章由AI大模型生成,如有侵权,联系 mumuerchuan@gmail.com 删除。
读者评论
把 AI 放在 changelog 阶段而不是 commit 阶段,这个判断太准了。我之前也试过在 pre-commit 里接 LLM,打断感真的很强,团队没几天就都跳过了 hook。用 commit history 來提炼反而容错率高,试了几次事实性错误确实少很多,而且不需要改大家提交习惯,落地阻力小太多。
对于“commit message 是写给协作者的,changelog 是写给用户的”这一点很认同。我之前用 conventional commits 加工具自动拼出 changelog,结果就是一堆 feat/fix 列表,非技术用户完全看不懂。缺少从技术细节到用户价值的“翻译”,这个才是真痛点。
我自己也维护几个开源项目,changelog 拖更的情况跟文章说的完全一致。印象最深的是上下文切换成本那段,发布越频繁越难跟上。现在用类似方法把 commit 分类和归并交给 LLM,我只做终审,认知负荷降了一档。
看到上下文切换需要 15-23 分钟的引用,很有共鸣。之前觉得忙到没空更新 changelog 是时间管理问题,其实本质是大脑在编码和写文档之间切换成本太高。把机械的筛选、归并甩给 Claude 之后,写 changelog 的心智负担确实小了很多。
好奇实际 prompt 是怎么设计的,尤其是把多条 commit 合并成一条用户友好的描述时,怎么控制它不丢失关键变更又不啰嗦。还有 API 成本,处理 200 多个 commit 的 history 会不会很烧 token?希望后续能有更多工程细节分享。