想象一下你是一家餐厅的老板,现在要招厨师。你有两种考法:一种是让应聘者当一天代理主厨,从买菜、切配到出餐、管后厨,全流程来一遍;另一种简单得多——让他做一道麻婆豆腐,考官递上菜名、食材清单和口味要求,他把成品端上来,评委当场夹一筷子,味道对了就过关。第二种就是“单菜考核”,它考的是厨师最核心的基本功:理解要求、处理食材、控制火候。HumanEval 对 AI 大模型做的事,正是这场“单菜考核”——它不给模型布置一个完整项目,而是递上一张张“单菜考卷”:一个函数签名、一段说明文字、几组输入输出测试。模型只需补全函数体,随后系统把测试用例像评委一样“尝”一遍,全对才算通过。这个基准由 OpenAI 在 2021 年发布,164 道手写题,体量不大,却成了整个 LLM 代码能力评测的行业标尺。本文就用这场“厨艺单考”,带你完整看懂它如何出题、如何判分、如何解读分数,以及这张考卷背后的漏洞。
搞懂 HumanEval,你就能看懂各大模型排行榜上的“代码分”是怎么算出来的,也能在模型选型与评测时亲手设计“单菜考试”,用最小成本验证真实编码能力。
目录
可视化演示
三步展示函数级代码生成:docstring/签名/补全三类输入形式、自回归逐token生成函数体、以及pass@k无偏估计与EvalPlus增强测试用例评测。
本节配有交互动效(进入视口后自动加载,约 3KB)。
三类输入形式 docstring: 自然语言→代码 签名:类型驱动 补全:上下文代码 输入→函数体 函数级生成聚焦算法实现。
评估粒度:一道菜的考试
如果想让一个 AI 厨师做菜,最简单的考法是什么?不是让它开餐厅,而是让它炒一盘宫保鸡丁:考官递上菜名、主料辅料清单、口味要求,它把成品端上来。这就是函数级代码生成——把“做菜”的最小完整单元拎出来单独考。HumanEval 对模型做的事,就是递上这张“单菜考卷”,让模型展示最核心的厨艺基本功。
有人可能会问:为什么不直接考完整项目?答案藏在“评估粒度”四个字里。代码生成可以分成三个层级:函数级只生成一个函数体,像“考一道菜”;文件级要求补全整个文件,像“配一桌宴席”;项目级要跨文件组织模块、管理依赖,像“经营一家餐厅”。函数级聚焦在算法实现与 API 调用能力上,把环境变量、文件 IO、外部依赖全部剥掉,只留纯粹的逻辑。
HumanEval 的函数级任务采用一问一答的结构。问题 = 函数签名 + docstring,签名像菜名和食材清单,docstring 是烹饪说明;答案 = 函数体。评估时,系统把测试用例像评委夹菜一样塞进函数去执行,全部用例通过才算过关。这种格式非常干净:不依赖任何外部文件,因此可以完全自动化地批量判分。
单菜考核的好处,就像考厨师只考一道麻婆豆腐:成本低、标准统一、人人都能上手。它牺牲了对复杂工程能力的考察,却换来了公平、可复现、可大规模执行的评估体验——这正是函数级评估成为业界基准起点的根本原因。
基准构造:164 道手写考题
出考卷是有讲究的。只考一道菜,厨师背熟菜谱就能蒙混过关;考一百多道菜,才能摸出真实水平。HumanEval 的考卷由 OpenAI 团队从零开始手写,一共 164 道 Python 题,每一道都像一个标准化的“单菜考题”,题目之间互不重复,且刻意覆盖不同知识点,防止“押题”。
与题目绑定的,是“可判分”的设计。每道题不仅给出函数签名和 docstring,还配了一批手写单元测试用例,平均每道约 7.7 个。这些用例就是评委的“味觉标准”:输入什么参数、应该输出什么结果,全部写死。没有这些用例,机器的“尝菜”就无从谈起。
从内容上看,这 164 道题覆盖了多个“菜系”:字符串处理、数学运算、链表操作、逻辑推理等。难度定位在中等偏易的编程面试题,既不刁钻也不送分,让大多数模型都能拿到一些分数,从而形成梯度,方便区分能力高低。题目多改编自常见算法书和面试题,但经过了重新表述,尽量与当时的公开训练数据错开,降低模型“背答案”的风险。
这份考卷刻意排除了所有“超纲材料”——没有外部库,没有文件读写,唯一允许的操作都在函数内部完成。正是这种干净利落的“厨房设置”,让 164 道菜之间的难度可比、不同模型的分数可比。题目是手写的,用例是手写的,整个数据集只有 164 条,却撑起了一个行业的标尺。
评估方式:评委实际尝菜
在 HumanEval 出现之前,评估代码生成常用 BLEU、编辑距离这类指标。它们本质上是“看照片评分”:把模型生成的代码和参考答案放一起,比一比字符重叠度。长得像就加分。但长得像不代表好吃——就像一碗麻婆豆腐摆盘再还原,味道不对照样不能过关。HumanEval 换了一个思路:不看了,直接端起来尝。
所谓“尝”,就是把生成的函数放进 Python 解释器执行,用单元测试的断言去比对实际输出。整个过程不需要人工介入,也不需要模型自圆其说。程序跑一遍,对就是对,错就是错,这是“功能正确性”与“文本相似度”最本质的区别。评判逻辑可以写成一句话:
这道公式的意思是:评委不是看“你像不像做了麻婆豆腐”,而是真的夹一筷子(喂入输入),尝一下咸淡(比对输出),评分表上每一项都打钩,才盖下“合格”的章。因此,即使模型写出了一堆风格怪异、和参考答案完全不像的代码,只要执行结果正确,HumanEval 依然给满分——这是它和传统文本指标最大的分水岭。
- 全称量词 $\forall$读作“任意”“全部”,表示后面的条件对集合里的每一个元素都必须成立,不能有例外。
- 测试集合 $T$这是该函数配好的所有测试用例的集合,每一个用例都包含一组输入和对应的期望输出,就像评委手里的评分表。
- 执行判定 $f(t_{\text{in}}) = t_{\text{out}}$把测试用例的输入 $t_{\text{in}}$ 喂给生成的函数 $f$,看返回结果是否等于期望输出 $t_{\text{out}}$,相等才算这一项达标。
指标解析:pass@k 过关概率
厨师考试难免紧张,一次发挥失常不代表不会做菜。于是考官改了规则:给你 k 次机会,每次重做一版,只要任意一次过关,就认定你具备这道菜的能力。这就是 pass@k 指标的核心思想。在真实评测中,模型对同一道题会采样 n 个候选答案,其中一部分能通过全部测试,一部分不能,然后我们再模拟“随机抽 k 次,至少抽中一次正确答案”的概率。
为什么不能简单地把正确率当成 pass@k?因为 n 个候选里可能有很多正确解,而我们要评估的是“在有限次尝试内抓住机会”的能力。假设模型采样了 n 个候选,其中 c 个是功能正确的,那么从 n 个里任取 k 个,一次都没抽中正确的组合数,就是从剩下 n-c 个错误候选里取 k 个的组合数。于是:
如果 k=1,公式退化为 pass@1 = c/n,也就是“一次性出手就成功”的概率,这是最严格也最常见的报告指标。如果 k 不断放大,相当于允许厨师反复试错补考,成功率自然节节攀升。所以看模型排行榜时,务必先确认报道的是 pass@1 还是 pass@100——别被“放水规则”骗了。
- 总组合数 $\binom{n}{k}$从 n 个采样候选中任取 k 个,一共有多少种取法,这是所有“尝试方案”的总数。
- 失败组合数 $\binom{n-c}{k}$如果正确候选只有 c 个,那么 k 次全抽到错误候选的取法数,就是从 n-c 个错误候选中取 k 个的方案数。
- 失败概率 $\dfrac{\binom{n-c}{k}}{\binom{n}{k}}$用“全抽错”的取法数除以总取法数,得到 k 次机会一次都不中的概率。
- 成功概率 $1 - \dfrac{\binom{n-c}{k}}{\binom{n}{k}}$用 1 减去失败概率,剩下的就是“至少抽中一次正确候选”的概率,也就是 pass@k。
意义地位:行业标尺
当整个厨师行业都认可“麻婆豆腐单菜证”,餐厅招人、厨师切磋就有了共同语言。HumanEval 在 LLM 世界扮演的正是这把标尺的角色。它定义了一个简单、透明、可复现的“菜品”,让所有 AI 厨师都能在同一张灶台上比试,分数一出来,高低立判。
它让不同实验室的模型能够在同一张考卷上横向对比。OpenAI 的 Codex、Anthropic 的 Claude、Meta 的 Llama,几乎所有主流模型的论文里都会写一句“在 HumanEval 上 pass@1 达到了 xx%”。这就好比运动员参加统一的百米赛跑,虽然训练方法各异,但计时器只有一个,成绩可以直接放进同一个排行榜。
除了对比价值,它的工程意义也很突出。数据集完全开源,复现成本极低;一次评估只需几分钟到几十分钟,非常适合研发迭代;它还被社区移植成多语言版本,衍生出 MBPP、HumanEval+、MultiPL-E 等一系列评测集,形成了一套完整的评测生态。码能力由模糊的“感觉”变成了可量化的分数。
一句话总结:HumanEval 给了每个 AI 厨师一张全国通用的“等级证书”。它只是入门级的考核,但正因为入门,才让所有选手站在同一条起跑线上。没有这把标尺,各家模型“代码能力很强”的说法就只是自说自话。
挑战局限:考试有漏洞
任何考试都有应试技巧,HumanEval 也不例外。第一道裂缝是“题库泄漏”:如果模型在训练阶段已经见过这 164 道原题,考试就变成了“默写”。不少研究已经发现,部分模型在 HumanEval 上的高分包含了对训练数据的记忆成分,导致分数虚高,无法真实反映泛化能力。
第二道裂缝是“难度天花板”。这 164 道题都是家常菜级别,大厨能做,刚学厨的新手也能做,大家分数都接近满分时,就分不出水平高低了。近年来头部模型在 HumanEval 上的 pass@1 已经逼近 90%,甚至更高,区分度明显下降。想进一步拉开差距,需要更难的题库。
第三道裂缝是“只考味道不看摆盘”。HumanEval 不评估代码风格、可读性、运行效率,只要测试通过就给分。于是模型可能产出“能跑但很丑”的代码——变量名混乱、逻辑绕弯、性能糟糕,这些在真实工程中恰恰是致命的。它也不涉及依赖管理、系统设计等大型工程能力,无法反映“经营餐厅”的综合素质。
所以,要把 HumanEval 当成“入门笔试”,而不是“终极裁判”。高分说明基本功扎实,低分说明能力有明显短板,但两个同分的大厨谁更厉害,还得另开一个更硬核的赛场。理解这些漏洞,才能正确解读排行榜上的数字,而不是被一个漂亮的分数冲昏头脑。
AI 应用场景
代码补全产品的上线评估
阶段:模型评估与上线验证
解决的问题:传统做法靠人工抽查代码补全质量,费时费力且标准不一。借助 HumanEval 的函数级评估,产品团队可以自动跑一遍单函数补全,快速判断新版本模型会不会“做菜”——写完的函数能否真正跑通测试,而不是只像参考答案。
具体案例:某 AI IDE 插件团队在迭代补全模型时,每周自动在 HumanEval 的 164 道题上跑 pass@1。某次改动后分数从 52% 掉到 47%,定位发现是微调数据里混入了过多注释样本,及时回滚挽救了上线计划。
模型迭代的回归防线
阶段:训练过程中的持续评测
解决的问题:大模型训练中经常出现“这边涨那边跌”的跷跷板现象。如果没有一把稳定的标尺,很难发现模型在追求对话流畅度时悄悄丢失了编码能力。HumanEval 的函数粒度轻量、执行快速,适合嵌入训练流水线做回归监控。
具体案例:某团队在 RLHF 对齐阶段每训练 500 步就跑一轮 HumanEval。结果发现对齐后模型的代码能力下降了 8 个百分点,于是调整奖励模型中的代码占比,在保持对话质量的同时把编码分拉了回来。
开源模型排行榜的计分标准
阶段:基准测试与社区对比
解决的问题:开源社区缺少统一的“厨艺考场”,各家用各家的测试集,分数不可比。通过将 HumanEval 作为排行榜的固定科目,社区可以快速横向比较不同模型的函数级编码能力,帮助用户选型。
具体案例:Hugging Face 的 Open LLM Leaderboard 以及各类代码模型榜单,都把 HumanEval 作为核心计分项之一。研究者上传新模型后,系统自动跑测试并更新排名,用户一眼就能看出哪个开源模型“做菜最靠谱”。
Python 演示
# -*- coding: utf-8 -*-"""极简 HumanEval 风格函数级评估演示。""" import randomfrom math import comb TASKS = [ { "name": "add", "signature": "def add(a, b):", "doc": "Return the sum of a and b.", "tests": [(1, 2, 3), (0, 0, 0), (-1, 5, 4)], "candidates": [ lambda a, b: a + b, # 正确实现 lambda a, b: a - b, # 做了减法 lambda a, b: a * b, # 做了乘法 lambda a, b: abs(a + b), # 加了绝对值 ], }, { "name": "is_even", "signature": "def is_even(n):", "doc": "Return True if n is even, else False.", "tests": [(2, True), (3, False), (0, True)], "candidates": [ lambda n: n % 2 == 0, lambda n: n % 2 == 1, lambda n: n % 2 == 1, lambda n: n % 2 == 0, ], },] def run_test(func, test): """执行单个测试:把输入喂给函数,比对期望输出。""" inputs, expected = test[:-1], test[-1] return func(*inputs) == expected def evaluate_one(func, tests): """评估单个函数:全部测试通过才算过。""" return all(run_test(func, t) for t in tests) def sample_candidates(task, n): """模拟模型从候选池中采样 n 个函数。""" return [random.choice(task["candidates"]) for _ in range(n)] def pass_at_1(correct, n): """pass@1:一次出手就成功的概率。""" return correct / n if n else 0.0 def pass_at_k(correct, n, k): """pass@k:k 次机会至少命中一次正确的概率。""" if correct == 0: return 0.0 return 1.0 - comb(n - correct, k) / comb(n, k) def main(): random.seed(42) n, k = 8, 2 print("=== 极简 HumanEval 函数级评估器 ===") for task in TASKS: print("\ntask:", task["name"], task["signature"], "-", task["doc"]) for idx, cand in enumerate(task["candidates"]): passed = evaluate_one(cand, task["tests"]) print(" cand %d -> %s" % (idx, "PASS" if passed else "FAIL")) samples = sample_candidates(task, n) correct = sum(evaluate_one(s, task["tests"]) for s in samples) print(" sampled=%d correct=%d pass@1=%.2f pass@%d=%.2f" % (n, correct, pass_at_1(correct, n), k, pass_at_k(correct, n, k))) print("\n=== 评估完成 ===") if __name__ == "__main__": main()
def evaluate_one(func, tests):定义“单菜考核”入口,一个函数配一批测试。return all(run_test(func, t) for t in tests)关键判分逻辑:测试全部通过才返回 True,等价于正文中的全称量词公式。samples = sample_candidates(task, n)模拟模型对同一道题采样 n 个候选答案。correct = sum(evaluate_one(s, task["tests"]) for s in samples)统计 n 个候选中功能正确的数量 c。return 1.0 - comb(n - correct, k) / comb(n, k)直接实现 pass@k 组合公式。
常见误区
误区HumanEval 分数高,说明这个模型能写整个项目、能独立完成一个软件系统的开发。我拿它当招聘技术负责人的笔试标准。
正解HumanEval 只是“单菜考核”,考的是单个函数的算法实现和 API 使用,不涉及文件组织、依赖管理、架构设计。它就像厨师考证只考一道麻婆豆腐,考过了只代表基本功扎实,不代表能经营整家餐厅。要看工程能力,得用 SWE-bench 这类项目级基准补充。
为何常见:因为“代码生成”这个说法听起来就是一个完整的编程行为,很多人误以为函数级就是全部,忽略了评估粒度这个前提。
误区两个指标都是评价代码生成质量,BLEU 分数高的模型,在 HumanEval 上一定也拿高分。
正解二者逻辑完全不同。BLEU 是“看照片评分”,比较生成文本和参考答案长得像不像;HumanEval 是“实际尝菜”,执行函数并验证输出。一个模型可以写出和参考答案完全不像但功能正确代码,在 BLEU 上得低分,却在 HumanEval 拿满分。反之,照抄参考格式但逻辑错的代码,BLEU 高而 HumanEval 低。
为何常见:文本生成领域的惯性思维,容易让人用“相似度”替代“正确性”,忽略代码是机器执行的可运行产物。
误区排行榜上 pass@100 是 95%,说明这个模型一百次里有九十五次能写出正确答案,能力很强。
正解pass@k 的含义是采样 n 个候选中包含正确候选时,从 n 个里抽 k 个、至少抽中一个的概率。当 k 接近 n 时,公式趋近于“只要候选池里有一个对的就算过”,分数自然很高。它反映的是模型“探索”的上限,而不是“稳定发挥”的水平。真正代表稳定性的指标是 pass@1。
为何常见:很多人把“多次机会的成功率”和“单次成功率”混为一谈,就像把“补考十次终于过了”当成“每次考试都能过”。
误区HumanEval 里跑通了所有测试用例,说明这段代码质量很高,可以直接用到生产环境。
正解HumanEval 只验证功能正确性,不评估代码风格、可读性、运行效率和边界健壮性。测试用例平均每道只有 7.7 个,覆盖有限。就像一道菜评委尝了一口说合格,不代表它摆盘精美、营养均衡、能端上宴席。生产级代码还需要人工审查和更多测试来兜底。
为何常见:因为“通过测试”在工程习惯里是质量信号,人们容易把“通过了几个用例”放大成“代码无懈可击”。
面试题
Q1. HumanEval 评估的核心粒度是什么?
- A.函数级代码生成← 正确答案
- B.文件级代码修改
- C.项目级模块重构
- D.代码片段自动翻译
答案:A。HumanEval 的评估粒度是函数级,要求模型补全单个函数的实现,就像考厨师只考一道麻婆豆腐。文件级和项目级分别对应“配一桌宴席”和“经营餐厅”,不在 HumanEval 的考察范围内。
Q2. HumanEval 数据集包含多少道手写题?
- A.一百六十四道题目← 正确答案
- B.一百一十六道题目
- C.二百零一道题目
- D.三百二十四道题目
答案:A。OpenAI 团队手写了 164 道 Python 题,每道题配函数签名、docstring 和多组单元测试用例。这是了解 HumanEval 时需要记住的基础数字,就像知道考卷一共有多少道菜。
Q3. 与 BLEU 文本相似度指标相比,HumanEval 的评判优势是什么?
- A.实际执行测试验证功能← 正确答案
- B.仅比较字符编辑距离
- C.依赖人工逐项代码审查
- D.只看代码注释是否完整
答案:A。BLEU 是“看照片评分”,只比较文本相似度;HumanEval 是“实际尝菜”,把函数放进解释器执行并验证输出是否等于期望结果。它捕捉的是语义正确性,而不是字符层面的相似性。
Q4. 在 pass@k 指标中,k 代表的含义是什么?
- A.允许抽样的候选数量← 正确答案
- B.正确函数体的代码长度
- C.测试用例的具体数量
- D.模型训练的迭代轮数
答案:A。pass@k 中的 k 表示允许尝试的次数,即从 n 个采样候选中随机抽取 k 个,只要其中有正确候选就算通过。k 越大,相当于给厨师的补考机会越多,分数也就越高。
Q5. HumanEval 的函数级评估流程包含哪些环节?
- A.提供签名与说明文档← 正确答案
- B.运行单元测试进行判分← 正确答案
- C.人工评审代码布局美观
- D.汇总统计通过率指标← 正确答案
答案:ABD。函数级评估先给模型函数签名和 docstring(A),再把生成结果丢进测试用例执行判分(B),最后汇总 pass@k 等指标(D)。整个过程全自动,不需要人工评审代码美观(C),所以 C 不选。
Q6. 计算 pass@k 概率时,需要用到哪些关键变量?
- A.采样候选总数 n← 正确答案
- B.功能正确的候选数 c← 正确答案
- C.允许尝试的次数 k← 正确答案
- D.数据集包含的题目数
答案:ABC。pass@k 公式是 1 - C(n-c,k)/C(n,k),核心变量是总采样数 n、正确候选数 c 和尝试次数 k。数据集题目数只决定考卷大小,不参与单题通过概率的计算。
Q7. 为什么函数级评估比项目级更适合作为起步基准?
- A.易于自动化测试公平比较← 正确答案
- B.能考察大规模架构设计
- C.不需要准备任何测试用例
- D.只能依靠人工进行判分
答案:A。函数级任务不依赖外部文件和环境,可以完全自动化跑测试,结果客观可复现,适合公平比较不同模型。它牺牲了对架构设计的考察,所以 B 和 D 都不对。
Q8. 已知采样总数 n=10,正确候选数 c=2,当 k=1 时 pass@1 的值是多少?
- A.五分之一即百分之二十← 正确答案
- B.四分之一即百分之二十五
- C.十分之四即百分之四十
- D.二分之一即百分之五十
答案:A。当 k=1 时,公式退化为 c/n = 2/10 = 0.2,也就是五分之一。这相当于厨师只出手一次就做对的概率,其他选项的数值都对应不同的比例。
Q9. 以下哪些是 HumanEval 作为评测基准的已知局限?
- A.存在数据污染影响可信度← 正确答案
- B.题目偏易难以区分顶尖模型← 正确答案
- C.不评估风格性能等工程指标← 正确答案
- D.可以完全模拟真实项目开发
答案:ABC。HumanEval 的局限包括:训练数据可能泄漏导致分数虚高(A)、164 道基础题难度天花板低(B)、只测功能正确性不评代码风格性能(C)。它只是单菜考核,无法模拟真实项目开发(D 错)。
Q10. 采样总数 n=5,正确候选数 c=2,尝试次数 k=2,pass@2 的值是多少?
- A.十分之七即百分之七十← 正确答案
- B.十分之三即百分之三十
- C.十分之六即百分之六十
- D.十分之九即百分之九十
答案:A。pass@2 = 1 - C(3,2)/C(5,2) = 1 - 3/10 = 0.7。失败组合数 C(3,2)=3,总组合数 C(5,2)=10,用 1 减去“两次都抽错”的概率 0.3,得到 0.7,即厨师有两次机会时至少做对一次的概率。