Lab-confirmed:本文 reference 實作來自我(Maki)自家在跑的個人 PKI(mk-brain / ERIKA Bot / Inbox Bot 等),不是 enterprise production case study。Code snippet 為 reference 設計,非實際在跑的版本。
RAG 會引用文件,不代表它在引用事實
很多團隊一談到 AI 幻覺,就會把 RAG 當成解藥。
模型不可靠,那就讓它去撈文件。
模型會亂講,那就把答案綁回知識庫。
這個直覺沒有全錯。
但如果你的知識庫本身沒有資料邊界,RAG 只是把錯誤變得更像真的。
PoisonedRAG 最刺耳的地方,不是它證明有人能投毒。
而是它證明需要投進去的惡意文件非常少。
DeepMind Agent Traps 把這件事再往前推一步:極小比例的污染,就足以把大量查詢導向錯誤回答。
對 builder 而言,這不是「模型會不會 hallucinate」那麼單純。
這是你的 retrieval pipeline 有沒有把 raw source、LLM 生成物、人工確認內容混成同一桶。
大多數團隊真正缺的不是更大的 embedding model。
而是來源鏈、祖先關係、可追溯性,以及一個很樸素的判斷:
這段文字到底算證據,還是只算 AI 自己講過的話?
如果沒有這層 distinction,向量資料庫就會慢慢變成內容果汁機。
今天一篇惡意文件被 embed。
明天一個自動摘要被再餵回去。
後天客服 agent 就開始引用一段「其實從來沒有人確認過」的內容,還說得理直氣壯。
Data boundary 要處理的,就是這條從資料到事實的升格流程。
Data Boundary:哪一些內容算證據
RAG 的問題很少是「有沒有撈到東西」,更常是「撈到的東西憑什麼算可信」。
Case:
主:PoisonedRAG 學術論文(2024,arXiv)——實證5 個惡意文件注入到 RAG 知識庫即可達到 90% 目標查詢的操控率。Indirect prompt injection 經由 RAG context 進入 LLM 流程,被 AI engine 答覆中當「事實」呈現。
次:DeepMind Agent Traps 論文(2026,圈內傳閱中)——0.1% 資料投毒可讓 80% 查詢歪掉。意味著就算你公司 Confluence / Notion / Slack 知識庫只有極小比例被植入惡意內容,整個 AI 客服或內部 RAG 助手會大規模產出錯誤答案——而多數團隊半年沒有任何 audit / 偵測機制。
核心問題:你的 RAG context 裡有沒有「llm_derived 的污染源」混進「raw_source」當事實?
Rule:每個 chunk 必須帶 provenance tier 標籤——不能讓 LLM 自己生的內容再餵回 training/RAG(Ouroboros effect,arXiv 2509.10509 已實證)。
如果沒有來源層級,retrieval quality 再高也只是更精準地撈到污染。
| Threat | Mitigation | Validated in (reference) |
|---|---|---|
| RAG poisoning(PoisonedRAG: 5 個惡意文件達 90% 操控率) | Tier 系統:raw_source / llm_derived / human_confirmed;retrieval 階段 filter,只 surface trusted tier | mk-brain knowledge layer |
| Ouroboros:LLM output 被當 raw 餵回 | 寫入時驗證 upstream_ids 不含自身祖先;每 entry 帶 provenance_hash 防 tier 被竄改 | memory-hall (mk-brain DB) |
| Sensitive Information Disclosure(LLM02) | PII 在 ingest 階段 redact(用 Microsoft Presidio MIT 純離線);不靠 LLM 自己過濾 | mk-brain ingest pipeline |
Reference 實作(示意非 production code,依你環境調整):
# Provenance tier system — 防 RAG 投毒 + Ouroboros
from enum import Enum
from pydantic import BaseModel, Field
class SourceTier(str, Enum):
raw_source = "raw_source" # 網頁、webhook 原始
llm_derived = "llm_derived" # LLM 產的 summary
human_confirmed = "human_confirmed" # 人類覆核過
class MemoryEntry(BaseModel):
content: str
source_tier: SourceTier
upstream_ids: list[str] = Field(default_factory=list)
provenance_hash: str # HMAC(content + tier + upstream_ids)
def retrieve_for_rag(query: str) -> list[MemoryEntry]:
"""RAG 檢索時不讓 llm_derived 進 production answer 的 context"""
candidates = vector_search(query, top_k=20)
return [c for c in candidates if c.source_tier != SourceTier.llm_derived]
對應 OWASP:這篇先看哪幾條
LLM04:2025 Data Poisoning:核心就是外部知識來源被投毒之後,RAG 仍把它當作可用證據。LLM08:2025 Vector and Embedding Weaknesses:向量層不是中立的儲存桶,檢索與排序本身就是攻擊面的組成部分。LLM02:2025 Sensitive Information Disclosure:如果你把 PII 清理放到回答階段才做,通常已經太晚,應該在 ingest 就切掉。
想動手做? 這篇文的概念有對應的 5 課完整課程: 資料邊界 101 →
也可以接著看下一個邊界:權限邊界