aider 的“自我编写率”统计可视化:blame.md 图表模板与 scripts/blame.py 数据管线全解析
aider 官方发布历史页面上那组“Aider 每次版本写了百分之多少新代码”的图表,由 aider/website/_includes/blame.md 这个 Jekyll include 模板渲染,其数据由 scripts/blame.py 基于 git blame 生成。读完本文,你能完整理解这两个图表(版本级 aider 代码占比柱状图、Aider/人类新增行数堆叠柱状图)的 Liquid 模板与 Chart.js 配置细节、背后 _data/blame.yml 的数据结构,以及 aider 如何把“AI 写的代码”从 git 提交历史中精确归因并沉淀为可复现的发布统计。
一、blame.md 是什么:嵌入发布历史页的图表 include
aider/website/HISTORY.md 是 aider 官网的“Release history”页面,它在页面开头声明:
Aider writes most of its own code, usually about 70-80% of the new code in each release.
并紧接着通过一行 {% include blame.md %} 引入 blame.md。这意味着 blame.md 本身不是一个独立页面,而是一个图表组件:Jekyll 构建官网时,把它展开进 HISTORY 页面,由浏览器端 JavaScript 负责实际绘图。
从源码结构看,这个 include 由三部分组成:
- 两个 canvas 容器:
blameChart与linesChart,各自包裹在一个固定高度 300px 的.chart-container里; - 一段内联 CSS:
.chart-container { position: relative; width: 100%; height: 300px; },保证图表在容器内自适应; - 一段 Chart.js 初始化脚本:加载 chart.js、moment 以及 chartjs-adapter-moment 三个库后,在
DOMContentLoaded时创建两张柱状图。
数据全部来自 Jekyll 站点数据 site.data.blame,对应的落盘文件就是 aider/website/_data/blame.yml。
二、数据模型:blame.yml 中每条版本记录的含义
site.data.blame 在 Liquid 模板里被当作品牌化迭代的数据源,模板用到的字段与 blame.yml 的实际结构一一对应。该文件目前共有 81 条版本区间记录(每个发布 tag 区间一条),每条记录形如:
- aider_percentage: 87.75
aider_total: 222
end_date: '2025-08-09'
end_tag: v0.86.0
file_counts:
aider/commands.py:
Paul Gauthier (aider): 7
Zexin Yuan: 1
aider/models.py:
Andrew Grigorev (aider): 3
Paul Gauthier: 3
Paul Gauthier (aider): 5
grand_total:
Paul Gauthier (aider): 219
Paul Gauthier: 16
start_tag: v0.85.0
total_lines: 253
各字段含义:
| 字段 | 含义 |
|---|---|
start_tag / end_tag |
统计区间的起点 tag 与终点 tag(一条记录对应两个连续发布版本之间) |
end_date |
终点 tag 的提交日期 |
total_lines |
该区间内计入统计的源码文件新增行数总和 |
aider_total |
其中归属于 aider 的行数 |
aider_percentage |
aider_total / total_lines * 100,保留两位小数 |
file_counts |
按文件细分的“作者 -> 行数”映射,作者名带 (aider) 后缀表示 aider 产出 |
grand_total |
按作者聚合的总行数(按行数降序) |
最早一条记录是 v0.5.0 -> v0.6.0(2023-06-15,aider 占比 31.33%,47/150 行),最新一条是 v0.85.0 -> v0.86.0(2025-08-09,占比 87.75%,222/253 行),与发布说明中“Aider wrote 88% of the code in this release”的口径一致。
三、图表一:blameChart——每个版本的 aider 代码占比
模板中第一张图是普通柱状图,配置要点如下:
- 数据构造:
labels由{% for row in site.data.blame %}'{{ row.end_tag }}'逐行生成为版本 tag 列表;data中每个点是一个对象{ x: '{{ row.end_tag }}', y: {{ row.aider_percentage }}, lines: {{ row.aider_total }}, end_date: '{{ row.end_date }}' },除了 y 轴数值外,还额外把“绝对行数”和“结束日期”塞进了数据点里供 tooltip 使用; - 坐标轴:x 轴为 category 类型、轴标题 "Version",刻度强制 45 度旋转(
maxRotation: 45, minRotation: 45)以容纳密集的 tag 标签;y 轴标题 "Percent of new code" 且beginAtZero: true; - 图表标题:
'Percent of new code written by aider, by release',字号 16,图例隐藏; - tooltip 回调:
label回调输出Aider's contribution: {百分比取整}% ({lines} lines),afterLabel回调追加Date: {end_date}。这个设计展示了 Chart.js 的一个常用技巧——把模板数据里的冗余字段挂在数据点上,让悬停提示比单纯显示 y 值信息量更大。
四、图表二:linesChart——Aider 与 Human 新增行数的堆叠柱状图
第二张图是 stacked: true 的堆叠柱状图,直观对比每个版本区间中两边各自写了多少行:
- 两个 dataset:
Aider的y取row.aider_total;Human的y用 Liquid 算术{{ row.total_lines | minus: row.aider_total }}现场计算(即总行数减去 aider 行数); - 堆叠设置:x 轴与 y 轴均设置
stacked: true,因此每根柱子的高度就是total_lines; - 图例:开启并放置在
chartArea内、reverse: true(Human 在上、Aider 在下); - tooltip:
label回调输出{dataset.label}: {value}; - 标题:
'Lines of new code, by release'。
值得注意的是 Human 行数的计算方式是“总数减 aider”,而不是在数据文件里单独存一个 human 字段——这保证了两张图的口径天然一致:只要 aider_total + human = total_lines,堆叠图就不会出现口径漂移。
五、数据从哪来:scripts/blame.py 的 git blame 归因算法
blame.yml 不是手写的,而是由 scripts/blame.py 在 aider 仓库上运行生成。该脚本是整条统计管线的心脏,核心逻辑分四步。
1. 只统计“源码文件”
脚本用一个白名单机制圈定统计范围(见 scripts/blame.py#L29-L49):
- 后缀为
.js、.py、.scm、.sh、Dockerfile、Gemfile的文件; .github/workflows/下的.yml工作流文件;aider/resources/下的.yml(模型元数据);- 显式列出的 5 个网站文件(
aider/website/index.html、share/index.md、_includes/head_custom.html、_includes/home.css、docs/leaderboards/index.md); tests/fixtures/languages/下的语言测试样例文件。
同时显式排除:所有 prompts.py(提示词文件)、tests/fixtures/watch* 测试夹具、install.sh/install.ps1 安装脚本。这正对应官方 FAQ 中对该统计口径的解释——“Only lines in source code files are counted, not documentation or prompt files”(见 aider/website/docs/faq.md 的 “How are the 'aider wrote xx% of code' stats computed?” 一节)。
2. 用 git blame 带相似度检测逐文件归因
对每个入选文件,get_counts_for_file 执行:
git blame -M100 -C100 -C -C --abbrev=9 {start_tag}..{end_tag} -- {fname}
参数含义在源码注释中写明:-M100 以 100% 相似度检测文件内移动的行,-C100 检测跨文件移动,再加两个 -C 进一步增大检测力度。这样当 aider 或人工把代码块整体挪动时,行数仍归属原作者而非“搬家”动作,避免统计虚高。脚本按 9 位短哈希(hash_len = len("44e6fefc2"))将每行 blame 输出映射回作者名并累计行数;以 ^ 开头的边界行会被跳过,文件在该区间不存在(no such path)时静默返回 None。
3. 如何判定一行代码“是 aider 写的”
判定发生在提交层面而非代码层面(get_commit_authors):对区间内每个 commit,取作者名 %an,再检查 commit subject 是否以 aider: 开头,或完整 message 中是否含 co-authored-by: aider(均做小写匹配)。满足任一条件时,该作者名被改写为 "{author} (aider)",例如 Paul Gauthier (aider)。之后 aider_total 就是所有作者名含 (aider) 的行数之和。
这套判定依赖 aider 与 git 的深度集成:aider 的自动提交默认带上 aider: 前缀或 Co-authored-by 尾注(发布历史中 --attribute-co-authored-by、--attribute-commit-message-author 等选项控制这些标注)。因此该统计的前提是“提交带有正确的作者标注”,aider/website/docs/faq.md 也明确说明了这一点:stats 是 “by doing something like git blame on the repo, and counting up who wrote all the new lines of code in each release”。
4. 汇总、百分比与 YAML 输出
blame() 汇总出 grand_total(按作者降序)、total_lines、aider_total、aider_percentage(保留两位小数)与 end_date(用 git log -1 --format=%ai 取终点 tag 日期)。命令行入口提供两种模式:
- 默认模式:
python scripts/blame.py {start_tag} [--end-tag TAG],只统计一个区间,并在末尾打印- Aider wrote {x}% of the code in this release.;start_tag缺省时自动取最新一个vX.Y.0tag(get_latest_version_tag(),即跳过补丁版本只认 minor 版本); --all-since模式:process_all_tags_since()遍历起点之后所有.0结尾的 tag,对每对相邻 tag 各算一次,若--output文件已存在,则按(start_tag, end_tag)键做“替换或追加”合并,再按 semver 排序后整体写回 YAML——这正是 blame.yml 能随新版本发布增量更新、且历史条目不被重复追加的机制。
六、统计结果如何流回文档与发布说明
管线闭环如下:scripts/blame.py --all-since --output aider/website/_data/blame.yml 生成/更新数据文件 → Jekyll 构建时 site.data.blame 注入 HISTORY.md 中展开的 blame.md include → 浏览器执行 Chart.js 脚本渲染两张图。同时,每个版本的发布说明末尾会人工(或借助脚本)写入一句 “Aider wrote XX% of the code in this release”,例如 v0.86.0 的 88%、v0.82.0 的 92%、v0.79.2 的 93% 均出现在 aider/website/HISTORY.md 中,与 blame.yml 对应区间的 aider_percentage 一致。
更早的一篇博客 aider/website/_posts/2024-05-24-self-assembly.md(标题“Aider has written 7% of its own code”)展示了同一方法论的全库视角:对当前仓库所有文件做 git blame 得到“aider 行 / 文件总行”的逐文件占比表。文中也坦承该数字是低估——black 等格式化工具的周期性重排会掩盖 aider 的行归属,这也解释了为什么 blame.py 里用 -M100 -C100 -C -C 尽量找回被搬动的行,以及为什么纯格式化改动多的版本(如 v0.76.1 仅 0%、v0.76.2 为 75%)会出现波动较大的百分比。
七、小结:一条可复现的“AI 写码占比”度量链路
- 展示层:aider/website/_includes/blame.md 是纯前端组件,两张 Chart.js 柱状图(占比图 + Aider/Human 堆叠图)的数据完全由
site.data.blame驱动,Liquid 模板只在构建期完成数据插值,y 轴数值、tooltip 中的行数与日期都来自数据文件的原始字段; - 数据层:aider/website/_data/blame.yml 按版本 tag 区间存储逐文件、逐作者的行数归因,
--all-since的增量合并策略让数据文件可以随版本持续演进; - 算法层:scripts/blame.py 用带 100% 相似度移动检测的
git blame逐文件统计,通过 commit 的aider:前缀 /Co-authored-by: aider尾注识别 aider 产出,并以源码文件白名单限定口径。
对任何想度量“AI 工具在自家项目中写了多少代码”的团队来说,这条“git 归因 → YAML 数据 → 站点图表”的链路提供了完整、可复制的参考实现,而且全程只依赖 git 提交历史本身,不需要额外埋点。
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
