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