news 2026/9/9 13:16:59

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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目录存放着一组用来衡量“执行型 Agent(Executor / 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.mdtask-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;

第三段是服务层,Partial<Product>让它对所有缺省字段都做“兜底填空”,这正是“无效输入也能被静默接受”的根源:

// src/services/product-service.ts import { Product } from '../types/product'; import { db } from '../database'; import { generateId } from '../utils/id'; export async function createProduct(input: Partial<Product>): Promise<Product> { 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 的字段级拆解:七条需求逐条落地

文档给出的需求清单是整份任务的核心,必须原样保留并逐条展开。任务要求新增校验而不是重写,所有规则都应落在“校验层”这一关注点上。

  1. name: required, string, 1-200 characters
  2. description: optional, string, max 2000 characters
  3. price: required, number, must be >= 0, max 2 decimal places
  4. category: required, must be one of the valid categories
  5. sku: required, string, must match pattern^[A-Z]{2,4}-\d{4,8}$
  6. Return 400 with descriptive error messages for validation failures
  7. Do not modify the Product interface or existing GET route

逐条解读其工程含义:

  • 规则 1(name)required意味着空串、纯空白、缺失都要被判为失败;“string”排除了数字与对象;“1-200 characters”给出了边界。注意 TS 的string类型只能约束“编译期类型”,无法阻止42{ name: 42 }这类运行时脏值,因此必须做运行时检查,typeof value !== 'string'是第一道闸。
  • 规则 2(description):唯一可选字段。可选的正确实现是“未提供则跳过,提供了就必须是字符串且不超过 2000 字符”,而不是“默认给空串”——后者会让description: 123这类错误静默通过,违背校验语义。
  • 规则 3(price):最易出边界问题的一条。>= 0排除负数;“number”排除字符串"19.99";而“max 2 decimal places”在浮点语境下需要专门处理——常见做法是验证Number.isInteger(price * 100),因为19.999 * 100 = 1999.9非整数即判定失败。此外还应考虑NaNInfinity、非有限数,这些都能绕过>= 0
  • 规则 4(category):枚举校验应与类型定义中'electronics' | 'clothing' | 'food' | 'other'严格一致。建议定义一个可复用的常量数组,由它推导联合类型,避免校验逻辑与类型定义“手写两份、悄然漂移”。
  • 规则 5(sku):正则^[A-Z]{2,4}-\d{4,8}$的含义是:2~4 个大写英文字母 + 一个连字符 + 4~8 位数字,且必须整体匹配(^$锚定)。如果业务上允许小写输入,可先toUpperCase()再校验——但是否规范化属于业务决策,任务只要求“必须匹配该模式”,实现时应避免自行放宽。
  • 规则 6(400 响应):校验失败要返回 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: Record<string, string>; } function validateProductInput(body: unknown): ValidationResult { const errors: Record<string, string> = {}; const input = (body ?? {}) as Record<string, 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' }); } });

这样的设计恰好同时满足规则的六与七:校验失败返回“字段 → 原因”的描述性 400;Product接口、GET路由与createProduct服务函数均保持原样,改动只发生在路由文件内(或抽出一个独立校验模块)。

值得注意的实现边界有四处:

  1. req.body运行时可能不是对象(如空请求体、JSON 数组),校验函数内先(body ?? {})兜底取值,避免直接解构抛错;
  2. name的“1 个字符下限”应指非空白内容,因此用name.trim().length === 0判空更符合“有意义的名称”这一业务直觉;
  3. sku必须用锚定正则做整体匹配SKU_PATTERN.test()若正则带g标志还会受lastIndex状态污染,实现中不要复用带g的正则实例;
  4. 小数位校验放在“已是合法有限数”之后,否则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 字符”校验(需求 1)
IMPL-IV-2CRITICALprice 必须按“非负、至多 2 位小数”校验,防止-519.999(需求 3)
IMPL-IV-3CRITICALSKU 必须匹配^[A-Z]{2,4}-\d{4,8}$(需求 5)
IMPL-IV-4MAJORcategory 必须在枚举内(需求 4)
IMPL-IV-5MAJOR校验失败返回带描述的 400 而非 500(需求 6)
IMPL-IV-6MAJOR不得修改 Product 接口与既有 GET 路由,校验只做增量(需求 7)

同时该文件记录了三个元信息:domain: "task"(任务型)、expectedVerdict: "scoped"isCleanBaseline: false。结合 agents/executor.md 与 deep-executor.md 中 Investigation Protocol 的任务分级约定(Trivial / Scoped(2-5 个文件、边界清晰)/ Complex),可以推断expectedVerdict: "scoped"表示评测期望执行 Agent 把本任务正确归类为“边界清晰的中等改动”——它涉及路由与可能的校验模块、但范围明确。isCleanBaseline: false则说明该任务故意埋有缺陷/缺口,不是用于误报率测试的“干净基线”。

一个值得指出的细节是:需求 2(description 上限 2000)属于必做项,但 ground-truth 并未为它单列 finding。这提示我们 ground-truth 是一种有取舍的验收采样,它聚焦于最容易做错、代价最高的校验点(必填字段的类型/长度/格式、错误语义、范围红线),而不是穷举每条需求。

六、底层原理:从 Agent 输出到量化分数,链路如何工作

executor 基准复用了 harsh-critic 评分体系。运行一条消息后,得分链路在 shared/parser.ts、shared/scorer.ts 与 harsh-critic/scoring/scorer.ts 中分四步完成:

  1. 解析(Parse):parseGenericOutput 把 Agent 的 Markdown 输出切成小节,识别Critical/Major/Minor级别的区块并把列表项提取为 findings;如果整段输出没有严重级别分区,则回退到全文本提取列表项。证据标记hasEvidence由正则判定——命中反引号代码片段或文件:行号形态即视为“带证据”。
  2. 匹配(Match):每一条 Agent finding 与该 fixture 的 ground truth findings 做关键词重叠匹配。MIN_KEYWORD_MATCHES = 2是最低命中门槛,而 scorer.ts 实际采用比例策略:所需命中数 =max(2, ceil(关键词数 × 0.4))。严重级别判定放宽到“相邻也算对”(ALLOW_ADJACENT_SEVERITY = true,即 CRITICAL 与 MAJOR 互认),降低措辞带来的抖动。
  3. 打分(Score):产出 truePositiveRate(检出率)、falseNegativeRate(漏检率)、falsePositiveRate(误报率)、severityAccuracy、missingCoverage、perspectiveCoverage、evidenceRate 以及 pre-commitment / gap-analysis / multi-perspective 三个过程合规布尔值,并按 SCORING_WEIGHTS(TPR 0.25、FNR 0.15、FPR 0.10、missing 0.20、perspective 0.10、evidence 0.10、process 0.10)合成compositeScore
  4. 聚合(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-validation(id 即去掉.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_KEYANTHROPIC_AUTH_TOKEN环境变量,必要时也支持ANTHROPIC_BASE_URL代理;对529/overloaded/rate/500等可重试错误做最多 5 次指数退避重试,max_tokens固定 8192。每条结果会记录 API 延迟、harness 开销与输入/输出 token 数,最终在results目录写入带时间戳的results_*.jsonreport_*.md以及固定名的results.jsonreport.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 输出”作为完成依据,呼应评测中evidenceRate(CRITICAL/MAJOR finding 是否带文件引用或代码证据)这一指标;
  • 警惕过程性失分:评分中 0.10 权重的过程合规项会检测 Agent 输出是否包含 pre-commitment(先承诺再调查)、gap analysis(还有什么是缺失的)与多视角分析,因此即便方案正确,输出组织方式也会影响综合分。

若把该任务交给执行 Agent 后出现漏检,可以对照 ground-truth 的 six findings 定位是落在哪一类:漏了 name/price/sku(CRITICAL 三类)通常意味着没有做字段级逐条实现;漏了 400 语义或范围红线(MAJOR 三类)则多半是“只补了逻辑、没纠正错误语义”或“顺手改了不该改的代码”——这恰是本文第一节路由代码中catch → 500Partial<Product>兜底两个坏味道希望引导 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 13:16:50

SpringBoot QPS监控实战:从自研滑动窗口到Prometheus+Grafana告警

说实话&#xff0c;我以前也觉得QPS监控是那种“大团队才需要搞的东西”&#xff0c;小项目压测一下没问题直接上线就行了。直到上个月我们一个订单查询接口在下午高峰期突然雪崩&#xff0c;从用户开始反馈到系统完全不可用&#xff0c;前后也就十来分钟。事后复盘的时候发现&…

作者头像 李华
网站建设 2026/9/9 13:16:25

遥感图像融合TIF算法:Python与MATLAB实战指南

简介&#xff1a;图像融合TIF算法&#xff08;Transform Invariant Fusion&#xff09;提供Python与MATLAB两种语言的实现代码&#xff0c;适合图像处理初学者与进阶开发者学习。该算法基于变换不变性设计&#xff0c;能在图像发生平移、缩放或旋转时仍保持稳定融合效果&#x…

作者头像 李华
网站建设 2026/9/9 13:15:19

从零搭建图像去雨Derain项目:数据合成、残差U-Net训练与调优实战

简介&#xff1a;一个基于Python实现的图像去雨&#xff08;Deraining&#xff09;项目&#xff0c;面向图像处理与计算机视觉学习者&#xff0c;旨在去除照片中的雨滴干扰&#xff0c;提升恶劣天气下拍摄图像的清晰度。项目围绕预处理、特征提取、雨滴建模与背景恢复等关键步骤…

作者头像 李华
网站建设 2026/9/9 13:13:25

Neokikoeru:端到端语音合成与声音克隆实战全解析

Neokikoeru这名字乍一看有点怪&#xff0c;如果拆开看就很有意思了&#xff1a;neo kikoeru&#xff0c;后面这个词在日语里是“聞こえる”&#xff0c;也就是“能听见”的意思。合在一起&#xff0c;就是“重新听见”或者说“用一种新方式去听”。做音频和语音相关的朋友应该能…

作者头像 李华
网站建设 2026/9/9 13:13:25

数字化转型总体方案设计:从信息化到数据闭环的落地指南

1. 先把“数字化”这个词拆清楚——别拿着信息化的旧地图找数字化的新大陆我这两年接触过不少企业管理者&#xff0c;一上来就说“我们想搞数字化转型”&#xff0c;再一聊&#xff0c;发现他们想的是“把ERP升级一下”“上个OA审批”“把Excel的活儿搬到系统里”。这其实不是转…

作者头像 李华