PKM(个人知识管理)构建手册 - with AI
Intro
距离 PKM(个人知识管理)构建手册 - Emacs 居然已经一年多了。里面关于 PKM 和 AI 结合的部分许多已经过时了,今年也有了新的思路。
AI 发展的速度实在是太快,从 Cursor Tab 到现在的 vibe coding、Claude Code CLI,从 ChatGPT 到现在的 parallel agents,从 RAG 到 MCP,从 prompt 到 context engineering。
但不管 AI 如何发展,大语言模型的能力如何提升,LLM 始终是一个输入到输出的过程。从 RAG 到今年的 MCP 和 context engineering ,LLM 获取外部资源的方式和能力有了质的飞跃,context window 也越来越大。数据和数据质量一直是 LLM 的核心之一。
起初折腾 PKM 的目的之一就是构建和 LLM 交互的数据。正如 Notion 创始人 Ivan Zhao 说的,现在的 AI 产品就像酿酒。你无法完全控制结果,只能创造一个环境,让其自行发展。因此,它更像是一种酿造过程,你无法强迫 AI。产品是酒桶,数据才是水、小麦和酒花。数据的质量越高,酿出的酒就越香。
AI 使得从 0 到 1 的开始变得更容易:
Before AI, learners faced a matching problem: learning resources have to be created with a target audience in mind. This means as a consumer, learning resources were suboptimal fits for you:
在AI之前,学习者面临一个匹配问题:学习资源必须针对特定受众创建。这意味着作为消费者,学习资源对你来说是不理想的匹配:
You're a newbie at
$topic_of_interest, but have knowledge in related topic$related_topic. But finding learning resources that teach$topic_of_interestin terms of$related_topicis difficult.你是$topic_of_interest 的新手,但对相关主题$related_topic 有知识。但是找到以$related_topic 为基础教授$topic_of_interest 的学习资源是困难的。
To effectively learn
$topic_of_interest, you really need to learn prerequisite skill$prereq_skill. But as a beginner you don't know you should really learn$prereq_skillbefore learning$topic_of_interest.为了有效地学习 $topic_of_interest,你需要学习先决技能 $prereq_skill。但是作为初学者,你不知道你应该在学习 $topic_of_interest 之前先学习 $prereq_skill。
You have basic knowledge of
$topic_of_interest, but have plateaued, and have difficulty finding the right resources for$intermediate_sticking_point你对 $topic_of_interest 有基本知识,但已经停滞不前,并且在寻找合适的 $intermediate_sticking_point 资源方面遇到困难。
via: https://elroy.bot/blog/2025/07/29/ai-is-a-floor-raiser-not-a-ceiling-raiser.html#license
Manual
这个手册的重点是分享一些思路,我选择的这些实现只是参考,找到适合自己的方法才是这个手册的目的。PKM 基本的构建思路都在 PKM(个人知识管理)构建手册 - Emacs ,这个手册侧重和 AI 的整合。
Prerequisites
LLM API KEY,终端或 IDE,AI 和编程的基础知识,还涉及到一点 self-hosting。
Principles
基于 transformer 架构的 LLM 本质上就是一个概率生成器,因此 PKM with AI 总体的原则就是:让 LLM 提升自己的能力,帮助自己找到实现目标的最大可能性方案。
- 让 AI 帮助自己提出更好的问题,而不是 AI 速读
- 让 AI 去掉无关信息,更好的聚焦 key points
- 让 AI 评估、测试自己的思考和方案,更好的迭代
- 让 AI 生成路标和地图,而不是代替自己走完全程
Methods
Google NotebookLM 是 AI 和知识、文档交互的一个很完美的实现,但我始终认为这种层面的知识管理和交互并不能很有效的提升思维。
各种 RAG 类知识管理项目或软件,从本质上达到的效果是:根据特定的文档获得更准确的检索结果的生成。GraphRAG 效果确实可以比传统 RAG 更好,但它们始终是在学习资料中「检索」和「生成」。 它可以提升知识管理的效率,但这和思维的提升是两码事。
而且,只要是生成,就会降低准确性,对我来说,我不想在检索知识的时候,看到 LLM 一大堆废话,只想准确快速的找到我想检索的信息。虽然通过特定的 prompt 可以优化 RAG 生成内容和原文一致性,但问题的关键不在这里,生成其实是不必须的,关键在检索。而传统的文档检索其实就相当够用了,我一直在 Emacs 里使用正则和 tags 进行检索,这比丢给 LLM 20 个文档,等个 1 分钟要快太多了。
所以 PKM with AI,我的思路是:让 LLM 更好的配合 PKM 完成检索,生成路标和知识网点地图,把检索做到极致,去掉生成的部分。
因此我构建了 NoNotes ,用向量相似检索自动补全替代 RAG 生成。创造来源于思维的流动,而思维的流动就是联想,我在需要相关概念的时候,直接检索光标当前内容,在高度原子化的 PKM 里通过向量相似检索补全相关的概念和引用来源。
随着 context engineering 概念的兴起,我觉得 memory layer,数据接口,是 PKM 和 AI 结合比较合适的方向,例如 supermemory 这个项目,从它刚开始就一直在关注,现在发展的挺不错的。
用 Dify、n8n 构建 Workflow 也是很不错的选择,但是对于 PKM 我不太想增加太多的中间流程,也不想花太多时间为了 PKM 去学它们。
信息获取 - 输入
获取零散信息、新闻我一直通过 RSS,ReadWise 今年不打算续订了,转向了 Hoarder 。修改了一下 obsidian-clipper 用来在网页里高亮阅读保存到 Hoarder。在 Emacs 里写了一个小脚本,用来同步 Hoarder 的高亮和笔记到本地。还搞了个小玩意 RSS-CLI 。
我尝试过好几个 AI 聚合信息的项目,起初体验是挺不错,每天 AI 自动根据信息源生成摘要和简报,但时间久了,感觉就像是在吃预制菜,甚至是二手预制菜(许多信息源也就是 AI 生成的)。而且由于是用同一个总结模板 prompt 在总结不同类型的信息,很多时候总结的要点都不准确。
还是在 RSS 阅读器里自己看更有滋味。
再一个,与其让 AI 从海量信息源里找到自己感兴趣想要的,不如转向关注自己感兴趣的人和圈子,多接触到具体的人和事。信息茧房其实也就是自己更愿意相信或接受什么罢了,不同的圈子和阶层都固然会有信息茧房,要不然就不是圈子了。
我的 RSS 订阅源是日积月累慢慢发现的,这种发现只有用心看了文章才会有,AI 速读总结不出作者隐藏在字里行间里的思维以及写作的表达技巧。
文档、知识管理 - 迭代
- 笔记编辑器选择
Notion 其实一直是一个很不错的选择,特别是现在的 AI 功能挺好用的,上手也没有什么门槛。obsidian 也不用多说,现在有很多很好用的 AI 插件,obsidian 的生态是真的活跃。
但是用了 Emacs 之后,其他的就真不想用了。
今年从 org-roam 转为使用 denote ,org-roam 的 UI 从来就没怎么用过,除了可以对外展示一下有多少笔记之外,我觉得没啥用。
Emacs 里的 org 文档作为初始笔记,高度原子化之后整理到 tiddlywiki,形成常青笔记和原子笔记。
在原子笔记里,我又精炼了一部分,向量化之后储存在向量数据库,通过 NoNotes 补全。如果遇到哪个概念补全不出来,那就说明这个概念还没有掌握或着精炼的程度不够。
cloud.Llamaindex 和 NoNotes 的实现类似,可以 index 文档,提供 RAG 接口,Qdrant 这类向量数据库加上 MCP 也可以达到类似的效果。自动补全用自己喜欢的语言再写一个和系统交互的 API 就 OK 了。
笔记的同步就用 GitHub 就好了,笔记的版本管理和备份是很必须的,GitHub 的 commit 对于管理笔记太合适了。如果特别看重隐私或者讨厌 GitHub,可以自托管 Gitea 或 GitLab。
- 迭代
每个星期我会让 LLM 总结一下 PKM 里新增的内容,用 python 写了一个小脚本获取我 GitHub 仓库的 git diff,可以选日期范围,指定的仓库。PKM 丢在了 GitHub ,脚本会生成一个 MD 文档,包含 git commit 新增的内容,把文档丢给 LLM,让它提出几个相关的深度思考问题。
在 prompt 的设计上,我喜欢让 LLM 扮演一个批判者,用完全相反的视角分析我的观点,往往会有意想不到的惊喜。
也可以用 n8n 或者 GitHub Actions 自动完成这个过程,让 LLM 每周敲打一下自己。
迭代是我觉得 PKM 里最重要的一环,让 AI 帮助自己提出更好的问题,找到自己 知识的缝隙 。
- 其他和 AI 交互的工具
一个 Gemini CLI 管理笔记的例子:Gemini CLI 在半小时内整理了 400 个笔记,在各个主题之间建立了有意义的联系,重命名、整理,合理构建笔记集合,via: https://x.com/karminski3/status/1939502900503355669
这些 CLI 工具不拿来编程,做 PKM 管理也很合适。比起 chat UI,这些 CLI coding 工具提供了更底层、更便捷的 LLM 接口。
在 Emacs 里,我一直用 gptel,也很方便。其他和 vibe coding 相关的我记录在了 Vibe Coding Tips ,小工具记录在了 碎片知识学习 - with AI 。
笔记和写作 - 输出
PKM 知识管理只是学习的方法,永远不是目的。做知识管理很容易就会做成了管理知识,变成了折腾各种笔记工具,学习各种笔记软件,甚至被笔记软件或平台捆绑。这也是 Nonotes 想避免的,笔记不应该被笔记软件或者笔记载体局限甚至捆绑。
学习资料在变成笔记的过程中,在精炼和重述的时候,思维已经得到了一些锻炼,但这还远远不够。
PKM 的迭代一定要有表达和输出。如果你思考而不输出,你只是以为自己在思考 ,脑子里知道了,和写出来,讲出来,画出来是两码事。
关于表达,我想讨论的重点不是表达能力和华丽的写作技巧,而是:表达和输出是自我和外界信息的交互、反馈和对抗。用嘴说话和面部表情是我们最平常最熟悉的思维输出和表达,写作、音乐、绘画、编程其实也是一样,只是用了不同的载体和形式。
如果把人和人的思维比作计算机,外界信息给定一个输入,人给出输出(没有反馈和输出这里也算作输出的一种,也就是输出为 0),每一个时间、空间下自己的输出就构成了当下别人眼里的自己和自己认为的自己,而每一个当下就构成了自我和物质世界之间的信息交互,也就是所谓的存在。
我挺喜欢汪峰的 存在 ,名字和自我介绍从我们出生开始,到最后刻在墓碑上,没有多少人给了自己这个问题很好的答案:我该如何存在。
希望在 AI 的帮助下,我能够找到我的答案。
自定义 Emacs 函数
simple claude:
使用 shell,快速运行 Claude Code 执行 claude -p 指令回答一些简单的问题,把结果输出到单独的 buffer。有时候不想用 gptel 就用这个。可以替换成 Gemini 和 Codex。
;; simple claude code shell command (defun my/claude-shell-command (prompt) "Execute claude command asynchronously and display the output." (interactive "sClaude prompt: ") (let* ((output-buffer-name "*Claude Output*") (output-buffer (get-buffer-create output-buffer-name)) (shell-program (or (getenv "SHELL") shell-file-name)) ;; Use shell-quote-argument (command-str (format "claude -p %s" (shell-quote-argument prompt)))) (with-current-buffer output-buffer (setq buffer-read-only nil) (erase-buffer) (setq-local header-line-format (format "Claude Output for prompt: %s" prompt))) ;; (display-buffer output-buffer) ; Show the buffer immediately (message "Claude command running asynchronously...") ;; Start the async process (let ((process (start-process "claude-process" output-buffer-name shell-program "-lc" command-str))) ;; Set a function to be called when the process finishes (set-process-sentinel process #'my/claude-process-sentinel)))) (defun my/claude-process-sentinel (process _event) "Sentinel for the claude async process. Handles success and error cases." (when (memq (process-status process) '(exit signal)) (let* ((buffer (process-buffer process)) (exit-code (process-exit-status process))) (cond ;; Case 1: Process failed (non-zero exit code) ((/= exit-code 0) (kill-buffer buffer) (if (= exit-code 127) (message "Error: 'claude' command not found. Please ensure it's in your shell's PATH.") (message "Error: Claude command failed with exit code %d." exit-code))) ;; Case 2: Process succeeded but produced no output ((zerop (with-current-buffer buffer (buffer-size))) (kill-buffer buffer) (message "Claude command finished with no output.")) ;; Case 3: Success (t (with-current-buffer buffer (setq buffer-read-only t) (goto-char (point-min))) (display-buffer buffer) (message "Claude command finished."))))))
浏览器划词提问:
利用 org-protocol 和 JavaScript 对浏览器页面里的特定内容在 Emacs 里用 gptel 提问
(require 'org-protocol) (require 'gptel) (require 'server) (unless (server-running-p) (server-start)) ;; Handler for gptel queries from browser (defun my/gptel-org-protocol-handler (info) "Handle gptel query from org-protocol. INFO is the data passed by org-protocol." (let ((text (plist-get info :text))) (when (and text (not (string-empty-p text))) (let ((query (decode-coding-string (url-unhex-string text) 'utf-8))) (message "Received gptel query: %s" query) ;; Create or switch to gptel buffer (let ((buffer (get-buffer-create "*gptel-browser*"))) (with-current-buffer buffer (unless (eq major-mode 'gptel-default-mode) (funcall gptel-default-mode)) (gptel-mode 1) (goto-char (point-max)) (insert "\n\n--- From Browser ---\n") (insert query) (insert "\n") (goto-char (point-max)) ;; Send the query to gptel (gptel-send) (display-buffer buffer))))))) ;; Register the protocol handler (setq org-protocol-protocol-alist (append org-protocol-protocol-alist '(("gptel-browser" :protocol "gptel" :function my/gptel-org-protocol-handler)))) ;; JavaScript bookmarklet for browser (copy this as bookmark URL): ;; Basic version: ;; javascript:(function(){const selectedText=window.getSelection().toString().trim();if(!selectedText){alert('Please select some text first');return;}const pageTitle=document.title;const pageUrl=window.location.href;const fullText=`From: ${pageTitle}\nURL: ${pageUrl}\n\nSelected text:\n${selectedText}`;const encodedText=encodeURIComponent(fullText);const protocolUrl=`org-protocol://gptel?text=${encodedText}`;window.location.href=protocolUrl;})(); ;; Enhanced version with prompt for question: ;; javascript:(function(){const selectedText=window.getSelection().toString().trim();if(!selectedText){alert('Please select some text first');return;}const pageTitle=document.title;const pageUrl=window.location.href;const userQuestion=prompt('Optional: Add a question or context:');let fullText=`From: ${pageTitle}\nURL: ${pageUrl}\n\n`;if(userQuestion){fullText+=`Question: ${userQuestion}\n\n`;}fullText+=`Selected text:\n${selectedText}`;const encodedText=encodeURIComponent(fullText);const protocolUrl=`org-protocol://gptel?text=${encodedText}`;window.location.href=protocolUrl;})();
Thanks
如果这些内容对你有所帮助,我会很开心。
AI 带来的便利性一定会让人变懒。变懒可以,但变笨不行。
另见:
- 我的 Emacs 键位设置
- PKM(个人知识管理)构建手册 - Emacs
- PKM(个人知识管理)构建手册 - Nvim
- 碎片知识学习 - with AI
- 结构化知识工作流 - with AI
- AI 驱使下的信息向量化
- 从有形到无形 - NoNotes,知识库自动补全
- 稍后阅读新方案 Emacs-Hoarder
Relative reading:Vibe Coding Casino , Vibe code is legacy code ,DHH: Future of Programming