资讯动态

Egg Skills 评测体系设计:静态校验与 LLM-as-Judge 动态评测实战

发布时间:2026/9/21 14:01:28 来源:尧图企业网站定制
后端Web框架【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa. https://307.run/eggcode项目地址https://gitcode.com/gh_mirrors/eg/egg点击查看免费下载packages/skills/是 Egg 框架为 AI Agent 提供的 skills 包以纯 Markdown 文档承载框架的编码知识与决策逻辑。本文围绕 packages/skills/PLAN.md 中设计的评测方案展开如何用「静态校验 动态评测LLM-as-Judge」两套机制验证 Skill 文件的结构正确性与 AI 基于 Skill 生成回答的质量。读完本文你将掌握该评测体系的分层设计思路、完整的 Vitest Claude API 实现代码以及它在当前仓库中的实际落地形态。评测体系的两层目标PLAN.md 明确将评测体系划分为两个层面二者互补静态校验—— 验证 Skill 文件的结构正确性、引用完整性。它不依赖任何 LLM 调用执行快、成本低可以在每次修改 Skill 后立即运行。动态评测—— 用 LLM-as-Judge 评估 AI 基于 Skill 生成回答的质量。它验证的是AI 拿到 Skill 之后能不能用对这是静态校验无法覆盖的。整个体系全部手动触发运行不集成 CI。这是因为动态评测依赖外部 LLM API既有成本又有不确定性适合作为开发流程中的手动质量闸门而不是持续集成中的自动化环节。Skills 目录现状与评测目录设计在介绍评测方案前先明确被测对象的现状。packages/skills/采用分层路由模式见 packages/skills/CLAUDE.md入口 skillpackages/skills/egg/SKILL.md—— 分析用户意图通过关键词匹配和决策逻辑路由到专业 skill专业 skills—— 提供特定领域的深度指导egg-core核心概念模块、依赖注入、生命周期、AccessLevel、后台任务、egg-controllerHTTPController、MCPController、Schedule、Ajv 校验、egg-unittestHTTP 接口测试、Service 测试、Mock。PLAN.md 为评测体系设计了独立的eval/目录与 skills 内容物理隔离packages/skills/ ├── egg/ ├── controller/ ├── tegg-core/ ├── eval/ # 新增评测目录 │ ├── static/ │ │ └── validate.test.ts # 静态校验测试 │ ├── dynamic/ │ │ ├── routing.eval.ts # 入口路由评测 │ │ └── quality.eval.ts # 内容质量评测 │ ├── fixtures/ │ │ ├── routing-cases.ts # 路由测试用例 │ │ └── quality-cases.ts # 质量测试用例 │ └── lib/ │ ├── skill-loader.ts # Skill 文件加载器 │ ├── judge.ts # LLM-as-Judge 核心逻辑 │ └── types.ts # 共享类型定义 ├── vitest.config.ts ├── package.json └── tsconfig.json需要说明的是这是 PLAN.md 中的目标目录设计仓库当前的eval/目录实际已落地为以 JSON 用例文件为主的形态详见下文仓库中的实际落地一节两者互为印证PLAN 提供完整的工程化方案骨架仓库现状展示了一条更轻量的落地路径。第一部分静态校验校验项静态校验通过读取并解析每个 SKILL.md 文件检查五类结构问题校验项说明Frontmatter 格式每个 SKILL.md 必须包含name、description、allowed-tools引用文件存在性SKILL.md 中提到的references/*.md文件必须存在交叉引用一致性入口 skill 提到的子 skill 目录必须存在且包含 SKILL.mdMarkdown 结构标题层级合理以#开头不跳级决策表完整性入口 skill 的路由表中每个 skill 都有对应目录这些校验项与仓库中实际的 frontmatter 约定完全对应。例如 packages/skills/egg/SKILL.md 以name: egg、description: 本技能用于处理 EGG 框架...、allowed-tools: Read开头packages/skills/egg-core/SKILL.md 的 frontmatter 同样齐备。allowed-tools统一为Read纯文档指导型不修改文件这也是静态校验可以直接断言的内容。实现方式使用 vitest node:assert 编写测试通过 Node.js fs API 读取文件并解析// eval/static/validate.test.ts import { describe, it } from vitest; import assert from node:assert/strict; import { loadAllSkills } from ../lib/skill-loader.ts; describe(Skill 静态校验, () { describe(Frontmatter, () { it(每个 SKILL.md 包含必填字段: name, description, allowed-tools, ...); }); describe(引用完整性, () { it(SKILL.md 中引用的 references/ 文件均存在, ...); it(入口 skill 引用的子 skill 目录均存在, ...); }); describe(Markdown 结构, () { it(标题层级不跳级, ...); }); });这里有三点工程细节值得注意断言库选择node:assert/strictNode.js 内置、零依赖符合 monorepo 的依赖纪律skill-loader.ts承担文件读取职责将读取 解析 frontmatter沉淀为公共模块静态与动态评测复用同一份加载逻辑引用完整性校验的价值references/目录是各专业 skill 的深度文档所在地如egg-controller下的http-controller.md、mcp-controller.md、schedule.md、ajv-validate.md、middleware.mdSKILL.md 中一旦写出不存在的引用文件名AI 在运行时就会拿到断裂的上下文——这是静态校验必须拦截的高频错误。第二部分动态评测LLM-as-Judge动态评测是这套体系的精华所在它不检查文件长什么样而是检查AI 用这些文件能产生多好的回答。评测被拆分为两个子场景分别对应入口 skill 和专业 skill 的职责。2.1 路由评测 —— 入口 Skill 是否正确路由测试egg/SKILL.md的决策逻辑给定用户查询判断 AI 是否路由到正确的子 skill。测试用例结构// eval/fixtures/routing-cases.ts export const routingCases: RoutingCase[] [ { query: 如何创建 HTTP controller, expectedSkill: controller, reason: 明确提到 controller属于协议实现, }, { query: SingletonProto 和 ContextProto 有什么区别, expectedSkill: tegg-core, reason: 关于对象生命周期属于核心概念, }, { query: 我需要创建一个可以被 HTTP 控制器使用的服务, expectedSkill: tegg-core, reason: 模糊意图按规则 1基础优先应路由到 core, }, // ... 更多用例 ];每个用例由query用户提问、expectedSkill期望路由目标、reason判定依据三要素组成。reason字段非常关键——它把入口 skill 中的决策规则显式编码为可断言的期望例如基础优先规则对应 packages/skills/egg/SKILL.md 中冲突解决规则 - 规则 1基础优先当问题同时涉及核心概念与控制器实现时从egg-coreskill 开始。测试实现// eval/dynamic/routing.eval.ts import { describe, it } from vitest; import assert from node:assert/strict; import Anthropic from anthropic-ai/sdk; import { loadSkillContent } from ../lib/skill-loader.ts; import { routingCases } from ../fixtures/routing-cases.ts; const client new Anthropic(); const AVAILABLE_SKILLS [controller, tegg-core]; describe(路由评测, () { // 加载入口 skill 作为 system prompt const entrySkillContent loadSkillContent(egg); for (const { query, expectedSkill, reason } of routingCases) { it(${query} → ${expectedSkill}, async () { // 1. 将 SKILL.md 作为 system prompt发送用户查询 const response await client.messages.create({ model: claude-sonnet-4-20250514, max_tokens: 1024, system: [ entrySkillContent, // 约束输出格式让 AI 只做路由决策 你是 EGG 框架技能路由器。根据上面的决策指南分析用户查询并选择应该加载的技能。, 可选技能: ${AVAILABLE_SKILLS.join(, )}, 只输出 JSON: {skill: 技能名, reason: 简要理由}, ].join(\n\n), messages: [{ role: user, content: query }], }); // 2. 解析 AI 回答中的路由选择 const text response.content[0].type text ? response.content[0].text : ; const parsed JSON.parse(text); // 3. 断言路由正确性 assert.equal( parsed.skill, expectedSkill, 路由错误: 期望 ${expectedSkill} 但得到 ${parsed.skill} \n 用例理由: ${reason} \n AI 理由: ${parsed.reason}, ); }); } });这个实现的巧妙之处在于用约束格式把开放问题变成可断言问题入口 skill 内容作为 system prompt 提供决策依据附加指令要求 AI只输出 JSON从而把路由是否正确转化为JSON 中的skill字段是否等于expectedSkill的机械比较。断言失败时输出的错误信息同时包含用例理由与 AI 理由方便人工复盘路由偏差。仓库中已有真实的路由评测用例集 packages/skills/eval/evals-routing.json共 11 条覆盖了各类意图形态例如明确协议意图帮我写一个接口 →egg-controller核心概念意图我想写个服务不知道用 SingletonProto 还是 ContextProto →egg-core模糊意图帮我搭一个新模块包含增删改查接口和 Service →egg-core基础优先先建模块再写接口问题排查意图我的 Inject 注入报错了找不到对象 →egg-core框架选型意图EventBus 和 BackgroundTaskHelper 怎么选 →egg-core。2.2 内容质量评测 —— 子 Skill 回答质量路由评测只验证选对了 skill内容质量评测则进一步验证用对了 skill。它针对各专业 skill 的领域问题用评判标准清单criteria刻画期望回答中必须出现的要素。测试用例结构// eval/fixtures/quality-cases.ts export const qualityCases: QualityCase[] [ { skill: controller, query: 如何创建一个 POST 接口接收 JSON body, criteria: [ 使用 HTTPController 装饰器, 使用 HTTPMethod 且 method 为 POST, 使用 HTTPBody() 获取请求体, 包含完整可运行的代码示例, ], references: [references/http-controller.md], // 需要加载的参考文档 }, { skill: tegg-core, query: 如何让一个服务可以被其他模块访问, criteria: [提到 AccessLevel.PUBLIC, 使用 SingletonProto 装饰器, 解释跨模块访问机制], references: [], }, // ... 更多用例 ];注意criteria的写法——它写的是回答中必须出现的技术要素而不是抽象的质量描述。例如要求提到 AccessLevel.PUBLIC而不是回答要专业这保证了 Judge LLM 的评分具备可核查性而不是凭感觉打分。references字段声明回答时需要加载的参考文档与 skill 的references/目录一一对应。测试实现// eval/dynamic/quality.eval.ts import { describe, it } from vitest; import assert from node:assert/strict; import Anthropic from anthropic-ai/sdk; import { loadSkillContent, loadReference } from ../lib/skill-loader.ts; import { qualityCases } from ../fixtures/quality-cases.ts; import { judge } from ../lib/judge.ts; const client new Anthropic(); describe(内容质量评测, () { for (const testCase of qualityCases) { describe([${testCase.skill}] ${testCase.query}, () { let aiResponse: string; // Step 1: 加载 skill 内容作为 system prompt向被测 LLM 提问 it(生成回答, async () { const skillContent loadSkillContent(testCase.skill); const refContents testCase.references.map((ref) loadReference(testCase.skill, ref)); const systemPrompt [skillContent, ...refContents].join(\n\n---\n\n); const response await client.messages.create({ model: claude-sonnet-4-20250514, max_tokens: 2048, system: systemPrompt, messages: [{ role: user, content: testCase.query }], }); aiResponse response.content[0].type text ? response.content[0].text : ; assert.ok(aiResponse.length 0, AI 应该返回非空回答); }); // Step 2: 用 Judge LLM 对回答逐项评分 it(通过质量评审, async () { const result await judge(client, { query: testCase.query, response: aiResponse, criteria: testCase.criteria, }); // 输出详细评分到 console 供人工查看 console.log( 得分: ${result.totalScore} (${result.passed}/${result.total})); for (const item of result.details) { const icon item.score 1 ? ✓ : ✗; console.log( ${icon} ${item.criterion}: ${item.reason}); } // 断言所有 criteria 都应满足 assert.ok( result.totalScore 0.8, 质量不达标: ${result.totalScore} 0.8\n result.details .filter((d) d.score 0) .map((d) ✗ ${d.criterion}: ${d.reason}) .join(\n), ); }); }); } });整个评测被设计为两阶段流水线第一个it生成回答被测 LLM skill 内容第二个it用独立的 Judge LLM 打分。两个步骤的分离带来两个好处每个用例可以独立失败、独立重跑不必为重新打分而重复调用生成接口aiResponse在 describe 作用域内共享为后续人工复查保留了原始回答内容。console.log的逐项评分输出✓/✗ 理由让每次运行都生成一份人可读的明细得分阈值设为 0.8即满足 80% 以上的 criteria 才算通过。仓库中的 packages/skills/eval/evals-egg-controller.json 和 packages/skills/eval/evals-egg-core.json 就是这种用例思想的 JSON 化落地。以控制器评测为例其expected_output字段相当于 criteria 的浓缩描述例如POST 接口用例要求HTTPController HTTPMethod POST HTTPBody ctx.status 201完整可运行代码参数校验用例要求从 egg/ajv 导入 Type/Ajv/Static定义 TypeBox SchemaInject() ajv: Ajvajv.validate()中间件执行顺序用例要求解释函数式和 AOP 分属两个独立执行阶段的完整时序大量用例还通过files字段附带带 bug 的上下文代码用于考察 AI 的诊断能力。LLM-as-Judge 核心实现Judge 是动态评测的裁判它接收用户问题 AI 回答 标准清单逐条判定每条标准是否满足// eval/lib/judge.ts import type Anthropic from anthropic-ai/sdk; import type { JudgeInput, JudgeResult, JudgeDetail } from ./types.ts; export async function judge(client: Anthropic, input: JudgeInput): PromiseJudgeResult { const criteriaList input.criteria.map((c, i) ${i 1}. ${c}).join(\n); const response await client.messages.create({ model: claude-sonnet-4-20250514, max_tokens: 1024, system: 你是 AI 回答质量评估专家。严格按照 JSON 格式输出评分结果。, messages: [ { role: user, content: 请根据评分标准对以下 AI 回答逐项评分。 ## 评分标准 ${criteriaList} ## 用户问题 ${input.query} ## AI 回答 ${input.response} ## 输出格式严格 JSON { details: [ { criterion: 标准内容, score: 0 或 1, reason: 简要理由 } ] }, }, ], }); const text response.content[0].type text ? response.content[0].text : ; const parsed JSON.parse(text); const details: JudgeDetail[] parsed.details; const passed details.filter((d) d.score 1).length; return { details, passed, total: details.length, totalScore: passed / details.length, }; }Judge 的设计有几个值得借鉴的点评分维度前置criteria由测试作者即 Skill 维护者编写把质量期望显式化避免了 LLM 自由发挥标准0/1 二值评分每条标准只有 0 或 1 两个取值totalScore passed / total简单可计算、可断言、可汇总强制 JSON 输出system 声明严格按照 JSON 格式输出用户消息中的输出格式模板进一步约束结构使结果可以直接JSON.parse单次调用完成评分所有标准放进同一次请求相比逐条调用成本与延迟都更低。评测报告运行评测后生成 JSON 报告作为人工 review 的数据基础{ timestamp: 2026-02-05T10:00:00Z, routing: { total: 10, correct: 9, accuracy: 0.9, failures: [ { query: ..., expected: tegg-core, actual: controller, reason: ... } ] }, quality: { controller: { cases: 5, avg_score: 0.85, details: [...] }, tegg-core: { cases: 5, avg_score: 0.90, details: [...] } } }报告按routing与quality两大块组织路由部分给出总数、正确数与准确率并完整保留失败用例query、expected、actual、reason供回归分析质量部分按 skill 维度聚合平均分details保留逐条评分明细。这种结构化输出既适合人眼扫读也方便后续扩展为可视化看板。第三部分技术选型与依赖组件选型理由测试框架vitest遵循 monorepo 标准断言库node:assert/strictNode.js 内置零依赖YAML frontmatter 解析gray-matter成熟的 frontmatter 解析库LLM 调用anthropic-ai/sdk使用 Claude API 做评测和 Judge报告输出JSON 文件简单可读方便后续扩展为可视化技术选型体现了克制原则测试框架跟随 monorepo 既有标准vitest断言直接用 Node.js 内置能力只有 frontmatter 解析gray-matter和 LLM 调用anthropic-ai/sdk引入外部依赖——前者解决 YAML 解析这个没必要自己写的问题后者是 LLM 评测的必需品。package.json scripts{ scripts: { test: vitest run --config vitest.config.ts eval/static/, eval: vitest run --config vitest.config.ts eval/dynamic/, eval:routing: vitest run --config vitest.config.ts eval/dynamic/routing.eval.ts, eval:quality: vitest run --config vitest.config.ts eval/dynamic/quality.eval.ts } }test— 运行静态校验快速无 API 调用eval— 运行全部动态评测eval:routing— 仅运行路由评测eval:quality— 仅运行内容质量评测四个脚本的分层很清晰test是零成本的结构体检可以高频执行eval系列依赖外部 API按需触发。动态评测需设置ANTHROPIC_API_KEY环境变量——这也是 PLAN 明确不集成 CI的实操原因之一。仓库中的实际落地evals-*.json 与评测工作流PLAN.md 是工程化方案而仓库当前的eval/目录已经以 JSON 用例文件的形式落地并在 packages/skills/CLAUDE.md 中沉淀了完整的评测工作流规范二者共同构成该评测体系的完整拼图。评测用例 JSON 格式落地形态将用例从 TypeScript 文件转为 JSON 数据文件{ skill_name: egg-controller, description: 控制器评测覆盖 http-controller、mcp-controller、schedule、ajv-validate, evals: [ { id: 1, prompt: 用户的任务描述, expected_output: 期望输出的关键要素描述, files: [{ path: 相对路径, content: 文件内容可选用于提供上下文或有 bug 的代码 }] } ] }与 PLAN 中的QualityCase结构相比JSON 化有两点演进prompt对应queryexpected_output对应合并后的 criteria并且新增了可选的files字段——允许用例携带完整的上下文文件甚至是有 bug 的代码从而支撑问题排查存量项目代码生成这类需要上下文的评测场景。例如 packages/skills/eval/evals-egg-controller.json 中的路由冲突用例就通过files附带了ApiController.ts源码要求 AI 诊断/api/:name与/api/health的匹配冲突。对比评测环境with-skill vs site-docsCLAUDE.md 规范了一个重要的评测方法论每个用例需要在两种环境下分别运行对比 skill 是否真的有效。环境system prompt可访问范围prompt 约束with-skillegg/SKILL.md入口 skill的完整内容仅packages/skills/目录site-docs角色声明 site/docs/的完整文件目录列表通过find site/docs -name *.md生成仅site/docs/目录两组 prompt 的差异仅在于参考资料不同不包含额外的流程提示如先判断使用哪个 skill让 AI 自然行动你是 EGG 框架开发专家。你只能通过 Read 工具读取 packages/skills/ 目录下的文件不能访问 site/docs/ 或项目源码。 {egg/SKILL.md 的完整内容} --- {eval prompt}你是 EGG 框架开发专家。你只能通过 Read 工具读取 site/docs/ 目录下的文件不能访问 packages/skills/ 或项目源码。项目文档目录如下 {完整的 site/docs/ 文件列表通过 find site/docs -name *.md | sort 生成} --- {eval prompt}这个对照实验回答了一个核心问题skill 的存在是否有增量价值如果 with-skill 环境的回答质量没有明显优于 site-docs说明 skill 内容需要改进——要么缺少文档未覆盖的知识要么与现有文档存在不必要重复。这与 packages/skills/CLAUDE.md 中Skill 的价值 文档 实践经验 - 重复内容的原则一脉相承。评测用例设计原则六类场景CLAUDE.md 要求每个 reference 文档至少覆盖 5 个以上评测用例并覆盖以下场景类型场景类型说明示例泛化需求描述不了解框架术语用口语化描述需求帮我加个参数校验精确需求描述明确指定技术方案和约束用 TypeBox 定义 Schemaemail 用 format: email使用咨询询问用法、区别、选型Optional 和 Null 有什么区别问题排查提供有 bug 的代码附 files要求诊断校验跑不起来帮我看看新项目代码生成在全新项目中从零开始生成功能代码帮我写一个创建订单的接口需要做参数校验存量项目代码生成在包含老 egg 代码的项目中生成或迁移代码帮我把这个老的 egg controller 改成 HTTPController 写法不常用 API需要查外部文档链接才能回答用 Tuple 定义元组校验这个场景矩阵非常实用——它防止评测用例只覆盖AI 最擅长的情况口语化的泛化描述考验意图识别带 bug 的代码考验诊断能力存量迁移考验对框架新旧写法的理解。检查 packages/skills/eval/evals-egg-controller.json 可以发现这些场景都已落地例如帮我给这个 Controller 加一个认证中间件精确需求描述、MCP Tool 的参数 Schema 写法有问题帮我看看问题排查、帮我把这个老的 egg controller 改成 HTTPController 写法存量迁移。评测输出与迭代流程评测结果保存到packages/skills/eval/skill-name-workspace/iteration-N/目录下已在.gitignore中通过*-workspace/忽略由/skill-creatorskill 管理packages/skills/eval/ ├── evals-egg-core.json # egg-core skill 评测用例 ├── evals-egg-controller.json # egg-controller skill 评测用例 ├── evals-routing.json # 入口路由评测用例 ├── .gitignore # 忽略 *-workspace/ 目录 └── skill-name-workspace/ # 评测输出gitignored由 /skill-creator 管理 └── iteration-N/ ├── REPORT.md # 对比评分报告 ├── GRADING.md # with-skill 通过率报告 └── {prefix}-{id}/ # 每个用例一个目录 ├── eval_metadata.json ├── with_skill/outputs/ └── without_skill/outputs/整个评测流程是闭环迭代的编写评测用例 → 运行评测with-skill 与 site-docs 双环境并行→ 评分与展示 → 改进 skill → 开启新一轮 iteration。iteration-N的版本化设计让每次 skill 改进的效果都可回溯、可对比。实施步骤PLAN.md 为整个体系给出了清晰的落地路线图共 8 步按依赖先行 → 基础库 → 静态 → 动态的顺序推进在 worktree (egg-skills-eval) 中添加依赖vitest、gray-matter、anthropic-ai/sdk添加vitest.config.ts和更新package.jsonscripts创建eval/lib/基础工具skill-loader、types、judge实现eval/static/validate.test.ts静态校验编写路由测试用例eval/fixtures/routing-cases.ts实现eval/dynamic/routing.eval.ts路由评测编写质量测试用例eval/fixtures/quality-cases.ts实现eval/dynamic/quality.eval.ts内容质量评测步骤 3 的基础工具skill-loader、types、judge被刻意前置静态与动态评测共享 skill 加载逻辑judge 独立成模块便于单独演进。步骤 4 先落地零成本的静态校验再进入步骤 58 的 LLM 依赖部分——这种先廉价后昂贵的推进顺序让体系在早期阶段就能获得收益。总结Egg Skills 评测体系的价值在于把AI 回答质量这个模糊目标拆解为可执行、可断言、可回归的工程问题静态校验用零成本的 Vitest 测试守住 Skill 文件的结构底线路由评测用约束 JSON 输出验证入口 skill 的决策逻辑内容质量评测用 criteria 清单 Judge LLM 的 0/1 评分量化回答质量双环境对照则从方法论上证明了 skill 相对纯文档的真实增量。PLAN.md 提供了这套体系的完整设计蓝图而 packages/skills/eval/ 下的 JSON 用例集与 packages/skills/CLAUDE.md 中的工作流规范则是这套设计在当前仓库中的实际落地证据。对于任何构建 AI Agent Skill 体系的项目这套静态把关 动态评测 对照实验的组合都具备直接的参考价值。赞分享后端Web框架【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa. https://307.run/eggcode项目地址https://gitcode.com/gh_mirrors/eg/egg点击查看免费下载相关推荐Agentic Awesome Skills 高级评估实战用 LLM-as-a-Judge 构建可靠的 LLM 输出评估系统Agentic Awesome Skills 高级评估实战用 LLM as a Judge 构建可靠的 LLM 输出评估系统 本指南以开源仓库 agenticAI 技能AI 插件Code2Prompt 分词机制详解tiktoken 编码、token 估算与源码级实现Code2Prompt 分词机制详解tiktoken 编码、token 估算与源码级实现 本文聚焦 Code2Prompt 项目Rust 版 CLI 工具开发工具AI 应用Worktrunk钩子审批机制全解团队共享命令安全运行的三道防线Worktrunk钩子审批机制全解团队共享命令安全运行的三道防线 Worktrunk 是一个面向 Git worktree 管理的命令行工具专为多 AI A开发工具CLI版本控制AI Agent人工智能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价