检索(下):图谱与矛盾探针
第 4 课讲混合检索(关键词 + 向量)。本课讲 gbrain 检索的两个「增值层」:知识图谱和矛盾探针。但我不想按「gbrain 有什么函数」来讲——那样你读完还是不知道自己该怎么用、以及造自己的工具时该抄什么。所以本课始终从两个立场讲:你作为用户要做什么、gbrain 自动替你做什么;以及作为架构师,这些设计背后的决策是什么、为什么值得抄。
一、图谱:你写链接,gbrain 自动织网
你作为用户只需要做一件事:写笔记时,用 markdown 链接把相关笔记连起来。gbrain 认两种写法:
[特斯拉](companies/tesla) # 标准 markdown 链接,指向另一篇笔记的路径
[[苹果公司]] # Obsidian 风格双括号,指向同名笔记
gbrain 自动替你做的事:它扫描你所有笔记,把「A 提到 B」织成一张网(A→B 一条边)。你完全无感——不用建图、不用标注、不花一分钱 LLM 成本。它用的是确定性正则(extractEntityRefs)扫你写下的链接语法,连代码块里的内容都自动排除(不会把代码示例当链接)1。
为什么这么设计(架构师视角):gbrain 完全可以像很多知识工具那样,用 LLM 去「抽取实体、自动建图」。但它刻意不这么做。理由是 LLM 抽实体慢、贵、而且每次结果可能漂移(同一段文字今天抽出一个实体、明天抽不出)。而「用户写链接」是一个免费、稳定、用户已经自愿提供的结构信号——你写 [特斯拉](companies/tesla) 时,网络结构就已经在文字里了,gbrain 只是读出来。可抄点:如果你造自己的工具,优先利用用户已有的书写习惯做结构提取,别急着上 大模型(LLM);LLM 该用在「用户没提供的信号」上,而不是去重新发现用户已经明写的东西。
图谱给你什么能力:当你检索「特斯拉」,gbrain 不只做关键词/向量匹配,还能顺着网跳到相邻笔记(比如你写特斯拉时链了「电池供应链」那篇,就能把那篇也带出来)。这是普通搜索做不到的「顺藤摸瓜」。图谱是 markdown 之上的派生结构,重建 brain 时随 markdown 一起重生——它永远是「缓存」,不是「事实源」2。
一个必须厘清的概念:链接 ≠ 实体。 你之前纠结过这俩是不是一回事,答案是不是,而且这是造工具时最容易混的两个维度:
- 链接(link)= 你主动写的
[名字](路径);gbrain 还会读链接周围的动词,把这条边标成带类型的关系(works_at/invested_in/founded/advises/mentions),而非无类型的「页连页」3。- 实体(entity)= gbrain 用另一套机制(NER)从正文文字里抠出来的真实对象,比如「苹果公司」「马斯克」,标成
person/company类型;而且高频实体它还会自动建一篇空笔记 + 反向链接(extractEntities→ enrichment-service)4。 所以:链接是你写的语法,实体是 gbrain 从正文识别的对象。两套结构、两个维度。你做工具时若把二者混为一谈,图谱和实体索引会互相污染。
二、矛盾探针:gbrain 帮你揪出前后打架的观点
你作为用户遇到的真实痛点:投资笔记里常有前后冲突的观点——1 月写「Q1 看空特斯拉」,9 月写「Q3 看多特斯拉」。普通搜索你一检索,两篇都蹦出来,得自己在脑子里判断「哪个还作数」。gbrain 的矛盾探针专干这事:它找出你知识里「检索时会被一起召回、但观点可能打架」的配对,让 LLM 判断是不是冲突,把可疑的标出来给你复核。
命令是你主动跑的:
gbrain eval suspected-contradictions --query "特斯拉" --top-k 5 # 针对某个主题跑一轮
gbrain eval suspected-contradictions trend --days 30 # 看 30 天趋势
gbrain eval suspected-contradictions review # 交互式逐条复核最近一轮发现
gbrain 不替你自动跑——这是关键,也是我之前讲错的。全仓搜不到任何 定时任务(cron) / scheduler 调它;它是纯手动命令,或你的 agent 经 MCP 接口协议(让 agent 连上 brain 的标准协议)(find_contradictions)按需调用。而且 gbrain doctor(健康体检)会提醒你「你 7 天没跑探针了,去跑一下」——这反证它不是后台自动的5。为什么 gbrain 不默认自动跑(架构师视角):探针要烧 LLM 钱(judge 判每对配对)。如果默认自动、无端天天跑,你的 向量化(embedding)/LLM 账单会悄悄膨胀。gbrain 把控制权留给你——你决定什么时候跑、跑哪些主题、花多少预算(--budget-usd 封顶)。它只提供「让你能挂自动」的钩子(比如 cron 每天跑 gbrain dream 那种方式),但绝不偷偷自己跑。
探针怎么标、标出来你在哪能看到:
- 命令行直接打印:哪些页 HIGH 级冲突、冲突维度、给一条
resolution_command修复命令。 review子命令:交互式看最近一轮发现,逐条复核。gbrain doctor面板:把最近一次跑批结果读出来,HIGH 冲突直接列在体检报告里[^contra-体检命令(doctor)]。- 你的 agent(MCP
find_contradictions):对话中随时能查「我 brain 里关于特斯拉有什么矛盾」。
为什么这么设计(架构师视角,几个值得抄的决策):
-
采样检索配对,而非全量两两比对。N 篇笔记两两比对是 N² 爆炸。gbrain 的做法是:先跑 hybridSearch,对「会被一起召回」的配对才送 LLM 判——把成本压到可承受6。可抄点:做质量自检别全量跑,借检索的「什么会被一起召回」来定位可疑对,成本随检索规模线性增长而非平方增长。
-
date pre-filter:>30 天不算矛盾。两篇都带明确日期、且相差 >30 天,探针默认跳过——它假定「1 月看空、9 月看多」大概率是观点随时间正常演化,不是真打架。但这个规则有精妙的保护:任一方缺日期不跳(让 judge 决定);同一段话里先说看空再说看多(flip-flop)也不跳——这种才是真问题,照样标7。可抄点:时间维度是矛盾判定的关键上下文,别把「时间演进」误判成「bug」;但同一时空内的自我打脸必须抓得住。
-
判定分细类,不是二元。verdict 不是简单「矛盾/不矛盾」,而是:
contradiction(同时点的真冲突)/temporal_supersession(新观点取代旧观点,不是错误)/temporal_regression(指标倒退)/temporal_evolution(合理演进)/negation_artifact(judge 误读否定词)/no_contradiction。你一眼能分清哪是真问题、哪只是时间推移[^contra-判定结论(verdict)]。 -
持久缓存 + prompt_version 版本化。每对配对的 judge 结论存进缓存表,key 含
chunk hash + model_id + prompt_version + truncation_policy——判定逻辑(prompt)一变就自动 miss、重判,保证「当时为什么这么判」可追溯到具体版本8。可抄点:任何 LLM 评判都要缓存 + 版本化,否则结果不可复现、问题无法追溯。 -
它只标不改。探针给的是
resolution_command(修复命令),最终判断权和修改权在你——gbrain 不替你删笔记、不替你合并观点9。这是「AI 监督者」定位的体现:机器负责发现,人负责决断。
三、eval run-all 与 compare:检索质量可量化
检索本身也有量化闭环:gbrain eval run-all 跑全套评测,gbrain eval compare 对比两次运行(baseline vs 当前策略)10。这让「我改了检索逻辑到底变好还是变坏」可测量,而非凭感觉——架构师视角:任何检索/排序改动都应配可回归的评测,否则你永远不知道一次「优化」是真优化还是把某些查询搞坏了。
四、能力边界(监督者须知)
- 矛盾探针不自动跑:要你手动触发或自己挂 cron;不挂就永远不跑,doctor 只会提醒你。
- 图谱零成本自动织:只要你用链接语法写,gbrain 无感建图;你不写链接,就没有图可跳。
- 实体(NER)和链接是两回事:实体是正文识别的对象,链接是你写的、带类型的边;造工具时别混。
- 探针标的是「可疑」不是「定论」:给修复命令,不自动改;最终判断权在你。
- 成本是你可控的:
--budget-usd封顶,不跑则不花钱;gbrain 不会无端烧钱。
练习题
gbrain 的图谱(带类型的边 works_at/invested_in 等)主要靠什么建立?
在 gbrain 里,「链接」(你写的 [名字](路径))和「实体」(NER 抽出的 苹果公司/马斯克)是什么关系?
矛盾探针(suspected-contradictions)是 gbrain 后台自动跑的吗?
1 月写「看空特斯拉」、9 月写「看多特斯拉」,矛盾探针大概率怎么处理?
矛盾探针标出冲突后,gbrain 会怎么做?
参考出处
Footnotes
-
代码
src/core/link-extraction.ts(extractEntityRefs:先stripCodeBlocks去代码块,正则匹配 markdown 链接[Name](path)与 Obsidian 式[[path]]/ 裸 slug;代码块内 slug 排除;addLinksBatch写links表)。零 LLM 成本、确定性。 ↩ -
文档
docs/architecture/system-of-record.md§「System of record」(图谱是 markdown 之上的派生结构,重建 brain 时随 markdown 重生;与第 1 课「DB 是派生缓存」一致,图谱也是缓存非事实源)。 ↩ -
代码
src/core/link-extraction.ts(extractEntityRefs抽取 markdown 链接后,用正则WORKS_AT_RE/INVESTED_RE/ZH_INVESTED_RE等匹配链接周围的动词上下文,把边推断为works_at/invested_in/founded/advises/mentions;优先级founded > invested_in > advises > works_at > mentions;零 LLM 调用,与 README 所述 typed-edge graph 为同一机制)。 ↩ -
代码
src/core/enrichment-service.ts(extractEntities用正则从正文抠出person/company命名实体,按 Inc/Corp/Labs 等后缀粗分类;高频实体由 enrichment-service 自动建页 + 反向链接)。与 link-extraction 由链接上下文推断出的「带类型边」是两套独立结构(前者来自正文 NER,后者来自你写的链接语法)。 ↩ -
代码
src/commands/eval.ts+src/commands/eval-suspected-contradictions.ts(矛盾探针是手动子命令,全仓无 cron/scheduler 调它;src/commands/doctor.ts:8166提示「No probe runs in the last 7 days. Run gbrain eval suspected-contradictions …」反证非自动);src/core/operations.ts:4452经 MCPfind_contradictions暴露给 agent 按需调。 ↩ -
代码
src/core/eval-contradictions/runner.ts(先hybridSearch取每 query 的 top-K 召回,对「会被一起召回」的配对才送 LLM judge;pair 数 = queries × min(topK(topK-1)/2+topK, 50),成本随检索线性而非 N²)。 ↩ -
代码
src/core/eval-contradictions/date-filter.ts(DATE_SEPARATION_DAYS=30;规则:两 chunk 都带显式日期且相差>30天→SKIP;任一方缺日期→不跳(judge 决定);同一段含两个不同日期(flip-flop)→不跳。保守偏 false-negative,宁可多烧 token 不漏真矛盾)。 ↩ -
代码
src/core/eval-contradictions/cache.ts(buildCacheKey含 chunk hash +model_id+prompt_version+truncation_policy;PROMPT_VERSION一变即 miss 重判,verdict 存eval_contradictions_cache表,结果可复现可追溯到判定版本)。 ↩ -
代码
src/core/eval-contradictions/auto-supersession.ts与types.ts(每发现带resolution_command修复命令,gbrain 不自动删/改笔记,判断与修改权留给人)。 ↩ -
代码
src/commands/eval.ts(run-all→eval-run-all.ts;compare→eval-compare.ts;BaselineStrategy 调hybridSearch作基线对比,使检索改动可回归测量)。 ↩