前阵子我在帮一个前端代码审查 Agent 搭评测集落点选的是 shadcn/ui 的 lint 能力。这里说的 lint不是指跑 ESLint 规则而是让 Agent 像一个资深前端一样去检查一个项目里 shadcn/ui 组件用得好不好、规范不规范、有没有潜在的坑。做下来一个很深的感受是评测集的设计质量直接决定了 Agent 的能力上限也决定了你后面迭代时是有方向地改还是瞎调 prompt。把这次踩过的坑梳理一下后面做 Agent 评测集的朋友可以少走弯路。很多人以为评测集就是攒一批题目、配上标准答案跑一下看分数。实际操作下来完全不是这么回事。评测集代表的是你对 Agent 行为的全部预期每一道题都在定义这个 Agent 在什么场景下必须会做什么、不能做什么。以 shadcn/ui lint 为例表面上是检查组件用法实际上评的是 Agent 的代码阅读能力、工具调用能力、跨文件上下文管理能力甚至包括它有没有耐心把一件事做完。这篇文章我把整个设计过程、分层策略、判定指标、反作弊方法以及常见坑全部拆开讲都是可以直接照着落地的经验。1. 为什么选 shadcn/ui lint 当评测场景1.1 评测集不是题目集是行为对齐器先回到一个根本问题评测集到底在测什么我自己的定义是评测集是可被自动验证的行为预期集合。它本质上是一份契约规定 Agent 在特定输入下应该产生什么行为然后我们用一个尽量客观的方式去验证行为是否发生。这个定义决定了选题方向。你不能随便选一个场景就开干场景必须具备三个特征任务边界清晰、对错可判定、真实场景中有代表性。shadcn/ui lint 正好三者都占。先说任务边界。审查组件用法是一个有限范围的任务不像写一篇产品文案那样有无穷多种正确回答。Agent 要么能找到缺的依赖要么找不到要么能识别出错误 variant要么识别不出。任务边界越清晰评测结果越可信。再说对错可判定。lint 类任务有一个天然的黄金标准——依赖缺失、prop 名拼写错误、组合函数调用方式不对这些是有客观对错的。这让自动评分成为可能不需要人工一条条看 Agent 的回复。最后是真实场景代表性。Agent 如果真的要在工程环境里帮人干活代码审查和代码修复是最常见的诉求之一。以 shadcn/ui 为落点是因为它不像普通 npm 包那样装完即用而是把源码直接复制进项目组件代码、依赖声明、tailwind 配置、路径别名分散在多个文件里。这意味着 Agent 必须跨文件收集信息、比对上下文才是真正在考验 Agent 的实战能力。1.2 lint 类任务为什么适合做基准我给不少 Agent 项目设计过评测集总结下来lint 类任务是最适合当第一个评测集的场景。原因很朴素打分容易而且不容易吵起来。比如让 Agent 做一个数据分析任务判定标准就很难定。你说它分析得不够深入它说你有你的观点我有我的结论这就没法自动评分。但 lint 不一样依赖缺失就是缺失variant 写错就是写错修复后项目能不能通过类型检查、能不能正常构建这些都是硬指标。换个角度来看lint 类任务的推理链条是可分解的。一个成功的 lint 行为至少包含四个环节读取目标文件、检索已知规范比如组件库 API、比对当前代码与规范的差异、执行修复并验证。任何一个环节出问题最终结果都会失败。评测集的分层设计恰好可以把这四个环节拆开测方便你定位 Agent 到底弱在哪。1.3 shadcn/ui 的特殊性源码即组件配置链复杂shadcn/ui 这个库选得特别刁钻也很考验 Agent。大多数 UI 组件库是安装即用组件打包在黑盒里审查时只需要看用户代码和 API 文档。但 shadcn/ui 的设计理念是 copy 源码进你的项目这意味着 Agent 要审查的不仅仅是调用方代码还包括组件源码本身。这带来三个层面的复杂度。第一依赖检测必须跨文件。一个 Button 组件用到了 class-variance-authority、clsx、tailwind-merge如果项目里没装这些包运行时会直接报错。Agent 不能只看组件文件还要去翻 package.json。第二配置链路长。shadcn/ui 依赖 components.json 里的路径别名配置、tailwind.config 里的 content 与 CSS 变量、以及 tsconfig 里的 paths。任何一个配置错了组件即使代码没写错也跑不起来。第三组件有内部契约。比如 Dialog 组件内部通常要求包含 DialogTitle 和 DialogDescription否则会影响可访问性。这种规范散落在源码里Agent 必须通过阅读源码自己归纳出来而不是靠预先记忆 API 文档。所以拿 shadcn/ui lint 当评测场景实际上是在用三个层面的复杂度筛 Agent 的能力。这也是我推荐大家选具体且复合的场景当评测集而不是选抽象但好答的场景的原因。2. 评测集的三层结构设计2.1 第一层单文件 lint测基础能力评测集落地我从单文件题开始。这类题的输入就是一段组件使用代码Agent 只需要读一个文件就能发现并修复问题。我构造的典型题目是这样给一段 page.tsx 代码里面用到了Button variantdanger请 Agent 检查代码是否符合 shadcn/ui 的规范并修复。正确答案是把 variant 改成destructive因为 Button 的 variants 里根本没有 danger 这一项。这类题考察的是最基础的东西Agent 是否熟悉 shadcn/ui 的 API 结构是否知道要去读button.tsx源码确认 variants 定义是否能把读源码作为默认行为而不是依赖记忆。单文件题的正确率如果过低说明基础能力没达标后面都不用测了。正常情况下单文件题的正确率应该在 80% 以上因为难度有限考察的是工具调用和基础代码理解能力。不过单文件题有一个容易被忽略的坑有些 Agent 会背答案。我在开发过程中发现只要评测集里多次出现同一种错误模式Agent 就会学会直接输出修复结果而不去读源码验证。这导致评测分数虚高但换一个新项目就现原形。所以单文件题也要定期换皮这个后面专门讲。2.2 第二层跨文件联动测上下文管理跨文件联动题是评测集的核心也是最有含金量的部分。这类题目要求 Agent 在多个文件之间建立关联才能发现并修复问题。举一个我实际用过的题项目里复制进了 shadcn/ui 的 Dialog 组件但 package.json 里没有radix-ui/react-dialog这个依赖。同时 components.json 里的 aliases 写的是/components/ui-kit而 tsconfig.json 里的 paths 却配置为/components/ui。Agent 如果只读 Dialog.tsx 和 page.tsx只能发现问题的一半必须再读 package.json、components.json、tsconfig.json 才能拼出完整图景。这类题的评测点在于上下文管理能力。普通 Agent 的上下文窗口有限一旦读了多个文件早期读到的信息可能在后续决策时被挤出有效注意力。好的 Agent 应该表现出先侦查再动手的节奏感而不是读了三四个文件就急着修复。跨文件题的设计有个原则每个文件只提供一部分线索任何单个文件都不足以给出完整答案。我一开始犯过错误把太多线索放在同一个文件里结果 Agent 读一个文件就全知道了后面根本不用看这类题就退化成单文件题了。要保证信息在文件间均匀分布才能强迫 Agent 做多文件协同推理。2.3 第三层整仓翻修测规划与执行第三层是最高难度的整仓翻修题模拟的是真实项目里的技术升级场景。这类题给 Agent 一个小型仓库里面有一个明确的升级任务系统性检查并修复所有相关问题。举个例子我给 Agent 出一个任务项目用的是 shadcn/ui 旧版本 Button 组件代码里到处用了sizeicon属性但旧版本组件不支持这个属性需要 Agent 自己升级 Button 组件定义并把所有调用处的类名、图标、尺寸全部适配好最后保证项目能通过tsc和eslint检查。这道题的难点在于它不是一道点状题而是一道面状题。Agent 需要做规划先评估整个项目的改动范围再制定执行顺序最后还要验证结果。我在设计时故意在仓库里埋了几个干扰项比如一个看起来相关但实际不用改的组件文件用来测试 Agent 会不会过度修改。整仓翻修题的评分标准也跟前面两层不同。前面两层我可以直接看有没有改对这一层我还会看有没有乱改。Agent 如果为了通过检查把类型声明全部改成any虽然 tsc 过了但没有任何工程价值。所以我的评分规则里明确有一条只允许修改与任务直接相关的文件凡是动了无关文件的即使检查通过也要扣分。3. 评测指标与自动判定3.1 结果跑分让 lint 结果变成可量化分数评测集做得再好如果判定环节主观最后还是一笔糊涂账。lint 类任务的优势在这里充分体现可以用修复后重新跑检查的方式来判定对错。我的判定流程如下出题时先准备一个坏代码仓库里面塞入各种问题。Agent 执行修复后评测系统自动在修复后的仓库上跑一套检查脚本包括tsc --noEmit、eslint、以及我自己编写的专项检查脚本。专项检查脚本会验证题目里埋的那几个错误是否被正确修复比如检查variantdanger是否被替换成合法值、缺失依赖是否被补上。这里有两个判定细节值得注意。第一跑 tsc 和 eslint 只能证明代码没有明显错误不能证明题目要求的点都改到了。比如 Agent 可以把出问题的代码片段直接删掉tsc 照样通过但这不是我们要的行为。所以专项检查脚本必须逐点核对题目目标。第二要对多改敏感。我在评分规则里加了修改范围限制通过 git diff 对比 Agent 修改前后的文件集合只允许目标文件被修改无关文件的改动会被标记为异常行为。这个规则帮我在早期就筛掉了一批看起来很聪明但实际不守规矩的 Agent 行为。3.2 pass1 和 passk怎么选成本怎么算评测指标我用两个维度pass1 和 pass5。pass1 是每道题只让 Agent 跑一次算通过率反映稳定的能力水平pass5 是同一道题允许跑 5 次只要有 1 次通过就算过反映能力上限。两者配合着看才有意义。如果 pass1 很低但 pass5 很高说明 Agent 能力存在但不稳定可能是上下文管理或工具调用顺序导致方差太大。如果两者都低那就是真的不会需要补训练、换模型或改工具定义。成本上跑评测集不是免费的甚至可以说很烧钱。以我的评测集为参考50 道题题目平均上下文约 3 万 token模型单次输出约 3 千 token。如果跑 pass1每道题一次调用总共消耗输入约 150 万 token、输出约 15 万 token。如果跑 pass5成本直接乘以 5输入 750 万 token、输出 75 万 token。采样方式输入 token 总量输出 token 总量按通用商用模型估算费用pass150 题每题 1 次150 万15 万约 6-8 美元pass550 题每题 5 次750 万75 万约 30-40 美元月度回归每周跑 2 次 pass11200 万120 万约 50-65 美元注意这里的费用只是模型调用费用没算工程和人工时间。所以我的建议是开发期每轮迭代先跑一个 10 道题的最小回归集后面会讲确认方向没问题再全量跑 pass1。只有发布前才跑 pass5 做最终验收。3.3 重试边界防止 Agent 无限自愈评测过程还有一个细节特别容易被人忽略要不要允许 Agent 重试如果不限制Agent 遇到报错可以不断自我修复理论上总能把题做对但消耗的 token 会失控。我在早期就吃过这个亏一道题让 Agent 跑了 40 多次工具调用花了十几美元最后结果还不对。我最终定的规则是每个任务最多允许 10 次工具调用超过直接判定失败。10 次对于单文件 lint 来说绰绰有余对于整仓翻修也基本够用。如果 Agent 在这个预算内完不成任务说明它要么规划能力不足要么上下文管理混乱卷土重来大概率也是同样的结果。提示重试边界要写进评测框架而不是靠模型自觉。我在 prompt 层面也会加一句你只有有限的工具调用预算请先侦查再行动但这只能作为辅助硬性上限必须在评测框架里拦截。4. 数据构造与反作弊4.1 三种数据来源真实采样、人工构造、程序化注入评测集的数据从哪来是很多人忽略但极其重要的环节。我试过三种来源各有优劣最终采用混合策略。真实仓库采样就是从 GitHub 上找实际使用 shadcn/ui 的开源项目提取其中真实的代码问题。优点是题目自然不是人工硬造出来的Agent 面对的场景更接近现实。缺点是问题分布不可控可能一个项目里全是配置问题另一个项目里全是依赖缺失没法均匀覆盖各个评测维度。另外还要处理许可证、仓库体积等问题。我控制真实采样比例在 30% 以内主要用来做验证集不放入核心评测集。人工构造就是自己造一个小项目故意往里面埋错误。优点是可控性强每个评测维度都能精确覆盖。缺点是有出题人视角问题——人觉得显而易见的错误Agent 未必理解题面到底要它做什么。我在这里踩过坑早期写了一道题请检查这个项目Agent 愣了半天不知道要检查什么。后来我把题面改成了请检查 src/components 目录下是否有缺失依赖并修复发现的全部问题结果立刻不一样。程序化注入是我最推荐的数据生产方式。写一个脚本从一份干净组件库出发自动按规则注入错误。比如随机删除 package.json 里的一个依赖、随机篡改一个 variant 值、随机改乱 components.json 的 alias。这样做的好处是可以在短时间内生成大量变体题且天然防止记忆——每次生成的题目都略有不同。4.2 题面三要素任务描述、约束条件、输出格式评测集质量的最关键节点在题面设计。题面写不好Agent 表现差你会分不清是 Agent 能力不行还是题面没说清楚。我自己总结一道合格的题面必须包含三要素。第一是任务描述要具体到目录级别。我见过太多模糊的题面比如请检查这个项目的代码质量这种题面别说 Agent人类也不知道该干嘛。正确写法是请检查 src/components/ui/button.tsx 是否遵循 shadcn/ui 的组件规范修复你发现的问题。第二是约束条件要说明能做什么、不能做什么。比如只允许修改与问题直接相关的文件比如不要安装新的 npm 包如果题目本来测的就是依赖检查能力装包会掩盖缺陷。约束条件写清楚了评分规则才有依据。第三是输出格式要让 Agent 返回结构化结果。我要求 Agent 最终输出一个 JSON 报告包含问题列表、修复建议、修改文件清单和自证结果。这个设计后来帮了大忙评测框架可以直接解析 JSON 做判定不需要靠字符串匹配去猜 Agent 意图。附一个我常用的输出格式{ issues: [ { file: src/components/ui/button.tsx, severity: high, category: dependency_missing, reason: The component imports class-variance-authority but it is not listed in package.json } ], action: fixed, modified_files: [package.json, src/components/ui/button.tsx], verification: npm run typecheck passed after fix }4.3 反污染如何防止 Agent 记住答案评测集的最大敌人是数据污染。如果你把评测集公开发布或者多次在相同题目上反复评测Agent 会学会背答案分数虚高但实际能力并没有提升。我做反污染的手段有三板斧。第一是程序化生成动态题目让每次评测的题目组合都不一样。固定维护 30 个种子仓库和 80 条错误注入规则评测时随机组合这样即使 Agent 见过其中一部分也不会让整套评测失效。第二是定期换皮。所有题目至少每两周做一次变量名替换包括项目名、组件前缀、导入路径风格。比如同一个依赖缺失错误这次出现在 button.tsx 里下次就放到 dialog.tsx 里再下次直接换成一个自定义业务组件。第三是严格隔离。评测集文件不打进 Agent 的 prompt评测完成后立即删除临时文件不给 Agent 留任何对照答案的机会。听起来像废话但我确实见过有团队把标准答案字段留在接口响应里Agent 直接读出来就答对了。注意不要轻易把评测集开源。一旦公开你评测的就不再是 Agent 的能力而是它有没有读过你的题库。评测集要保持私有、持续更新才能作为长期可信的团队基准。5. 评测结果解读与迭代5.1 首轮评测结果怎么读先分层归因评测集跑完不是只看一个总分就完事要按层归因。我首轮评测跑完后的数据分布帮我定位到了不少问题。单文件层分数如果低于 60%别急着怪模型大概率是你的工具定义有问题。比如我的 file_read 工具最初要求 Agent 提供绝对路径但 Agent 经常用相对路径调用导致读文件失败。修复方式是工具层做一层路径解析自动把相对路径转成绝对路径分数立刻涨了一截。跨文件层分数如果低于 50%问题通常出在上下文管理上。我观察到一个典型模式Agent 读文件时按字母序逐个读读完 ui/button.tsx 就忘了 components.json 里写了什么。这种情况靠调整 prompt 效果有限我更推荐在工具设计上做文章比如给 file_read 工具加一个最近读取文件摘要的自动追加功能让模型随时能回顾关键信息。整仓翻修层分数低大多数是规划问题。Agent 跳过了全局侦查阶段直接开始改第一个看到的文件改到一半发现要动的面越来越大最后在工具调用预算内完不成任务。这类问题比较难治我在评测报告里会单独列一个规划质量维度用修改文件数、工具调用顺序等间接指标去量化。5.2 最小回归集让迭代跑得快全量评测集 50 道题跑一次 pass1 只需要几分钟加上人类分析时间一个迭代周期怎么也得半天。但开发期你可能会一天改十几次 prompt不可能每次改完就跑全量。我的做法是维护一个最小回归集。从全量评测集里挑 10 道代表性题目覆盖单文件、跨文件、整仓翻修三个难度层比例大概 3:4:3。每次调整 prompt、改工具 schema、换模型先跑最小回归集得到没变差的信号后再跑全量做最终确认。这个习惯让我避免了好几次改好 A 打坏 B的问题。有一次我优化了 Agent 的单文件 lint 能力最小回归集跑完发现跨文件层的题目掉分了。原因是新 prompt 里加了太多关于精确定位问题文件的引导导致 Agent 在跨文件场景里没耐心读全所有相关文件。发现问题后我立刻修正避免了等全量评测跑完才发现问题的被动局面。5.3 常见问题速查表评测集维护过程中我积累了一些高频问题的排查经验整理成速查表方便团队新成员快速定位问题。症状最可能的原因优先排查方向所有 Agent 都在同一道题上失败题面表述有歧义或标准答案写错了人工把题面完整做一遍确认标注答案是否真的可被验证总分数一直卡在某个区间不涨评测集难度分布失衡简单题太少或太难按分层统计各层通过率调整题目比例Agent 修好 A 问题又引入 B 问题评测集缺跨文件场景Agent 只做局部推理增加第二层跨文件题强制全局分析评测成本持续飙升单题上下文过长或重试限制失效检查工具调用次数日志压缩输入上下文收紧重试预算单个 Agent 在相同题集上分数越来越高但换新题就崩评测集被记忆性污染检查题目是否长期未更新启动换皮机制这五条足够覆盖我踩过的绝大多数坑。如果还没命中建议先回到题面三要素去复盘——任务描述、约束条件、输出格式大部分评测集问题都能归结到这三项没写好。再分享一个我在实际操作中的体会评测集不是一次性工程它和 Agent 本身一样需要持续维护。每当我往评测集里加一道新题我实际上是在给 Agent 定义一条新的行为准则。下次再做 Agent 评测我大概会从一开始就把题面是否能跑通、判定是否自动、版本是否可控、反作弊是否跟上这四件事当成硬性要求而不是最后才补。像写测试用例一样对待评测集Agent 才不会跑偏。