Show HN:Claude 的「承重」词汇

查看原文 HN 讨论

文章摘要

Louis Abraham(HN 用户 Labo333)做的这个页面只有一屏,没有折叠、没有滚动叙事,却是本期最扎实的一次数据分析。它回答的问题很具体:如果只看 GitHub Pull Request 描述里用的词,AI 写的文本在过去一年半里占了多大比例,又长什么样?

结论先行:把 2025 年 1 月以来的 GitHub PR 描述按「用词方式」聚成 10 类,其中一类在 2025 年初只占语料的 0.7%,到 2026 年 8 月的最近四周已经占到全部「人类署名」 PR 的 39.5%,并且还在以每周约 +1.2 个百分点的速度上升(最近 12 周的最小二乘拟合)。这一类最具代表性的词,任何用过编程 agent 的人都会觉得眼熟——排在最前面的依次是 load-bearing(承重的)、plainlyquietlyrefusalsurvivedre-derivedhalvesassertednobodygenuinelydeliberatelypremiserefusespre-fixoutrightbyte-identicalrulinggenuinehandedcarriesdiedcarryingnowheredisagreedasymmetry,再往后还有 seamchokepointbackstoplevervacuouswedgedprovablyfalsifiedmutation-checkedbyte-for-byteself-healsrides 等等。

数据是怎么来的,作者写得极其坦诚。他先讲了一个失败:最自然的数据源 GH Archive(GitHub 公共事件流的归档)已经不能用了——2025 年年中以来这个流几乎只剩 PushEvent,2024 年 8 月 12 日某一小时里有 13,555 个 IssueCommentEvent,而 2026 年 8 月 10 日同一小时只有 86 个;而 push 事件里没有文本,GitHub 在 2025 年 10 月移除了 commit 数组。所有镜像读的是同一个流,所以谁都修不好。项目的早期版本正是建在归档上,当时报告 load-bearing 出现在 17 篇文档里——错了 158 倍,因为评论从流里消失了,不是从 GitHub 上消失了。

真正能用的是 GitHub 的 search API,原因只有一个:created: 接受时间戳而不只是日期,所以窗口可以只有几分钟宽,而且每条返回都带完整正文。做法是每天抓 10 个五分钟窗口,从一天的每个 2.4 小时区块里各抽一个,精确到秒。这个「精确到秒」不是讲究:早先的版本用 5 分钟的整数倍,而那恰好是 cron 触发的粒度,于是每个窗口都正好开在自动化批量开 PR 的瞬间。抽样种子由日期决定,所以整个语料只凭日期就能复现;每天一个约 1.4 MB 的不可变文件,提交后永不改写——仓库的历史就是抽样的历史。查询本身带两个过滤器,把一页里可用的描述从 43 条提到 97 条:按名字排除四个 App(pulldependabotrenovategithub-actions,它们占 App 署名正文的 90%),以及排除空正文(占全部 PR 的 45%,search API 没有「是否为空」限定符,改用「正文里必须包含十个功能词之一」精确实现)。作者还诚实地标了一条局限:2026 年的一天有约 46 万条匹配 PR,一个五分钟窗口约 1250 条,而一页只有 100 条——所以这是抽样而不是枚举(search API 无论匹配多少最多只返回 1000 条);均匀布点意味着这不是时间上的偏差,只是随着 GitHub 变忙,有效窗口宽度在收窄。截至发布时的规模是:603 个采集日、其中 595 天构成 85 个完整周(2025-01-06 至 2026-08-17)、461,121 条描述、51,079,244 次词出现、19,798 个越过频次下限的词

清洗规则同样有讲究。「词」被定义为「包含至少一个字母的字母、数字、斜杠、连字符和下划线的连续串」,所以 load-bearingsnake_case--all-targetssrc/main 都能完整存活;不做词干化、不做 n-gram、不用停用词表。链接坍缩成域名、HTML 标签整体保留,因为先按标点切分会把 href 这种碎片顶进某一类的特征词里。破折号(em dash)是唯一一个不要求含字母的例外,而它当之无愧:2024 年初每万词出现 0.2 次,2026 年中是 123 次。描述的中位长度是 65 个词。被丢掉的是按登录名形状判断的非人类账号——以 [bot]-bot 结尾的,加上 copilot注意这一点很关键:所有明显的机器人都被剔除了,剩下的 39% 是署了人名的 PR。

模型部分刻意做得很朴素:k 个「写法」各是词表上的一个固定分布,每条描述被硬分配给最近的那一个,距离用的是词频该用的 KL 散度(也就是把平方距离换成 KL 的 k-means,长描述对中心的拉力更大)。作者反复强调一件事:模型里没有任何时间参数。一组中心覆盖整个窗口,拟合里没有按周的参数,也没有任何自由度去描述或安放一个趋势。页面上画出的每条曲线都是事后归因——每条描述只凭自己的词被分类,周是之后才数出来的。所以「某种写法在上升」这件事,只可能是因为人们写的东西真的变了,别无出处。训练用 KL 下的 greedy k-means++ 加 Lloyd 迭代跑到精确不动点(没有容差要挑,也没有迭代次数要猜),跑 8 个种子取代价最低的一个发布——但作者明说多次重启不是为了找到更好的答案(代价和最终报告的份额相关性只有 +0.03),只是为了让每日任务总能发出点东西。页面声称的是「这一类出现了」,所以有两个阈值去校验而非挑选它:前八周低于 2%,后八周不低于 20%。作者还给出了无条件拟合的到达率作为证据:32 次单独拟合里有 31 次到达,剩下 1 次里领先的那一类和另一类混在了一起。

词的排序不靠模型,靠计数:因为分配是硬的,每次词出现都恰好属于一类,所以可以直接算「类内频率 / 类外频率」的比值。分母里加了半个 MIN_AUTHORS 的伪计数,作者说这个伪计数是「排名」和「抽奖」之间的差别——没有它,榜首会由 4200 万分之二到七这种量级的计数决定,一个类外出现三次的词能压过一个类外出现 158 次的词,差距不比自身噪声更大,而且两个词都罕见到没人会注意。load-bearing 类内出现 929 次、类外 82 次,比值 39 倍,稳居第一;页面上字号按比值的对数缩放,到第 1000 个词降到 5 倍。点选某个词会把图表换成这个词自己的历史,并且用「每百万词出现次数」这个比率而不是原始计数——因为语料每周大小不同(最瘦的一周 380,404 词,最肥的一周 1,406,687 词),画原始计数等于把语料的增长画成词的到来。

作者还专门列了一张「任意选择」的表,标注哪些常数是测出来的、哪些是判断、哪些看着答案选的K=10 明确标为「看着结果选的」,SEED 标为「有后果的——种子会移动头条数字」。剩下的 9 类里,其他 7 个的特征词一看就能认出是什么:一类是构建/测试命令行参数(--frozen--locked--test-threads--all-features),一类是 WebKit 的 CI 队列名(mac-wk2ios-simwpe-wk2),一类是西班牙语/法语/葡萄牙语(queparadansmudan),一类是 PyTorch 社区的 reviewer 用户名加基准单位(ns/opb/opbfloat16)以及 seems/basically/perhaps/probably 这类人类的迟疑用语,一类是 Qodo/PR-Agent 生成的「企业级」形容词堆砌(comprehensivemaintainabilityscalablestreamlinedenterprise-grade),一类是 Home Assistant / Expensify 的模板 checklist,还有一类是 Renovate/Nix/Snyk 的自动化模板字段。

页面本身也值得一提:Bauhaus/瑞士平面风格(作者说 Claude 给这套设计起名叫 “Rasterfeld”,即「网格之场」),黑底配三原色,十二栏网格,一千个词在自己的格子里滚动而不是把图表推下屏幕,词是点选而不是悬停(这样选中状态会保持,双手离开鼠标也能读面板)。整站没有构建步骤,数据由 GitHub Actions 每天更新,语料和站点分居两个分支(Pages 站点上限 1 GB,而语料每天长 1.4 MB)。

HN 评论精华

这条 Show HN 拿到 679 分、320 条评论,是本期讨论最热闹的一条。有意思的是,评论区几乎没人质疑方法论,全都在吐槽 Claude 的文风——数据只是给了大家一个共同的靶子。作者本人(Labo333)全程在场,回帖非常密集,并根据反馈当场加了搜索框、把采集量提到每天 1000 条 PR、加了浏览其他聚类的功能。