gbrain eval suspected-contradictions:用 LLM 裁判量化检索矛盾率的完整实战指南
【免费下载链接】gbrainGarry's Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain
gbrain eval suspected-contradictions是 gbrain 内置的"疑似矛盾探针"(contradiction probe):它对检索结果进行采样,把片段与片段、片段与 takes 组成对偶(pair),交由 LLM 裁判判定是否存在与用户查询相关的事实性矛盾,最后聚合成一份带置信区间的校准报告。本指南将带你从架构、CLI 全参数、裁判判决体系、头部数字解读、处置命令、成本模型、信任姿态到时间轴语义完整走通这条探针,读完后你能独立运行、解读并据此做出"要不要上更大规模矛盾处理方案"的数据驱动决策。本文以 docs/contradictions.md 为骨架,并结合src/core/eval-contradictions/与src/commands/eval-suspected-contradictions.ts的源码实现展开。
为什么需要这条探针
gbrain 对"已治理"(curated)页面上的矛盾,其实已经有一套既有的处理机制,文档中概括为 compiled-truth-plus-timeline 与 source-boost:
- 当
companies/acme.md声称 MRR 是 $2M,而一份 2024 年的聊天记录说 MRR 是 $50K 时,curated 页面会在排序上压过聊天记录; takes.active过滤会隐藏被显式取代(superseded)的 takes;- 按来源层级(source-tier)施加的 recency decay 会让排序偏向更新的内容。
但这些机制有一个共同的盲区:没有任何一个机制在度量"未被标记的语义矛盾到底在检索结果中出现的频率"。文档直言:如果没有探针,每一次"是否要上更大的方案(chunk 级revises字段 + 排序改动)"的决策都只是"凭感觉"(vibes)。这条探针的价值就是把这种感觉替换成可引用的证据——输出的是数据,而"对数据采取什么行动"的决定权始终在操作者手上。
从源码看,这套度量体系在 src/core/eval-contradictions/ 目录下实现,由runner.ts作为编排器(orchestrator),配合date-filter.ts(日期预过滤)、cache.ts(持久化缓存)、judge.ts(LLM 裁判)、calibration.ts(Wilson 置信区间)、severity-classify.ts(严重度归类)、auto-supersession.ts(处置命令生成)、trends.ts(趋势)等模块组成。
架构总览
文档给出的架构图完整呈现了一次探针运行的端到端数据流:
┌──────────────────────────────────────┐ │ gbrain eval suspected-contradictions │ └──────────────────┬───────────────────┘ │ ┌──────────────────▼───────────────────┐ │ For each query: hybridSearch top-K │ │ → cross_slug_chunks + intra_page │ │ chunk-vs-take pairs │ └──────────────────┬───────────────────┘ │ ┌──────────────────▼───────────────────┐ │ Date pre-filter: skip pairs whose │ │ dates are >30d apart (Codex fix: │ │ same-paragraph-dual-date overrides) │ └──────────────────┬───────────────────┘ │ ┌──────────────────▼───────────────────┐ │ Persistent cache lookup │ │ (chunk_a_hash, chunk_b_hash, model, │ │ prompt_version, truncation_policy) │ └────────┬─────────┬────────────────────┘ hit│ │miss │ ▼ │ ┌─────────────────────────┐ │ │ LLM judge call │ │ │ → JudgeVerdict │ │ │ confidence floor ≥ 0.7 │ │ └─────────┬───────────────┘ │ │ ▼ ▼ ┌──────────────────────────────────────┐ │ Aggregate per-query + global stats │ │ Wilson 95% CI on headline % │ │ source-tier breakdown │ │ hot pages + resolution proposals │ └──────────────────┬───────────────────┘ │ ▼ ProbeReport JSON │ ┌──────────────────┼──────────────────────┬───────────────┐ ▼ ▼ ▼ ▼ doctor (M1) local read (M3) synthesize (M2) trend (M5) surfaces find_contradictions informational persistent findings unscoped callers block in prompt tracking对照 src/core/eval-contradictions/runner.ts 的实现,一次运行被拆成 8 步:
- 从三种来源之一加载查询(文件 / 单条 / 捕获表);
- 对每个 query 执行
hybridSearch取 top-K 结果(嵌入成本单独记账); - 生成对偶:跨 slug 的
cross_slug_chunks+ 每个页面内的intra_page_chunk_take(后者通过engine.listActiveTakesForPages批量加载活跃 takes); - 应用日期预过滤(见下节),通过的对偶进入缓存或裁判;
- 按
combined_score(两侧检索得分之和)排序,deterministic与score-first两种采样策略控制判定顺序; - 成本跟踪:前置估计 + 运行中累计软上限;
- 裁判错误(judge_errors)作为一等公民计数,失败对偶进入类型化计数器而非静默丢弃;
- 聚合出
ProbeReport:逐查询结果 + 全局统计 + Wilson CI + 来源层级分解 + 热点页面。
CLI 使用手册:run / trend / review 三件套
命令入口在 src/commands/eval-suspected-contradictions.ts,提供三个子子命令。默认子命令是run:
gbrain eval suspected-contradictions [run] [--queries-file FILE.jsonl | --query "..." | --from-capture] [--top-k N=5] [--judge MODEL] (default routes via resolveModel → models.eval.contradictions_judge → utility-tier (Haiku) fallback) [--limit N] [--budget-usd N] [--output FILE] [--max-pair-chars N=1500] [--sampling deterministic|score-first] [--no-cache] [--refresh-cache] [--json] [--yes] gbrain eval suspected-contradictions trend [--days N=30] [--json] gbrain eval suspected-contradictions review [--severity info|low|medium|high] [--since YYYY-MM-DD]run:执行一次探针
查询来源三选一,必须恰好传一个(否则 exit 2):
--queries-file FILE:JSONL 或每行一条纯文本查询,JSONL 行提取其中的query字段;--query "...":单条查询;--from-capture:从eval_candidates表读取最近 100 条(可用--limit截断)。若表为空,命令会打印启用捕获的提示并退出 2。捕获默认关闭,需要export GBRAIN_CONTRIBUTOR_MODE=1或配置eval.capture: true开启。
关键参数一览(默认值来自 parseFlags):
| 参数 | 默认值 | 说明 |
|---|---|---|
--top-k N | 5 | 每个查询取前 N 条检索结果用于组对 |
--judge MODEL | 配置链解析 | 覆盖裁判模型,优先级最高 |
--limit N | 无 | 限制评估的查询数量 |
--budget-usd N | TTY $5 / 非 TTY $1 | 运行累计成本的软上限 |
--max-pair-chars N | 1500 | 每个对偶成员的 UTF-8 安全截断长度 |
--sampling | deterministic | deterministic或score-first |
--no-cache | false | 禁用持久化缓存(基准测试用) |
--refresh-cache | false | 运行前清理过期缓存行 |
--json | false | 报告以 JSON 输出到 stdout |
--yes/-y | false | 跳过预算前置拒绝与成本提示 |
裁判模型的解析链值得单独说明:v0.34 起--judge未指定时,通过resolveModel按models.eval.contradictions_judge配置键 → utility-tier 默认 → 全局models.default→ 环境变量GBRAIN_CONTRADICTIONS_JUDGE_MODEL的顺序解析,兜底是 utility-tier 默认模型(Haiku),且刻意不硬编码 Anthropic——只有 OpenAI Key 的安装不会被路由到不可用的厂商。
输出纪律与退出码
- stderr:人类可读摘要;
- stdout:仅在
--json时输出 JSON(schema_version: 1),保留给管道消费; - 退出码:
0成功;1超预算且未传--yes;2查询来源互斥或捕获表为空。
摘要会渲染头部指标、Wilson CI、逐判决(verdict breakdown)分布、裁判错误五桶(parse_fail/timeout/http_5xx/refusal/unknown)、缓存命中率、来源层级分解、成本、耗时与热点页面(最多 5 个,格式为slug (appearances, max severity))。特别地,若运行状态为judge_failed(所有裁判调用都出错、零判决),摘要会打印醒目的*** JUDGE FAILED ***横幅并拒绝渲染 "0 / N contradictions" 头部数字——把一个全错的运行伪装成"零矛盾"是不诚实的,此时命令以退出码 1 结束,防止 cron/CI 把"裁判没跑"误读为"没找到矛盾"。这一诚实性谓词实现在 src/core/eval-contradictions/run-health.ts。
trend 与 review
trend [--days N=30]:读取eval_contradictions_runs表渲染 ASCII 趋势图,每行包含日期、裁判模型、查询数、含矛盾数、总标记数、Wilson CI 与 ASCII 柱状条;最新一次运行还会附上逐判决分布(见 trends.ts);review [--severity ...] [--since YYYY-MM-DD]:从最近 90 天内的最新一次运行中取出 findings,按严重度 high → medium → low → info 排序逐条展示 verdict、axis 与resolution_command,无需重跑探针即可复查。
对偶构造:cross_slug_chunks 与 intra_page_chunk_take
每种查询的对偶分两类(ContradictionKind,见 types.ts):
- cross_slug_chunks:top-K 结果中每对不同 slug 的 chunk 组成一对(同一 slug 跳过),数量为 C(K,2),按
combined_score(两成员检索得分之和)排序; - intra_page_chunk_take:每个结果页面内,chunk 与该页面的每条活跃 take 组成一对。take 没有检索得分,权重取 1.0,使页内对偶能与跨 slug 对偶在确定性排序中同台竞争。
PairMember统一了两端形状:slug、chunk_id / take_id、source_tier、holder(take 持有者)、文本、effective_date(页面级生效日期,ISO 日期或 null)。其中take_row_num(takes 在页面内的按页行号)在 gbrain#4169 修复中从全局主键改为按页行号——这是takes supersede --row真正寻址的字段,之前把全局take_id渲染进--row会让每条生成的处置命令都报 "Row #N not found"。
日期预过滤:三条规则,省钱不误伤
hybridSearch召回的结果天然带有时间形状——/daily/、/meetings/、季度快照非常多。如果每对都送裁判,时间线型内容会吞噬裁判调用成本。日期预过滤(date-filter.ts)用三条规则在掏钱请 LLM 之前把明显是"季度更新"的常见情形拦下来:
- 两侧都含显式 YYYY 类日期且相差超过 30 天(
DATE_SEPARATION_DAYS = 30)→ 跳过(最明显的季度更新情形); - 任一侧缺显式日期 → 不跳过,交给裁判判断;
- 任一侧的同一段落内出现两个不同日期 → 不跳过。这是 Codex 指出的关键修正:"1 月我是 CFO / 3 月我不是 CFO"这种翻转(flip-flop)是真实的矛盾或更新,预过滤绝不能悄悄杀掉它。
检测器刻意保守:漏过滤(该跳没跳)只浪费 token;误过滤(真矛盾被跳掉)会毁掉整条探针的意义,因此策略宁漏杀不误杀。
日期正则支持YYYY-MM-DD、YYYY/MM/DD、Mon DD YYYY、Mon YYYY、Q1-4 YYYY与裸YYYY六种形态(Mon-DD-YYYY 必须排在 Mon-YYYY 前才能捕获日期,裸年份放在最后避免抢走完整模式的年份)。
此外,v0.34 / Lane B 引入第四种返回路径both_have_effective_date:当两侧都带有页面级effective_date时也不跳过——此时裁判能看到(from: YYYY-MM-DD)时间锚并显式分类为 supersession/regression/evolution,30 天跳过规则对 v2 判决枚举反而会误杀它本该浮现的案例。
LLM 裁判与六值判决体系
每个幸存对偶调用一次裁判(judge.ts)。裁判提示词是规范文本,由PROMPT_VERSION = '2'版本化——提示词一改,旧缓存判决全部失效。提示词的关键设计:
- 查询条件化(Codex 外部意见修复):裁判看到用户的实际查询,判断的是"与所问内容相关的矛盾",而不是自由形态的成对分歧;
- 时间锚:每侧显示
(from: YYYY-MM-DD)或(date unknown),来自pages.effective_date; - 持有者可见:take 对偶展示
holder garry等——"Garry 持有 X" vs "Garry 持有 not-X"是翻转,而"Alice 持有 X" vs "Bob 持有 not-X"不是; - 置信度底线:只有
contradiction判决要求confidence >= 0.7,低于阈值会被双重降级为no_contradiction(C1 双重强制),防止模型无视提示词;其余五个判决没有置信度底线,因为它们是信息性分类而非错误标记。
判决枚举有六个成员(文档原话:"区分真实矛盾与合理的时间变化"):
| 判决 | 含义 |
|---|---|
no_contradiction | 兼容,不进入 findings |
contradiction | 同一时间点上的真实冲突 |
temporal_supersession | 新声明更新/取代旧声明,不是错误 |
temporal_regression | 指标/状态随时间倒退(如 MRR 从 $200K 跌到 $150K),值得标记 |
temporal_evolution | 合法的时间演进,既非取代也非倒退 |
negation_artifact | 一侧含显式否定被表层 token 误读为肯定声明,数据本身正确 |
裁判返回 JSON,解析走四策略兜底(严格 JSON → 剥离 ```json 围栏 → 常见修复:尾逗号/单双引号 → 在正文中找第一个平衡的 JSON 结构),全部失败则计入judge_errors.parse_fail而非编造空对象。v1 形状的contradicts: boolean响应仍被接受为小模型的修复路径。
严重度规则(Severity Rubric)
裁判为每条 finding 分配严重度,排序与归类实现在 severity-classify.ts:
| 级别 | 规则 | 示例 |
|---|---|---|
info | 时间性信号(supersession / evolution),非错误 | 角色随时间正常变更 |
low | 命名/格式差异 | "Alice Smith" vs "A. Smith" |
medium | 可能过期的事实数值 | 营收数字、员工数、估值 |
high | 身份/结构性声明 | 创始人/CEO/CFO 角色、公司状态 |
严重度排序为high > medium > low > info(内部排名字典SEVERITY_RANK = { info: 0, low: 1, medium: 2, high: 3 })。当裁判返回无效严重度字符串时,按判决映射到默认值:temporal_regression → high、negation_artifact → low、contradiction → medium、temporal_supersession / temporal_evolution → info。Doctor 按严重度降序呈现 findings;无来源过滤的可信本地调用方可以用review --severity high之类的操作拉取高优先级项。
如何解读头部数字:Wilson 95% 置信区间
探针输出的头部指标是queries_with_contradiction / queries_evaluated,配一个 Wilson 95% 置信区间。文档示例:
Queries with >=1 contradiction: 12 / 50 (24%) Wilson CI 95%: 14–37%含义是:有 95% 的置信度认为真实比例落在 14% 到 37% 之间;24% 是最可能值(point estimate),但受采样噪声约束。当small_sample_note触发(n < 30)时,区间宽到无法据以行动。实现细节在 calibration.ts:采用 Wilson 而非正态近似的原因是小样本与极端 p 值(接近 0 或 1)下正态近似会失效——"恰恰在我们最关心的位置失效"。边界被钉死:k === 0时下界恰为 0,k === n时上界恰为 1,防止浮点残差(6e-18、0.9999…)泄露进报告。
对"更大的方案(chunk 级revises字段)"的决策标准:
| Wilson CI 下界 | 说明 | 行动 |
|---|---|---|
| < 5% | source-boost + recency-decay + curated 页面已足够 | 到此为止,当前范围正确 |
| 5–15% | 真实但有限 | 由操作者权衡成本与收益 |
| > 15% | 真实且显著 | 规划更大的方案 |
解读时还有两个必须注意的诚实性细节:头部指标统计的是"严格 contradiction"(verdict === 'contradiction'),而报告同时给出更宽的queries_with_any_finding(任何非no_contradiction判决的查询数),帮助操作者分辨探针主要发现的是真矛盾还是时间性噪声;若一次运行所有裁判调用全部出错,run_status为judge_failed,CLI 与 doctor 都会拒绝把它渲染成干净的绿色结果。
何时行动:resolution_command 处置命令
每条 finding 都携带resolution_command字段——可直接寻址、且对需要人工判断的部分保持诚实。处置分类是确定性的(无 LLM),实现在 auto-supersession.ts,但裁判的resolution_kind提示优先。
gbrain takes supersede <slug> --row N --claim '<replacement>'—— 用于 intra_page 类 finding(较新的 take 应取代旧的)。--row是按页行号,--claim必填;当胜方本身是 take、且具有无歧义的声明(时间性取代)时命令完全可直接粘贴,否则带显式<replacement claim>占位符——分类器选的是"行动"而非"赢家",绝不会从任意 chunk 散文里编造 take。渲染时对 slug 与声明文本做 POSIX 单引号转义('\''拼接、换行折叠为空格),防止远程 MCP 写出的 slug 把 shell 元字符带进操作者的终端;gbrain dream --phase synthesize --slug <slug>—— curated 实体的 compiled_truth 需要更新(cross_slug curated-vs-bulk)。实际渲染为gbrain dream --phase synthesize # re-synthesize; contradiction on <slug>:gbrain dream没有按 slug 的参数(synthesize 阶段按 transcript/queue 而非页面划定范围),flag 注册表门禁会拒绝--slug,所以把 slug 作为 shell 注释携带,保证命令可粘贴且真实;# manual review: ...—— 故意分歧(debate)类与裁判不确定类 finding 渲染为人工复查注释;mark-as-debate子命令尚不存在(tracked with #4102),因此不会铸造任何粘贴后会失败的命令。
v2 新增的判决各有专属处置:temporal_supersession→ 当两侧都有日期时选出较新一侧为幸存者,对较旧一侧渲染带--since <newerDate>的 supersede 命令(胜方为 take 时声明直接填入,胜方为 chunk 时带占位符);temporal_regression与negation_artifact→# flag_for_review信息性注释;temporal_evolution→# temporal_evolution: ... record in timeline when the gbrain timeline writer lands提示。
用gbrain eval suspected-contradictions review --severity high即可在不重跑探针的情况下按严重度检查 findings。
成本模型:每 100 个查询约 $0.50
默认裁判是claude-haiku-4-5,约 $1/Mtok 输入、$5/Mtok 输出。默认截断--max-pair-chars为 1500 字符/对偶,每次裁判调用约 500 输入 + 80 输出 token。预算上限默认 TTY $5 / 非 TTY $1:
- 约 $0.0006 / 次裁判调用
- 约 $0.005 / 查询(日期预过滤 + 缓存命中后)
- 约 $0.50 / 100 个查询
持久化缓存意味着对同一查询集每晚重跑几乎零成本(直到PROMPT_VERSION提升)。成本模型实现在 cost-tracker.ts:--budget-usd是软上限,双层执行——前置估计(保守上界 = 对偶数 × 单次预算 + 查询嵌入费)超限且未传--yes时拒绝启动(PreFlightBudgetError,exit 1);运行中每次裁判调用后累计真实用量,超限即停止并输出部分报告(cap hit mid-run; report is partial)。价格查询走ANTHROPIC_PRICING统一表,经splitProviderModelId支持anthropic/claude-...斜杠前缀形态,未知模型静默回退 Haiku。
前置还有一个成本提示层(cost-prompt.ts):当PROMPT_VERSION自上次持久化运行以来发生变化时,TTY 下会打印一次性重判成本估计并给出 10 秒 Ctrl-C 窗口(GBRAIN_PROBE_PROMPT_GRACE_SECONDS可调);非 TTY(autopilot/脚本)自动继续,GBRAIN_NO_PROBE_PROMPT=1完全跳过。提示与硬上限相互独立、双层组合:先看估计,再在运行中被上限拦住。
信任姿态:只读、受限、防泄露
- 探针永不修改大脑数据。运行只读 pages/takes/chunks,写入只落在
eval_contradictions_runs和eval_contradictions_cache两张表; find_contradictions是 read 作用域,且不在 subagent 允许清单中。存储的报告仅临时对无来源过滤的可信本地调用方可见;远程或来源作用域(source-scoped)调用方收到{ contradictions: [], note }加可用性说明,其请求不会加载存储的报告(见 src/core/ops/insights.ts 中scope: 'read'定义与远程短路);- fixture 构建脚本仅本地运行。脱敏器(redactor)加
isCleanForCommit门禁让意外提交私有数据变得困难,但操作者必须在每次提交前检查每一处脱敏。脱敏会话(fixture-redact.ts)做四层处理:PII 擦除(邮箱/电话/SSN/JWT/卡号)→ slug 重写(people/garry→people/alice-example等,会话内稳定映射)→ 引号内姓名检测 → 金额混淆(营收乘盐值 1.7 保留数量级形态);无法确定安全改写时 fail-closed 输出[REDACT?]哨兵串,由操作者提交前解决。
时间轴语义:不把"事实变了"当成矛盾
这是探针最精细的部分。pages.effective_date被穿进裁判提示词((from: YYYY-MM-DD)标签),让探针不会对"仅仅是变了的事实"大惊小怪。同一信号的延伸基建:
gbrain eval trajectory <entity>:展示按时间排序的类型化声明历史,回归项内联标记(src/commands/eval-trajectory.ts);gbrain founder scorecard <entity>:把四个信号(accuracy、consistency、growth direction、red flags)聚合成稳定 JSON 契约(src/commands/founder-scorecard.ts);- MCP op
find_trajectory:read 作用域、对远程调用方做可见性过滤,向 Agent 暴露同一数据。
文档特别强调的不变量:探针的temporal_supersession判决与 consolidate 阶段的valid_until回写都遵守auto-supersession.ts的"NEVER auto-applies"原则——探针只发出可直接粘贴的命令,只有consolidate才能写valid_until,而且这一条被 grep 守卫钉死。测试 test/eval-contradictions/no-valid-until-write.test.ts 逐文件扫描src/core/eval-contradictions/与命令层,断言没有任何代码路径 UPDATEfacts.valid_until;所有合法写入点必须在允许清单上(如persistence/canonical-projections.ts恢复显式 valid_until、INSERT 携带列值等),新增写入站点必须先通过该守卫。
持久化:两张表与缓存键设计
表结构定义在 src/schema.sql:
eval_contradictions_cache:持久化裁判判决。复合主键为(chunk_a_hash, chunk_b_hash, model_id, prompt_version, truncation_policy)——把prompt_version与truncation_policy纳入键意味着任何提示词编辑都会干净地使旧判决失效;expires_at提供 TTL,cache.ts定期清扫。内容哈希用 sha256(UTF-8 输入、小写 hex),且对 (a, b) 与 (b, a)顺序无关(两个哈希字典序排序后作键),因为检索顺序可能改变对偶方向而判决是对称的;eval_contradictions_runs:每次运行一行,report_json携带完整ProbeReport供回放,是trend子命令与 doctorcontradictions检查的数据源,索引ran_at DESC。
缓存默认 TTL 30 天(--no-cache与--refresh-cache分别用于基准测试与运行前清扫)。缓存键形态、类型守卫(v1 形状的contradicts: boolean行会在 v2 守卫下判为 miss,与 prompt_version 过滤形成双重保险)均在 cache.ts 实现。
相关文档与下一步
- 成本纪律与推荐夜间节奏、趋势跟踪工作流:见 docs/eval-bench.md;
- 探针报告还接入了 doctor 检查(
doctor的 contradictions 面)与 synthesize 阶段的提示词信息块,形成"探测 → 呈现 → 处置 → 追踪"闭环。
运行这条探针的正确姿势总结:固定一个查询集(--queries-file),每晚以同一模型与参数跑run,用trend观察 Wilson 区间随时间演化;PROMPT_VERSION提升后注意一次性重判成本;对review --severity high输出的resolution_command逐条人工确认后粘贴执行。记住:探针输出的是数据与建议命令,从不自动改写大脑——行动与否、如何行动,始终由操作者决定。
【免费下载链接】gbrainGarry's Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考