尧图精选

oh-my-claudecode Harsh-Critic 基准评分关键字匹配校准:归一化、短语回退与动态阈值的工程实现

🕒 发布时间:2026/9/10 12:16:29 📁 来源:尧图网络
oh-my-claudecode Harsh-Critic 基准评分关键字匹配校准归一化、短语回退与动态阈值的工程实现【免费下载链接】oh-my-claudecodeTeams-first Multi-agent orchestration for Claude Code项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-claudecode本篇技术指南聚焦 oh-my-claudecode 仓库中 Harsh-Critic 评测基准benchmarks/harsh-critic/README.md一次关键的评分匹配校准设计即以 SCORING_MATCH_CALIBRATION.md 为决策文档、以 scoring/scorer.ts 为实现落点的改进。你会掌握为什么 LLM 评测的关键字命中不能使用严格子串匹配、文本归一化与短语回退如何消除格式化差异带来的误报漏检、按关键字集合规模动态抬高阈值背后的数学与工程权衡以及如何用一组无 API Key 的 Vitest 回归测试守住精确率边界。为什么需要校准关键字匹配的本质矛盾Harsh-Critic 基准用一组预埋缺陷 已知 ground truth的 fixture计划、代码、分析三类文档对比harsh-critic与critic两个评审 Agent 的差距发现能力。而评判一个 Agent 是否找出了某个 ground truth 缺陷走的是关键字匹配路线每个 ground truth 条目维护一组 signal keywordsAgent 输出的某条 finding 只要包含足够多的关键字就被判定为命中。问题在于旧匹配规则过于刚性匹配规则固定为countKeywordMatches 2常量定义见 scoring/types.ts 中的MIN_KEYWORD_MATCHES字符串检查使用原样小写的includes(...)不做任何归一化。这对真实模型输出极不友好。以 ground truth 中真实的 location 字段为例见 ground-truth/code-payment-handler.json缺陷定位常写成processPayment():47-52或processPayment():93-100而 Agent 复述时几乎不可能逐字复刻这种格式。文档列举了三类典型变体标点 / 分隔符差异new-hire与new hire符号差异processPayment():47-52与processPayment 47 52短语变体关键字短语中的多个 token 之间被 Agent 插入了额外标点。文档特别强调了一个重要的失败定位这类失配是基准评分的假阴性false negative不是模型质量回退。换言之模型明明发现了缺陷、措辞语义正确仅因格式不同而被打成未检出这会系统性低估 Agent 真实能力导致基准对两个 Agent 的对比结论失真。校准方案总览三层防线为缓解上述问题校准方案对评分匹配做了三项叠加改动正好对应 scorer.ts 中三个关键函数文本归一化normalizeTextForMatch统一大小写、做 UnicodeNFKC归一化、把标点与分隔符折叠成空格短语回退匹配keywordMatchesText中的 phrase fallback 分支保留先直接子串匹配的快速路径多 token 关键字在归一化文本中全部出现即命中动态阈值requiredKeywordMatches基数仍为MIN_KEYWORD_MATCHES 2但当关键字集合扩大到 6 个时要求命中 3 个约 40% 的比例下限防止大集合因容易碰中一两个通用词而产生意外匹配。三者层层递进归一化解决写法不同短语回退解决断句不同动态阈值解决放宽带来的精确率回退。源码级对照文本归一化与三级匹配scorer.ts 中的归一化实现把变化无常的自然语言折叠成可比较的稳定形态function normalizeTextForMatch(value: string): string { return value .toLowerCase() .normalize(NFKC) .replace(/[*_#()[\]{}.,;!?|\\]/g, ) .replace(/[-/:]/g, ) .replace(/\s/g, ) .trim(); }注意三点工程细节NFKC归一化处理全角/半角字符、兼容字符如 K 等对模型输出混入全角标点或特殊字符的场景尤为关键反引号、星号、下划线等 Markdown 装饰字符与引号、逗号、分号被替换为空格因为 Agent 输出中 finding 文本往往带code()或*斜体*这类格式连字符与斜杠-、/、:被折叠为空格这是让new-hire与new hire、processPayment():47-52与processPayment 47 52等价的核心手段。而 keywordMatchesText 实现了先严后宽、逐级回退的三级匹配管道function keywordMatchesText(text: string, keyword: string): boolean { const lowerText text.toLowerCase(); const lowerKeyword keyword.toLowerCase(); if (lowerText.includes(lowerKeyword)) { // 第一级直接子串快速路径 return true; } const normalizedText normalizeTextForMatch(text); const normalizedKeyword normalizeTextForMatch(keyword); if (!normalizedKeyword) return false; if (normalizedText.includes(normalizedKeyword)) { // 第二级归一化后子串 return true; } const keywordParts normalizedKeyword.split( ).filter(Boolean); if (keywordParts.length 1) return false; // 第三级短语回退——所有 token 均出现顺序无关 return keywordParts.every((part) normalizedText.includes(part)); }这个快速路径优先的设计意义在于大多数直白输出仍然命中第一级includes几乎没有额外开销只有直接命中失败时才进入正则归一化最后才尝试短语 token 全集的顺序无关匹配every。第三级特意要求所有短语 token 都出现而非任一 token 出现避免把高颗粒度关键字如processPayment降级成模糊词。动态阈值为什么按集合规模而非全局下调简单的修法自然是把阈值全局降到 1但这会大面积牺牲精确率——尤其是race、error、retry这类高频词极易单点误命中。因此设计选择了按关键字集合规模动态抬升的requiredKeywordMatchesscorer.tsfunction requiredKeywordMatches(keywords: string[]): number { if (keywords.length 0) return 0; // 随集合规模放大抑制大集合上的意外匹配 // 4/5 个关键字 - 要求 2 个命中6 个关键字 - 要求 3 个命中。 const proportional Math.ceil(keywords.length * 0.4); return Math.min( keywords.length, Math.max(MIN_KEYWORD_MATCHES, proportional), ); }其数学含义是required min(N, max(2, ceil(0.4 * N)))落地效果如下表关键字集合规模 N0.4N 向上取整实际要求命中数是否保持旧行为422是522是633否由 2 提升至 3也就是说45 关键字条目仍要求 2 个命中文档明确此点为兼容性保证只有当集合大到 6 个时命中要求才按 40% 比例下限抬到 3。这样既抑制了大关键字集合上碰中一两个常见词即误判为命中的风险又没有全局放松任何小集合的匹配纪律。判定总入口textMatchesGroundTruthscorer.ts即countKeywordMatches requiredKeywordMatches。为什么不引入语义匹配可控性优先校准文档明确拒绝了三条候选路线并给出理由保持严格 includes 固定阈值被拒绝理由是对真实输出中标点/格式变体过于脆弱而这正是本改动要解决的痛点全局把固定阈值降到 1被拒绝会造成大面积精确率损失尤其对常见词common terms而言几乎失控基于 embedding 的语义匹配器被暂缓rejected for now理由是复杂度更高、确定性更差、审计更困难。最终方案的取舍哲学可以概括为确定性与可审计性优先。从 scorer.ts 全貌看匹配全程是纯字符串运算——无 embedding 模型调用、无 LLM-in-the-loop 评分、无同义词库任何一次命中/未命中都可被逐行推导复现这正是回归测试与人工审计成立的前提。基准评测需要稳定、可解释的结论来支撑 Agent 之间harsh-criticvscritic的 A/B 对比引入黑盒语义打分会让差距来自提示词还是评分器这个问题不可回答。回归测试用确定性用例锁定精确率边界校准方案单独强调没有全局降低命中阈值、大集合需要更多证据并配套了正反两类边界测试全部落地在 scoring/tests/scorer.test.ts且无需 API Key即可运行见 vitest.config.ts 的 include 配置。测试夹具通过工厂函数构造最小化的 ground truth 与 Agent 输出。其中三类核心用例精确对应文档Validation一节的承诺标点/连字符健壮性ground truth 关键字含new-hire、sameSite、cookie、csrfAgent 输出为New hire note: session cookie is missing SameSite and enables CSRF risk.要求命中F1——验证new-hire与new hire的归一化等价性6 关键字阈值负例2/6 失败关键字为alpha~foxtrot六个Agent 只提到alpha bravo要求结果为空、F1计入 missed——守住不能只靠一两个词碰中的下限6 关键字阈值正例3/6 通过Agent 提到alpha bravo charlie要求命中F1——确认 40% 比例阈值3 个在语义足够时放行。在匹配达到阈值后评分模块还会继续用 computeSeverityAccuracy 与severityMatches校验严重级是否一致相邻级别CRITICAL↔MAJOR、MAJOR↔MINOR视为通过见 types.ts 的ALLOW_ADJACENT_SEVERITY true再按 7 维权重true positive 25%、missing coverage 20%、false negative 15%、evidence 10%、perspective 10%、process compliance 10%、false positive 10%汇总成 compositeScore——权重配置见 types.ts。因此匹配校准的任何回归都会直接传导到 composite 分数上测试必须独立守住匹配层。本地验证命令npx vitest run benchmarks/harsh-critic/scoring/__tests__ # 或进入基准目录按该目录 vitest 配置运行 npx vitest run --config benchmarks/harsh-critic/vitest.config.ts风险缓解与整体验证策略放宽归一化理论上会带来假阳性false positive上升的风险校准文档给出的缓解措施与源码实现一一对应匹配阈值并未全局下调45 关键字条目仍要求 2 个命中等价性与动态阈值独立演进更大关键字集合要求更多证据6/6 场景由 2 个升级为 3 个等价于在放宽容忍度的同时对最容易碰巧命中的大集合反向收紧补了正反两侧的回归测试既有2/6 必须失败的负例也有3/6 必须通过的正例确保阈值既不过严也不过松。在更高一层的验证节奏上文档明确了一个重要的方法论约束单元测试套件先行、live 基准重跑有意分离。live 基准需要真实调用模型 API存在成本与结果方差详见 run-benchmark.ts 对claude-opus-4-6的默认模型与指数退避重试因此不应与代码改动合并前同时执行——否则无法区分评分器改动与模型输出波动对前后指标的影响。推荐的时序是在改动合并后单独重跑 live 基准获得干净的 before/after 报告这与 README.md 中LLM 输出随运行变化建议重跑 3 次取平均、固定模型版本的可复现性建议一致。在仓库中复现与观察评分链路如需端到端观察校准效果可在配置好ANTHROPIC_API_KEY后运行基准先了解成本README 记录全量跑 8 fixtures × 2 agents 约 $3–5单 fixture 约 $0.5–1# 单 fixture 快速验证开发期更省成本 ANTHROPIC_API_KEYsk-... npx tsx benchmarks/harsh-critic/run-benchmark.ts \ --agent both --fixture code-payment-handler # 无 API 的管线自检加载 fixture 与 ground truth跳过 API 调用 npx tsx benchmarks/harsh-critic/run-benchmark.ts --dry-run结果的 JSON/Markdown 报告写入 benchmarks/harsh-critic/results/gitignored控制台打印 per-fixture 的 composite、TP rate、FN rate 与 missing coverage 对比表。调试匹配本身可留意 parser.ts 的EVIDENCE_PATTERN——它从 Agent 输出提取processPayment():47-52、src/auth.ts:42这类证据标记用于判定 finding 的hasEvidence与匹配层的关键字归一化共同决定证据率与命中结果。小结这次评分匹配校准是一次典型的评测基建自我修正它没有改变被评测的 Agent 提示词而是修复了度量它们的方式。从 SCORING_MATCH_CALIBRATION.md 到 scoring/scorer.ts 的落地展示了一套可复用的 LLM 文本评测准则——用确定性、可审计、关键字锚定的匹配替代子串硬比对用归一化与短语回退吸收格式噪声用动态阈值对冲精确率风险用无 API 的边界测试锁定行为最后把 live 重跑隔离到干净的 before/after 对比中。这套方法论同样适用于任何以人工标注关键字 模型自由文本为核心的 Agent 评测流水线。【免费下载链接】oh-my-claudecodeTeams-first Multi-agent orchestration for Claude Code项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-claudecode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →