筆記一直是程序員最重要的效率工具之一,幾乎每一代程序員都會試圖從自己的工作方式出發,開發或改造一套屬于自己的筆記系統,從最早的文本文件、Wiki,到 Word、印象筆記、Notion,本質上都是在解決同一個問題:如何在高強度的信息處理環境中,保留可用的思考結果。但現實是,筆記系統始終是不夠好的,尤其是對每天需要處理大量文字、代碼和決策的人來說。
過去幾年我一直使用 Notion 作為主力筆記工具,常見的做法是開一個表格,每條記錄帶時間戳、鏈接和上下文說明,處理 bug 時在 icon ??上放一只小蟲子,把所有線索、想法、日志和代碼都塞進去,直到有一天我連續在電腦前工作了將近十二個小時,回過神來發現當天已經產生了接近兩百條記錄。Notion 的每一條記錄本質上都是一個可以無限展開的頁面,而不是 Excel 里的一個單元格,即便每一頁只有兩三千字,二百頁也已經是四十萬字,這個規模早已遠遠超出人類在一天之內能夠閱讀、理解和消化的極限,而且這并不是一次性的極端情況,而是逐漸變成了常態。
正是在這種背景下,我在最近幾周明顯進入了一種奇怪的狀態:事情很多,但推進極慢,下意識回避工作,睡眠時間不斷拉長,再加上東岸連續的大雪,被困在家里幾天,又干脆出去玩了幾天雪,表面看像是怠工或逃避,但真實感受是一種深度的認知疲憊。與此同時,大語言模型變得異常強大,代碼、文檔、方案幾乎可以瞬間生成,但這種能力提升并沒有讓我成為一個更高效、更清醒的個體,反而帶來強烈的挫敗感,因為我發現這種狀態并不只發生在我身上,和一些朋友交流后,大家普遍都在經歷類似的認知過載:個人能力被工具放大到一定程度之后,隨之而來的不是解放,而是更嚴重的反噬。
工業化語言生產機
回頭看,問題集中體現在三個方面:第一,信息和文本的生成規模已經徹底超出人類的生理極限,這與個人能力強弱無關;第二,生成速度過快,導致根本記不住自己看過什么,LLM 必須持續回答、持續發散、持續給路徑,卻從不提供“到此為止”的信號;第三,也是最致命的,是隱性漂移,狀態完全依賴窗口維持,窗口不斷疊加,幾天之后再回看幾天前的代碼,已經無法理解當時的情境,同一個概念、腳本和索引在命名、路徑和 ID 上不斷漂移,vault_index、index_vault、inventory_index 之類的差異一路堆疊,幾周之后再看整個項目,只剩下一種徹底失控的感覺。
被雪困住的那幾天里,我反復思考這個問題,逐漸意識到這并不是個人管理能力的問題,而是機制層面的必然結果:LLM 本質上是一個 zero-shot universe,幾乎零成本生成內容,不會疲勞,不會猶豫,也不會做“是否值得繼續”的決策,prompt 只是軟約束,它沒有硬停機條件,它是一臺持續生產語言和符號的工業化機器,而人類的大腦并不具備相同的承載能力;漂移不是偶然,而是這種機制的必然產物,拉長上下文并不能解決問題,因為模型永遠只是在預測下一個 token,他要找不到最正確的答案他就給你猜一個;更重要的是,整個過程沒有責任主體,每天幾百上千次 prompt 強烈依賴當下環境,事后幾乎無法復現,也無法追責。
這是一個治理Governance的問題
正是在這種情況下,我開始意識到,問題并不在于“記得不夠多”,而在于在這個時代,繼續沿用“多記一點總有用”的傳統筆記假設,本身就是不可持續的。Notion 并不是沒有搜索功能,但當我真的需要解決一個具體問題、回溯自己過去的思考時,搜索出來的往往是成百上千條結果,其中大量內容彼此沖突、語境缺失、時間錯位,幾乎無法直接使用,搜索本身并沒有降低認知負擔,反而把問題放大了。也正是在這個背景下,在這個 repo 連續開發了幾天之后,我又一次嘗試去重新搭建自己的記錄體系——這種嘗試其實已經發生過很多次,絕大多數都以失敗告終,但這一次我明顯感覺到有些地方開始對路了,至少已經值得寫出來和別人分享。關鍵并不在于我是否找到了一個“更好的工具”,而在于我發現,這根本不是工具層面的問題,而是人在這個階段不可回避地遭遇了自身的生理與認知約束。一方面,我確實需要一個更貼合我個人工作方式的系統,但另一方面,更重要的是必須把注意力放回真正的核心問題上:這個問題既不是筆記,也不是程序,更不是靠一個軟件、一個庫就能一次性解決的工程問題,它的核心只有一個——知識的治理。
這是個Governance的問題。
簡單說,“知識的治理”是什么意思?從更大的尺度看,人類社會中幾乎所有真正困難的問題,本質上都是治理問題。政治是治理問題,國家如何被治理;法律是治理問題,立法、司法、執法如何分工與約束;憲法本身也是治理問題,不只是寫下來,而是如何被解釋、如何被遵守、如何保證它真的被遵守。把尺度縮小,團隊管理、制度設計、流程約束,同樣是治理問題,而不是工具問題。治理的核心從來不在于“有沒有信息”“能不能訪問”,而在于哪些東西被承認為有效、哪些判斷具有權威、哪些狀態可以進入長期記憶、哪些必須被限制、凍結或廢棄。
回到個人層面,所謂“知識管理”的真正難點也在這里:不是信息不夠,不是搜索不強,而是缺乏一套能夠裁決、約束和負責的機制。正因為如此,這個問題其實已經被無數程序員反復嘗試解決過,從早期的個人知識庫、標簽系統、到后來的雙鏈、圖譜、向量化檢索,再到 Obsidian、各種語義搜索和 AI 助手,它們解決的更多是“如何找到內容”“如何關聯內容”“如何生成內容”,但幾乎都默認了一個前提:內容一旦寫下,就天然具有價值,只要存得住、搜得到,就算成功。這個解決方案和各種開源項目到處都是,我自己也是借鑒了的。我現在也是把內容全部存成 .md文檔,obsidian vault可讀。But this is not the point!
在信息和生成能力遠低于今天的年代,搜到就賺到。但在一個內容可以零成本爆炸式生成的時代,這個前提本身已經失效了。真正缺失的并不是更聰明的搜索算法,也不是更復雜的表示方式,而是一套能夠回答“什么可以被承認為知識”“什么只是過程噪聲”“什么需要被追責”“什么必須可復現”“什么不允許悄然漂移”的治理結構。
所以,我說說我現在自己嘗試的這種方案,這個repo,雖然開始沒多久,但是我感覺這個方向很行。如果你有這個需求,也許我能啟發一些目前同樣困擾的人。
一個Sovereign Log, 簡單代碼,昂貴寫入,沒有接LLM,后期可以接,幫我定住我最核心的認知。
不是為了高頻記錄而設計的,恰恰相反,它刻意讓寫入變得昂貴、緩慢和需要思考;它的實現保持在盡可能簡單的代碼層面,不依賴復雜系統,也不直接接入 LLM,因為一旦接入,零成本生成就會立刻侵蝕它作為“裁決記錄”的性質。現階段都是硬編碼。我先簡單說說都包括啥,現在連UI都沒有,就已經簡單的解決了我大量的問題。
├── _system
├── docs
├── inbox
├── requirements
├── sovereign_log
├── templates
└── tools
這里面有兩個.md組成的文本庫,一個是sovereign_log,另一個是doc。
Sovereign_log
當然,一切并不是從一個宏大的系統開始的。我最初建立的只是一個極小、極其克制的 vault,它的起點和任何一個普通的 Obsidian vault 并沒有本質區別。如果你看到它的 graph,會發現幾乎沒有什么結構特征:沒有刻意構建的 backlink,沒有主題網絡,也不追求連接密度。唯一真正不同的地方在于數量——極少。這是一個被我刻意維持的約束。在這個 vault 里,每一篇內容都不是草稿,不是“先寫下來再說”,而是經過反復推敲的結果。我會與 AI 多輪對話,壓縮表達、校正邊界、澄清含義。早期我主要使用英文寫作,同時輔以少量中文翻譯,是作為一種語義校驗手段,用來確認我是否真正理解了自己寫下的東西。隨著系統成熟,這個 vault 很可能會完全切換為英文。這個階段的 Sovereign_Log,本質上是一個高度精煉、完全由個人主權控制的知識集合,其核心原則非常簡單:不要亂寫,不要當成草稿,數量不是越多越好,而是越精良越好。有時一整天的思考、推演和結構對齊,最終只會留下 1–2 篇筆記,是一種有意為之的熵控制。你一定要維持這種紀律,否則又跟你其他筆記一樣,照搬過來一大堆東西。
![]()
Doc
Doc 在形式上依然是“筆記”,但它并不是 Sovereign_Log 的自然延伸,而是一次明確的躍遷。Doc 中的內容并非寫得更正式,而是被提升過的文本,處于一種介于敘事語言與可執行代碼之間的 IR 狀態。它們尚未成為代碼,但已經不再允許自由敘述。在設計這個知識庫時,我刻意引入了“昂貴性”這一反直覺原則,用來對抗大語言模型生成文字與代碼過快、過多的問題。我認為,真正進入知識庫核心層的內容,必須在制度上是昂貴的,而不是在情緒或表達上顯得復雜。這種昂貴性體現在三個層面。
首先是格式昂貴。Doc 不是自由文本,任何內容想要進入這一層,必須滿足固定結構并通過機器可執行的格式檢查。確保這些文本在原則上是可解析、可索引、可審計、可約束的。一篇格式不合格的內容是根本不具備進入該層的資格。
其次是流程昂貴。內容不能一開始就進入 Doc,它必須先在 Sovereign_Log 中存在,經過時間沉淀,在真實使用中反復被搜索、引用、轉化為代碼或決策,逐漸顯現出對系統行為的實際影響,才會成為候選項,并在明確授權后被提升進入 Doc。這一機制在未來引入團隊共享知識庫時尤為關鍵,因為它明確阻斷了任何人、任何模型、任何一時興起的想法對核心知識層的直接污染。Doc 被視為一種稀缺資源,而不是存放“整理后內容”的地方。
第三,也是現階段最重要的一點,是語義昂貴。我對 Doc 的核心要求并不是表達清晰或邏輯自洽,而是語義必須達標。所謂語義達標,意味著條文之間不能互相矛盾,不允許隱含沖突,不允許模糊責任與邊界,每一條聲明都必須具備生成 binding 代碼的潛力。我觀察過許多文檔規模龐大的知識庫,它們的共同問題并不是信息不足,而是語義雜亂、定義漂移、條文沖突,最終無法轉化為真實可執行的系統約束。這樣的知識庫看似厚重,實則不可執行。而 Doc 的目標恰恰相反:每一條文本,都是潛在的系統約束;每一條語義,都是未來代碼的來源。因此,語義本身也必須進入可被機器介入和審計的范圍。
├── docs
│ ├── decisions
│ ├── governance
│ │ ├── CLAIM-AUTHORING-STYLE-V0-1.md
│ │ ├── EVIDENCE_SUGGEST_AUDIT.md
│ │ ├── RPT-2026-01-26-RUNTIME_ENV_GOVERNANCE.md
│ │ ├── SD-0007_RUNTIME_ENV_FREEZE.md
│ │ ├── SD-0008_INDEX_CONSOLIDATION.md
│ │ ├── SD-0009_DOC_IR_SPEC_v0.md
│ │ ├── SD-0010 — Normative Boundary & Semantic Consistency Rules
│ │ ├── SD-0011_ID_Taxonomy_and_Identity_Ledger.md
│ │ └── SD-0012 — Observability Causal Structure & Span Identity
│ ├── invariants
│ │ ├── INV_CATALOG_v0.md
│ │ ├── INV_DOC_IR_BOOTSTRAP_v0.md
│ │ └── INV_INDEX_ROOTS_0001.md
│ ├── Machine-Checkable Invariants.md
│ ├── runs
│ ├── SD-0001-System-Constitution.md
│ ├── SD-0002-Derived-Artifact-Path-Freeze.md
│ ├── Sovereign Log — Write Protocol v1.0.md
│ ├── stage_contracts
│ │ └── PRE_STAGE_5.md
│ └── taxonomy
│ ├── INDEX_TYPES.md
│ └── TDES.md
我現在的系統是完全沒有接入任何 LLM 的,至少在這一層沒有。這些約束、篩查和升級機制,全部是在代碼層面完成的。我一直相信,如果你是一個看到這里的程序員,并且你覺得這套方法對你有借鑒意義,那么它在技術上并不復雜,甚至可以說是非常直白的工程實現。這里我不展開具體代碼細節,只用一個非常具體的例子來說明我所謂的“語義昂貴”到底是什么意思。
以語義篩查為例。我每次跑 audit,通常都會順手生成兩份報告:一份是給機器繼續消費的 JSON,一份是給人看的 Markdown。下面這段就是我某一次跑出來的語義審計報告摘要。
這次 audit 的標識是 AUDITDOCSEMANTIC — 20260130T205405Zdocsemantic_audit,整體結果是 ok: False,意味著這次檢查在制度上是不通過的。系統掃描了 docs 目錄下的文檔,共識別出 77 條 claims,使用的是基于 embedding 的相似性與語義模式匹配(這里用的是 all-MiniLM-L6-v2,閾值 0.92,top-k 為 8,最大配對 200)。需要特別說明的是,這里對 modality、target、predicate 的推斷仍然是啟發式的,屬于 v0.1 階段,這一點在 meta 里是明確寫出來的,后續會被提升為顯式的結構化 claim tags。也正因為如此,重復或沖突在默認情況下只是 warning,但在這次配置里,我把沖突嚴重性提升為了 error,要求必須被解決。
真正關鍵的是下面這條 violation:SEM-CNF-001。它指向的是一個語義沖突,而且不是模糊沖突,而是非常具體的 modality 沖突。系統檢測到,在兩個不同的文檔中,針對同一個 (target, predicate) 組合——也就是 ('GLOBAL', 'evidence')——出現了相互矛盾的規范性斷言:一條是 must,另一條是 must_not。沖突來源被精確定位到了 SD-0011#C-0004 和 DOC-EVIDENCE-SUGGEST-AUDIT-V1#C-0003 這兩條具體的 claim 上。這意味著,在當前 Doc 層的語義空間中,系統同時被告知“這件事必須發生”和“這件事必須不能發生”,而且兩者作用域完全一致。
這類錯誤在我的系統里是不可接受的。一旦這樣的語義進入核心 Doc 層,任何試圖從中生成 binding 代碼、策略或運行時約束的行為,都會立刻失去確定性。因此 audit 是直接要求你做出選擇。hint 里給出的路徑也非常明確:要么通過既定的優先級體系來裁決(例如 INV 高于 SD,高于 taxonomy),要么明確使用 supersede 機制,讓其中一條 claim 在制度上失效。這是哪一條規則在系統中繼續存活的問題。
對我來說,這正是“語義昂貴”的具體體現。不是靠人腦記住“這里好像有點矛盾”,而是讓系統在 Doc 層直接拒絕語義不閉合、不一致、不可執行的狀態。也正因為有了這樣的機制,Doc 才不再是一個“寫得很嚴肅的文檔集合”,而是真正開始具備約束未來代碼與系統行為的資格。
AUDITDOCSEMANTIC — 20260130T205405Zdocsemantic_audit
ok: False
violations: 1
Meta
"docs_dir": "docs",
"claims_count": 77,
"dup_threshold": 0.92,
"topk": 8,
"max_pairs": 200,
"dup_severity": "warn",
"model": "sentence-transformers/all-MiniLM-L6-v2",
"notes": [
"v0.1 modality/target/predicate inference is heuristic; promote to structured claim tags later.",
"duplicates are warnings by default; set --dup_severity error to hard-enforce."
Violations
1. SEM-CNF-001 (error)
path: docs/governance/SD-0011_ID_Taxonomy_and_Identity_Ledger.md | docs/governance/EVIDENCE_SUGGEST_AUDIT.md
message: Modality conflict (must vs must_not) on (target,predicate)=('GLOBAL','evidence'): SD-0011#C-0004 vs DOC-EVIDENCE-SUGGEST-AUDIT-V1#C-0003
hint: Resolve via priority (INV > SD > TAXONOMY ...) or supersede one of the conflicting claims.
meta: 46502865b8712493e52dffd4a6b6871cd994c162f900ea8e6e7a8c3ebbcb5873
怎么實現的
從技術實現上講,這套機制并不依賴大語言模型,也不需要任何“智能理解”。它的核心思想是:把文檔的語義壓縮成一組可計算、可比較、可失敗的結構化要素,然后用一套完全確定性的規則對這些要素做審計。整個系統可以拆解為幾個相對獨立的技術層,你可以按需要取用或替換其中任意一層,而不影響整體成立。
第一步是聲明式語義單元的引入。文檔不再被當作一整塊自然語言,而是被要求顯式標注最小治理單元——也就是穩定編號的 claim。它們本質上等價于“規范性斷言”,每一條都是一個潛在的系統約束。實現上不需要復雜解析器,只要用簡單、魯棒的文本模式(例如固定前綴或行結構)就可以穩定抽取。這一步的關鍵不是解析能力,而是強迫作者在寫作階段就做出“這是我要被系統治理的句子”的選擇。
import re
from dataclasses import dataclass
CLAIM_RE = re.compile(r"(?m)^\\s*-\\s*(C-\\d{4})\\s*:\\s*(.+?)\\s*$")
@dataclass
class ClaimRaw:
claim_id: str
text: str
def extract_claims(md: str) -> list[ClaimRaw]:
out = []
for cid, txt in CLAIM_RE.findall(md):
out.append(ClaimRaw(claim_id=cid, text=txt.strip()))
return out
復現要點:你只需要一個穩定、可被 grep 的寫作格式(比如 - C-0001:)。解析就能保持極簡。
第二步是規范性強度(modality)的離散化。系統并不嘗試理解句子的全部含義,而是只關心它在制度上的態度:是必須、禁止、允許,還是無法判定。這可以通過一組確定性的關鍵詞或規則完成,而不是統計模型。重要的是,這一步把“模糊的自然語言語氣”壓縮成一個有限集合,使得后續沖突判斷變成一個有限狀態問題,而不是語言理解問題。
import re
MODALITY_RULES = [
("must_not", re.compile(r"\\b(must not|shall not|forbidden|prohibited|not allowed)\\b", re.I)),
("must", re.compile(r"\\b(must|shall|required|mandatory)\\b", re.I)),
("forbid", re.compile(r"\\b(forbid|prohibit|disallow|deny)\\b", re.I)),
("allow", re.compile(r"\\b(allow|permit|allowed)\\b", re.I)),
def infer_modality(text: str) -> str:
for name, rx in MODALITY_RULES:
if rx.search(text):
return name
return "unknown"
復現要點:這不是 NLP,是可審計的規則表。你可以按你組織的寫作詞匯(MUST/SHALL/禁止/允許)擴展規則。
第三步是約束對象(target)的錨定。為了避免所有規則都在一個全局空間里相互干擾,系統需要一個機制,把每條 claim 映射到一個明確的約束對象上。實現方式可以非常簡單,例如通過顯式標注、路徑前綴、資源名或作用域標識符。只要這個 target 是穩定、可比較的,就足夠用于治理。未能明確錨定的規則,可以被保守地歸入全局作用域,從而承擔更高的沖突風險,這本身就是一種設計激勵。
import re
BACKTICK_RE = re.compile(r"`([^`]+?)`")
def infer_target(text: str) -> str:
# Prefer explicit anchors like `docs/` `_system/` `sovereign_log/`
for m in BACKTICK_RE.finditer(text):
tok = m.group(1).strip()
if tok.startswith(("docs/", "_system/", "sovereign_log/")):
# collapse file path to a directory-ish prefix for stability
parts = tok.split("/")
if len(parts) >= 2:
return parts[0] + "/" + parts[1] + "/"
return parts[0] + "/"
return "GLOBAL"
復現要點:你要的不是“精準路徑”,而是穩定可比對的 target key。寫作上用 backticks 做顯式錨點,工程上就能把 GLOBAL 降到最少。Global會給你很多的false positive, 沒完沒了的修。其實一般的claims都是針對特定域的,你不會動不動就寫一個“我這條整個系統都必須通用。”
第四步是謂詞維度的粗分類(predicate)。這是為了避免不相關的規則在同一 target 上產生誤報沖突。系統并不需要一個完整的本體論,只需要一個非常小、可擴展的謂詞集合,用來區分“這是關于存放位置的規則”“這是關于權限或權威性的規則”“這是關于證據或生成機制的規則”等。這一步同樣可以通過關鍵詞或標簽完成,其目標是縮小沖突比較的語義空間,而不是追求精細分類。
def infer_predicate(text: str) -> str:
t = text.lower()
if any(w in t for w in ["authoritative", "canonical", "binding", "truth layer"]):
return "authority"
if any(w in t for w in ["faiss", "semantic index", "vault_index", "index root", "index output"]):
return "index"
if any(w in t for w in ["write to", "stored under", "must exist only at", "reside at", "live under"]):
return "placement"
if any(w in t for w in ["admitted", "accepted", "effective", "governance-valid"]):
return "admission"
if any(w in t for w in ["evidence", "ingest", "payload", "manifest"]):
return "evidence"
return "general"
在完成上述四個要素之后,每一條文檔聲明在系統中都會被壓縮成一個結構化記錄:(modality, target, predicate)。到這里,文檔已經不再是自然語言集合,而是一個可以被機器枚舉、分組和比較的約束集合。
基于這個結構,語義沖突檢測就變成了一個純規則問題:在同一個 (target, predicate) 空間內,是否同時存在互斥的規范性態度(例如 must 與 must_not,allow 與 forbid)。一旦出現這種情況,系統可以直接判定為不可執行狀態,并給出精確到聲明級別的沖突定位。這里不涉及概率、不涉及模型判斷,也不需要人工解釋,沖突即失敗。
在此之上,可以疊加一個近似重復檢測層,用于發現語義上高度相似、但尚未構成直接沖突的聲明。這一層可以使用任何向量化或相似度技術來實現,其角色更接近于“治理衛生檢查”,而不是硬約束。是否將其視為警告還是錯誤,完全取決于你對系統嚴格程度的要求。
整個審計流程的輸出應當是機器優先的:結構化結果用于后續自動化處理,人類可讀報告用于審查與決策。關鍵在于,審計必須能夠失敗,并且失敗應當通過明確的進程退出、狀態標志或流水線信號向外傳播,這樣文檔治理才能真正進入工程系統,而不是停留在規范建議層。
如果你想在自己的系統中復現這一機制,并不需要復制具體實現。你只需要確認幾件事:你是否愿意引入顯式的聲明式語義單元;你是否愿意接受把自然語言壓縮為有限語義維度所帶來的約束;以及你是否愿意讓“文檔語義不一致”像代碼編譯錯誤一樣中斷流程。一旦這三個問題的答案都是“是”,那么無論你使用哪種語言、哪種工具、哪種存儲結構,這套“語義昂貴”的文檔治理模式都可以被復現出來。
為什么不引入大模型?當然我后期肯定會引入的。但是現在我需要搭建一個完全斷開大模型的基層。這個原因我覺得讀到這里的人應該都能大概理解。因為你想要的是歷史與治理必須由確定性機制承擔,模型只能作為建議者,而不能成為裁決者。失敗條件是穩定的,系統邊界是清晰的。這件事情目前讓我感覺上就舒心很多。
硬編碼就萬能嗎?當然不是!它覆蓋不了復雜語義。關鍵詞、規則表、枚舉謂詞,本質上都是低分辨率的認知壓縮。它無法理解隱喻、上下文、反諷,也無法自動適應新的表達方式。硬編碼會顯得笨、慢、保守。每加一個 predicate、每調一個規則,都需要人來做判斷。這在早期看起來效率很低。但我恰恰需要這種“慢”。它是一種摩擦機制,用來對抗“生成過快”的系統風險。在一個可以一秒鐘生成一百條規則的時代,慢本身就是一種安全設計。它迫使規則的作者為進入核心層付出認知成本。這也是我認為程序員容易陷入的一種精神陷阱,就是很容易把自己抽離。認為自己不是系統機制的一部分。我個人反而因為現在大量自己做項目,容易理解這種“自身必須帶入,自身必須收到約束”的想法。
核心當然是Vault Index and Query
好,我們先把系統總圖停在這里,剩下的那些“細枝末節”(derived 層怎么落盤、怎么隔離、各種 tools/templates、審計與報告的目錄法、生命周期與清理策略)確實規模很大,但它們本質上都是工程衛生問題:只要你的邊界清楚(Truth / Decision / Evidence / Derived),再加上寫入權限與路徑約束,技術實現不會難,更多是“制度一致性”和“長期可維護性”的問題。
我這里真正想展示的,是我的 vault_indexer:它不是一個“更好用的搜索”,而是一個把筆記系統變成證據底座(evidence substrate)的索引機制。現在幾乎所有筆記應用遲早都會走到“語義檢索 + chunk”這條路,但我在做的時候發現,決定它最終價值的不是檢索本身,而是你如何組織證據 chunk、如何讓它們具備可引用、可復現、可審計的屬性,以及最關鍵的:這個索引產物在你的系統里到底處于什么層級(Truth 還是 Derived)。
我的做法是把索引嚴格定位為 Derived:它永遠不擁有“真理權”。它只負責把 docs/、sovereign_log/ 這些文本資產,編譯成一個面向檢索的中間產物:把文檔切成穩定粒度的 chunks(通常是段落級或標題-段落組合級),為每個 chunk 生成穩定標識(chunk_id)、保留來源路徑、heading/段落位置、以及內容哈希(或可追溯的 digest)。然后對 chunk 文本做 embedding,構建向量索引(FAISS 或同類),最終讓你可以用自然語言 query 去命中一組證據 chunks。注意這里“證據”不是泛指相關文本,而是具備引用結構的對象:每個 chunk 都能被精確地指向、被復制到 evidence pack、被后續工具鏈復用,甚至被審計系統要求“你生成的結論必須引用這些 chunk_id”。
這就直接解釋了為什么 LLM 在我這里不是“寫作機器”,而是“最佳 query 手”。LLM 的強項不是憑空生成,而是:把一個模糊問題拆解成可檢索的子問題、構造有效 query、對返回的證據進行結構化歸納,并把歸納結果映射回開發動作(改哪個文件、加哪個 invariant、補哪個 claim、寫哪個 diff)。當你已經建立了信息底座——也就是把核心原則、邊界、約束、歷史責任都凍結在 Doc / Sovereign_Log 里——LLM 最合理的用法就是站在索引之上,做“證據驅動的開發協作”。你問一個問題,它先檢索,再給你一個帶引用的證據包(JSON/MD),你再用這個證據包去推進決策與代碼,而不是讓它用記憶和幻覺替你編故事。
也因此,我的方法和很多以“搜索”為核心的 Obsidian 插件有一個結構性差異:大多數插件的目標是“更快找到筆記”,而我的目標是“讓檢索結果成為可治理的證據對象”。插件式搜索通常停留在 UI 體驗層:關鍵詞或語義匹配 → 打開筆記 → 人腦判斷;它們很少解決這些問題:檢索命中的內容有沒有穩定引用?是否能在 CI/審計里復現同一個結果?檢索產物是否會被錯誤地當成權威?能否形成“問題 → 證據 → 決策 → 代碼 → 回寫證據”的閉環?而我的 vault_indexer 把檢索的輸出直接做成一種可落盤、可版本化、可再利用的中間態(pack / report / audit),并且明確把它放在 Derived 層:它可以被重建、可以被替換模型、可以被清理,但不能反向污染 Truth/Doc 的權威性。
換句話說:很多筆記插件把搜索當作“閱讀入口”,我把索引當作“開發輸入口”。前者優化的是找筆記,后者優化的是把筆記變成工程證據,讓 LLM 以“證據分析器 + query 編譯器”的身份參與開發。這也是為什么當你的系統底座一旦穩定(規則、邊界、歷史責任明確),索引檢索就不再是錦上添花,而會變成你推進長期工程的主引擎:你不是在“寫更多筆記”,你是在用檢索把過去的制度與證據,持續地喂回現在的決策與代碼。
廢話不多說,直接給你看證據。
我們先從一份非常真實的 query_vault 結果說起。需要先說清楚一個前提:這個系統現在還在早期階段,它不是那種“裝完就立刻很神”的工具。它需要你在真實項目中長期使用、長期維護,并且真正把它當成核心系統來用,才會逐漸成長為你自己的“第二大腦”。在這個階段,檢索結果的 score 普遍不會特別高,這是正常現象。對我現在的庫來說,0.7 已經算是相當不錯、可以認真閱讀的命中率了。
原因很簡單:語義索引的質量,最終取決于你往里放了什么。你需要一邊做真實的開發、一邊寫真實的規則、一邊不斷用“已經被你驗證過的優質內容”去填充這個庫;同時還要持續升級、清理和維護。這個過程是累積性的:越用越好用,越用越省心,而不是一開始就給你“標準答案”。這才是第二大腦應有的成長方式。
從結構上看,一次 query_vault 的 run,本質上只是一個可復現的檢索結果對象。它并不試圖直接回答問題,而是把“當時系統認為可能相關的證據”列出來。這個對象里最重要的幾個字段是:
run_id:這次檢索的唯一標識。你可以把它當成一次“證據采樣”的編號,后續可以落盤、歸檔、甚至被審計。
query:你當時輸入的原始問題,用于回溯“當時你在想什么”。
model:用于向量化的 embedding 模型,它定義了“相似”的數學意義。
results[]:真正的核心內容——chunk 級別的命中結果,而不是文件級搜索。
每一條 result,都不是“這篇文檔命中了”,而是“這一個具體段落(chunk)被認為相關”。因此,每條 result 都帶著一組對工程師來說非常關鍵的元信息:
chunk_id:這個段落的穩定身份。后續引用證據時,你引用的是它,而不是“我記得哪篇文檔里有一句話”。
note_path / heading_path / paragraph_index:用于精確定位原文位置。
hash:該段落內容的哈希,用來保證“你現在看到的證據”和“未來審計時看到的證據”是同一段文字。
mtime:最后修改時間,幫助你判斷新舊、是否存在語義漂移風險。
score:相似度分數,只是排序信號,不是“正確性”或“權威性”的度量。
snippet:展示用的截斷文本,只能當預覽,不能當證據全文。
所以你要對這個結果有一個非常清醒的認識:
它是一份**“可引用的檢索命中清單”**,而不是“已經回答你問題的證據”。
接下來才是關鍵:你問的是什么問題?你希望這份證據幫你解決什么?
在這次 query 里,問題是:
run session trace ordering
這并不是在問“Run、Session、Trace 分別是什么”,而是在做一次圍繞Run / Session / Trace 的存在順序與合法性(ordering & existence constraints)的證據檢索。
換句話說,這是一個非常“硬”的問題。你真正關心的不是定義,而是:
哪些推斷是被系統明確禁止的?
哪些順序關系必須顯式聲明,而不能從時間或結構中“猜”出來?
這也是為什么這次命中的 chunk,即便 score 只有 0.6~0.7,依然是高價值證據。比如:
明確禁止從 trace 連續性推斷 session 存在
明確禁止用時間順序替代 run / session 的顯式聲明
明確要求 trace 必須綁定到合法 session,否則就是 non-legitimate
明確區分 Run / Session / Trace 各自回答的問題域
這些內容并不是“百科式解釋”,而是制度級約束。它們的價值不在于“講清楚概念”,而在于防止你和 LLM 在開發過程中犯錯。
這也正好引出了 LLM 在這里的正確角色定位。
在這個系統里,LLM 不是用來腦補解釋的,而是一個“證據分析器”。它不應該看到 snippet 就開始編故事,也不應該試圖用時間順序、因果敘事去“合理化”缺失的結構。相反,它應該被這些 chunk 錨定住行為邊界:
這些推斷是被明確禁止的 → 不要做
這些關系必須顯式聲明 → 如果缺失,就指出缺口
如果規則已經存在但沒有被完美回答 → 就用已有證據做錨,而不是重新發明規則
即便這個問題在你的系統里還沒有一個“教科書式的最終答案”,0.7 分左右的證據依然有巨大價值:它為 LLM 提供了方向約束,防止概念漂移,防止在日常開發中慢慢把系統“想歪”。
這就是我做這套 vault_indexer + query 機制的初衷:
不是讓檢索直接給你答案,而是讓它在你和 LLM 之間,持續提供一個可以被引用、被校驗、被審計的證據錨點。只要這些錨點是穩定的,你的系統就不會在長期使用中悄悄變形。
"run_id": "20260131T014112Z_query_vault_b52ba27830",
"query": "run session trace ordering",
"scope": {
"note_path_prefix": null
},
"topk": 12,
"per_note_cap": 2,
"exclude_temporal_note": false,
"model": "sentence-transformers/all-MiniLM-L6-v2",
"results": [
"chunk_id": "f91b794ab50cc20b12ee18d2a74a532ed5b0630d",
"note_path": "docs/governance/SD-0011_ID_Taxonomy_and_Identity_Ledger.md",
"heading_path": "",
"paragraph_index": 65,
"hash": "ed7cc7a84aa209ae63eb30ef531ab5cfd5cb0a649b8b98da78db3c9d3cf0f73c",
"mtime": 1769822404.9170113,
"score": 0.7674295067787171,
"snippet": "- Trace continuity MUST NOT be used to infer the existence of a session. - Session existence MUST NOT be inferred from trace structure. - Time ordering alone MUST NOT substitute for explicit run or session declaration.",
"boost": {
"prefix": "docs/",
"factor": 1.1
},
"chunk_id": "74abc9e9857e9ee0f805584d3b95dee8c0142f29",
"note_path": "sovereign_log/Session-Run-Trace-Formal Definitions.md",
"heading_path": "",
"paragraph_index": 29,
"hash": "f08025c0500946d913c04bbecbb5ee1ed106120b5d9ecc3f21d9a5254395578b",
"mtime": 1769737995.4226983,
"score": 0.6208187341690063,
"snippet": "- A Run **must exist** before any Session. - A Session **must exist** before Events are promoted. - A Trace **must exist** for any causal claim. - Time ordering **must never replace** trace structure."
},
"chunk_id": "8c355b0da6ce66924ec7fdb83adc71dc6a4e17d7",
"note_path": "docs/governance/SD-0011_ID_Taxonomy_and_Identity_Ledger.md",
"heading_path": "",
"paragraph_index": 60,
"hash": "c056ccfc21a6cd4e9352ed599b679a0018f95bbf828fd0f017fdb9ff6af51c90",
"mtime": 1769822404.9170113,
"score": 0.5683699011802674,
"snippet": "- A `trace_id` MUST be associated with a valid `session_id`. - Trace-level artifacts that lack an explicit session binding are non-legitimate.",
"boost": {
"prefix": "docs/",
"factor": 1.1
},
"chunk_id": "b8ecd51c38bb6cffceb35546af4f27176d67027d",
"note_path": "sovereign_log/Session-Run-Trace-Formal Definitions.md",
"heading_path": "",
"paragraph_index": 32,
"hash": "32a2b22ffaf1ffcabcb49924a30bdcbbf00c46b052403ca5ea32e9ad1739b0c6",
"mtime": 1769737995.4226983,
"score": 0.5365391969680786,
"snippet": "> **Run answers “which execution.” > Session answers “which lifecycle.” > Trace answers “why.”**"
},
我們現在已經非常的操之過急的,習慣于,每次自己有什么問題,馬上寫prompt,馬上要得到答案。然而LLM又非常善于給你編織“完美答案”。這是非常危險的。這就是漂移的根源。
如果你考慮自己弄一個,那么可參考兩個核心腳本:index_vault.py; query_vault.py
代碼層面的復現其實并不難,真正的難點也不在代碼本身,而在于你是否愿意接受這樣一套知識治理制度。在現階段,我是刻意不把 LLM 接入系統執行鏈路的。我只會把已經生成好的證據(evidence packs)和審計報告,交給 LLM 去“解釋”和“建議”。無論你是在交互窗口里用 LLM,還是將來把它接入系統內部,這一點在本質上并不會改變:LLM 的強項是理解問題、組織合適的 query、并在你提供的證據約束下給出建議,而不是替你決定什么是真理。真正決定系統質量的,是你如何看待證據、如何讓知識變得可治理。這是我目前非常明確的立場。
因此,這套東西并不是“一個搜索腳本”,而是一個最小可用的 Evidence Retrieval Substrate。它的目標非常克制:
把 vault(Markdown)編譯成 chunk 級的語義索引(Derived、可重建、沒有真理權)
查詢時返回的是可引用的證據單元(chunk_id + 精確定位 + 內容 hash + snippet)
把每一次查詢的結果落盤為 evidence pack(JSON + MD),供后續 LLM 和人類在開發中反復引用
如果你要復現,真正需要抓住的并不是 FAISS 或 embedding 模型,而是這三條不變量:
chunk 身份必須穩定、引用必須可追溯、輸出必須可重放。
腳本 A:index_vault —— 構建語義索引
技術選型與原理
輸入:docs/、sovereign_log/ 下的 Markdown
輸出:_system/artifacts/vault_index/(明確屬于 Derived 層)
它做的事情本質上是一次“編譯”:
1、只在 vault 內掃描 Markdown 文件
2、將每個文件切成段落級 chunks(paragraph chunks)
3、對每個 chunk:
丟棄過短內容(min_chars)
生成一個穩定的 chunk_id
記錄最小但關鍵的元數據(note_path、paragraph_index、hash、mtime)
4、使用 sentence-transformers 將 chunk 文本編碼成向量(并歸一化)
5、使用 FAISS 構建向量索引(IndexFlatIP,等價 cosine)
6、將索引產物落盤:
meta.jsonl:每一行對應一個 chunk 的身份與來源
index.faiss:向量索引
config.json:索引配置與可復現參數
之所以選擇 IndexFlatIP,原因非常簡單:在歸一化 embedding 的前提下,內積等價 cosine,這是最穩定、最可解釋的基線方案。在你真正跑進性能瓶頸之前,沒有必要引入 IVF/HNSW 之類的復雜結構。
必須保留的不變量
1、chunk 切分必須穩定
split_to_paragraph_chunks() 的行為一旦改變,paragraph_index 就會漂移,歷史引用就會失效。
2、chunk_id 必須與內容綁定
推薦的策略是:hash(path + paragraph_index + content_hash)
這樣可以確保:內容一變,ID 必然變化,避免“引用幻覺”。
3、meta 與 index 順序必須嚴格對齊
FAISS 向量的順序,必須和 meta.jsonl 的行順序一一對應,否則整個索引就不可用。
腳本 B:query_vault —— 查詢并生成證據包
技術選型與原理
輸入:自然語言 query
讀取:_system/artifacts/vault_index/{meta.jsonl, index.faiss}
輸出:
_system/artifacts/packs/citations/.json
_system/artifacts/runs/.md
核心流程是:
1、載入 meta 與 FAISS index
2、用同一個模型、同一種歸一化方式對 query 編碼
3、先做一次寬松檢索(raw_k 通常是 topk 的 10 倍以上)
4、再進行后處理過濾:
scope_prefix:限制檢索空間
exclude_temporal_note:剔除不應參與治理的內容
per_note_cap:避免單個文件刷屏
5、回到原文讀取 snippet(通過 paragraph_index 重切 chunk)
6、進行輕量排序調整(docs_boost / prefix boost)
7、生成 evidence pack(JSON)與 run note(MD),并統一寫入 Derived root
這里的關鍵價值在于:
檢索的輸出不是 UI 展示結果,而是一個可以被落盤、被引用、被審計的證據對象。
必須保留的不變量
索引模型與查詢模型必須一致
否則 embedding 空間不一致,分數沒有任何意義。
snippet 只能是展示層
證據的真實身份永遠來自 meta(chunk_id / hash),snippet 只是為了讓人讀得懂。
所有輸出必須寫入 Derived root
嚴格限制在 _system/artifacts/ 下,并綁定 run_id,這是保證可重放、不污染 Truth/Doc 層的關鍵。
最小復現 Checklist(給工程師)
1、明確你的 Vault 輸入域(1–2 個目錄即可)
2、實現一個穩定的 chunker(段落級是最優起點)
3、定義清晰的 chunk_id 生成策略
4、構建 index 與 meta,并保證順序一致
5、查詢時輸出的是“證據對象”,而不是路徑列表
6、加上最基本的治理過濾(per-note cap、scope)
普通搜索:找到筆記 → 人腦判斷
這套系統:找到 chunk 證據 → 生成 evidence pack → 支持審計 / 引用 / LLM 分析 → 反哺真實開發
索引只是入口,真正的產物是:
可引用的證據單位,以及可以被反復使用和復查的證據包。
寫個 wrapper:把 Vault 直接接入真實項目 Repo,邊開發邊用,邊開發邊反哺
你在寫代碼的時候,隨時可以 query 自己寫過的系統原則、治理規則、歷史約束、失敗邊界;同時你在開發過程中遇到的新問題、新決策、新證據,也會被反向沉淀回 vault。兩邊一起增長,兩邊一起防止概念漂移——項目不會因為忙而忘記制度,制度也不會因為脫離實戰而空轉。
我目前是把 Vault 變成“項目的外置證據底座”
把 sovereign_knowledge 當成一個獨立、長期存在的知識/制度庫,然后在真實項目 repo 里寫兩個極薄的腳本:
skq:把“開發中遇到的問題”直接扔給 vault 的 query_vault.py
ski:在你更新了 vault 內容后,重建語義索引(index_vault.py)
這兩個腳本的關鍵價值不是“省打字”,而是把查詢變成一種開發動作:
你在寫代碼時,隨時可以從自己的制度庫里抽取證據 chunk,生成可落盤的 evidence pack,然后再把 pack 交給 LLM 或自己讀,防止概念漂移。
給屏幕前程序員的復現指南
1、把你的知識庫 repo clone 到任意路徑(例如 ~/sovereign_knowledge)
2、在你的項目 repo 里放兩個腳本:
scripts/skq(query)
scripts/ski(index)
3、給執行權限:chmod +x scripts/skq scripts/ski
4、配置一次環境變量(可選):
export SK_VAULT=~/sovereign_knowledge
export PYTHON=python3(或 venv python)
5、日常開發用法:
更新了 vault 內容:scripts/ski
寫代碼遇到問題:scripts/skq "run session trace ordering" --scope_prefix docs/
這就完成了“邊開發邊用”的閉環:你不需要把 vault 代碼搬進項目,也不需要在項目里維護一套索引管線;你只需要把它當作外置制度底座,隨時 query。
一個很關鍵的點:我為什么認為這種“外置庫 + wrapper”比插件更像工程
因為它把檢索的輸出變成了穩定產物(pack/run note)而不是 UI 瞬時結果;也因為它把“知識治理”從筆記工具里抽離出來,變成可以進入 CI、進入審計、進入代碼評審的工程對象。這就是“第二大腦”之所以能長期生長的地方:它不是靠你記得住,而是靠工具鏈持續生成可追溯證據。
一個多年工程經驗的大哥曾經說過,LLM時代,文本和代碼要一樣多。但是你總不能所有的項目全部塞一個文本庫吧。
最后,LLM強大的解構問題能力,設計query能力,并且從證據段中讀取只字片語,生成建議的能力。這是現階段,這個辦法可以在你密集使用LLM的同時,減輕焦慮的一個辦法。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.