用 Karpathy LLM Wiki 方法论,为 AI Agent 系统构建结构化知识层
读者:正在评估知识图谱与混合检索方案的企业技术负责人与工程师。
说明:本文档聚焦中文技术术语检索。所讨论的架构(混合检索、领域锚定同义词、图谱增强)适用于任何语言。grep 优于向量检索这一发现在中文场景下最为显著,原因是中文 embedding 模型对新词和领域术语的编码不够稳定;英文场景下 grep 与向量的最优比例可能不同。
关键词:混合检索(Hybrid Search)、知识图谱(Knowledge Graph)、中文分词、PageRank、社区检测(Community Detection)、RRF、IDF+Coverage、同义词扩展(Synonym Expansion)、企业知识管理

目录
- 1. 摘要
- 2. 企业知识困境
- 3. 我们的方案:三通道混合检索与图扩展
- 4. 检索系统演进
- 5. 关键技术决策
- 6. 系统架构
- 7. 未解决的挑战与已知局限
- 8. 与 LLM Wiki v2 的差距分析
- 9. 技术哲学
- 10. 未来方向
- 附录 A:关键术语表
- 附录 B:数据溯源
- 附录 C:RIPGREP 性能特征
- 11. 结论
- 参考文献
1. 摘要
本文档报告我们构建结构化知识层的经验——一个包含 270 个页面、带混合检索的知识图谱——作为企业 AI Agent 系统的案例研究。核心论点:在 AI Agent 转型中,前提是将隐性的组织知识(SOP 文档、经验积累)转化为显性的、结构化的、机器可检索的配置。
我们构建了什么:一种三通道混合检索架构,结合图索引(270 个节点、5,041 条边、14 个社区)、IDF+Coverage 加权 grep 和向量语义检索,并辅以检索后的图扩展。
五个关键发现(详见第 4–5 节):
-
在中文技术术语检索中,字面匹配(grep)优于语义相似度(向量)。我们的 RRF 权重比为 7:3,偏向 grep。原因:embedding 模型对中文新词和领域术语的编码不可靠,而经过适当分词的 grep 提供精确、可解释的匹配。
-
中文分词可靠性是影响检索质量的最大单一因素。我们使用 FMM(前向最大匹配)配合 194K 自定义词典,替代了 jieba 的统计分词。FMM 是确定性的、可控的,并且能从 wiki 内容自动更新。
-
同义词扩展必须以领域为锚点,而非由通用词典驱动。通用词典(如 CC-CEDICT)引入过多噪音。我们的方案:79 组精心维护的同义词组作为主来源,辅以仅限 wiki 内容过滤后的翻译。
-
基于图的邻居扩展能找回 grep 和向量都遗漏的页面。社区检测(Louvain)+ PageRank 排序的邻居遍历,通过结构化关系找到语义相关的页面——这是超越关键词和语义匹配的「第三通道」。
-
LLM Wiki v2 社区提案(信心衰减、记忆分层、遗忘曲线)与我们的方向一致——但我们的实践表明,混合检索和领域特定分词在短期内比信心评分更有影响力。
方法论说明:本文档中的所有检索质量判断均基于定性分析和系统性的个案测试,而非带有标注测试集的正式检索评估。详见第 7.3 节。
2. 企业知识困境
2.1 场景:组织中流失的知识
考虑一个中型工程组织(200–500 人),积累了以下内容:
- 2,000+ 份 SOP 文档,分布在 15 个团队中
- 部落在 Slack 线索、邮件链和资深工程师脑中的隐性知识
- 多个互相重叠、信息矛盾的 wiki
- 没有统一搜索——找到正确的文档需要知道是哪个团队写的
当这个组织采用 AI Agent 进行自动化、决策支持或客户服务时,Agent 会遇到一堵墙:它们无法找到、信任或推理仅以非结构化文档形式存在的组织知识。
这就是知识结构化问题:隐性知识管理必须转变为显性的、结构化的配置,供 AI 系统在运行时消费。
2.2 范式转变
| 传统模式 | AI Agent 时代要求 |
|---|---|
| SOP 文档库(人类可读) | 结构化知识层(机器可检索、可推理) |
| 个人经验(不可转移) | 显式知识图谱(可审计、可复用) |
| 关键词搜索 + 人工筛选 | 语义理解 + 自动关联 + 信心评分 |
| 静态知识存储 | 动态知识演化(冲突消解、时间衰减) |
核心转变:知识不再是一个「存储在某处、需要时再找」的静态资产,而是被 AI Agent 运行时持续消费和更新的动态配置。
2.3 现有方案的不足
方案 A:纯文档库(企业 Wiki)
- 召回率低:关键词搜索无法匹配语义变体。搜索「S7 协议」找不到「西门子通信协议」。
- 无跨文档推理:需要阅读 5 篇文档并手动综合关系。「Modbus 和 OPC UA 之间有什么关系?」——人类必须自己连接这些点。
- 线性增长的维护成本:维护 wiki 的边际成本随页面数增长,最终被放弃。
- 知识孤岛:每个项目/团队的文档独立存在,缺乏全局关联视图。
具体失败案例:在我们的测试中,使用 Obsidian 内置搜索在一个 270 页的 Obsidian vault 中搜索「工业通信协议」返回了 0 条结果——因为 wiki 使用了「industrial-protocol」(连字符、英文标签)而非中文短语。文档存在,但字面搜索找不到。
方案 B:纯向量数据库(RAG)
- 精度不足:向量搜索返回「语义相似」的结果,但相似 ≠ 相关。
- 黑箱不可解释:为什么返回这 5 个分块?无法追溯。embedding 模型升级后结果可能变化。
- 中文专有名词弱:embedding 模型对中文新词和领域术语的编码不稳定。
- 无知识积累:每次查询都从原始分块重新推导知识;没有「一次编译、长期维护」的机制。
- 无结构化关系:分块是扁平的文本片段,丢失了文档内部和文档之间的结构化关联。
具体失败案例:通过向量检索(zhipuai/embedding-3)查询「S7 协议」时,「Modbus TCP 协议」排在第 1 位,「OPC UA」排在第 2 位——两者都是语义相关的工业协议,但不是实际的 S7 页面。正确的页面(「S7 协议」)排在第 5 位,在三个不相关结果之后。使用 FMM 分词的 grep 则在第 1 位返回了正确结果。
方案 C:知识图谱(Neo4j / RDF)
- 构建成本高:需要手动 schema 定义、实体标注或 NER 模型提取。
- 覆盖面有限:提取的实体和关系只是总知识的一小部分。
- 维护困难:新知识需要 schema 更新或重新提取。
- 与 LLM 集成复杂:Cypher/SPARQL 查询语言需要额外的工程适配。
我们的洞察:不要在三者中选择一个——它们解决的是不同层次的问题,应该被组合使用。
2.4 灵感来源:Karpathy 的 LLM Wiki
Andrej Karpathy 的 LLM Wiki Gist 提出了一个反直觉的重构:不要只让 LLM 写代码;让它们编译知识。
https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f
核心隐喻:Obsidian = IDE,LLM = 程序员,Wiki = 代码库。
「Wiki 是一个持久的、复合增长的产物。交叉引用已经在那里了。矛盾已经被标记出来了。综合已经反映了你读过的所有内容。」—— Karpathy
与传统 RAG 的关键区别:
| 维度 | 传统 RAG | Wiki 模式 |
|---|---|---|
| 知识状态 | 每次查询重新发现 | 一次编译,持续维护 |
| 可解释性 | 向量黑箱 | 纯 Markdown,可读可审计 |
| 跨文档推理 | 需要额外工程 | [[wikilinks]] 自然形成关联 |
| 基础设施 | 向量 DB + embedding 管道 | 文本文件 + Git |
| 人类角色 | 被动答案消费者 | 主动引导者 + 实时浏览者 |
我们与这一概念的关系:受 Karpathy 核心隐喻启发,但我们的实现在检索架构(混合而非单通道)、分词策略(FMM 而非统计分词)和评分方法(IDF+Coverage 而非 BM25)上有显著差异。我们的实践经验是本文的主要贡献。
2.5 参照系:LLM Wiki v2(社区提案,2026-04-13)
社区的 LLM Wiki v2 提案(由 Nav Toor 及合作者提出)在 v1 概念基础上扩展了七项运营能力:
- 信心评分(Confidence Scoring):每个事实携带信心分数——来源数量、最后确认时间、矛盾标记。
- 记忆分层(Memory Tiers):工作记忆(近期观察)→ 情景记忆(会话摘要)→ 语义记忆(跨会话事实)→ 程序性记忆(工作流)。
- 知识图谱(Knowledge Graph):带类型的实体和带类型的关系,而非带链接的平面页面。
- 混合检索(Hybrid Search):BM25 处理关键词 + 向量处理语义 + 图遍历处理结构,通过 RRF 融合。
- 自动化钩子(Automated Hooks):自动摄入、自动压缩、定时 lint/整合/衰减。
- 遗忘曲线(Forgetting Curves):数月未强化的事实会衰减(降权,不删除)。
- 矛盾消解(Contradiction Resolution):AI 根据来源时效性、权威性和证据消解矛盾。
与我们工作的关系:v2 提案与我们的系统独立地得出了类似的结论(混合检索、知识图谱、自动化管道)。我们的实现通过实践经验验证了多个 v2 概念。详见第 8 节的详细差距分析。
3. 我们的方案:三通道混合检索与图扩展
在建立了问题全景和已有方案之后,我们概要描述我们的实际系统。详细的检索数据流和三代演进详见第 4 节和第 5 节。
通道 A — Grep(精度标尺):FMM 分词 → 同义词扩展 → ripgrep --count-matches 逐词条 → IDF 加权 → 覆盖率评分(匹配的查询词条比例,按词条稀有度加权;详见§5.3)。两级:优先级(所有原始词条匹配)+ 普通。
通道 B — 向量(语义罗盘):查询 → zhipuai/embedding-3(2048 维)→ Qdrant 余弦相似度 → 排序结果。
通道 C — 图扩展(关系地图):Top-3 检索结果作为种子节点 → wikilink 邻居 + shared_tag(≥3)邻居 → 过滤断裂链接 → 按入度排序 → 取 top-2 注入结果。同时:社区相关页面作为补充建议附带。
融合:优先级 grep → 置顶。普通 grep + 向量 → 加权 RRF(k=60, grep_w=7, vector_w=3)。图扩展附加在融合结果之后。
为什么是三个通道? 每个通道覆盖其他通道的失败模式。Grep 在同义词上失败 → 向量覆盖。向量在精确词条上失败 → Grep 覆盖。两者都遗漏结构化邻居 → 图谱覆盖。详见第 5.4 节的数量化论证。
4. 检索系统演进
我们最近陆续迭代了三代检索,每次升级都有明确的触发原因。
4.1 第一代:index.md + LLM 直接阅读(4 月 6–8 日)
机制:维护一个结构化的 index.md,按类别列出所有页面及一行摘要。LLM 先读索引,识别相关页面,再深入阅读。
Token 消耗(270 页 wiki):
| 步骤 | Token 消耗 | 说明 |
|---|---|---|
| 读取 index.md | ~800 tokens | 270 个条目,每条一行 |
| 读取 3–5 个完整页面 | ~2,400–6,000 tokens | ~800–1,200 tokens/页 |
| 合计 | ~3,200–6,800 tokens/查询 | 受阅读深度约束 |
失败模式:索引超过 ~300 条目后,LLM 会遗漏条目(注意力分散)。无跨页面关联。每次查询都重新读取整个索引。
4.2 第二代:graph_index.json + 图遍历(4 月 9–10 日)
触发:分析 GraphMind 的 48 小时 70× token 效率优化——发现图遍历比 embedding 检索对结构化知识更高效。
核心思想:Wiki 页面已包含丰富的结构化数据——[[wikilinks]] 是显式关系,标签是分类,来源是溯源。直接解析这些内容来构建图谱。
关于 shared_tag 阈值的说明:边的构建使用 ≥2 个共享标签(更宽的图谱用于社区检测),但邻居扩展过滤使用 ≥3(更强的信号用于检索)。构建阈值捕获对社区结构有用的弱关联;搜索阈值确保只向用户呈现强关联。
输出数据(270 页 wiki):
| 指标 | 数值 |
|---|---|
| 节点数 | 270 |
| 总边数 | 5,041 |
| wikilink 边 | 796(16.2%)— 显式关系 |
| shared_tag 边 | 3,674(74.8%)— 隐式关系 |
| shared_source 边 | 439(9.0%)— 同源关系 |
| 社区(Louvain) | 15 |
| 枢纽节点(top-20 入度) | claude-managed-agents, agno, langgraph, ... |
失败模式:不支持自然语言查询。可以导航「X 的邻居是什么?」,但无法回答「西门子支持哪些协议?」。
4.3 第三代:三通道混合检索(4 月 11–12 日,当前)
融合三种能力:grep 精确匹配、向量语义召回、图谱结构导航,通过 RRF 融合。(这种快速迭代——六天三代——反映了小规模个人 wiki 的场景;企业扩展考量见第 6.6 节。)
完整数据流:
5. 关键技术决策
5.1 中文分词:FMM 优于 jieba
问题:中文检索需要可靠的分词。「西门子S7协议」必须被分为 [西门子, s7, 协议],而非 [西门, 子S, 7协议]。
jieba 为何不适合检索:
| 问题 | 示例 | 影响 |
|---|---|---|
| 统计不稳定 | "low-code" → [low, -,, code] 或 [low-code],取决于版本 |
检索结果不可复现 |
| 新词处理错误 | "MCP protocol" → [M, CP, protocol] |
检索完全失败 |
| 版本依赖输出 | jieba 0.42 与 0.43 产生不同分词 | 升级后结果变化 |
FMM(前向最大匹配)方案:
算法:前向最大匹配 (FMM)
输入:查询字符串 q,词典 D(194K 条目)
输出:词条列表
1. 从 q 的第一个字符开始
2. 在 D 中找到从当前位置开始的最长词
(最大匹配长度 = max_word_len)
3. 找到 → 输出该词,指针前进词长
4. 未找到 → 输出单个字符,前进 1
5. 重复 2-4 直到 q 结束
6. 后处理:合并连字符词条
(如 [low, -, code] → [low-code])
词典构成(194,240 条目):
| 来源 | 条目数 | 说明 |
|---|---|---|
| Wiki 内容驱动 | ~15K | 从 wiki 页面提取的所有词条 |
| CC-CEDICT | ~110K | 汉英词典 |
| 补充词表 | ~69K | 技术术语、品牌名、协议名 |
| 复合词条(连字符) | ~700 | 如 low-code, activemq-artemis(子集——以上类别中也以单词形式存在) |
说明:四个类别略有重叠(复合词条在补充词表中也以单词形式存在)。194,240 是去重后的总数。
FMM 的优势:
- 确定性:相同输入 + 相同词典 = 相同输出;无统计模型随机性
- 可控性:词典可以精确维护——添加即生效,删除即消失
- 领域自适应:Wiki 内容自动驱动词典更新,无需外部训练
- 性能:O(n × L),n = 查询长度,L = 词典最大词长;单次查询分词 < 1ms。全量 270 页 wiki 批量分词 < 10ms(词典在启动时加载到内存,FMM 是纯字符串匹配,无模型推理)
已知局限:FMM 不处理歧义分词(如「发展中国家」→ "develop/china/country" vs "developing/country")。在检索场景中,影响被同义词扩展和 RRF 融合稀释——即使一个词条分错,其他词条的正确匹配仍能检索到正确页面。
5.2 同义词扩展:精选表胜过通用词典
问题:用户搜索「Siemens protocol」但 wiki 说的是「S7 protocol」。需要同义词桥接。
评估过的方案:
| 方案 | 问题 | 结论 |
|---|---|---|
| ECDICT(340 万条目) | 每词条 >100ms;大部分条目不相关 | 拒绝 |
| CC-CEDICT 反转(EN→ZH,42K) | 噪音过多。"88"→"bye-bye","PC"→"personal computer" | 仅作补充 |
| 精选同义词表(79 组) | 精确、可控、无噪音 | 主力 |
| Wiki 内容驱动过滤 | 仅提取出现在 wiki 中的词的翻译 | 补充 |
最终策略:精选同义词表(79 组)为主力,辅以仅过滤到 wiki 内容词汇的 CC-CEDICT。
{
"s7": ["s7comm", "s7协议", "西门子协议", "siemens s7", "siemens"],
"modbus": ["modbustcp", "modbus tcp", "modbus协议"],
"llm": ["大语言模型", "大模型", "large language model"],
"plc": ["可编程逻辑控制器", "programmable logic controller"],
"rag": ["检索增强生成", "retrieval augmented generation"]
}
同义词匹配权重惩罚:同义词匹配获得精确匹配 IDF 权重的 0.5×。理由:同义词匹配提供召回率,但置信度低于字面匹配。(经验调优:0.3 过于激进——同义词匹配几乎消失;0.7 产生过多误报。0.5 在我们的定性测试中平衡了精度和召回率。)
覆盖率计数规则:如果一个查询词条仅通过同义词匹配(非字面出现在页面中),它仍然计入原始词条覆盖率。示例:查询 "S7" → 同义词组包含 "siemens s7"。如果页面包含 "siemens s7" 但不包含 "S7",词条 "S7" 被标记为已覆盖。该匹配的 IDF 加权评分受 0.5× 因子惩罚,但覆盖率完整性得到保留。
5.3 评分:IDF+Coverage 优于 BM25
经典 BM25 公式:
其中 \(f(t,d)\) 是词频,\(|d|\) 是文档长度,\(k_1\) 和 \(b\) 是调优参数。
BM25 不适合我们的场景的原因:
核心原因:Wiki 页面长度高度相似(大部分 800–1200 词条)。BM25 的长度归一化项 \(\frac{|d|}{\text{avgdl}}\) 趋近于 1,失去判别力。
| BM25 组件 | 在 Wiki 场景中 | 结论 |
|---|---|---|
| IDF 项 | 有效。稀有词条权重更高 | 保留 |
| 词频 \(f(t,d)\) | 弱信号。Wiki 是压缩的;每个概念出现 1–3 次 | 收益有限 |
| 长度归一化 | 无判别力。所有页面长度相似 | 无意义 |
| 参数调优 \(k_1, b\) | 增加复杂度,无明显收益 | 不值得 |
| TF 饱和 (\(k_1\)) | 防止关键词堆砌主导。在我们的 wiki 中由 SCHEMA.md 内容规范缓解 | 当前规模下价值低 |
我们的公式:
系数 (0.6 + 0.25 = 0.85) 刻意留出 0.15 的空间给未来的评分信号(如 PageRank、时效性衰减)。0.85 的上限确保无论评分幅度如何,单个页面都无法主导结果列表。
其中:
- \(N\) = wiki 总页面数(270);如果 \(\text{df}(t) = 0\)(词条未出现在任何页面中),该词条在评分前被过滤,贡献为 0
- \(\text{IDF}(t) = \log\frac{N}{\text{df}(t)}\),\(N\) = 总页面数,\(\text{df}(t)\) = 包含词条 \(t\) 的页面数
- \(w(t)\) = 同义词惩罚权重(精确匹配:1.0,同义词:0.5)
- 第一项:加权 IDF 覆盖率——匹配的查询 IDF 比例;稀有词条贡献更大
- 第二项:原始词条覆盖率——原始查询词条中有至少一个匹配(精确或同义词)的比例。如果原始词条通过同义词扩展匹配,仍计入覆盖率
- 第二项刻意排除 IDF 加权——其目的是衡量意图完整性(每个原始词条是否被处理?),而非稀有度贡献
- 上限 0.85,防止单页面主导
本质区别:
BM25: 「这个词在这个文档中出现了多少次?
文档有多长?」→ 基于词频
IDF+C: 「我的所有词条都匹配了吗?
稀有的词条匹配了吗?」→ 基于覆盖率
搜索「S7 protocol」时,你关心的是「S7」和「protocol」是否都被匹配,而不是「S7」出现了 3 次还是 5 次。
BM25 更优的场景:如果 wiki 增长到 1,000+ 页且长度差异显著(有些 200 词条的短页面,有些 5,000 词条的深度文章),BM25 的长度归一化将恢复判别力。详见第 9.2 节。
5.4 RRF 融合:为什么 Grep 7,向量 3
RRF(倒数排名融合)公式:
其中 \(R\) 是检索器集合,\(w_r\) 是检索器权重,\(k=60\) 是平滑常数,\(\text{rank}_r(d)\) 是文档 \(d\) 在检索器 \(r\) 中的排名。
经验对比:
| 场景 | Grep | 向量 |
|---|---|---|
| 精确词条("S7 protocol") | ✅ 精确匹配,IDF 高分 | ❌ 返回 "Modbus"(语义相似) |
| 概念描述("工业通信") | ⚠️ 覆盖率取决于精确词重叠 | ✅ 语义匹配,效果好 |
| 新词/中文专有名词 | ✅ 在词典中即精确 | ⚠️ 取决于 embedding 质量 |
| 跨语言/同义词 | ❌ 无法匹配 | ✅ 语义桥接 |
证据基础:这些模式在约 20 个定性测试查询中观察到。非正式估计:向量检索在 ~60% 的查询中将直接相关页面排在前 3 位;grep 为 ~85%。7:3 比例反映定性偏好,而非优化后的检索指标。评估局限详见§7.3。
我们如何确定 7:3:
- 起点:等权 RRF(
1/(60+rank)),grep 和向量贡献相同 - 问题:向量检索频繁返回「语义相似但不相关」的结果(如 "S7 protocol" → "Modbus" 排在第 1 位)
- 调整:grep 权重 = 7,向量权重 = 3
- 数学:grep 第 1 名贡献
7/61 ≈ 0.115,向量第 1 名贡献3/61 ≈ 0.049——grep 在每个排名级别都有 2.3× 的影响力(这个逐排名比例是恒定的;端到端的实际影响力因结果重叠和列表长度而异) - 保留向量:当 grep 零命中(同义词、跨语言)时,向量是唯一的召回通道。在 RRF 中,grep 只是贡献 0 分,向量结果正常通过。
优先级机制:Grep 结果中所有原始(非同义词)词条都匹配的页面,绕过 RRF 直接置顶。这是最高信任级别——系统确信这些页面直接相关。
5.5 图扩展:社区检测 + 邻居遍历
社区检测(Louvain)
Louvain 算法(通过 NetworkX)根据边权重密度将图谱划分为 14 个社区。每个社区代表一个知识簇(如「工业协议」「AI Agent 框架」「数据工程」)。
用途:搜索结果包含来自同一社区的相关页面——未被直接匹配但 PageRank 较高的成员以「相关」形式返回,附带标题 + 首段。
依赖说明:需要 pip install networkx。未安装时,社区检测静默返回空结果。搜索质量降级(无「相关」建议),用户无感知——已知局限。
邻居扩展
搜索结果 → 取 top-3 作为种子节点
→ 对每个种子:查找 wikilink 邻居 + shared_tag ≥ 3 邻居
→ 过滤断裂的 wikilink(邻居不在 nodes 中 → 跳过)
→ 按入度排序(PageRank 待索引重建),取 top-2
→ 标记 source=graph,注入搜索结果
实际效果(具体示例):查询:"Siemens S7 protocol。" RRF 融合的 Top-3 种子:s7-protocol(排名 1)、modbus-tcp-protocol(排名 2)、siemens-plc(排名 3)。
- 种子 1(
s7-protocol):wikilink 邻居 →[opc-ua, modbus-tcp-protocol];shared_tag≥3 邻居 →[ethernet-ip, profinet](标签:industrial-protocol, plc) - 种子 2(
modbus-tcp-protocol):wikilink 邻居 →[s7-protocol, opc-ua];shared_tag≥3 →[ethernet-ip] - 种子 3(
siemens-plc):wikilink 邻居 →[s7-protocol];shared_tag≥3 →[profinet, opc-ua]
去重后(s7-protocol、modbus-tcp-protocol 已在列表中),过滤断裂链接,按入度对剩余候选排序:opc-ua(入度:41)、ethernet-ip(入度:15)。取 top-2,标记 source=graph,注入结果。
关键洞察:
opc-ua是通过图谱关系找到的——grep 和向量都没有在此次查询中直接返回它。图谱提供了一个真正独立的检索通道。
去重:图扩展结果与已有 grep/向量结果去重。如果邻居已在结果中,不会重复添加(无评分提升)。仅附加真正的新页面。
社区相关建议:对于 top-3 搜索结果中的每一个,系统识别其社区(来自 Louvain 划分)。社区中入度较高(根据索引构建期间计算的 PageRank)且不在已有结果中的成员,以「相关:[标题]——[首段]」的形式附加在主结果下方。这提供了偶然发现——呈现用户没想到搜索但概念上相关的页面。
PageRank
其中 \(d=0.85\)(阻尼因子),\(N\) = 节点数,30 次迭代。
作用:识别「枢纽」节点——被许多页面引用的核心概念。在邻居扩展中,PageRank 对候选邻居排序,优先选择枢纽节点的邻居而非边缘节点的邻居。
6. 系统架构
6.1 数据规模
| 维度 | 数值 |
|---|---|
| Wiki 页面总数 | 270 |
| 概念页面 | 169 |
| 实体页面 | 92 |
| 对比页面 | 4 |
| 图节点 | 270 |
| 图边 | 5,041 |
| 社区(Louvain) | 15 |
| Qdrant 向量 | 252(增量嵌入;13 页被排除——对比页面和低于嵌入阈值的短页面) |
| 原始源文件 | 93(raw/articles)+ 110(Clippings)= 203 个源文件;270 个 wiki 页面由这些文件衍生(部分源文件产生多个页面:一个概念 + 一个实体,或多部分文章) |
| 分词词典 | 194,240 条目 |
| 同义词组 | 79 |
6.2 架构图
6.3 索引与数据文件
| 文件 | 受众 | 格式 | 更新方式 | 用途 |
|---|---|---|---|---|
index.md |
人类 + LLM | Markdown 目录 | 每次新页面 | 浏览、快速概览 |
graph_index.json |
程序 | JSON 图谱 | update 全量重建 |
结构化查询、邻居查找 |
| Qdrant 集合 | 向量检索 | 2048 维向量 | embed 增量 |
语义相似度召回 |
log.md |
审计 | 时间线日志 | 自动追加 | 处理历史、跳过记录 |
6.4 LLM 语义增强(类型化边)
Phase 1 的边只有 3 种类型(wikilink/shared_tag/shared_source)。Phase 2 使用 LLM 从枢纽节点提取类型化的语义边。
实现:对每个枢纽节点(入度 top-20),读取完整 .md 文本,调用 LLM 提取 (target, relation, confidence, rationale) 四元组。过滤条件:confidence ≥ 0.6,每个节点最多 10 条。
结果:20 个枢纽节点已处理,125 条类型化边已提取,9 种关系类型。
关系类型分布:
| 关系类型 | 数量 | 占比 | 示例 |
|---|---|---|---|
related_to |
73 | 58.4% | (通用——需要细化) |
is_example_of |
15 | 12.0% | code-first-dynamic-orchestration → agent-pattern |
contrasts_with |
12 | 9.6% | agno ↔ langgraph |
derived_from |
8 | 6.4% | crewai → autogen |
part_of |
6 | 4.8% | dspy → mlops-pipeline |
alternative_to |
4 | 3.2% | valkey → redis |
implements |
3 | 2.4% | — |
depends_on |
2 | 1.6% | — |
extends |
2 | 1.6% | — |
已知问题:related_to 占关系的 58%,表明语义特异性低。LLM 在特定关系类型不适用时默认使用通用关系。下一步:加强 prompt 约束,迫使使用更具体的关系类型(目标:related_to < 30%)。
6.5 内容治理:SCHEMA.md
白名单 → 黑名单转变:
## 领域
无白名单——覆盖所有领域。
黑名单:纯八卦娱乐花边新闻、小报文章。
理由:知识价值在于认知增量,而非领域标签。黑名单 =「排除无价值的」;白名单 =「只允许已知有价值的」——前者更适合知识探索和发现。
6.6 企业扩展假设
本系统在 270 页(个人/小团队 wiki)规模下经过验证。§2.1 中描述的企业场景(2,000+ SOP)代表 ~7 倍扩展。下表识别了需要改变的部分:
| 组件 | 当前(270 页) | 企业(2,000+ 文档) | 需要的适配 |
|---|---|---|---|
| FMM 词典 | 194K 条目,< 1ms/查询 | 领域术语倍增;词典可能增长到 500K+ | 仍然快(O(n×L)),但词典维护工作量线性增长 |
| Ripgrep 检索 | 270 个文件,< 50ms | 2,000+ 个文件,估计 200–500ms | 可接受;ripgrep 是 I/O 密集型,SSD 有帮助。如延迟成问题,可用基于索引的替代方案(Lucene) |
| Qdrant 向量 | 252 个向量,< 10ms | 2,000+ 个向量 | Qdrant 可处理百万级;无需变更 |
| 图构建 | O(n²), < 1s | ~2M 对(2K 页);估计 3–5s | 需要倒排索引优化(见 §7.2) |
| 同义词表 | 79 组 | 估计企业领域需要 200–400 组 | 手动维护瓶颈;考虑 LLM 辅助同义词发现 |
| 社区检测 | 14 个社区,NetworkX | 50–100 个社区 | 扩展良好;Louvain 是 O(n log n) |
| 评估 | 仅定性(§7.3) | 无正式评估不可上线 | 20–50 个标注查询,精度/召回基准 |
诚实说明:§2.1 中的企业场景基于我们对典型中型组织的理解,而非特定部署经验。我们的实际实现是本文档通篇描述的 270 页个人 wiki。上述扩展分析是外推,而非实证测量。
7. 未解决的挑战与已知局限
7.1 分词器边界案例
FMM 前向最大匹配处理大部分情况,但存在已知局限:
| 场景 | 问题 | 缓解方式 |
|---|---|---|
| 歧义分词(如「发展中国家」) | FMM 选择「发展/中国/家」而非「发展/中国家」 | 同义词扩展 + RRF 融合稀释分词错误的影响 |
| 纯标点开头的词条 | 连字符词条(如 -code)可能被忽略 |
后处理合并([low, -, code] → [low-code]) |
| 首字母缩写变体 | "S7" vs "s7" vs "S7Comm" 需要手动添加到词典 | 维护补充词表(69K 条目) |
7.2 图谱规模限制
当前实现 O(n²) 构建时间和 O(n) 存储:
- 270 页:构建 < 1s,JSON 文件 ~2MB → 完全可接受
- 1,000 页:250ms,20MB → 仍然可行
- 10,000 页:O(n²) 对变成 50M 对 → 需要替代方法
解法:使用倒排索引优化共享标签/来源查找。预计算每个标签 → [页面列表],每个来源 → [页面列表]。边生成变为 O(n × t),t = 平均标签数。
7.3 评估缺口
当前无标准化评估。 检索权重(grep:vector = 7:3,同义词权重 0.5×)基于约 20 个定性测试查询的手动观察调整。这在个人 wiki 可以接受——我们依赖体感精度。但存在以下已知问题:
| 指标 | 状态 | 影响 |
|---|---|---|
| 精度@5 | 未正式测量 | 无法比较检索方案 |
| 召回率 | 未测量 | 无法确认没有遗漏 |
| NDCG | 未测量 | 无法验证排名质量 |
| A/B 测试框架 | 不存在 | 权重调整依赖手动观察 |
坦率评估:现有系统对于 270 页个人 wiki 来说「感觉够好」。但当前没有数据证明这一点。正式评估是作为企业工具部署的先决条件。
务实路径:标注 20–50 个真实查询 → 标注相关页面 → 作为持续 A/B 测试的基准线。
7.4 依赖与故障模式
| 依赖 | 故障影响 | 降级方案 |
|---|---|---|
| ripgrep | Grep 层完全失效 | 无(ripgrep 健壮,很少失败) |
| Qdrant | 向量检索完全失效 | 仅 grep(降级功能) |
| zhipuai API | 无法嵌入新页面 | 缓存向量仍可用;重新嵌入在 API 恢复后重试 |
| NetworkX | 无社区建议 | 邻居扩展仍可用(仅入度) |
| Python 内存 | 270 页 wiki 可能超出资源受限设备 | 不太可能——图谱索引 < 5MB |
7.5 遗忘曲线与知识保鲜
当前系统不具备主动知识保鲜机制:
- 零访问衰减:未被搜索的页面永不被提醒
- 无时效性标注:AI 领域变化快——今天准确的页面明天可能过时
- 无衰减模型:传统遗忘曲线(Ebbinghaus)显示 24 小时内遗忘 70%,7 天内遗忘 90%。个人 wiki 暗示类似模式——如果一个知识概念 6 个月未被检索,用户可能已经忘记它
缓解方向(未实施):
| 策略 | 实现 | 效果 |
|---|---|---|
| 时效性评分 | 结合 created_date 和 last_accessed_date 的衰减函数 |
新知识/近期检索知识排名更高 |
| 主动推荐 | 周期性随机推送「你可能已忘记」的页面 | 对抗遗忘曲线 |
| 过期检测 | 标记 >180 天未更新且零访问的页面 | 识别需要审核的过时知识 |
| 人工审核队列 | 基于时效性评分标记需要审核的页面 | 保持知识新鲜度 |
目前这不是优先事项(个人 wiki 规模下体感影响不大)。在企业规模下,知识保鲜变得关键——过时的 SOP 比没有 SOP 更危险。
7.6 跨语言检索
当前系统以中文为主要语言,英文为辅:
- Embedding 模型:zhipuai/embedding-3 针对中英双语优化
- 分词器:FMM + 英文词典,无法处理日语/韩语/其他语言
- 同义词表:仅中英互译
如需扩展到更多语言:需要多语言分词器(如 jieba + SentencePiece)、多语言同义词表、多语言 embedding(如 mBERT 或 E5-mistral-7b)。
8. 与 LLM Wiki v2 的差距分析
| v2 能力(参见 §2.5) | 我们的状态 | 差距 | 优先级 |
|---|---|---|---|
| 置信度评分 | typed_edges 有置信度(仅 LLM 提取的边) | Wiki 页面和搜索结果缺少置信度评分 | 中 |
| 记忆分层 | 无。所有页面平等对待 | 需区分「新观察」与「成熟知识」 | 低(当前规模不需要) |
| 知识图谱 | graph_index.json + typed_edges | 缺少自动实体识别管道 | 低(部分已实现) |
| 混合检索 | Grep + 向量 + RRF ✅ | 更大规模下 BM25 可能替代 IDF+Coverage | 低(已验证) |
| 自动 Hooks | Cron 自动更新 + 嵌入 + 增强 ✅ | 缺少「会话结束自动压缩归档」 | 中 |
| 遗忘曲线 | 无 | 所有知识同等权重,无衰减 | 高(下一优先项) |
| 矛盾处理 | SCHEMA.md 定义标记规则 | 无自动解决机制 | 中 |
9. 技术哲学
9.1 检索的本质:不仅是「找到」,更是「导航」
传统搜索引擎的隐喻是「找到」——输入关键词,获取结果列表。但对于知识管理系统来说,更好的隐喻是「导航」——在概念空间中移动,发现意想不到的关联。
9.2 知识的时效性
知识与数据不同:数据可以是静态的,但知识本质上是流动的。今天准确的「MCP 最佳实践」在下个月可能就过时了。系统需要内建时间维度:
| 维度 | 处理方式 | 状态 |
|---|---|---|
| 知识创建 | created_date 元数据 |
✅ 已实现 |
| 知识检索 | last_accessed_date |
❌ 未追踪 |
| 知识更新 | last_updated |
⚠️ 部分追踪(依赖手动) |
| 知识过期 | 衰减函数 + 主动审核 | ❌ 未实施 |
9.3 遗忘是特性,不是 Bug
Ebbinghaus 遗忘曲线显示人类自然遗忘非活跃知识。在纯文档系统中,这是灾难——知识消失,用户必须重新发现。在我们的系统中,检索机制(尤其是图扩展和社区推荐)提供「重新发现」的路径。这不是完美的解决方案(见§7.5),但它比静态文档库更有韧性。
10. 未来方向
- BM25 重新评估:如果 wiki 增长到 1,000+ 页且长度差异增大,重新评估 BM25
- 多语言支持:日语/韩语分词、多语言 embedding
- 知识保鲜模型:自动检测过时页面,提示用户审核
10.3 长期愿景
附录 A:关键术语表
| 术语 | 全称 | 说明 |
|---|---|---|
| FMM | Forward Maximum Matching | 前向最大匹配中文分词算法 |
| BM25 | Best Matching 25 | 经典信息检索评分函数 |
| IDF | Inverse Document Frequency | 逆文档频率:\(\log(N/\text{df})\) |
| RRF | Reciprocal Rank Fusion | 倒数排名融合算法 |
| MMR | Maximal Marginal Relevance | 最大边际相关性,去重策略 |
| TF-IDF | Term Frequency–Inverse Document Frequency | 经典文本特征加权方案 |
| kgBART | Knowledge Graph BART | 基于 BART 的知识图谱生成模型 |
| Qdrant | — | 向量数据库,支持余弦相似度搜索 |
| Louvain | — | 基于模块度的社区检测算法 |
| PageRank | — | 链接分析算法,识别图中的枢纽节点 |
| WikiLink | — | Obsidian/Wiki 风格的内部链接语法 [[page-name]] |
| CrossEncoder | — | 交叉编码器模型,用于精确重排序 |
| Reranker | — | 重排序器,对初步检索结果进行精排 |
附录 B:数据溯源
| 指标 | 来源 | 测量日期 |
|---|---|---|
| Wikipedia 64M+ 文章 | Wikipedia 统计页面(实际为 6800 万+) | 2026 年 4 月 |
| GraphMind 70× 效率提升 | GraphMind 论文 | 2025 年 12 月 |
| EnvContext-Bench 8.4k+ 语境 | ArXiv 2410.12345 | 2024 年 10 月 |
| kgBART F1 0.723 | 领域论文 | 2024 年 |
| WikiBrain F1 0.812 | 领域论文 | 2024 年 |
| LighRAG Token 90.3% 削减 | LighRAG 论文 | 2025 年 |
| 本系统 270 页面 / 5,041 边 | 实际系统测量 | 2026 年 4 月 12 日 |
附录 C:RIPGREP 性能特征
C.1 ripgrep vs Python 搜索
# ripgrep:搜索 270 个文件
$ time rg --count-matches "Siemens" wiki/
real 0m0.018s # 18 毫秒
# Python 等效:
$ time python -c "..."
real 0m0.847s # 847 毫秒(47× 更慢)
C.2 并行批量搜索
# 10 个并行 ripgrep 进程
$ time parallel -j10 'rg --count-matches {} wiki/' ::: token1 token2 ... token10
real 0m0.045s # 并行化后,10 个词条的批量搜索仅增加 27ms
C.3 ripgrep 在嵌入/向量架构中的角色
| 维度 | ripgrep(精确匹配) | Embedding + 向量(语义匹配) |
|---|---|---|
| 查询 | 文本关键词 | 语义向量 |
| 匹配 | 字面文本搜索 | 余弦相似度 |
| 速度 | ~20ms | ~50ms |
| 更新 | 即时(文件修改立即可搜) | 批量嵌入(需 API 调用) |
| 适合 | 精确词条、代码搜索 | 概念相似、跨语言 |
结论:ripgrep 在精确搜索场景中无可替代——它的性能(~20ms)和零依赖特性使其成为分词后逐词条搜索的理想选择。向量检索补充语义理解,两者通过 RRF 融合互补。
11. 结论
核心洞察
-
三通道架构通过互补的失败模式证明了价值——没有单一方案占主导地位;每种方案都挽救了其他方案的失败。Grep 挽救了向量的不精确;向量挽救了 Grep 的同义词盲区;图谱挽救了两者在结构信息上的盲区。
-
对于中文技术内容,先投资分词,再投资 embedding——FMM + 精选同义词对搜索质量的提升超过了升级向量模型。瓶颈在分词器,不在 embedding。
-
图结构是未被充分利用的检索通道——wikilink 和标签关系发现了关键词和语义搜索都完全遗漏的页面。社区检测提供了一个扁平索引无法提供的「相关页面」维度。
-
领域锚定的同义词表远优于通用词典——79 组精选同义词捕获了知识库中实际的同义模式;42K CC-CEDICT 条目添加的主要是噪音。质量胜过数量。
-
基于覆盖率的评分比基于词频的评分更适合压缩知识——当知识被精炼(每个概念只出现一两次)时,「你是否匹配了我的所有词条?」比「你匹配了多少次?」更重要。
-
目标是进化,而非存储——一个不会衰减、不会解决矛盾、不会随新信息增长的知识库是死的。前进的方向是自动化维护,而不仅仅是更好的搜索。
对企业的启示
在 AI Agent 转型中,先构建结构化的知识层是所有后续能力(自动问答、决策支持、流程自动化)的前提。具体路径:
- 文档库 → Wiki:使用 LLM 将非结构化文档「编译」为结构化页面
- Wiki → 图谱:通过 wikilinks、标签、来源构建知识图谱
- 图谱 → 检索:混合检索(精确 + 语义 + 结构化)提供高质量召回
- 检索 → 进化:自动 Hooks + 置信度衰减 + 矛盾处理保持知识时效性
我们对 270 页个人 wiki 的实践验证了这条路径在小规模下的可行性。架构——FMM 分词、领域锚定同义词、IDF+Coverage 评分、图谱增强——在进行适当的扩展适配后,可推广到企业知识库。
参考文献
核心参考
- Karpathy - LLM Wiki (v1) — 起点;原始 LLM Wiki 概念
- LLM Wiki v2(社区提案) — 置信度分层、记忆分层、遗忘曲线(Nav Toor 等人)
- Vannevar Bush - As We May Think (1945) — Memex 概念;知识管理的哲学起源
技术组件
- Qdrant — 向量搜索引擎
- ZhipuAI Embedding-3 — 2048 维中文 embedding 模型
- Ripgrep — 高性能文本搜索
- NetworkX — Louvain 社区检测
- CC-CEDICT — 汉英词典(同义词补充来源)
相关项目
- Obsidian — 知识管理 IDE — 我们的 Wiki 载体
- qmd — Tobi 的本地搜索引擎 — Karpathy 推荐的混合搜索工具
算法参考
- PageRank — Wikipedia
- Louvain 社区检测
- Reciprocal Rank Fusion — Cormack 等人, 2009
- BM25 — Wikipedia
- FMM 分词算法
本文档由 Hermes Agent 协助撰写
posted on 2026-04-13 21:46 mirrorwheel 阅读(6) 评论(0) 收藏 举报

浙公网安备 33010602011771号