Retrieved Context 的 Provenance
沒有 provenance 的 retrieval,等於把每一段字都偽裝成同一級證據。
Hook
你做了一個整理內部知識的 RAG pipeline。
它會從 memhall 撈筆記。
也會從向量資料庫撈文件切片。
也會把舊會議記要一起交給 LLM 做綜整。 某次你查: 「我們上次 deploy freeze 的決策到底是什麼?」 系統撈回來五筆內容。 其中四筆看起來像原始資料。 另一筆是兩週前另一個 LLM 產生的「會議摘要」。
但這筆摘要在 packing 時沒有被標示。
它只是安靜地混在其他 context 裡。 下游 LLM 看到它寫: 「團隊已同意把所有 emergency patch 改成先上 production 再補 ticket。」
於是模型很自然地把它當成共識。 問題是, 那其實不是共識。 那只是兩週前另一個模型看錯會議內容後,
寫進 memory 的錯摘要。
這時候真正缺的不是更會總結的模型。 而是讓下游模型看見: 這段內容到底是 raw source,
還是某次 LLM 推導後的產物。 換句話說, retrieved context 也要帶 provenance。
而且要在 packing 時就帶進去。
Learn
先畫清楚跟 data-boundary-101 L02 的分工
data-boundary-101 Lesson 02 已經教過:
寫入資料庫時,
每筆 entry 應該怎麼標 source_tier、
upstream_ids、
provenance_hash。
那一課處理的是 write path。 本課不重講那套寫入規則。 本課專注的是另一個問題:
當你把資料從 store 撈出來,準備餵給下游 LLM 時,provenance 要怎麼一起傳遞?
這是 retrieval path。
很多系統寫入時其實有 metadata。
但一到 prompt packing,
就把 metadata 丟掉了。
最後模型看到的只剩一團純文字。
那等於 provenance 在最關鍵的一跳失效。
為什麼 retrieved 時還要再標一次
有人會問:
「資料庫裡不是本來就有 source_tier 嗎?」
有,
但那是給你的系統存的。
下游 LLM 並不會自己去查資料表。
它只看得到你最後塞進 context window 的那一串字。
如果你在 packed context 裡不把 tier 一起帶上,
模型收到的就是無標籤純文本。
無標籤純文本最大的問題不是資訊不夠。
而是每一段內容都長得像同級證據。
這正是錯誤推論最常發生的地方。
最小 packed context 格式
本課先用一個很簡單的包法:
<context source="memhall:e123" tier="raw_source">
...
</context>
<context source="memhall:e456" tier="llm_derived">
...
</context>
這個格式沒有學術包袱。 它只是做到兩件事:
- 每段內容有獨立邊界
- 每段內容帶著最少必要 provenance
source 讓模型和工程師都知道這段來自哪裡。
tier 則直接告訴模型:
這是一手來源, 還是過去某個 LLM 的推論。
tier 不只是 metadata
很多人第一次做 packing,
會把 provenance 當成註腳。 好像只是為了方便除錯。 這樣低估了它。 在 retrieved context 裡,
tier 其實是下游 LLM 的閱讀提示。
它像一個免責聲明。
也像一個處理指引。
同樣一句話:
如果它來自 raw_source,
模型可以把它當第一手證據。
如果它來自 llm_derived,
模型就應該把它當成過往的推論、 摘要、 或中間產物。 它可以參考。 但不該自動升格成事實。
一個下游模型應有的 reading stance
如果你把 packed context 標好了,
下游 LLM 至少應該被預期做到這三件事:
- 看到
raw_source時,優先當證據讀 - 看到
llm_derived時,當成可疑但有用的輔助訊號 - 遇到不同 tier 衝突時,優先報告衝突,而不是自己悄悄合併
這裡不要誤會。 我們不是說 LLM 一定會百分之百照做。 而是說: 如果你連 tier 都沒給它看, 那它連有機會做對都沒有。
llm_derived 為什麼特別需要標
llm_derived 最大的危險不是它容易錯。
而是它很像人話。 一段 AI 摘要只要文筆夠順,
隔兩週再被別的 agent 撈出來時,
很容易看起來像某種「已整理過的知識」。
這就是 context 邊界裡最陰的污染。 它不一定有明顯惡意。 但它會默默把模型的事實基礎換掉。
所以你一定要讓下游模型知道: 這不是原始資料。 這是過往某次模型加工過的內容。
三個禁忌
這一課有三條很硬的禁忌。 不是建議。 是禁忌。
禁忌一:不得把 llm_derived 當 instruction 餵回
這條跟 data-boundary-101 的 Anti-Ouroboros 是同一個精神。
LLM 摘要可以存在。
可以檢索。
可以當線索。
但不可以因為它寫得很完整,
就把它當成新的 instruction 或 ground truth 餵回。
否則你不是在做 retrieval。 你是在放大過去模型的偏誤。
禁忌二:不得在 packed context 裡混入未標 tier 的內容
只要有一段沒標 tier, 整個 pack 的可讀性就被破壞。 因為下游模型無法分辨: 那段是工程師忘了標, 還是它本來就跟其他段落不同級。 所以規則很簡單: 沒有 tier, 就不要進 pack。 寧可少一段, 也不要讓一段無標籤文字混進來。
禁忌三:不得讓 LLM 自己決定要用哪些 tier
這點很常被忽略。 有些團隊會把所有 tier 都塞進去, 再對 LLM 說: 「請自己判斷哪些比較可信。」
這是把 boundary 判斷外包給模型。 不行。 哪些 tier 可以進 pack,
應該由 deterministic policy 決定。 例如:
- customer-facing answer 只允許
raw_source+human_confirmed - internal research mode 才允許附帶
llm_derived
能不能進 context, 是系統決策。 不是模型自由裁量。
一個最小 pack_context(...)
下面這段 demo 只示範 pack,
不示範 retrieval ranking。
因為本課要看的不是搜尋演算法。
而是 provenance 怎麼跟著每筆 entry 一起進去。
你應該看到:
<context source="memhall:e123" tier="raw_source">
deploy freeze 原始會議逐字。
</context>
<context source="memhall:e456" tier="llm_derived">
上次會議摘要:建議縮短 freeze。
</context>
注意這個格式的價值不是 XML 漂不漂亮。
而是每段 retrieved content 都有明確包裝。 這讓你後面要做:
- filtering
- audit
- leak investigation
- conflict reporting
時都有依據。
pack 的單位要以 entry 為主,不要整包混成一坨
如果你把五筆結果先串成一大段,
再只在最外層標一個 tier,
你其實已經失去 granularity。 下游模型只知道: 「這整包也許來自 memhall。」 但不知道哪一段是 raw, 哪一段是 LLM 摘要。 所以 pack 的最小單位應該是 entry。
不是 query response。
entry-level 的標籤,
才足夠支撐真正的 provenance reasoning。
把 provenance 當成 retrieval contract
你可以把這件事理解成: retriever 不是只負責找內容。
retriever 還負責把內容的身份一起帶出來。
否則下游 generator 看到的只是去身分化的文字。 一旦文字被去身分化, 你就會很難回答這兩個關鍵問題:
- 這段是不是原始資料?
- 這段是不是舊模型的推論?
context boundary 的核心不是資料量。 而是身份感。
哪些欄位一定要露給下游模型
本課最少只要求兩個:
sourcetier
其他像 upstream_ids、
created_at、
author、
confidence,
可以視情況帶。
但 tier 幾乎是必帶。
因為沒有它,
模型無從知道要不要降低信任。
而 source 則讓回答可以回指。
回指不只是引用比較漂亮。
它也是日後查核與 debug 的起點。
這一課要先帶走的規則
先記住五條:
data-boundary-101Lesson 02 管寫入 provenance,本課管 retrieval packing- 每筆 retrieved context 都要帶
source與tier llm_derived不是 ground truth,更不是 instruction- 未標 tier 的內容不准混進 packed context
- 哪些 tier 可以進 pack,必須由系統 policy 決定
如果你只做到了「DB 裡有 provenance」,
但 prompt packing 時全丟掉,
那等於在最後一公尺把安全帶拆掉。
AI 協作:學了這個,跟 AI 怎麼配合?
這個技能在 AI 協作中的定位,
是讓你要求 AI 幫你設計 retrieval contract,
而不是只產生一個會把所有字串糊成大雜燴的 prompt builder。
你的人類優勢:
- 只有你能決定哪些 tier 在你的產品裡能被下游模型看到,哪些應該先被 filter 掉。
- 只有你能判斷某些
llm_derived內容是否只適合當內部研究訊號,而不適合進 customer-facing 回答。
可以這樣跟 AI 說:
我有一個 RAG retrieval pipeline。請幫我設計
pack_context(entries),把每筆 entry 包成<context source=\"...\" tier=\"...\">...</context>。請強調llm_derived不能當 instruction,也不要讓模型自己決定哪些 tier 該用。我要的是 retrieval path 的 packing 規則,不是寫入資料庫的 provenance 課。
Do
互動示範
挑戰任務
請寫 pack_context(entries: list[Entry]) -> str。每筆 entry 都要包成 <context source="..." tier="...">...</context>。測試資料有兩筆:("memhall:e1", "raw_source", "deploy SOP 原文") 和 ("memhall:e2", "llm_derived", "上次整理摘要")。最後印出 packed string。