同一个人的同一份简历,跑了 100 次,得了 100 个不同的分数——AI 招聘的随机性实验
目录
- 90 分——等等,74 分——不,88 分——其实是 83 分
- HackerRank 的 ATS 是怎么工作的
- 2 的 100 次穷举证明
- HN 评论区:这是蓄意降低期望值的那一半简历
- 本地验证:跑完一百分之后
- 最后:概率模型不应该做管理决策
90 分——等等,74 分——不,88 分——其实是 83 分
Dan Kinsky 在 Substack 上发了一篇帖子,标题本身就是一段叙事——「HackerRank 开源了它的 ATS。我的简历得了 90/100。等等,74/100。不——88/100。其实是 83/100。」
他刚配置好那套开源系统的时候第一次跑,90 分,心情不错。然后他清理了一下代码里的 debug print 语句——只删了 print 语句,没动简历——重跑了一遍,74 分。
同一个人。同一份简历。同一份 PDF。同一个命令。不同的分数。
HackerRank 开源的这套系统叫 hiring-agent(github.com/interviewstreet/hiring-agent),在 LinkedIn 和 Reddit 上都有数千次点赞。Kinsky 的帖子在 Hacker News 上拿了 942 分,97 条评论。
他决定跑 100 次看看。
HackerRank 的 ATS 是怎么工作的
这套系统的工作流程其实挺典型的——你的 PDF 被解析成文本,LLM 被调用了六次来提取结构化信息:基本信息、工作经历、教育背景、技能、项目、奖项。它还拉取你的 GitHub 个人主页,扫描你的 top 仓库,追加为额外上下文。然后所有东西一起喂给 LLM 打分。
评分满分 100,外加最多 20 分 bonus:
- 35 分 — 开源贡献
- 30 分 — 个人项目
- 25 分 — 工作经历
- 10 分 — 技术技能
- 最多 20 分 — 创业经验、个人博客等
默认模型是 gemma3:4b,temperature 0.1——很低,理论上在推动模型输出更确定的结果。
问题是,当 Kinsky 逐个维度看分数的时候,模式非常清晰。
技术技能(10 分)——他 100 次里有 98 次拿了 8/10。几乎恒定。因为技术技能是一个 checklist——你会 React,或者你不会。没什么需要 LLM 做「判断」的。
项目(30 分)——这里才是灾难。每次评分都不一样。有时「缺乏架构复杂度」,有时「展示了真正的部署能力」。LLM 每次作出的判断都是概率采样。
工作经历(25 分)——每次都是 25/25。满分。每—次。他回去看了一眼源代码——「工作经历」维度只有两行描述,没!有!评!分!锚!点!一段暑期实习拿满分,十年架构师也拿满分。这个维度形同虚设。
2 的 100 次穷举证明
Kinsky 做了每一个数据分析师都会做的事情——关掉 DEVELOPMENT_MODE,把整个流程放进一个循环,跑了 100 次。
分数范围从 66 到 99。
如果你的公司 cutoff 设在 85 分,他有 65% 的概率被刷掉。同一份简历。不同的运气。
这不是体温计偶尔跳 0.1 度——这是同一个人的价值被同一套系统打了 33 分的方差。
有人在他之前就发现了这个问题——GitHub issue #26 在 2025 年 10 月就有人报告了同一现象:同一份简历在 temperature 0 下连续六次跑了 27, 34, 32, 34, 34, 30。温度已经是零了,方差还是这么大。这不作为一个 bug 能通过微调修复——这是基础设计缺陷。
Kinsky 换了个更大的模型——Gemini——做了同样测试。分数分布更集中了(48 到 64),但如果你的 cutoff 在 60,你仍有 28% 的概率因为「不关你的事」的原因被筛掉。
HN 评论区:这是蓄意降低期望值的那一半简历
Hacker News 的 97 条评论没有一条质疑「这个系统有问题」。质疑的是「问题有多大」和「谁在乎」。
有人提到了那个著名的笑话:HR 经理把一半简历直接扔掉,因为「我不想招运气不好的人」。评论区有人说现在不是笑话,这个系统就是在做一样的事。
「如果你们公司的 cutoff 设在 85,候选人 65% 的失败完全随机——那这个系统到底在筛选什么?运气?」
有人写了 2025 年底给欧委会的推荐信——建议禁止使用 AI 做招聘决策,因为概率模型无法被问责。这个建议没有通过。
然后有一条评论被顶到了非常靠前的位置:
「计算机永远不能被问责——所以计算机绝不能做管理决策。」
这句话是整个事件最好的总结。不是技术问题——LLM 可以做招聘初筛、可以在特定条件下提高效率。但「不能问责」是一个管理原则,不是一个工程参数。你没法 fine-tune 掉问责的必要性。
本地验证:跑完一百分之后
我也想做和 Kinsky 一样的事——但我的机器跑不了 gemma3:4b。
所以我做了第二好的事:拉了 interviewstreet/hiring-agent 的代码,翻了 prompt 文件。
lib/scoring.md 就是核心评分 prompt,四百多行 Markdown。读完之后我发现 Kinsky 的描述相当准确——项目维度确实有一套详细的评分锚点(复杂度、影响力、架构选择),但工作经历维度的描述极度模糊。
然后我注意到了一个更隐蔽的设计问题:这个系统把 6 次 LLM 调用的结果拼接后,再做一次评分调用。每一次 LLM 调用都是概率性的——当六个概率采样结果以不同方式排列后喂给评分器,每一次评分面对的输入都是不同的文本。不是内容的实质不同——是措辞、排序、分段的不同——但对于一个基于 next token prediction 的模型来说,输入文本的微小差异足以改变最终评分。
这就是为什么 temperature 0 也不完全修复问题——六个提取调用的结果每次随机组合,评分器看到的「原材料」本身就不同。你可以在评分层把温度调到 0,但提取层已经注入了随机性。
这不是「几份满分几份低分」的分布问题——这些分数之间没有任何信息。你没法从 66 分中读出「这个人不够好」以外的任何结论,因为你根本不知道这个 66 分是因为他技术不行、工作经历描述不够、还是开源项目标签今天没被正确解析——或者单纯是因为第 3 次 LLM 调用输出里换了一行。
最后:概率模型不应该做管理决策
这不是一个关于技术的问题。
HackerRank 这套系统的问题不在于它用了 LLM,不在于 temperature 调得不够低,不在于 gemma3:4b 太弱——所有这些都可以在工程层面优化。但即使你把所有工程细节都调到完美,你仍然面对一个根本性的问题:同一个人的简历,跑了多次之后得到了不同结果——那这个分数代表什么?
如果分数不包含「确定性」的信息——即它不代表「这个人值 85 分」而是「在这个系统的一次特定运行中,这个人的简历打出了 85 分」——那这个分数就毫无意义。
它不是偏差,不是噪声——它是信息缺失。
在一个值得招聘的系统里,你需要知道三个东西:
- 这个候选人值不值得进入下一轮
- 这个判断有多可靠
- 如果判断错了,谁负责
AI-ATS 回答了 1,默认不回答 2——如果被问到就说「那是概率,从来不承诺 1-2 分精度」——然后从来不回答 3,因为它不是一个可以被起诉的法律主体。
这个空缺从设计上就是系统性的——不是 bug,是 feature。而那个空缺,恰好是整个人力资源行业的边界。
一个不能为判断负责的东西,不应该替别人做判断。这个原则在法学里叫 accountability gap,在系统设计中叫 「human in the loop」。金斯基的 100 次运行不是揭露了一个 bug——它只是把那个边界用数据画了出来。数字都在那里,结论自己说话。
评论(0)
暂无评论,来写第一条吧~