oh-my-claudecode executor 基准实战:为 Express POST /api/products 端点实现输入校验的任务拆解与自动化评测

发布时间:2026/9/9 13:17:07
oh-my-claudecode executor 基准实战:为 Express POST /api/products 端点实现输入校验的任务拆解与自动化评测 oh-my-claudecode executor 基准实战为 Express POST /api/products 端点实现输入校验的任务拆解与自动化评测【免费下载链接】oh-my-claudecodeTeams-first Multi-agent orchestration for Claude Code项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-claudecode在 oh-my-claudecode 中benchmarks/executor目录存放着一组用来衡量“执行型 AgentExecutor / Deep Executor”实现质量的基准任务而 task-input-validation.md 正是其中最具代表性的一个它模拟一个真实到可以直接落地的 TypeScript Express 场景——POST /api/products端点目前“来者不拒”需要为它补齐字段级输入校验。读完本文你将掌握该任务的全部需求细节与可运行的参考实现思路理解它的 ground-truth 打分口径并能用仓库自带的基准运行器亲手复现一次对执行 Agent 的质量评估。一、先看清这份文档在仓库中的身份一个 Executor Benchmark Fixture首先需要厘清task-input-validation.md并不是 oh-my-claudecode 某个运行模块的说明文档而是一份面向执行 Agent 的合成任务规格task fixture。它被放在任务类目录 fixtures/tasks/ 下与task-add-timestamp.md、task-notification-refactor.md并列每一份都对应一份同名的 ground-truth 标注文件。这种结构决定了它的两个读取姿势给 Agent 读它是一张“工单”要求执行 Agent 基于其中的 Context、既有代码和 Validation Requirements 给出实现方案给评测器读它的校验点是可枚举、可判定的评测器可以依据 ground-truth/task-input-validation.json 中的 6 条关键“发现findings”自动判断 Agent 有没有抓到要害。从 run-benchmark.ts 的源码可以看到fixture 内容会被直接拼进用户消息function buildUserMessage(fixtureContent: string): string { return Implement the following task. Describe your approach, the files you would modify, and the changes you would make:\n\n${fixtureContent}; }也就是说文档正文本身就是在评测中交给模型的原样输入。理解这一点后续所有讨论都有了坐标系。二、任务背景与既有代码文档给出了什么文档首先交代了背景POST /api/products当前对任何输入都直接放行、不校验需要“在创建商品之前加上合理的输入校验”。随后给出了三段伪仓库代码——注意这三段中的src/前缀是任务设定内的“假设项目”文件并非本仓库真实源码读者在实现时应当把它当作一个独立的小型 Node 工程看待。第一段是商品类型定义// src/types/product.ts export interface Product { id: string; name: string; description: string; price: number; category: electronics | clothing | food | other; sku: string; inStock: boolean; createdAt: Date; }第二段是 Express 路由入口其中POST /api/products直接把req.body交给createProduct任何异常都吞成笼统的500// src/api/routes/products.ts import { Router } from express; import { createProduct } from ../../services/product-service; const router Router(); router.post(/products, async (req, res) { try { const product await createProduct(req.body); res.status(201).json(product); } catch (err) { res.status(500).json({ error: Failed to create product }); } }); router.get(/products/:id, async (req, res) { const product await getProduct(req.params.id); if (!product) return res.status(404).json({ error: Product not found }); res.json(product); }); export default router;第三段是服务层PartialProduct让它对所有缺省字段都做“兜底填空”这正是“无效输入也能被静默接受”的根源// src/services/product-service.ts import { Product } from ../types/product; import { db } from ../database; import { generateId } from ../utils/id; export async function createProduct(input: PartialProduct): PromiseProduct { const product: Product { id: generateId(), name: input.name || , description: input.description || , price: input.price || 0, category: input.category || other, sku: input.sku || , inStock: input.inStock ?? true, createdAt: new Date(), }; await db.products.insert(product); return product; }三段代码相互配合暴露出三个可被攻击的坏味道路由层无校验、服务层默认值吞掉脏数据、错误码永远 500。这正是文档要求执行 Agent 去“读代码、找模式、定方案”的素材。三、Validation Requirements 的字段级拆解七条需求逐条落地文档给出的需求清单是整份任务的核心必须原样保留并逐条展开。任务要求新增校验而不是重写所有规则都应落在“校验层”这一关注点上。name: required, string, 1-200 charactersdescription: optional, string, max 2000 charactersprice: required, number, must be 0, max 2 decimal placescategory: required, must be one of the valid categoriessku: required, string, must match pattern^[A-Z]{2,4}-\d{4,8}$Return 400 with descriptive error messages for validation failuresDo not modify the Product interface or existing GET route逐条解读其工程含义规则 1namerequired意味着空串、纯空白、缺失都要被判为失败“string”排除了数字与对象“1-200 characters”给出了边界。注意 TS 的string类型只能约束“编译期类型”无法阻止42或{ name: 42 }这类运行时脏值因此必须做运行时检查typeof value ! string是第一道闸。规则 2description唯一可选字段。可选的正确实现是“未提供则跳过提供了就必须是字符串且不超过 2000 字符”而不是“默认给空串”——后者会让description: 123这类错误静默通过违背校验语义。规则 3price最易出边界问题的一条。 0排除负数“number”排除字符串19.99而“max 2 decimal places”在浮点语境下需要专门处理——常见做法是验证Number.isInteger(price * 100)因为19.999 * 100 1999.9非整数即判定失败。此外还应考虑NaN、Infinity、非有限数这些都能绕过 0。规则 4category枚举校验应与类型定义中electronics | clothing | food | other严格一致。建议定义一个可复用的常量数组由它推导联合类型避免校验逻辑与类型定义“手写两份、悄然漂移”。规则 5sku正则^[A-Z]{2,4}-\d{4,8}$的含义是24 个大写英文字母 一个连字符 48 位数字且必须整体匹配^、$锚定。如果业务上允许小写输入可先toUpperCase()再校验——但是否规范化属于业务决策任务只要求“必须匹配该模式”实现时应避免自行放宽。规则 6400 响应校验失败要返回 HTTP 400并给出“能说明哪个字段、为什么失败”的描述性错误。这正是文档第一节路由代码里catch一律 500 的反面教材——校验错误属于客户端问题绝不应当被当作服务端故障吞成 500。规则 7约束红线不改Product接口、不动既有GET /products/:id路由。这是一条改动范围约束等价于要求“最小可行变更”校验应作为新增逻辑内联在 POST handler 或独立校验函数/中间件不得顺手重构既有代码。四、一份贴合七条需求的参考实现思路任务没有规定必须用哪个校验库且从规则 7 的“最小改动”精神看纯 TypeScript 手写一个校验函数是零依赖、可复制的最稳方案。参考实现可以收敛为一个返回“字段级错误列表”的纯函数便于路由层一次性收集所有问题// src/api/routes/products.ts 中新增的校验函数示意 const VALID_CATEGORIES [electronics, clothing, food, other] as const; const SKU_PATTERN /^[A-Z]{2,4}-\d{4,8}$/; interface ValidationResult { valid: boolean; errors: Recordstring, string; } function validateProductInput(body: unknown): ValidationResult { const errors: Recordstring, string {}; const input (body ?? {}) as Recordstring, unknown; const name input.name; if (typeof name ! string || name.trim().length 0) { errors.name name is required and must be a non-empty string; } else if (name.length 200) { errors.name name must be at most 200 characters; } if (input.description ! undefined) { if (typeof input.description ! string) { errors.description description must be a string when provided; } else if (input.description.length 2000) { errors.description description must be at most 2000 characters; } } const price input.price; if (typeof price ! number || !Number.isFinite(price) || price 0) { errors.price price is required and must be a non-negative number; } else if (!Number.isInteger(price * 100)) { errors.price price must have at most 2 decimal places; } const category input.category; if (typeof category ! string || !(VALID_CATEGORIES as readonly string[]).includes(category)) { errors.category category must be one of: electronics, clothing, food, other; } const sku input.sku; if (typeof sku ! string || !SKU_PATTERN.test(sku)) { errors.sku sku must match the pattern ^[A-Z]{2,4}-\\d{4,8}$; } return { valid: Object.keys(errors).length 0, errors }; }随后在 POST handler 里接入并只在通过校验时才继续调用服务层router.post(/products, async (req, res) { const { valid, errors } validateProductInput(req.body); if (!valid) { return res.status(400).json({ error: Validation failed, details: errors }); } try { const product await createProduct(req.body); res.status(201).json(product); } catch (err) { res.status(500).json({ error: Failed to create product }); } });这样的设计恰好同时满足规则的六与七校验失败返回“字段 → 原因”的描述性 400Product接口、GET路由与createProduct服务函数均保持原样改动只发生在路由文件内或抽出一个独立校验模块。值得注意的实现边界有四处req.body运行时可能不是对象如空请求体、JSON 数组校验函数内先(body ?? {})兜底取值避免直接解构抛错name的“1 个字符下限”应指非空白内容因此用name.trim().length 0判空更符合“有意义的名称”这一业务直觉sku必须用锚定正则做整体匹配SKU_PATTERN.test()若正则带g标志还会受lastIndex状态污染实现中不要复用带g的正则实例小数位校验放在“已是合法有限数”之后否则NaN * 100等运算结果会干扰判定分支。若要进一步验证方案正确性可在路由测试中用 supertest 等工具对六类非法输入缺 name、超长 description、负数 price、两位以上小数、非法 category、非法 sku 格式断言400再对一条合法 payload 断言201。这类断言对应着 ground-truth 中“CRITICAL/MAJOR”分级背后的验收语义。五、评测口径ground-truth 如何定义“这次实现算不算合格”任务文档本身不携带答案答案在 task-input-validation.json 中。它把校验要求转译成 6 条带严重级别、带关键词的 findings供评测器做自动匹配Finding ID严重级别含义对应需求条目IMPL-IV-1CRITICALname 必须按“必填、string、1-200 字符”校验需求 1IMPL-IV-2CRITICALprice 必须按“非负、至多 2 位小数”校验防止-5、19.999需求 3IMPL-IV-3CRITICALSKU 必须匹配^[A-Z]{2,4}-\d{4,8}$需求 5IMPL-IV-4MAJORcategory 必须在枚举内需求 4IMPL-IV-5MAJOR校验失败返回带描述的 400 而非 500需求 6IMPL-IV-6MAJOR不得修改 Product 接口与既有 GET 路由校验只做增量需求 7同时该文件记录了三个元信息domain: task任务型、expectedVerdict: scoped、isCleanBaseline: false。结合 agents/executor.md 与 deep-executor.md 中 Investigation Protocol 的任务分级约定Trivial / Scoped2-5 个文件、边界清晰/ Complex可以推断expectedVerdict: scoped表示评测期望执行 Agent 把本任务正确归类为“边界清晰的中等改动”——它涉及路由与可能的校验模块、但范围明确。isCleanBaseline: false则说明该任务故意埋有缺陷/缺口不是用于误报率测试的“干净基线”。一个值得指出的细节是需求 2description 上限 2000属于必做项但 ground-truth 并未为它单列 finding。这提示我们 ground-truth 是一种有取舍的验收采样它聚焦于最容易做错、代价最高的校验点必填字段的类型/长度/格式、错误语义、范围红线而不是穷举每条需求。六、底层原理从 Agent 输出到量化分数链路如何工作executor 基准复用了 harsh-critic 评分体系。运行一条消息后得分链路在 shared/parser.ts、shared/scorer.ts 与 harsh-critic/scoring/scorer.ts 中分四步完成解析ParseparseGenericOutput 把 Agent 的 Markdown 输出切成小节识别Critical/Major/Minor级别的区块并把列表项提取为 findings如果整段输出没有严重级别分区则回退到全文本提取列表项。证据标记hasEvidence由正则判定——命中反引号代码片段或文件:行号形态即视为“带证据”。匹配Match每一条 Agent finding 与该 fixture 的 ground truth findings 做关键词重叠匹配。MIN_KEYWORD_MATCHES 2是最低命中门槛而 scorer.ts 实际采用比例策略所需命中数 max(2, ceil(关键词数 × 0.4))。严重级别判定放宽到“相邻也算对”ALLOW_ADJACENT_SEVERITY true即 CRITICAL 与 MAJOR 互认降低措辞带来的抖动。打分Score产出 truePositiveRate检出率、falseNegativeRate漏检率、falsePositiveRate误报率、severityAccuracy、missingCoverage、perspectiveCoverage、evidenceRate 以及 pre-commitment / gap-analysis / multi-perspective 三个过程合规布尔值并按 SCORING_WEIGHTSTPR 0.25、FNR 0.15、FPR 0.10、missing 0.20、perspective 0.10、evidence 0.10、process 0.10合成compositeScore。聚合Aggregate同一 Agent 跑完多个 fixture 后取均值布尔指标用多数票。task领域的 fixture 在进入评分前还会被 scorer.ts 的CANONICAL_DOMAIN_PROJECTION投影为 canonical 的analysis领域从而无缝复用 harsh-critic 的 canonical scorer——这解释了为什么一份“改代码的任务”也能用“评审式”的关键词打分框架来度量。七、亲手复现运行这条 executor 基准仓库提供了完整的基准运行器 run-benchmark.ts用npx tsx即可执行。其 CLI 参数由 shared/runner.ts 的parseCliArgs支持--agent name/--agents a,b只跑某个 Agent 变体或指定多个--fixture id只跑单个 fixture例如task-input-validationid 即去掉.md扩展名的文件名--output-dir path报告输出目录默认benchmarks/executor/results--model model覆盖模型默认claude-opus-4-6--dry-run不调用任何 API仅验证流水线可通。只针对本文档跑一次新旧对比的命令形如npx tsx benchmarks/executor/run-benchmark.ts --fixture task-input-validation运行器默认把新合并的executor与旧的deep-executor两个变体在同一个 fixture 上各跑一遍从 run-benchmark.ts 头部注释可见这一对比意图。Agent 系统提示的加载顺序在 runner.ts 中定义优先取仓库根 agents/executor.md取不到再回退到 prompts/deep-executor.md。API 调用经由callClaude完成读取ANTHROPIC_API_KEY或ANTHROPIC_AUTH_TOKEN环境变量必要时也支持ANTHROPIC_BASE_URL代理对529/overloaded/rate/500等可重试错误做最多 5 次指数退避重试max_tokens固定 8192。每条结果会记录 API 延迟、harness 开销与输入/输出 token 数最终在results目录写入带时间戳的results_*.json、report_*.md以及固定名的results.json、report.md。--dry-run适合无密钥场景下的自检它会加载 fixtures、构造 user message、解析 CLI 参数但在真正发起 API 调用前停住并打印 “Pipeline validated”。八、这份 fixture 折射出的执行 Agent 行为规范把任务文档、基准运行器和两份 Agent 提示放在一起还能读出 oh-my-claudecode 对“合格执行 Agent”的行为期望这恰恰也是本任务的隐性考点先分类再动手deep-executor.md 与 agents/executor.md 的 Investigation Protocol 都要求第一步把任务归入 Trivial / Scoped / Complex对应本任务的expectedVerdict: scoped保持最小可行变更两条提示反复强调 “Prefer the smallest viable change” 与 “Do not introduce new abstractions for single-use logic”对应需求 7 的“不碰 Product 接口与 GET 路由”红线先探索、匹配既有模式再实现要求用 Glob/Grep/Read 摸清路由命名、错误处理与返回格式等既有惯例避免自造风格迥异的新模式——对应本任务中“仿照既有 Express handler 风格接入校验”的期望以证据收尾Success Criteria 要求给出“fresh 的 build/test/lsp_diagnostics 输出”作为完成依据呼应评测中evidenceRateCRITICAL/MAJOR finding 是否带文件引用或代码证据这一指标警惕过程性失分评分中 0.10 权重的过程合规项会检测 Agent 输出是否包含 pre-commitment先承诺再调查、gap analysis还有什么是缺失的与多视角分析因此即便方案正确输出组织方式也会影响综合分。若把该任务交给执行 Agent 后出现漏检可以对照 ground-truth 的 six findings 定位是落在哪一类漏了 name/price/skuCRITICAL 三类通常意味着没有做字段级逐条实现漏了 400 语义或范围红线MAJOR 三类则多半是“只补了逻辑、没纠正错误语义”或“顺手改了不该改的代码”——这恰是本文第一节路由代码中catch → 500与PartialProduct兜底两个坏味道希望引导 Agent 识别的地方。综上task-input-validation.md 既是一份可直接练习的“Express 输入校验实战工单”也是理解 oh-my-claudecode 执行 Agent 评测体系的上佳切片从七条需求到参考实现从 ground-truth 关键词打分到run-benchmark.ts的端到端流水线它把“代码正确性”量化为可复现的 composite score。读者既可以把这份任务当作 TypeScript/Express 校验编码的演练题也可以沿着 ground-truth、shared/runner.ts 与 harsh-critic/scoring/scorer.ts 继续深入理解一套面向 AI Agent 的自动化代码评测是如何设计与落地的。【免费下载链接】oh-my-claudecodeTeams-first Multi-agent orchestration for Claude Code项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-claudecode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考