你有没有过这样的经历?收藏了 500 篇技术文章,买了 20 本电子书,存了无数 PDF 论文------然后每次需要某个知识点时,还是去 Google 重新搜一遍。
你的硬盘里躺着一座金矿,但你永远懒得去挖。不是因为懒,是因为没人帮你整理。
今天我们要聊的这个项目,就是来当那个"帮你整理的人"的。而且它不嫌烦、不喊累、不会做到一半跑去刷手机------它是一个 LLM。
一、故事的起点:为什么我们需要重新思考知识管理
1.1 一个古老而尴尬的困境
先说个扎心的事实:人类发明了搜索引擎、笔记软件、知识库工具,但绝大多数人的知识管理状态,依然是"收藏即埋葬"。
你把文章存进收藏夹的那一刻,心里想的是"以后再看"。但"以后"永远不会来。那些精心分类的文件夹、那些打了标签的笔记、那些花了半小时整理的思维导图------它们静静地躺在硬盘里,像一座座从未被发掘的陵墓。
原因很简单:维护知识库是一件反人类的事情。
更新交叉引用?烦。保持摘要最新?烦。发现新资料和旧资料矛盾了还要去改?更烦。人类的大脑天生就不适合做这种重复性的、低刺激的、需要持续注意力的"图书管理员"工作。
1.2 RAG 的承诺与局限
后来,RAG(Retrieval-Augmented Generation)出现了。它的思路很直接:你把文档丢进去,提问的时候它去检索相关片段,然后让 LLM 基于这些片段生成回答。
听起来很美好,但 RAG 有一个根本性的问题------它每次都是从零开始。
你问它一个问题,它去检索、拼凑、回答。你再问一个相关问题,它又从头检索一遍。没有任何积累,没有任何沉淀。知识不会随着使用而增长,就像一个人每天早上醒来都失忆了一样。
Karpathy 在他的 llm-wiki.md 里把这个问题描述得很透彻:
Ask a subtle question that requires synthesizing five documents, and the LLM has to find and piece together the relevant fragments every time. Nothing is built up.
翻译成人话就是:每次提问都要重新拼图,拼完就散,下次再拼。
1.3 LLM Wiki 的破局思路
LLM Wiki 项目的核心洞察只有一句话,但足够颠覆:
与其每次查询都从头检索,不如让 LLM 增量地构建和维护一个持久化的 Wiki。
知识只编译一次,然后持续更新,而不是每次查询都重新推导。
这就像是从"每次做饭都去菜市场买菜"变成了"在家里建一个食材库,有人帮你随时补货和整理"。你只需要负责想吃什么菜,剩下的交给那个不知疲倦的"厨房管理员"。
而这个"厨房管理员",就是 LLM。
二、技术架构全景:三层蛋糕的配方
2.1 三层架构:原始层、知识层、规则层
LLM Wiki 的架构可以用一个三层蛋糕来理解:
┌─────────────────────────────────────────────┐
│ Schema 层(规则与配置) │
│ schema.md + purpose.md --- 告诉 LLM 怎么干活 │
├─────────────────────────────────────────────┤
│ Wiki 层(LLM 生成) │
│ entities/ concepts/ sources/ ... --- 知识本体 │
├─────────────────────────────────────────────┤
│ Raw 层(不可变原始资料) │
│ raw/sources/ --- 你的 PDF、DOCX、Markdown... │
└─────────────────────────────────────────────┘
最底层是 Raw Sources------你上传的原始文档,PDF、Word、网页剪藏,什么格式都行。这一层是不可变的,LLM 只读不写。它是你的"事实来源"(source of truth),就像学术研究中的原始文献。
中间层是 Wiki ------这是 LLM 的"作品"。它读取你的原始文档,提取实体、概念、论点,生成结构化的 Markdown 页面,页面之间用 [[wikilink]] 交叉引用。这一层完全由 LLM 拥有和维护,你只负责看,不负责写。
最顶层是 Schema ------这是"规则书"。它告诉 LLM:Wiki 的结构是什么样的,有哪些页面类型,摄入新文档时要遵循什么流程。还有一个叫 purpose.md 的文件,定义这个 Wiki 为什么存在------它的目标、关键问题、研究范围。Schema 是结构规则,Purpose 是方向意图,两者缺一不可。
2.2 三大核心操作:Ingest、Query、Lint
整个系统围绕三个核心操作运转:
Ingest(摄入):你丢一份新文档进来,LLM 读取它,写一个摘要页面,更新相关实体和概念页面,在 index.md 里登记,在 log.md 里记录一笔。一份文档可能触及 10-15 个 Wiki 页面。
Query(查询):你问问题,LLM 搜索相关页面,读取它们,综合出一个带引用的答案。有价值的回答还可以"存档"回 Wiki,变成新的知识页面------你的探索也会让知识库变得更丰富。
Lint(检查):定期让 LLM 给 Wiki 做体检。找矛盾、找过时信息、找孤立页面、找缺失的交叉引用。就像代码的 linter 一样,保持知识库的"健康度"。
2.3 技术栈选型:为什么是这些
来看技术选型,每一层都有讲究:
| 层级 | 技术选择 | 为什么 |
|---|---|---|
| 桌面框架 | Tauri v2(Rust 后端) | 轻量、安全、跨平台,Rust 处理文件 I/O 和解析速度快 |
| 前端 | React 19 + TypeScript + Vite | 生态成熟,类型安全,Vite 开发体验极佳 |
| UI 组件 | shadcn/ui + Tailwind CSS v4 | 不锁定组件库,样式可定制性强 |
| 编辑器 | Milkdown(ProseMirror 内核) | WYSIWYG 编辑,支持数学公式插件 |
| 图谱可视化 | sigma.js + graphology + ForceAtlas2 | 性能好,支持力导向布局,交互流畅 |
| 向量数据库 | LanceDB(Rust 嵌入式) | 可选模块,Rust 原生,与 Tauri 后端天然契合 |
| 状态管理 | Zustand | 轻量、无样板代码、TypeScript 友好 |
| 文档解析 | pdf-extract + docx-rs + calamine | 纯 Rust 实现,无需外部依赖 |
这里有几个值得玩味的选择。
为什么用 Tauri 而不是 Electron? 因为 Electron 打包一个 Hello World 就要 150MB,而 Tauri 利用系统自带的 WebView,安装包可以小到 10MB 以下。对于一个需要频繁读写本地文件的知识库应用来说,Rust 后端的 I/O 性能也是关键优势。
为什么用 Zustand 而不是 Redux? 因为这个项目的状态管理需求虽然复杂(多对话、多项目、图谱缓存、摄入队列),但并不需要 Redux 那种重型架构。Zustand 用几行代码就能创建一个 store,没有 action、reducer、middleware 的概念负担,代码量少了一半。
为什么 LanceDB 是可选的? 因为不是所有人都需要向量搜索。对于中小规模的知识库(几百个页面),分词搜索 + 图谱扩展已经够用了。向量搜索是锦上添花,不是必需品------这种"渐进增强"的设计理念贯穿了整个项目。
三、核心实现深度剖析:拆解引擎盖下的精密齿轮
3.1 两步思维链摄入:先想清楚,再动手
这是整个项目最精妙的设计之一。
原始的 Karpathy 方案是单步摄入:LLM 一边读文档一边写 Wiki。听起来高效,但实际效果不好------因为 LLM 在"理解"和"写作"之间没有切换,容易写出流水账式的摘要,遗漏关键实体,或者忽略与已有知识的关联。
LLM Wiki 把它拆成了两步:
第一步(分析):LLM 阅读源文档 → 产出结构化分析
├── 关键实体、概念、论点
├── 与现有 Wiki 内容的关联
├── 与已有知识的矛盾和张力
└── Wiki 结构建议
第二步(生成):LLM 基于分析结果 → 生成 Wiki 文件
├── 带 frontmatter 的资料摘要页
├── 实体页面、概念页面 + 交叉引用
├── 更新 index.md、log.md、overview.md
├── 标记需要人工判断的审核项
└── 预生成深度研究的搜索查询
这就像写论文:先做文献综述和分析(第一步),再动笔写正文(第二步)。先想清楚要写什么,再写,质量自然比边想边写高得多。
在代码层面,摄入流程的核心在 src/lib/ingest.ts 中。这个文件是整个项目最复杂的模块之一,处理了从文件读取、内容清洗、图片提取、LLM 调用、页面合并到缓存管理的完整链路。
一个容易被忽略但非常关键的细节是 SHA256 增量缓存。每次摄入前,系统会先计算源文件内容的哈希值,如果和上次一样就直接跳过:
async function sha256(content: string): Promise<string> {
const encoder = new TextEncoder()
const data = encoder.encode(content)
const hashBuffer = await crypto.subtle.digest("SHA-256", data)
const hashArray = Array.from(new Uint8Array(hashBuffer))
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("")
}
这看起来是个小优化,但实际使用中能省下大量 LLM token 和时间。想象一下:你有 200 篇论文的知识库,重新打开应用时不带缓存的话,200 篇全部重新摄入,按每篇 0.5 美元的 API 成本算就是 100 美元。有了缓存,只有真正变化的文件才会被重新处理。
缓存还有一层防护:检查缓存记录的所有写入文件是否仍然存在于磁盘。如果有文件被手动删除了,缓存就会被判定为过期,触发完整重新摄入。这个细节避免了"幽灵条目"的问题------点击一个不存在的页面链接,比没有缓存更让人崩溃。
3.2 四信号知识图谱:不只是连点成线
大多数知识图谱工具只做一件事:把有链接的页面连起来。LLM Wiki 不满足于此,它构建了一个四维关联度模型。
在 src/lib/graph-relevance.ts 中,四个信号的权重定义如下:
const WEIGHTS = {
directLink: 3.0, // 直接链接
sourceOverlap: 4.0, // 来源重叠
commonNeighbor: 1.5, // Adamic-Adar 共同邻居
typeAffinity: 1.0, // 类型亲和
} as const
逐个拆解:
信号一:直接链接(权重 3.0) ------最直观的。页面 A 用 [[页面B]] 链接到了页面 B,它俩就有关系。双向链接各算一次。
信号二:来源重叠(权重 4.0) ------这个权重最高,有意思。如果两个页面都引用了同一份原始文档(通过 frontmatter 中的 sources: [] 字段),说明它们来自同一个"知识源头"。这比人为添加的链接更有意义------它反映了知识的客观来源关系,而不是作者主观的关联意图。
信号三:Adamic-Adar(权重 1.5)------这是一个经典的图论指标。如果页面 A 和页面 B 有很多共同邻居(都链接到了同一批页面),但它们之间没有直接链接,那它们很可能在讨论相关的话题。Adamic-Adar 的精妙之处在于:共同邻居的"信息量"与其度数成反比。一个连了 100 个页面的超级节点(比如 "AI")作为共同邻居,贡献很小;一个只连了 3 个页面的特定节点(比如 "Transformer 架构")作为共同邻居,贡献很大。
代码实现里这一步的计算是:
for (const neighborId of neighborsA) {
if (neighborsB.has(neighborId)) {
const neighbor = graph.nodes.get(neighborId)
if (neighbor) {
const degree = getNodeDegree(neighbor)
adamicAdar += 1 / Math.log(Math.max(degree, 2))
}
}
}
1 / Math.log(degree) 就是 Adamic-Adar 的核心公式------度数越高的邻居,贡献越小。
信号四:类型亲和(权重 1.0)------实体和实体之间更可能有意义的关系,概念和概念之间也是。但实体和查询页面之间的关系就比较弱了。项目定义了一个亲和度矩阵:
const TYPE_AFFINITY: Record<string, Record<string, number>> = {
entity: { concept: 1.2, entity: 0.8, source: 1.0, synthesis: 1.0, query: 0.8 },
concept: { entity: 1.2, concept: 0.8, source: 1.0, synthesis: 1.2, query: 1.0 },
// ...
}
这四个信号加权求和,就得到了任意两个页面之间的关联度分数。这个分数不仅用于图谱可视化(边的粗细和颜色),还用于查询时的图谱扩展------找到和搜索结果相关的更多页面。
3.3 多阶段检索管线:从关键词到语义到图谱
当你在聊天框输入一个问题时,背后发生的事情远比"搜索关键词"复杂。LLM Wiki 的检索管线分为四个阶段:
阶段一:分词搜索
对英文,做分词 + 停用词过滤。对中文,用 CJK 二元组分词------把"每个"拆成 ["每个", "个..."],虽然简单但 surprisingly effective。标题匹配还会额外加 10 分。搜索同时覆盖 wiki/ 和 raw/sources/ 两个目录。
阶段 1.5:向量语义搜索(可选)
如果用户开启了向量搜索(默认关闭),系统会调用任意 OpenAI 兼容的 /v1/embeddings 端点生成页面 embedding,存储在 LanceDB 中。余弦相似度能发现即使没有关键词重叠也语义相关的页面。
向量搜索的结果会和分词搜索结果合并:已有的匹配获得分数提升,新发现的结果被加入候选池。基准测试显示,开启向量搜索后整体召回率从 58.2% 提升到 71.4%------13 个百分点的提升,对于"找得到"和"找不到"的差别来说,非常可观。
阶段二:图谱扩展
把搜索结果作为种子节点,用前面讲的四信号关联度模型,做 2 跳遍历,发现更深层关联的页面。带衰减因子,越远的页面权重越低。
这一步是 LLM Wiki 区别于传统 RAG 的关键------它不只在文档中搜索,还在知识网络中"顺藤摸瓜"。
阶段三:预算控制
这一步解决一个很现实的问题:LLM 的上下文窗口是有限的。你不能把 500 个页面全塞进去。
src/lib/context-budget.ts 实现了一个精密的预算分配器:
┌─────────────────────────────────────────────────────┐
│ maxCtx (100%) │
├──────┬───────────────┬──────────────────┬───────────┤
│ idx │ pages │ history + sys │ resp │
│ 5% │ 50% │ ~30% │ 15% │
└──────┴───────────────┴──────────────────┴───────────┘
索引预算 5%(够列出所有页面标题),页面内容预算 50%,聊天历史和系统提示约 30%,响应预留 15%。页面按搜索 + 图谱关联度的综合分数排序,优先塞高分页面,直到预算用完。
这个设计有一个很巧妙的点:单页截断上限会随总预算缩放。以前是硬编码 30000 字符,不管你的上下文窗口是 4K 还是 1M。现在一个 1M 窗口的模型,单页可以放到更多内容,不会浪费预算。
阶段四:上下文组装
把选中的页面编号、附上完整内容(不是摘要),加上 purpose.md、语言规则、引用格式说明、index.md,组装成最终的系统提示。LLM 被要求按编号引用页面:[1]、[2],这样你能知道答案的依据来自哪个页面。
3.4 多 LLM 提供商支持:一个接口,千军万马
LLM Wiki 支持的 LLM 提供商列表读起来像一份 AI 军备竞赛的参展名单:OpenAI、Anthropic、Google Gemini、Ollama、DeepSeek、Qwen、Kimi、MiniMax、智谱 GLM、小米 MiMo......还有"自定义"选项,只要兼容 OpenAI API 格式的端点都能接。
实现这套兼容层的核心在 src/lib/llm-providers.ts。设计思路很优雅------定义一个 ProviderConfig 接口,每个提供商提供三个东西:
interface ProviderConfig {
url: string // 请求地址
headers: Record<string, string> // 请求头
buildBody: (messages, overrides?) => unknown // 构建请求体
parseStream: (line: string) => string | null // 解析流式响应
}
不同提供商的 API 差异很大。OpenAI 把采样参数放在顶层,Gemini 要嵌套在 generationConfig 里还要改名(top_p → topP,max_tokens → maxOutputTokens)。Anthropic 的 system prompt 不在 messages 数组里,而是单独的字段,还支持 cache_control 做缓存。图片的格式更是五花八门:OpenAI 用 image_url.url,Anthropic 分出 source.media_type,Gemini 用 inline_data。
代码里有一段注释特别能说明问题------关于为什么本地 LLM 端点(Ollama 等)需要特殊处理 Origin 头:
export function localLlmOriginHeader(): Record<string, string> {
return { Origin: "http://localhost" }
}
Tauri 的 WebView 会自动注入自己的 origin(macOS 上是 tauri://localhost,Windows 上是 http://tauri.localhost)。Ollama 的 CORS 白名单接受 tauri://* 但不接受 http://tauri.localhost------于是 Windows 用户会碰到 403。解决方案是强制设置 Origin: http://localhost,这个值在 Ollama 的默认白名单里。这种跨平台 CORS 的坑,不踩过根本想不到。
流式响应的解析也各有千秋。Gemini 2.5/3.x 的推理模型会在一个事件里交替输出 thought: true(思维链)和正常文本的 parts,旧代码只取 parts[0].text,后面的内容全丢了。修复方案是遍历所有 parts,跳过标记为 thought 的,拼接其余文本。
这些细节看起来琐碎,但正是它们决定了"能不能用"和"好不好用"之间的差距。
3.5 Rust 后端:脏活累活我来扛
前端负责交互和展示,真正的"体力活"------文件解析、格式转换、文件监听、HTTP 服务------全在 Rust 后端。
多格式文档解析 是最典型的"脏活"。src-tauri/src/commands/fs.rs 中的 read_file 函数是一个分发器,根据文件扩展名选择不同的解析路径:
-
PDF :用 pdfium FFI 提取文本和图片,结果缓存在
.cache/目录 -
DOCX:docx-rs 解析,保留标题、加粗斜体、列表、表格结构,转成 Markdown
-
PPTX:ZIP 解压 + XML 解析,逐页提取标题和列表
-
XLSX/XLS/ODS:calamine 库,正确处理单元格类型、多工作表,输出 Markdown 表格
这里有一个非常重要的设计决策:所有解析操作都通过 spawn_blocking 在 tokio 的阻塞线程池中执行。注释里解释了原因------PDF 和 Office 文档的解析涉及 FFI 调用和大量同步 I/O,可能耗时 10 秒以上。如果在 async 函数体里直接跑,会阻塞 tokio worker 线程,饿死同线程上的其他异步任务(比如导入进度 UI 的重渲染)。spawn_blocking 把工作移到专门的阻塞线程池,这是 Tokio 的标准实践,但很多 Tauri 项目都会忽略。
文件监听 是另一个精巧的 Rust 实现。src-tauri/src/commands/file_sync.rs 使用 notify crate(跨平台文件系统监听)实现了 Source 文件夹的自动监听。当你在应用外部往 raw/sources/ 文件夹里拖文件时,应用会自动检测并触发摄入流程。
监听器的设计考虑了很多边界情况:用 MD5 哈希做文件变更检测(不只是看 mtime),有重试机制(最多 3 次),有"应用自身写入忽略"机制(应用自己写文件时不会触发重复摄入,4 秒窗口),甚至在 Linux 上有定期全量重扫的兜底逻辑(因为 Linux 的 inotify 在某些场景下可能丢事件)。
本地 HTTP API 是 Rust 后端的重头戏。src-tauri/src/api_server.rs 使用 tiny_http crate 在 127.0.0.1:19828 启动一个 JSON API 服务器,支持以下端点:
GET /api/v1/health --- 服务状态(无需鉴权)
GET /api/v1/projects --- 项目列表
GET /api/v1/projects/{id}/files --- 文件树
GET /api/v1/projects/{id}/files/content --- 读取文件内容
GET /api/v1/projects/{id}/reviews --- 审核项列表
POST /api/v1/projects/{id}/search --- 混合检索
GET /api/v1/projects/{id}/graph --- 知识图谱
POST /api/v1/projects/{id}/sources/rescan --- 触发重新扫描
这个 API 服务器的设计体现了几个安全意识:
第一,只绑定 127.0.0.1,不对外网暴露。外部机器无法访问你的知识库。
第二,Token 鉴权。可以生成 API Token,也可以选择允许本机无鉴权访问(方便本地脚本调用)。
第三,路径白名单 。文件读取只允许 wiki/ 和 raw/sources/ 等公共路径,内部状态文件不暴露。
第四,速率限制。每秒 120 个请求的上限,最多 64 个并发请求,防止恶意调用打垮服务。
第五,panic 防护 。每个请求处理器都包在 catch_unwind 中,即使某个请求处理 panic 了,也不会拖垮整个服务器。
这些细节加在一起,让这个本地 API 既能被外部工具方便地调用,又不会成为安全漏洞。
3.6 Chrome 扩展:知识库的"入口"不止一个
LLM Wiki 还有一个 Chrome 浏览器扩展,让你一键剪藏网页内容到知识库。
扩展用 Manifest V3 开发,核心流程是:注入 Mozilla 的 Readability.js 提取文章正文(去掉广告、导航、侧边栏),再用 Turndown.js 把 HTML 转成 Markdown(支持表格),然后通过本地 HTTP API(端口 19827)发送给桌面应用,自动触发摄入。
extension/popup.js 里的内容提取逻辑值得一看。它先注入两个库到当前页面:
await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["Readability.js", "Turndown.js"],
})
然后执行提取函数,用 Readability 解析文章,用 Turndown 转换格式。Turndown 还自定义了表格规则------因为 HTML 表格转 Markdown 不是开箱即用的:
turndown.addRule("tableCell", {
filter: ["th", "td"],
replacement: (content) => ` ${content.trim()} |`,
})
turndown.addRule("tableRow", {
filter: "tr",
replacement: (content) => `|${content}\n`,
})
还有一个细节:自动过滤小于 10x10 像素的图片(通常是追踪像素),避免它们污染 Markdown 输出。
如果 Readability 提取失败(比如页面结构太特殊),还有 fallback 逻辑------直接取 document.body.innerText 作为兜底。这种"优雅降级"的思路贯穿了整个扩展的设计。
3.7 MCP 服务器:让 AI Agent 成为你的知识库管理员
MCP(Model Context Protocol)是 2024 年兴起的一个标准,让 AI Agent 能够以统一的方式访问外部工具和数据源。LLM Wiki 内置了一个 MCP 服务器,让 Claude Code、Codex 等 AI Agent 可以直接查询你的知识库。
MCP 服务器在 mcp-server/src/index.ts 中实现,暴露了 8 个工具:
| 工具名 | 功能 |
|---|---|
llm_wiki_status |
检查 API 是否可达,列出当前项目 |
llm_wiki_projects |
列出所有已知项目 |
llm_wiki_files |
列出项目文件 |
llm_wiki_read_file |
读取项目中的文本文件 |
llm_wiki_reviews |
列出审核项(默认未处理的) |
llm_wiki_search |
混合检索(关键词 + 向量) |
llm_wiki_graph |
查询知识图谱 |
llm_wiki_rescan_sources |
触发源文件重新扫描 |
设计上有一个很重要的决策:MCP 服务器不直接扫描项目文件夹,不复制应用的搜索和图谱逻辑。每个工具调用都是转发给本地 HTTP API 的。这意味着 MCP 客户端使用的是和应用完全相同的项目注册表、文件权限、搜索后端、图谱后端------不存在"两边逻辑不一致"的风险。
安装也极其简单,一行命令:
npx skills add https://github.com/nashsu/llm_wiki_skill.git --skill llm_wiki_skill
安装后,你可以在 Claude Code 里直接说"我的知识库里关于 Transformer 是怎么说的",Agent 就会调用 llm_wiki_search 工具搜索你的 Wiki,引用页面路径方便你核对。
四、实际应用场景:知识库的 N 种活法
4.1 学术研究:读论文的新姿势
想象你在做一个关于"大语言模型推理能力"的文献综述。
传统方式:下载 50 篇论文,用 Zotero 管理,手动写笔记,手动整理成表格,手动找论文之间的联系。一周后你已经忘了第一篇论文讲了什么。
用 LLM Wiki:把 50 篇论文 PDF 拖进去,LLM 自动提取每篇的核心贡献、方法、数据集、结果,生成实体页面(人物、机构、数据集)、概念页面(Chain-of-Thought、Self-Consistency、Tree of Thoughts),自动建立交叉引用。你在知识图谱里一眼就能看到哪些论文共同引用了 CoT,哪些用了相同的数据集。
当你问"CoT 和 Self-Consistency 的关系是什么"时,LLM 不是去 50 篇论文里重新检索,而是直接读取已经整理好的概念页面,给你一个带引用的综合回答。
更妙的是,图谱洞察功能会告诉你"惊奇连接"------比如某篇数学推理的论文和某篇代码生成的论文通过某个共同概念产生了意想不到的关联,这种跨领域的连接往往是创新的灵感来源。
4.2 技术团队的知识沉淀
技术团队最痛苦的事情之一:项目文档永远过时。
代码在变,架构在变,但 Wiki 没人更新。因为更新文档的 ROI 太低了------你花一小时更新文档,没人看,也没人感谢你。
LLM Wiki 可以改变这个动态。把 Slack 讨论记录、会议纪要、设计文档、代码评审记录都丢进去,LLM 自动维护一个持续更新的团队知识库。新来的同事不用再问"这个模块为什么这么设计"------他可以直接问 Wiki。
审核系统在这里特别有用:LLM 在摄入时发现"这个设计决策和三个月前的那份文档矛盾了",会标记为审核项,附带预生成的搜索查询,方便你追踪原因。是需求变了?还是之前的决策有误?
4.3 个人成长:给自己建一本"人生百科"
这是 Karpathy 原始方案里提到的一个场景,也是最有温度的一个。
把你的日记、播客笔记、读书摘录、健康数据、心理咨询记录都存进去。LLM 会帮你建立一张关于你自己的知识网络------你的目标如何随时间演变,你的情绪模式和什么因素相关,你读过的书之间有什么潜在联系。
purpose.md 在这个场景下特别重要。它不是冷冰冰的配置文件,而是这个 Wiki 的"灵魂"------你定义自己关心的关键问题、研究方向、演进中的论点。LLM 在每次摄入和查询时都会读取它,确保知识库的生长方向和你的个人目标一致。
这就像有了一个永远在线、永远耐心、记得你所有经历的"数字分身秘书"。
4.4 竞品分析:让 LLM 帮你盯着对手
把竞品的产品文档、新闻稿、用户评测、财报电话会议记录都丢进去。LLM 自动提取产品特性、定价策略、用户痛点、市场定位,建立竞品之间的对比页面。
当竞品发布新功能时,你只需要把新闻稿丢进去,LLM 会自动更新相关的对比表格,标记出和自家产品的差异。图谱洞察可能会告诉你"竞品 A 的新功能和你三个月前放弃的方向有关"------这种信息差可能就是下一个产品决策的关键输入。
五、设计哲学:代码背后的思考方式
5.1 "编译一次,持续维护" vs "每次重新检索"
这是 LLM Wiki 和传统 RAG 最根本的区别,也是整个项目的设计基石。
RAG 是"即时检索"------每次查询都从头搜索原始文档。优点是简单,缺点是没有积累。
LLM Wiki 是"预编译"------知识在摄入时就被提取、结构化、交叉引用,查询时直接使用编译好的知识网络。优点是查询质量高、知识有积累,缺点是摄入成本高(需要调用 LLM)。
这就像是解释执行和编译执行的区别。RAG 是解释器,每次都从头解释源代码。LLM Wiki 是编译器,先把源代码编译成机器码(Wiki),之后直接执行机器码。
trade-off 很明显:如果你只查询一次,RAG 更划算。如果你会反复查询、需要跨文档综合、希望知识随时间增长------LLM Wiki 的优势就越来越大。
5.2 "人类策展,LLM 维护"的角色分工
Karpathy 的原始方案里有一句话特别精辟:
Obsidian is the IDE; the LLM is the programmer; the wiki is the codebase.
翻译过来就是:Obsidian 是 IDE,LLM 是程序员,Wiki 是代码库。
人类的角色是什么?策展人 。你决定读什么、关注什么、问什么问题。LLM 的角色是什么?维护者。它做所有的脏活累活------总结、交叉引用、归档、记账。
这个分工之所以有效,是因为它顺应了人类和 LLM 各自的优势。人类擅长判断什么重要、提出好问题、理解上下文。LLM 擅长处理大量文本、保持一致性、不知疲倦地做重复性工作。
维护知识库之所以难,不是因为读和想难,而是因为"记账"难------更新交叉引用、保持摘要最新、标记矛盾。人类放弃 Wiki 是因为维护成本增长得比价值快。LLM 不会感到无聊,不会忘记更新一个交叉引用,可以一次性修改 15 个文件。Wiki 保持维护,因为维护成本趋近于零。
5.3 渐进增强:从简单到复杂的平滑路径
LLM Wiki 的一个重要设计原则是"渐进增强"------核心功能开箱即用,高级功能按需开启。
-
不配置向量搜索?分词搜索 + 图谱扩展已经能用。
-
不安装 Chrome 扩展?直接拖文件导入也行。
-
不开启 MCP?应用本身功能完整。
-
不配置 Deep Research?普通聊天和摄入照常工作。
这种设计降低了入门门槛。你不需要一开始就理解所有功能,先跑起来再说。随着使用深入,再逐步开启高级功能。
5.4 跨平台不是口号,是细节的堆砌
跨平台兼容性是 LLM Wiki 做得特别扎实的地方。不是嘴上说"我们支持三大平台",而是在代码里处理了大量平台差异。
路径规范化 :统一的 normalizePath() 函数在 22+ 个文件中使用,把 Windows 的反斜杠统一成正斜杠。这看起来是小事,但如果不做,Windows 上的路径在 JSON 里需要转义,在正则里需要转义,在 URL 里需要转义------到处都是坑。
Unicode 安全:中文文件名按字符切片而不是按字节切片。按字节切片会在多字节字符中间截断,导致崩溃。这种 bug 在纯英文环境下永远不会出现,但在中文环境下是定时炸弹。
关闭行为差异:macOS 上关闭按钮是隐藏窗口(应用后台运行,点 Dock 图标恢复),Windows/Linux 上是弹出确认对话框。这符合各平台的用户习惯------macOS 用户习惯关了就是藏起来,Windows 用户习惯关了就是退出。
六、未来趋势:知识管理的下一个十年
6.1 从"个人 Wiki"到"组织知识中枢"
LLM Wiki 目前定位是个人知识库,但它的架构天然适合扩展到团队场景。
想象一下:团队的每个成员都有自己的 LLM Wiki 实例,摄入各自的文档和笔记。通过某种同步机制(比如 Git),这些 Wiki 可以合并成一个组织级的知识网络。LLM 负责发现跨个人的知识关联------"小王上周的会议纪要和小李三个月前的设计文档讨论了同一个问题"。
MCP 协议的标准化让这种跨 Agent 的知识共享变得更加自然。不同人的知识库可以成为不同 Agent 的工具,Agent 之间可以互相查询对方的 Wiki。
6.2 多模态知识库的深化
目前 LLM Wiki 已经支持图片摄入------从 PDF 中提取内嵌图片,用视觉 LLM 生成描述。但这只是多模态的起步。
未来的方向包括:视频内容的自动转录和关键帧提取、音频的说话人分离和主题建模、代码仓库的结构化解析(不只是读 README,而是理解代码架构)。当知识库不再局限于文本文档,它就真正成为了"全息记忆"。
6.3 主动学习与知识发现
当前的 LLM Wiki 是被动的------你喂什么它吃什么。未来的方向是主动学习。
图谱洞察功能已经迈出了一步:它主动发现知识空白(孤立页面、稀疏社区),并建议你去做 Deep Research。未来可以更进一步:LLM 基于现有知识网络,主动推荐你应该读什么论文、关注什么新闻、追踪什么趋势。
这就像从"图书管理员"进化成了"研究助理"------不仅帮你整理已有的知识,还告诉你还缺什么知识。
6.4 知识的时效性管理
知识会过时。今天正确的结论,明天可能就被新研究推翻了。当前 LLM Wiki 的 Lint 功能可以发现矛盾,但时效性管理还可以更精细。
未来可能引入"知识置信度"和"时效标签"------每个知识点标注来源日期、引用次数、是否有矛盾证据。当新信息摄入时,系统自动评估对已有知识的影响,生成"知识更新报告"。
6.5 与 Agent 生态的深度融合
MCP 协议的出现让 AI Agent 生态迎来了爆发。LLM Wiki 作为一个本地知识库服务,天然适合成为 Agent 的"长期记忆"。
未来的 Agent 不再是"无状态"的------它们可以持久地访问你的知识库,在对话中积累上下文,跨会话保持一致性。LLM Wiki 的本地 HTTP API 和 MCP 服务器就是为这种融合准备的基础设施。
想象一下:你的 AI 编程助手不仅知道你当前项目的代码,还知道你三个月前读过的架构论文、上周的会议决策、你个人的编码偏好------所有这些都在你的 LLM Wiki 里,Agent 通过 MCP 随时查询。
七、上手实践:五分钟跑起来
7.1 安装
最简单的方式是下载预编译版本:
-
macOS :
.dmg(支持 Apple Silicon 和 Intel) -
Windows :
.msi -
Linux :
.deb或.AppImage
如果你是开发者,也可以从源码构建:
git clone https://github.com/nashsu/llm_wiki.git
cd llm_wiki
npm install
npm run tauri dev # 开发模式
npm run tauri build # 生产构建
前置条件:Node.js 20+ 和 Rust 1.70+。
7.2 配置
-
启动应用,创建新项目,选择一个场景模板(研究、阅读、个人成长、商业、通用)
-
进入设置,配置 LLM 提供商------填入 API Key 和模型名称
-
可选:配置网络搜索提供商(Tavily、SerpApi 或 SearXNG)用于 Deep Research
-
可选:开启向量搜索,配置 embedding 端点
7.3 开始使用
-
进入"资料源",导入文档(支持 PDF、DOCX、PPTX、XLSX、Markdown、图片等)
-
观察"活动面板"------LLM 自动构建 Wiki 页面,逐文件显示进度
-
用"聊天"查询你的知识库
-
浏览"知识图谱"查看页面之间的关联
-
查看"审核"处理需要你关注的项目
-
定期运行"Lint"维护 Wiki 健康度
7.4 进阶:接入 AI Agent
如果你想让 Claude Code 或 Codex 直接查询你的知识库:
-
在设置中开启 API + MCP
-
构建 MCP 服务器:
npm run mcp:build -
安装 Agent Skill:
npx skills add https://github.com/nashsu/llm_wiki_skill.git --skill llm_wiki_skill -
在 Agent 中直接提问:"我的知识库里关于 X 是怎么说的"
八、写在最后:当知识管理遇见 LLM
回顾整个项目,我最感慨的是它对"知识管理"这件事的重新定义。
过去几十年,知识管理工具的演进方向是"让人更方便地管理知识"------更好的编辑器、更强的搜索、更漂亮的界面。但 LLM Wiki 指出了另一条路:让 LLM 来管理知识,让人专注于思考和决策。
这不是工具的升级,是范式的转变。
从 Vannevar Bush 1945 年提出 Memex 的构想,到如今 LLM 让这个构想真正可行,中间隔了 80 年。Bush 当年无法解决的问题是"谁来维护那些关联线索"------现在 LLM 给出了答案。
LLM Wiki 项目的价值不在于它用了多么前沿的技术(虽然技术确实很硬核),而在于它把一个深刻的洞察------"知识应该编译一次并持续维护,而不是每次重新推导"------变成了一个普通人可以下载安装、开箱即用的桌面应用。
它不完美。摄入大量文档时的 API 成本不低,图谱可视化在页面数量过千时会有性能压力,多 LLM 提供商的兼容性维护是个无底洞。但它的方向是对的,实现是扎实的,而且------也许是最重要的------它让"维护一个知识库"这件事从反人类变成了令人愉悦。
下次你再看到一篇好文章想收藏的时候,也许可以换个思路:不是收藏了"以后再看",而是丢进 LLM Wiki,让它成为你知识网络的一部分。以后不用再"看",直接"问"就好了。
毕竟,知识的价值不在于拥有,而在于连接。而 LLM Wiki,就是那个帮你织网的人。
许可证:GPL v3.0
