
半年前我想找回一篇讀過的論文只記得它講的是用某種約束讓生成結(jié)果更穩(wěn)定具體標(biāo)題、作者、發(fā)表年份全忘了。我在三個不同的筆記文件里翻了四十分鐘最后在一份命名為臨時-2.md的文件第 137 行找到了它正文只有一句話這篇思路不錯。那一刻我意識到問題不在我記性差而在于我把論文研究記錄當(dāng)成了日記來寫。后來我把整套流程重做成了一套叫l(wèi)jg-paper的記錄體系其中的核心是paper-map——一份定義每條論文記錄必須長什么樣的字段映射表外加一套能自動跑起來的校驗?zāi)_本。這篇就聊這套東西字段為什么這么切、每個字段到底該填什么、怎么用腳本把靠自覺變成跑一遍就知道有沒有錯。適合正在做文獻(xiàn)綜述、需要長期維護(hù)閱讀筆記、或者帶人做課題的朋友參考不需要什么前置基礎(chǔ)能看懂 YAML 和命令行就行。1. 論文筆記從自由發(fā)揮改成填表到底解決了什么1.1 自由格式筆記的三個失效現(xiàn)場我先復(fù)盤一下自己原先的寫法估計很多人跟我一樣。第一種失效是檢索失效筆記是散文想找哪幾篇討論過采樣偏差只能靠全文搜索而我當(dāng)時用的詞是分布不均搜采樣偏差是搜不到的。第二種是對比失效想橫向比較三篇論文的方法差異得把三個文件并排打開人眼對齊五分鐘能完成的事拖成半小時。第三種最隱蔽叫遺忘失效一條記錄寫著讀完方法有意思三個月后我完全不記得有意思指的是哪一點這條記錄等于作廢。這三種失效的根源是同一個自由文本把結(jié)構(gòu)交給了寫作者的臨場狀態(tài)。心情好的時候記得詳細(xì)趕時間的時候一句話打發(fā)最后整個筆記庫的質(zhì)量方差極大你沒法對一堆方差極大的數(shù)據(jù)做任何批量操作。1.2 paper-map 的定位它不是目錄是索引契約我最初也試過加模板就是那種標(biāo)題 / 作者 / 摘要 / 感想四段式。用了兩周就廢了因為它只是個空殼填不填、填多少全靠自覺。paper-map 的區(qū)別在于它帶約束每個字段有類型、有取值范圍、有必填與否而且這套約束能被機器讀取并檢查。說白一點paper-map 是一份契約——你承諾每條記錄都包含這十幾個字段工具承諾只要字段齊全就能按任意維度篩選、聚合、對比。契約的價值不在于寫得漂亮而在于違約會被立刻發(fā)現(xiàn)。這也是為什么字段體系和自動化驗證必須一起做只做前者等于立了個沒人執(zhí)行的規(guī)矩。1.3 每加一個字段都是一筆長期負(fù)債這里必須潑一盆冷水。字段不是越多越好每加一個字段你未來每寫一條記錄就多一次決策成本而且這個成本是線性疊加的。我第一版設(shè)計了 28 個字段包括實驗環(huán)境數(shù)據(jù)集規(guī)模代碼是否開源復(fù)現(xiàn)難度等等結(jié)果寫了十幾條之后開始逃避最后整件事停擺。第二版砍到 15 個核心原則是只保留會被查詢的字段。判斷方法很粗暴——問自己我會不會按這個字段篩選或排序。會就留只是填了顯得完整一律砍掉。像復(fù)現(xiàn)難度這種我從來沒真正排序過的字段全部刪掉需要的時候?qū)懺谧杂蓚渥⒗铩?. paper-map 的四層字段結(jié)構(gòu)與取舍邏輯整套字段我按職責(zé)分成四層標(biāo)識層、內(nèi)容層、關(guān)系層、狀態(tài)層。分層不是為了好看是因為這四層的變更頻率完全不同——標(biāo)識層幾乎不動內(nèi)容層寫完就凍結(jié)關(guān)系層會隨著閱讀量增長不斷追加狀態(tài)層則天天在變。變更頻率不同的東西混在一起改起來會互相牽連。2.1 標(biāo)識層讓每條記錄有唯一可尋址的名字標(biāo)識層解決的是引用問題。字段包括id、title、authors、year、venue、link。id我用固定格式p{年份}-{三位序號}比如p2024-031。為什么不用標(biāo)題做 ID因為標(biāo)題會重名、會有特殊字符、會因為翻譯而變。為什么不用隨機 UUID因為人腦記不住寫交叉引用的時候要復(fù)制粘貼很煩。短序號是唯一一個機器唯一 人眼可讀 手打不累的方案。序號只在當(dāng)年遞增跨年重新從 001 開始。這個細(xì)節(jié)很重要如果用全局遞增一年后序號會變成四位數(shù)手打成本上去了如果完全不編號靠時間戳人眼排序時又要在腦子里做一次字符串比較。authors我存成數(shù)組而不是字符串就是為了后面能按作者聚合。link我要求必須是可訪問的穩(wěn)定地址優(yōu)先用 DOI 形式的永久鏈接而不是搜索引擎結(jié)果頁——后者半年后大概率失效。2.2 內(nèi)容層問題、方法、結(jié)論的三段式內(nèi)容層是這套體系的重心只有五個字段problem、method、result、limitation、takeaway。這個切法借鑒了技術(shù)寫作里的問題—方案—證據(jù)結(jié)構(gòu)但做了兩處調(diào)整。第一處是把limitation單獨拎出來因為我在做綜述時最需要的信息恰恰是這篇沒解決什么而這部分內(nèi)容在原文里通常是作者輕描淡寫帶過的不單獨設(shè)字段就一定會漏。第二處是加了takeaway強制回答這篇對我當(dāng)前的工作有什么用這一條是防止讀完就忘的關(guān)鍵閘門——寫不出 takeaway說明這次閱讀沒有產(chǎn)出不如不記。字段的具體定義如下表字段類型必填約束用途problem字符串是30–120 字陳述句按問題域聚類method字符串是60–300 字含機制描述方法對比result字符串是含至少一個可量化信息或明確結(jié)論證據(jù)留存limitation字符串否≤150 字找切入點takeaway字符串是≤150 字必須寫可用于/不適用于行動轉(zhuǎn)化注意result的約束里寫了含至少一個可量化信息或明確結(jié)論。這不是形式主義是因為我發(fā)現(xiàn)自己的歷史筆記里大量出現(xiàn)效果不錯提升明顯這種無法驗證的表述而真正有價值的是在某某設(shè)定下比基線高了 X 個點或者在樣本量小時反而不如基線。前者可以引用后者可以成為你的研究切入點。2.3 關(guān)系層論文不是孤島關(guān)系層是我認(rèn)為最容易被忽略、但收益最高的一層。字段有三個builds_on、contradicts、same_thread值都是id數(shù)組。builds_on記錄這篇建立在哪篇之上contradicts記錄這篇的結(jié)論和哪篇沖突same_thread記錄哪幾篇屬于同一條研究線索。三者的區(qū)別在于語義強度第一個是有向的繼承關(guān)系第二個是有向的沖突關(guān)系第三個是無向的聚類關(guān)系。為什么要區(qū)分這么細(xì)因為做綜述的時候你需要的東西不一樣。寫研究脈絡(luò)章節(jié)時你要builds_on構(gòu)成的鏈寫爭議點章節(jié)時你要contradicts構(gòu)成的對抗組而當(dāng)你只是想在某個方向上快速找?guī)灼獙ψx時same_thread最省事。如果只用一個籠統(tǒng)的related字段這三種查詢?nèi)甲霾涣?。關(guān)系字段還有一個隱性好處它逼你在記錄時思考定位。填寫builds_on的那一刻你必須回答這篇到底是在誰的肩膀上這個動作本身就是一次理解檢驗。2.4 狀態(tài)層記錄的生命周期狀態(tài)層管的是這條記錄現(xiàn)在處于什么階段字段包括status、depth、added_at、review_at。status用四態(tài)枚舉inbox只存了鏈接、reading在讀、digested已消化、archived歸檔不再用。四態(tài)比讀/未讀兩態(tài)好在哪好在inbox這個緩沖區(qū)的存在。以前我遇到感興趣的論文必須當(dāng)場讀完做筆記否則就丟了有了inbox我可以先存鏈接攢到周末批量處理心理負(fù)擔(dān)小很多。depth用三檔skim只看摘要和圖表、digest通讀并做結(jié)構(gòu)化記錄、deep復(fù)現(xiàn)或推導(dǎo)過。這個字段決定了你后面引用這條記錄時的可信度——一條skim級別的記錄其result字段大概率是從摘要里抄的引用要謹(jǐn)慎。review_at是我后來加的是個日期用來標(biāo)記什么時候該回來看一眼。有些論文當(dāng)時看不懂但半年后你的知識背景變了再看會有新發(fā)現(xiàn)。這個字段本質(zhì)上是個輕量的間隔重復(fù)機制。3. 填寫規(guī)范每個字段的邊界在哪字段定義清楚只是第一步真正的坑在怎么填。同一個字段兩個人能填出完全不同的東西校驗?zāi)_本攔不住這種偏差。所以除了 schema我還寫了一份填寫規(guī)范把每個字段的該寫什么、不該寫什么用正反例固定下來。3.1 problem 字段寫缺口不寫領(lǐng)域最常見的錯誤是把problem寫成領(lǐng)域名。比如寫成目標(biāo)檢測這不是問題是范圍。合格的寫法是描述一個具體的缺口比如在遮擋比例超過 40% 的場景下現(xiàn)有方法的召回率下降到不可用水平。判斷標(biāo)準(zhǔn)很簡單能寫成一個疑問句嗎。現(xiàn)有方法在什么條件下失效——能問出來說明它是個問題問不出來說明只是個標(biāo)簽。我把這條寫進(jìn)了規(guī)范開頭因為它是最高頻的錯誤。另一個邊界是長度。problem我限制在 30–120 字。低于 30 字通常信息不足高于 120 字往往是因為把背景介紹也塞進(jìn)去了。背景不用寫那是論文本身的事你要記的是它的靶子在哪。3.2 method 字段寫機制不寫流程method是重災(zāi)區(qū)。我早期寫出來的是這樣先用編碼器提取特征然后過一個注意力模塊最后接分類頭。這是流程復(fù)述把論文的圖 2 翻譯了一遍文字沒有任何信息增量。后來我改成一句話回答這個東西為什么能work它的關(guān)鍵機制是什么這個機制替換掉了原來的什么代價是什么。比如寫成把全局歸一化換成逐組歸一化犧牲了跨組的信息交互換來在小批量下的穩(wěn)定性。這一句話里有機制、有取舍、有適用條件比三段式流程描述有用得多。規(guī)范里我給了個檢驗方法把 method 字段單獨拿給別人看對方能不能判斷出這篇論文和另一篇的差別。能就合格不能說明你寫的是通用流程不是這篇的特點。3.3 tags 字段受控詞表別自由發(fā)揮tags我強制從受控詞表里選不允許自造。詞表存在倉庫根目錄的一個文件里大概三四十個詞分三組任務(wù)類比如分類生成檢索、方法類比如對比學(xué)習(xí)蒸餾剪枝、場景類比如資源受限在線多模態(tài)。為什么強制因為自由標(biāo)簽半年后必然出現(xiàn)目標(biāo)檢測檢測object detection目標(biāo)識別四個同義詞然后你的篩選就廢了。受控詞表本質(zhì)上是在維護(hù)一套一致的命名空間代價是你偶爾會想說某個意思但沒有現(xiàn)成的詞——這時候正確做法是提一個新增詞條的改動而不是臨時造一個。詞表我建議控制在 50 個詞以內(nèi)。超過這個數(shù)人會開始記不住然后又開始自由發(fā)揮。3.4 關(guān)系字段填寫順序有講究關(guān)系字段我規(guī)定了一個填寫順序先填same_thread再填builds_on最后填contradicts。先說為什么先填same_thread。聚類是最容易判斷的這條和那三條講的是同一件事這個直覺很可靠。先把聚類做出來后面兩個字段的候選集就從整個庫縮小到了同一條線索里工作量小一個數(shù)量級。builds_on要后填因為你需要先確認(rèn)對方的方法確實被這篇繼承或改進(jìn)了而不是單純引用了。有些論文的引用只是相關(guān)工作里提了一句那不算builds_on。contradicts放最后因為它的判定門檻最高需要你真的讀過兩篇的實驗設(shè)定才能判斷結(jié)論是否真的沖突。我的經(jīng)驗是二三十篇里能找出一兩對真沖突就不錯了如果發(fā)現(xiàn)滿屏都是沖突大概率是你把適用條件不同誤判成了結(jié)論沖突。一個反直覺的提醒contradicts字段寧可留空也別硬湊。虛假的沖突關(guān)系比沒有關(guān)系更有害它會讓你在寫綜述時得出錯誤的對立結(jié)論。4. 自動化驗證把自律變成命令行字段寫清楚、規(guī)范也寫了下一個問題是執(zhí)行。人是有惰性的趕時間的時候必然走捷徑。所以我把所有能機器判斷的規(guī)則全部交給了腳本剩下的才靠人。4.1 驗證規(guī)則分成三檔我把校驗規(guī)則分成三檔按嚴(yán)格程度遞增檔位檢查內(nèi)容失敗后果L1 結(jié)構(gòu)YAML 能否解析、必填字段是否存在、字段名是否拼錯直接報錯L2 類型類型是否正確、枚舉值是否合法、ID 格式是否匹配、字符串長度區(qū)間直接報錯L3 語義引用 ID 是否存在、標(biāo)簽是否在詞表內(nèi)、contradicts是否雙向、日期是否合理警告或報錯可配分檔的意義在于漸進(jìn)。我剛遷移存量數(shù)據(jù)的時候只開 L1跑通之后開 L2最后才開 L3。如果一開始全開幾百條歷史記錄會瞬間刷出上千條錯誤人會直接放棄。4.2 用 schema 描述 paper-mapL1 和 L2 我用 JSON Schema 描述存成schema/paper-map.schema.json。摘一段核心部分{ $schema: https://json-schema.org/draft/2020-12/schema, title: paper-map, type: object, required: [id, title, year, status, depth, problem, method, result, takeaway, tags], additionalProperties: false, properties: { id: { type: string, pattern: ^p[0-9]{4}-[0-9]{3}$ }, year: { type: integer, minimum: 1950, maximum: 2100 }, status: { enum: [inbox, reading, digested, archived] }, depth: { enum: [skim, digest, deep] }, problem: { type: string, minLength: 30, maxLength: 120 }, method: { type: string, minLength: 60, maxLength: 300 }, result: { type: string, minLength: 20, maxLength: 300 }, takeaway: { type: string, minLength: 10, maxLength: 150 }, tags: { type: array, minItems: 1, uniqueItems: true, items: { type: string } }, builds_on: { type: array, items: { type: string, pattern: ^p[0-9]{4}-[0-9]{3}$ } }, contradicts: { type: array, items: { type: string, pattern: ^p[0-9]{4}-[0-9]{3}$ } }, same_thread: { type: array, items: { type: string, pattern: ^p[0-9]{4}-[0-9]{3}$ } } } }幾個關(guān)鍵選擇解釋一下。additionalProperties: false是故意的——它會把字段名拼錯的情況直接抓出來。我踩過一次坑把takeaway打成了takeaways結(jié)果所有記錄的該字段都被當(dāng)成未填而 schema 沒報錯因為多了個未知屬性而已。加上這一行之后這類錯誤立刻暴露。minLength這些約束別設(shè)得太樂觀。我一開始給problem設(shè)了 50 字下限結(jié)果發(fā)現(xiàn)自己寫的時候經(jīng)常剛好卡在 45 字左右然后再也沒法精簡。后來放到 30 字反而寫得更順。約束的目的是攔住敷衍不是逼你注水。4.3 語義校驗得自己寫腳本L3 的規(guī)則 schema 表達(dá)不了得寫腳本。我用一個單文件腳本tools/validate.py核心邏輯大概是這樣import sys, re, yaml, json from pathlib import Path ROOT Path(__file__).resolve().parent.parent TAGS set(json.loads((ROOT / schema/tags.json).read_text())) def load_all(folder): records {} for p in sorted(Path(folder).glob(*.yaml)): data yaml.safe_load(p.read_text()) records[data[id]] (p, data) return records def check_semantics(records): errors, warns [], [] for rid, (path, r) in records.items(): for tag in r.get(tags, []): if tag not in TAGS: errors.append(f{path.name}: 未知標(biāo)簽 {tag}) for field in (builds_on, contradicts, same_thread): for ref in r.get(field, []): if ref not in records: errors.append(f{path.name}: {field} 引用了不存在的 {ref}) if ref rid: errors.append(f{path.name}: {field} 不能引用自己) if r[status] digested and r[depth] skim: warns.append(f{path.name}: 狀態(tài)已消化但深度為 skim確認(rèn)一下) return errors, warns if __name__ __main__: records load_all(sys.argv[1] if len(sys.argv) 1 else papers) errs, warns check_semantics(records) for w in warns: print(f[warn] {w}) for e in errs: print(f[error] {e}) print(f\n共 {len(records)} 條記錄{len(errs)} 個錯誤{len(warns)} 個警告) sys.exit(1 if errs else 0)這段腳本里最值得說的是最后那個sys.exit(1 if errs else 0)。退出碼決定了它能不能接進(jìn)自動化流程——返回非零鉤子就會攔截提交。警告只打印不攔截因為警告類規(guī)則總有合理的例外硬攔會讓人想辦法繞過。還有一處細(xì)節(jié)status digested卻depth skim我設(shè)成了警告而不是錯誤。原因是有些論文確實只看摘要就能判斷不值得深讀直接標(biāo)已消化是合理的腳本沒資格替人做這個決定。4.4 接進(jìn)提交流程腳本單獨跑很容易忘得掛到動作路徑上。我用的是提交前鉤子配置文件寫成這樣repos: - repo: local hooks: - id: paper-map-validate name: paper-map 字段校驗 entry: python tools/validate.py papers language: system files: ^papers/.*\.yaml$這樣每次改動papers/下的文件提交前都會自動跑一遍。如果只是想臨時跳過用--no-verify也行但我建議別養(yǎng)成習(xí)慣——一旦開始繞過整套約束就名存實亡了。除了鉤子我還加了一個每周跑一次的批量檢查輸出一份清單哪些記錄還是inbox狀態(tài)超過 30 天、哪些reading卡了兩周沒動。這個清單比校驗錯誤更有用它管的是進(jìn)度而不是格式。一個實操建議把校驗?zāi)_本的輸出做成兩行摘要放在最前面比如共 214 條3 錯 7 警。人掃一眼就知道要不要細(xì)看比刷屏幾十行錯誤友好得多。4.5 報錯信息要能直接指導(dǎo)修改這條單獨拎出來說因為它是決定這套東西能不能堅持下去的關(guān)鍵。校驗?zāi)_本最常見的失敗不是漏檢而是報了錯但看不懂怎么改。我最初的報錯是validation failed: papers/p2024-031.yaml??吹竭@個我完全不知道哪里錯了還要自己去讀 schema。后來改成三段式文件名、字段、期望值。比如[error] p2024-031.yaml: method 長度 41低于下限 60 [error] p2024-037.yaml: tags 含未知標(biāo)簽 目標(biāo)識別最接近的是 目標(biāo)檢測 [error] p2024-052.yaml: builds_on 引用了不存在的 p2023-118第二條那個最接近的是我后來加的用編輯距離算一下就行幾行代碼的事但省下來的時間很多——人看到未知標(biāo)簽第一反應(yīng)是自己造詞錯了還是拼寫錯了給出候選詞就能立刻判斷。5. 存量記錄遷移把兩百多條歷史筆記塞進(jìn)新字段新體系上線時最麻煩的不是設(shè)計是存量。我當(dāng)時有 230 多條散落在各處的舊筆記格式五花八門。直接重寫不現(xiàn)實我的做法是三步走。5.1 先做一次空字段統(tǒng)計第一步不是動手填是量化缺口。我寫了個小腳本把舊筆記按行切分粗略映射到新字段然后統(tǒng)計每個字段的填充率。結(jié)果出來我嚇了一跳method填充率 62%limitation只有 11%takeaway只有 8%。這個數(shù)據(jù)直接影響了我的策略。如果limitation和takeaway的缺口是均勻分布的那我可能要重新考慮這兩個字段是不是必要但 11% 和 8% 這個量級說明它們不是難填而是從來沒被要求填過。這反而驗證了設(shè)字段的價值——正是因為沒強制這兩塊最有價值的信息一直是空白的。5.2 半自動填充別指望全自動第二步是補全。我試過用腳本從摘要里抽句子填problem和result效果很差——抽出來的句子大多是背景鋪墊或者帶著一堆限定語的長句讀起來不像人話。后來改成腳本生成草稿 人工確認(rèn)效率高很多。具體做法是腳本從原文里抽出摘要的前兩句、結(jié)論段的第一句拼接成一個草稿放進(jìn)method的位置同時在前面加一個TODO:標(biāo)記。然后我逐條過把TODO改寫成真正的機制描述。230 條里能直接改兩句就用的大約三分之一需要重讀的另外三分之一剩下的三分之一直接歸檔——因為我發(fā)現(xiàn)有些筆記對應(yīng)的論文我根本不記得為什么存它了說明它對我沒有價值。這個三分之一歸檔的結(jié)果值得說一下。遷移過程本身是一次盤點它強迫你重新面對每一條記錄而很多記錄在重新面對的那一刻就自動失效了。所以別把遷移當(dāng)成純負(fù)擔(dān)它是有產(chǎn)出的。5.3 復(fù)核要分優(yōu)先級第三步是人工復(fù)核。我沒有平均用力而是按一個優(yōu)先級隊列來先復(fù)核被其他記錄引用過的也就是在圖里有入邊的節(jié)點再復(fù)核digested狀態(tài)的最后才是inbox里的。理由很直接被引用過的記錄一旦有錯錯誤會在整個引用鏈上傳播而inbox里的記錄本來就只是個待辦錯了也不影響什么。這個排序讓我在只復(fù)核了 40% 記錄的情況下把最關(guān)鍵的那部分全部校準(zhǔn)了。6. 跑起來之后的檢索、迭代與踩坑6.1 幾個高頻查詢組合體系跑順之后最有價值的不是記錄得很整齊而是能做一些以前做不了的查詢。我常用的有這么幾個按問題域 深度篩tags含某個任務(wù)詞 且depth為deep用來快速定位某個方向上我真正讀懂過的論文。這個組合在寫相關(guān)工作章節(jié)時特別省時間因為它把看過和讀懂區(qū)分開了。反向引用查詢給定一篇論文找出所有builds_on指向它的記錄。這相當(dāng)于一個局部的影響力分析能看出某篇論文在你的閱讀范圍里被后繼工作繼承了幾次。如果一篇論文被繼承了很多次而你只記了一筆說明它值得回去重讀。沖突對掃描把所有contradicts關(guān)系拉出來看看哪些沖突對集中在同一個標(biāo)簽下。集中出現(xiàn)的地方往往就是該領(lǐng)域真正沒定論的點也是最容易出成果的地方。6.2 什么時候該加字段什么時候該刪字段體系的迭代我定了個簡單規(guī)則某個信息你連續(xù)三次在自由備注里重復(fù)寫就該升格成字段某個字段連續(xù)半年沒有參與過任何篩選就該刪。前一條我實際觸發(fā)過兩次。一次是數(shù)據(jù)集我在備注里反復(fù)寫用了什么數(shù)據(jù)后來升格成dataset字段一次是代碼是否可用也是為了快速篩可復(fù)現(xiàn)的工作升格成了code字段。后一條也觸發(fā)過。review_at我本來設(shè)成必填強制自己排復(fù)習(xí)時間結(jié)果半年后發(fā)現(xiàn)我從來沒有按這個字段做過任何查詢每次都是隨手翻。后來改成選填只在當(dāng)時沒看懂、想以后再看的記錄上填使用率反而上去了。6.3 我踩過的三個坑第一個坑是過度規(guī)范化導(dǎo)致記錄變慢。有段時間我要求method必須寫滿 150 字結(jié)果每寫一條要磨十分鐘一周只記了三條。后來把下限降到 60 字記錄速度回來了質(zhì)量也沒明顯下降。約束的作用是防敷衍不是防簡潔。第二個坑是忽略 YAML 的類型陷阱。year我一開始寫成了字符串2023在 schema 里定義的是integer校驗直接炸了。更隱蔽的是id里的p2024-031YAML 會把它當(dāng)普通字符串沒問題但如果哪天寫成純數(shù)字的2024YAML 會解析成整數(shù)pattern校驗就永遠(yuǎn)通不過。所以我的規(guī)范里加了一條所有 ID 類字段必須帶字母前綴。第三個坑是把校驗錯誤當(dāng)成噪音。有一陣子警告積累到幾十條我開始習(xí)慣性忽略腳本輸出。這就是危險信號——一旦輸出被無視整套驗證就等于不存在。后來我給自己定了個規(guī)矩警告數(shù)超過 5 條就停下來清理寧可少記幾條新的也不讓積壓。這個規(guī)矩執(zhí)行了幾個月效果比任何技術(shù)改進(jìn)都明顯。6.4 一個不太起眼但很省事的技巧最后分享一個小的。我在倉庫根目錄放了一個templates/paper.yaml里面所有字段都預(yù)填了占位符和長度提示比如id: p2025-000 # 格式 p{年份}-{三位序號} title: year: 2025 status: inbox # inbox | reading | digested | archived depth: skim # skim | digest | deep problem: # 30–120 字寫缺口不寫領(lǐng)域 method: # 60–300 字寫機制不寫流程 result: # 有量化信息或明確結(jié)論 limitation: takeaway: # 對我當(dāng)前工作有什么用 tags: [] # 從 schema/tags.json 里選新建記錄時直接復(fù)制這個文件改個 ID 就開始填。注釋就寫在行尾填的時候眼睛掃一眼就知道邊界在哪不用再去翻規(guī)范文檔。這個改動聽起來微不足道但它把查規(guī)范這個動作從流程里消掉了而正是那些需要額外查文檔的小摩擦最后會把習(xí)慣磨沒。這套東西我從最開始的 230 條舊筆記跑到現(xiàn)在六百多條字段改過四輪校驗?zāi)_本重寫過兩次?;仡^看真正起作用的不是字段設(shè)計得多精巧而是那條最簡單的原則能交給機器判斷的規(guī)則一條都不要留給人。