huggingface_hub 实现每周发布:AI、开源工具、人工审核闭环
Hugging Face 把周更流程完全开源,用 GLM-5.2 生成发布说明初稿,再加确定性校验和人工修订,成本低到两毛五一次。想提高发版频率的 Python 库维护者可以直接 fork 适配。
Hugging Face 将 huggingface_hub 的发布周期从每 4‑6 周缩短至每周,全部由单个 GitHub Actions 工作流自动完成。流程依赖开源工具和开权重模型(当前为 Z.ai 的 GLM‑5.2)来起草发布说明和 Slack 公告,但保留人类在最终审核环节的决定权。自动步骤包括版本号更新、提交标签推送、PyPI 发布、下游测试分支创建、发布说明草稿、Slack 公告草稿、归档、后置版本提升以及对合入 PR 的评论。所有组件均基于开源生态构建,任何维护者都可直接复制使用。
每周以 AI、开源工具和人工审核为环节,发布一次 huggingface_hub
huggingface_hub is the Python client at the base of the Hugging Face ecosystem. transformers, datasets, diffusers, sentence-transformers and dozens of other libraries depend on it to talk to the Hub. Every week we don't ship a new release is a week of fixes and features stuck on main. 过去很长一段时间,我们每 4 到 6 周发布一次。现在我们改为每周发布一次,全部通过一个 GitHub Actions 工作流完成。我们使用开源工具和开放权重的模型构建了这个工作流,并在唯一需要判断力的环节保留了人工审核。这篇文章中没有任何内容需要供应商合同、闭源模型,或你无法自行运行的基础设施。这从一开始就是设计目标,因为我们希望这个工作流能被其他维护者直接拿来并加以改造。
读完这篇文章后,你将掌握构建自己工作流所需的一切。
我们的起点
旧的流程部分自动化,大部分手动操作。
已经在持续集成(CI)中的部分:
- 标签推送后自动发布到 PyPI。
- 在下游库中打开测试分支,并固定发布候选版本(release candidate)。
每次仍需手动完成的操作:
- 创建发布分支,在 __init__.py 中更新版本号,提交、打标签、推送。
- 观察下游 CI 运行并分类排查失败原因。
- 逐一审阅上次发布以来合并的所有合并请求(PR),并手动编写发布说明:按主题分组,附上上下文,语气不像 git 日志转储。
- 在发布候选期结束后,推出稳定版发布。
- 起草内部的 Slack 公告和社交媒体帖子。
- 打开发布后的 PR,将主分支版本提升到下一个 dev0。
为新版撰写高质量的说明是最费力的部分,需要汇总数十个不同主题的 PR。技术上没什么难点,但需要几个小时的专注时间。再加上公告工作,一个小版本发布往往要分散在好几天内完成,总共花半天时间。
两种工作类型
所以我们决定简化整个流程。看那张清单,工作可以分为两类。
有些步骤纯粹是机械性的,可以自动化:更新版本号、提交、打标签、推送、打开下游测试分支、打开发布后的 PR。这些步骤不需要任何人思考,只需要每次都按正确的顺序发生,而这正是 CI 工作流擅长的事情。
剩下的部分则截然不同。撰写发布说明、决定突出哪些内容、构思面向人类读者的公告措辞——这些是脑力活。正是这类判断力让发布手册多年来一直沿用。这正是 AI 的用武之地,它能在几秒钟内把一张空白页变成扎实的初稿。但这也是我们必须小心的地方,因为一篇看起来信心十足却暗藏细微错误的初稿,比压根没有初稿更糟糕。
设计原则:开放组件,任何人都可复用
当我们决定修复这个问题时,我们事先设定了一个约束条件:每一个可动部件都必须能够被任何维护者自行运行。没有 API 背后我们无法替换的闭源模型,没有专有发布平台,没有秘制配方。
以下是完整的技术栈:
| 组件 | 功能 |
|---|---|
| GitHub Actions | 编排整个发布流程 |
| OpenCode | 驱动模型的智能体运行时 |
| 开放权重模型(目前使用 Z.ai 的 GLM-5.2) | 草拟发布说明和 Slack 公告 |
| HF Inference Providers | 提供模型服务 |
| PyPI Trusted Publishing | 发布包 |
第二个原则:模型草拟,人类决策。语言模型擅长把三十条简洁的 PR 标题变成可读的发布说明,但它们不擅长被盲目信任。因此,工作流是人工监督的:模型完成第一轮处理,确定性脚本检查其输出,然后在任何内容发布之前由人工审核和编辑(下文有更多细节)。
流水线概览
整个工作流是一个文件 `.github/workflows/release.yml`,通过 Actions 界面手动触发。它只接受一个输入:
on:
workflow_dispatch:
inputs:
release_type:
type: choice
options:
- minor-prerelease
- minor-release
- patch-release
从那里开始,任务大致按以下顺序运行:
- 准备。计算下一个版本,创建或复用发布分支,更新 `__version__`,提交,打标签,推送。
- 发布到 PyPI。构建并上传 `huggingface_hub`。同时,构建并上传 hf CLI 作为独立的 PyPI 包。
- 发布说明。对比自上一个标签以来的提交范围,从 GitHub API 拉取 PR 元数据,然后让模型草拟一份结构化的变更日志(这里有一份最近的示例)。保存为 GitHub 发布草稿。
- 下游测试分支。对于 RC 版本,在 transformers、datasets、diffusers、sentence-transformers 中打开一个固定了 RC 版本的分支,这样它们的 CI 能快速告诉我们是否破坏了某些东西。
- 在 Slack 上发布公告。阅读相关说明,并以我们团队的风格撰写一份内部公告。
- 归档说明。将原始的 AI 草稿和人工编辑后的版本并排上传到 Hugging Face 存储桶。
- 发布后版本号升级。在稳定版本发布后,在主分支上提交一个 PR,将版本号升级到下一个 dev0。
- 对已合并的 PR 添加评论。在发布范围内的每一个 PR 下,留下一条“此功能已在 vX.Y.Z 中发布”的评论。
- 同步 CLI 文档。将重新生成的 hf CLI 技能文档提交 PR 到我们的技能仓库。
- 向 Slack 报告。每一步都会以帖子回复的形式报告其状态;最终的任务会用 ✅ 或 ❌ 更新根消息。
剩下的人工操作步骤包括:审阅并发布草稿版发布说明,以及审阅并发布一条内部 Slack 消息。这两个步骤正是我们希望保留人工参与的环节。
信任但验证:人工参与的核心
以下是大家对于 AI 生成的发布说明最担心的失败模式:模型悄悄遗漏了一个 PR,或者凭空捏造了一个不在本次发布中的 PR。一份几乎正确的变更日志比没有变更日志更糟糕,因为没人会再去重新核查它。
我们并不指望生成的发布说明一次性就能做到完整,而是用确定性方法进行验证。在模型运行之前,一个 Python 脚本会检索所有属于本次发布的 PR,并将它们作为基准事实存储起来。
PR_NUMBER_PATTERN = re.compile(r"\(#(\d+)\)$")
pr_numbers = [
int(m.group(1))
for commit in commits_since_last_tag
if (m := PR_NUMBER_PATTERN.search(commit.title))
]
save_manifest(pr_numbers)
然后模型根据这些 PR 起草说明。完成后,我们会将模型输出与最初的 PR 列表进行核对:
expected = set(load_manifest())
found = extract_pr_refs(notes_md)
missing = expected - found
extra = found - expected
如果有任何遗漏或多余的 PR,我们不会直接报错,也不会发布错误的文件。而是把差异信息反馈给智能体,要求它只针对那些 PR 进行修正:
for _ in range(MAX_ITERATIONS):
missing, extra = validate(notes)
if not missing and not extra:
break
run_agent_fix(missing_prs=missing, extra_prs=extra)
这正是让整个流程值得信赖的模式:一个非确定性的模型被包裹在确定性的护栏之中。模型擅长撰写流畅的文字,但难以做到穷举无遗。所以我们让它负责写作,让代码来保证一致性。
为模型提供依据,防止其凭空编造
完整性是一方面,准确性是另一方面。一个仅凭 PR 标题来总结内容的模型,很可能会乐呵呵地编造出一个与实际 API 不符的代码示例。
为了防止这种情况,在获取 PR 元数据时,我们还会从每个 PR 中提取实际的文档差异:即该 PR 所修改的 `docs/` 目录下所有 `.md` 文件的统一差异。
def fetch_doc_diffs(pr):
return [
{"filename": f.filename, "status": f.status, "patch": f.patch}
for f in pr.get_files()
if f.filename.startswith("docs/") and f.filename.endswith(".md") and f.patch
]
这段差异信息会进入模型的上下文窗口,这样当它编写“这是新的 CLI 命令”时,会引用 PR 作者实际写在文档中的示例。这与之前的逻辑相同:给模型真实的原始素材和明确的任务。
提示词本身以技能(Skills)的形式存在:即小的 Markdown 文件(SKILL.md 及引用模板),它们被检入到代码仓库中。发布说明技能(release-notes skill)详细说明了如何挑选重点、如何组织章节、何时添加文档链接等。它读起来就像新手上手指南——这正是正确的思维模型。
人工审核环节
在候选发布版本(RC)发布后,GitHub 上会有草稿版本的 release,其中包含 AI 的初稿。此时人工介入:
- 审核人员阅读草稿,调整语气和重点,修正模型过度强调或忽略的内容。
- 只有在此之后,他们才会触发正式版本(minor-release)的发布流程,将候选版提升为最终版。
审核人员的时间用于打磨——把原来需要半天时间的写作工作,变成十五分钟的编辑工作。
我们还保留了完整的记录,以便持续改进。我们将两个文件并排归档到 Hugging Face 的存储桶中:一个是原始的 AI 草稿(在候选版发布时、任何人修改之前上传),另一个是人工编辑后的版本(在最终版本发布时上传)。
hf cp release_notes_raw.txt "hf://buckets/huggingface/releases/huggingface_hub/${V}/release_notes_raw.txt"
hf cp release_notes_edited.txt "hf://buckets/huggingface/releases/huggingface_hub/${V}/release_notes_edited.txt"
每周收集这两个版本,我们就拥有一个不断增长的数据集,包含“模型写了什么”与“我们希望它写什么”的对比。这个数据集可以用来更新智能体的技能。
开放且安全的管道
重新设计发布流程也是一个加强安全性的好机会,特别是防范供应链攻击。
不使用 PyPI Token。发布采用受信发布(Trusted Publishing)机制:PyPI 验证由 GitHub 为该精确工作流生成的短期 OIDC Token,并为每个制品签发 PEP 740 认证/Sigstore 溯源证明。不存在需要泄漏或轮换的长期密钥。
permissions:
id-token: write
attestations: write
- uses: pypa/gh-action-pypi-publish@v1.14.0
with:
attestations: true
智能体运行时是固定版本且经过验证的。我们不会用 curl | bash 的方式直接下载并使用最新的 OpenCode。我们会固定一个版本,并在运行前检查其 SHA256 值:
curl -fsSL https://opencode.ai/install | bash -s -- --version "${OPENCODE_VERSION}"
echo "${OPENCODE_SHA256} $(which opencode)" | sha256sum -c -
开放的工具并不意味着草率的工具。
那么,这花费了多少成本?
几乎不花什么成本。一次完整发布(包括发布说明和 Slack 上的公告,涉及 20 到 40 个 PR 以及几轮提示词工程)在 Inference Providers 上大约只需 0.25 美元。由于开放权重按使用量计费,每周唯一真正的问题是“有没有值得发布的东西?”,而答案总是“有”。
实际发生了什么变化
发布节奏从每 4 到 6 周一次变成了每周一次。真正有趣的是那些次级效应:
- 发布说明变好了,而不是变差了。初稿始终存在,因此审阅时间都花在打磨上。分组更加一致,遗漏的内容也更少了。
- 问题更早暴露。下游测试分支在每个候选版本窗口期内就能捕捉到集成问题。
- 贡献者的反馈循环缩短了。自动生成的“已在 vX.Y.Z 版本中发布”评论的重要性超出了我们的预期。当有人在已关闭的 PR 上报告问题时,每个人都能立即看到修复所在的版本。以前这需要手动查找标签。
让它为你所用
这是我们最在意的一点。工作流是围绕 huggingface_hub 设计的,但整体结构是通用的。
几乎可以直接复用:
- 触发逻辑和版本号递增逻辑(先次预发布,再次发布,最后补丁发布)。
- 信任但验证循环:确定的清单、模型草稿、验证、重新提示。这是可迁移的核心思想,与你生成什么内容无关。
- OIDC 可信发布、固定版本且通过校验和验证的运行时、Slack 线程化。
- 基于技能(skill)的提示词:替换模板,保留结构。
我们特有的部分:
- 下游仓库列表及其依赖锁定格式。
- 技能中具体的章节分类和语气风格。
- Slack 和存储桶目标。
如何适配:fork 工作流文件和脚本,指向你的包,按照你项目的风格重写技能的 Markdown 文件,设置两个仓库变量(模型 ID 和你的 OpenCode 版本),在 PyPI 上设置可信发布,如果没有下游依赖就删除下游测试任务。信任但验证循环是值得原样复用的部分——正是它让生成的工件可以安全发布。
下一步计划
- 自动分类下游故障。目前工作流程会打开测试分支,由人工阅读 CI 结果。一个显而易见的下一步是检查失败的日志,并在内部 Slack 消息中报告它们。
- 扩展该模式。这些内容大部分是通用的。我们预计会在生态系统中其他 Python 库中大量复用。
要点总结
发布流程中那些原本需要人工专注工作半天(撰写发布说明、起草公告、协调下游检查)的部分,恰好是模型擅长起草的部分。其余一切都是机械性的,可以放在一个 YAML 文件中。诀窍从来不是“让 AI 做所有事”,而是让模型起草、让确定性代码验证、让人做决策。这套方案完全基于开源工具和开放权重构建,因此成本几乎为零,任何人都可以运行。
完整的工作流文件是公开的。如果你维护一个 Python 库,欢迎 fork 它、适配它,并告诉我们效果如何!
本文提及的模型 1
社区
· 或发表评论