如果要在一台工作站上做本地 PubMed/PMC 搜尋與問答

如果要在一台工作站上做本地 PubMed/PMC 搜尋與問答

假設我們要做一個只在一台工作站運行的文獻工具:它有類似 PubMed 的欄位搜尋;在合法可用時,能找到 PMC 全文中的段落;簡短答案也能準確回到原始證據。

我不會先下載全部資料,也不會先選 LLM。我會先在一個小而有代表性的範圍內做出真正可用的搜尋,量過效果和成本之後,只擴大那些證明有用的部分。

PubMed 和 PMC 是兩個不同的資料層

這個區別會決定整套系統的結構。

  • PubMed 提供書目記錄,以及在有收錄時提供摘要。NLM 每年發布一次 XML baseline,並每天發布 update。Download PubMed Data 的官方說明要求先載入 baseline,再按編號順序套用 update,並替換修訂或刪除的記錄。
  • PubMed Central(PMC) 為部分文獻提供全文。但免費閱讀不等於可以重用。PMC Open Access Subset 明確說明:並非所有 PMC 文章都可供文字探勘;授權因文章而異;自動取得內容必須使用 PMC 認可的服務。

原型階段先限定主題和年份,透過官方 API 取得有限樣本。要做可持續更新的本地 PubMed mirror,再改用年度 baseline 和依序套用的每日 update。PMC 全文只從認可的 OA subset 管道取得,而且每筆記錄都要保留該文章的授權資料。

不要爬取公開文章頁面。

在匯入之前,先定義第一個有用結果

先寫出 40–60 個這台工作站必須處理的查詢,至少包括:

  1. 精確的 PMID、PMCID、DOI、作者或標題片語;
  2. 縮寫及其全名;
  3. 用不同詞語表達的同一個生醫概念;
  4. 帶有年份、期刊、publication type 或 MeSH 條件的查詢;
  5. 答案位於某個已知段落的問題;
  6. 不同論文彼此矛盾的問題;
  7. 資料集中沒有充分答案的問題。

在調參之前,為每個查詢標出相關文章或段落。這就是評估集。沒有它,語意搜尋 demo 可能看起來很好,但精確識別碼、篩選條件和重要的「查無充分證據」都可能已經失效。

第一個里程碑應該很窄:

在一個主題樣本內,讓正確文章進入前十名,並能打開支持結果的摘要或 PMC 章節。

做到這一步之後才加入問答。

維護一份 canonical article ledger

用關聯式資料庫管理身分、版本、權利和匯入狀態;原始 XML 與派生索引分開保存。實用的記錄可以包括:

article_key
pmid | pmcid | doi | manuscript_id
title | abstract | journal | publication_date
authors | publication_types | mesh_descriptors
source_dataset | source_version | retrieved_at
license_code | license_url | reuse_class
xml_sha256 | parser_version | indexed_at

PMID、PMCID 和 DOI 是別名,不是可互換的主鍵。官方 PMC ID Converter API 可以對應已進入 PMC 的文章識別碼,也能返回版本資訊。大量處理時應使用 PMC 提供的批次 ID 表,不要逐篇發出數百萬次 API 請求。

把三種狀態分開保存:

  • 存在 PubMed 書目記錄;
  • 存在 PMC 全文記錄;
  • 該全文的條款允許預定用途。

這可以避免搜尋結果誤導讀者,以為每篇摘要都有本地全文,或每個 PMC 頁面都能再發布。

解析文獻結構,不要只留一袋文字

解析 PubMed XML 時,保留標題、摘要小節、作者、期刊、日期、publication type、化學物質和 MeSH heading。NLM 發布了現行的 PubMed DTD 與元素文件,程式不應依賴偶然的 XML 排列。

對允許使用的 PMC 全文,要保留文章標題、章節層級、段落次序、圖表說明、參考文獻和穩定識別碼。每個 passage 應有一個清楚地址,例如:

PMCID: PMC1234567
section: Results > Adverse events
paragraph: 4
source_sha256: ...

先按章節和段落邊界切分。只有遇到特別長的章節時,才以 token window 作為補充。單獨顯示一個 passage 時,它仍應可讀,也必須能回到原文的對應章節。

MeSH 當作有版本的詞彙資料匯入。保留 descriptor ID、preferred label、entry term 和 tree number,用於查詢建議和篩選;不要暗中把每個查詢展開到所有下位詞。

先做好詞彙檢索

生醫搜尋中有很多字串不應交給 embedding 猜測,例如基因符號、試驗識別碼、藥名、劑量、PMID 和精確片語。

我會用 Tantivy 這類精簡的 inverted index,建立大致如下的欄位:

pmid, pmcid, doi       exact + stored
title                  text + positions + stored
abstract               text + positions + stored
body                    text + positions
mesh_id                 exact, repeated
publication_type        exact, repeated
journal, year, language filters
passage_id, section     stored

PubMed 記錄以文章為單位建立索引;允許使用的 PMC 全文以 passage 為單位建立索引。兩層可以一起搜尋,但結果頁要明確標示「書目/摘要」和「全文」,兩者不是同一件事。

加入 vector 前先跑評估集。精確 ID 應該是確定性查找;片語和 filter 要能容忍大小寫及標點差異。每個結果都應顯示標題、來源 ID、日期、命中片段,以及由哪些欄位命中。

把 MedCPT 加成第二條檢索通道

詞彙搜尋會漏掉真正的同義改寫。到這時才測試 MedCPT,而不是取代已經正常工作的詞彙索引。

NCBI 發布了一組 MedCPT Query EncoderMedCPT Article Encoder。model card 使用 query encoder 處理短問題或搜尋詞,使用 article encoder 處理標題與摘要;兩者的 vector 位於同一空間。對應的 Bioinformatics 論文 說明了以 PubMed 搜尋紀錄中的 query/article pair 訓練,並在生醫檢索任務上評估的方法。

先遵循這個 input contract:

query_vector = encode_query(user_query)
article_vector = encode_article([title, abstract])

以穩定的內部 article ID 對應 vector。第一版程式內索引用 Faiss 就夠:它把固定維度 vector 加入索引並返回近鄰。metadata 和 rights filter 仍留在 article ledger;不要讓 vector 的序號成為論文唯一身分。

MedCPT article model card 展示的是標題和摘要。任意全文段落的 embedding 是新的實驗,不是文件保證的用途。如果需要 passage-level semantic search,就另外建立帶標籤的段落評估集並單獨量測。

融合名次,不要混合不可比的分數

每次查詢依序做:

  1. 直接處理 PMID、PMCID、DOI 和引號片語;
  2. 取得約 100 個詞彙候選;
  3. 取得約 100 個 MedCPT 候選;
  4. 移除不符合權利條件或使用者 metadata filter 的記錄;
  5. 用 reciprocal-rank fusion 合併兩組排名;
  6. 如有需要,只 rerank 前 20–30 筆;
  7. 在生成任何答案之前先顯示搜尋結果。

一個很小的 rank-fusion 函式就足夠:

def rrf(*ranked_lists, k=60):
    score = {}
    for rows in ranked_lists:
        for rank, article_id in enumerate(rows, start=1):
            score[article_id] = score.get(article_id, 0.0) + 1.0 / (k + rank)
    return sorted(score, key=score.get, reverse=True)

不要直接平均 BM25 和 cosine 的 raw score,它們不是同一種尺度。候選數量與 fusion 參數應在 held-out query 上調整,而不是用開發時反覆看過的例子。

讓問答成為證據之上的視圖

QA layer 收到的應是一小包 evidence,不是整個 corpus:

[E1] PMID ... — title + abstract sentence(s)
[E2] PMCID ... — Methods > Eligibility, paragraph 2
[E3] PMCID ... — Results > Primary outcome, paragraph 1

要求模型只根據這些 passage 給出短答;每個重要 claim 後附 citation;不同論文不一致時分開呈現;沒有足夠依據時回答「這個 collection 中沒有充分證據」。

回答完成後,再用程式檢查每個被引用的 evidence ID 都存在。文字旁持續顯示 evidence panel,讀者可直接打開原 passage。流暢的一段話末尾放三個連結,並不等於有 provenance。

對生醫文獻,我會把 “Search only” 設為預設。QA 是探索和整理文獻的 view,不是直接用於診斷或治療決策。NCBI 自己的 MedCPT model card 也有相同邊界。

在擴大到完整 feed 前先量測

記錄四組數字:

Layer實用檢查
Ingestionparsed、rejected、updated、deleted、rights-eligible、hash changes
RetrievalRecall@10、MRR、filter correctness、exact-ID success
QAsupported-claim rate、citation accuracy、no-answer abstention
Operationsindex time、peak RAM、disk per article/passage、query latency

樣本應大到足以包含難解析 XML、缺失摘要、重複識別碼、修訂和很長的 PMC 章節。實測每筆資料的 bytes 和 seconds,再據此推算。不要用一個小 demo 決定硬體或承諾 full-corpus build。

一個 768 維 float32 vector 在未計 index 和 metadata overhead 前,已佔 768 × 4 = 3,072 bytes。請測量實際索引的磁碟成本。float16 或 quantization 要在 full-precision 已建立 retrieval baseline 之後再測。

更新也是產品的一部分

第一次建立 index 不難;資料持續變化時仍可信,才是真正的系統。

  • 保存每個 ingestion batch 的 source filename、checksum 和處理結果;
  • 按發布順序套用 PubMed update,明確處理 revised 和 deleted citation;
  • 新年度 baseline 發布後重新建立,不要無限累積無法驗證的 patch state;
  • 只為 canonical title 或 abstract 已變更的記錄重算 embedding;
  • 在 manifest 保存 parser 和 model version;
  • 在 active index 旁建立新 generation,通過測試後以 atomic pointer 切換;
  • 在下一代驗證完成前保留一代 rollback。

介面應顯示本地 dataset 日期。“Local” 不應暗中變成「陳舊而且無法稽核」。

我會交付的最小架構

approved NCBI downloads
        ↓
immutable XML + batch manifest
        ↓
canonical article / license / ID ledger
        ├── Tantivy article + passage index
        └── MedCPT article vectors → Faiss
                    ↓
          filtered rank fusion
                    ↓
        search results + evidence cards
                    ↓
          optional cited QA view

每一層都能獨立建立和測試,因此適合先放在一台工作站上。從一個主題範圍開始,穩定 source ledger;等搜尋品質和儲存成本都有真實數據,再擴大 ingestion。

我維護 LazyingArt 本地、重視 provenance 的文件工具,也處理過多語書籍和研究 collection;但我沒有可供展示的 PMC 專用客戶 deployment 或客戶成果。LKT sample report 展示的是:在一個範圍有限、屬於專案自己的 collection 上,如何整理 data、rights、citation 和 go/no-go;它不是生醫系統成果。

任何 collection-fit 工作都會先以 metadata 和一個已確認權利的代表性樣本,在現有的一台機器上開始。硬體、完整 PubMed/PMC ingestion、production deployment、medical validation 和直接 clinical use,都不在這個有限檢查範圍內。

Leave a Reply