逐項檢視原始架構的每個模組,提出替代方案與待確認問題。
目標:讓 AI 從「被動接收資料」變成「主動使用工具」。
RAG(Retrieval-Augmented Generation)在 2023-2024 年成為 AI 應用的標準做法。當時的模型 context window 小、推理能力有限,需要系統幫忙篩選資料再餵給模型。這個思路在當時是合理的。
但現在的模型已經不一樣了。Claude Sonnet / Opus 有 200K+ 的 context window,能理解複雜指令、使用工具、做多步推理。模型的能力已經超越「被動接收 chunks 再組合回答」的角色 — 它有能力自己判斷需要什麼資料、自己去找。
這份文件不是在說 RAG 沒用。RAG 的語意搜尋在某些場景仍然有價值。但當我們把 所有資料存取 都建立在 RAG 上面時,等於是在用 2023 年的假設去設計 2026 年的系統。
原架構每次請求都自動觸發三路向量搜尋,然後把結果全部塞進 context。這代表兩件事:
第一,AI 沒有選擇權。不管使用者問什麼,系統都會搜三個 collection、把命中的 chunks 注入。AI 看到的資料是系統幫它挑的,不是它自己判斷需要的。有時候使用者只是閒聊打招呼,系統還是會去撈基因報告和專科知識。
第二,上下文是不完整的。把一份基因報告切成 chunks 再用相似度撈,AI 拿到的只是跟 query 最像的幾個片段,不是完整的文件。它看不到前後文、看不到報告的整體結論、沒辦法比較不同指標之間的關係 — 因為那些資訊分散在其他 chunk 裡,而那些 chunk 這次沒有被搜到。AI 是在拼拼圖,但永遠只拿到幾塊。
這兩個問題加在一起,意味著我們在限制 AI 的能力,而不是在發揮它的能力。
與其幫 AI 決定該看什麼,不如建一個「環境」讓它自己運作。這個概念叫做 AI Harness — 包圍在 AI 外面的系統,提供工具和資料的存取介面,但不替 AI 做決策。
具體來說:AI 像一個登入系統的使用者。它有目錄可以瀏覽(ls)、有檔案可以讀取(cat)、有索引可以查閱、有技能可以載入、有子代理可以派遣。它根據使用者的問題,自己判斷這次需要什麼資料、用什麼工具去取。
這不是理論 — 這種模式已經被 Claude Code、Cursor、Windsurf 等 AI 編程工具驗證過了。它們讓 AI 在一個有工具的環境裡自主運作,效果遠好於把所有程式碼切 chunk 做 RAG。同樣的思路可以應用在健康諮詢場景。
原架構:系統替 AI 決定該看什麼(RAG 自動注入)。
建議方向:系統給 AI 工具,讓它自己決定該看什麼(Harness 模式)。
使用者提問 → 系統自動把 query embed → 同時搜 3 個 Qdrant collection → 把搜到的 chunks 全部塞進 system prompt → Claude 回覆
使用者提問 → AI 看到問題 → 判斷需要什麼資料 → 用工具(bash / skill / 記憶檢索 / 語意搜尋)去取 → 基於讀到的內容回覆
原架構把使用者的基因報告 embed 進 Qdrant,用向量搜尋撈相關片段。
但如果報告本身不長,直接存成檔案讓 AI 讀完整版會更好。
基因報告是一份有結構的文件 — 有分類、有數值、有結論。使用者問「我的心血管風險高嗎?」的時候,AI 不只需要看到心血管那一段,它可能還需要看到相關的代謝指標、家族史風險、整體結論,才能給出完整的回答。
RAG 搜到的是跟 query 最相似的幾個 chunk。如果心血管風險的結論寫在報告的最後一段「整體評估」裡,但 chunk 切割剛好沒有把那段跟心血管關鍵字放在一起,這段就會被漏掉。
更直接的問題是:如果一份報告只有幾頁,為什麼要切?現代模型的 context window 足以裝下整份報告。讓 AI 直接讀完整報告,比讓它拼湊碎片更有效。
報告被切成片段,embed 進 user_genes_report collection。每次搜尋靠 score threshold 0.72 撈相關的 chunks。AI 只看到片段,看不到完整報告。
每個使用者的報告存成獨立檔案,有一個 index/llms.txt 告訴 AI 有哪些報告。AI 看索引、判斷要讀哪份、然後讀完整內容。
報告有多長?如果只有幾頁結構化內容,直接全文讀取最有效率。如果長達數十頁,可能需要分段或搭配摘要索引。
報告頻率?一年一次?每月?更頻繁?這影響索引的設計 — 如果報告很多,index 需要更豐富的 metadata(日期、類別、摘要)讓 AI 快速篩選。
報告格式統一嗎?如果不同類型的檢測(全方位 vs. 單項)格式差異大,索引裡需要標明類型,讓 AI 知道每份報告涵蓋哪些檢測項目。
原架構用向量相似度做「專科路由」— 本質上是用 embedding 當分類器。
但 AI 模型本身就有理解語意的能力,給它一份目錄就能自己判斷。
原架構用 embedding 相似度決定「這個問題屬於哪個專科」。但這其實是用一個比較笨的方法(向量距離)去做一個 LLM 本身就很擅長的事(理解語意分類)。
Claude 看到一份索引寫著「可用的專業領域:睡眠、營養、心血管、抗老」,它完全有能力判斷使用者問的「我最近一直睡不好,跟基因有關嗎?」應該對應到睡眠領域。不需要先 embed 這句話,再去跟四個 collection 算 cosine similarity。
Skill 系統還有一個實際的好處:維護成本低很多。新增一個領域 = 加幾個 markdown 檔案 + 更新索引。不需要建新的 Qdrant collection、設計 embedding pipeline、調 threshold。知識的更新也是直接改檔案,不用重新 embed。
另外,Skill 可以同時包含知識和回答指引(persona)。原架構把這兩個拆開 — specialist_knowledge 放知識,sysSpecialist 放 persona — Skill 系統可以把它們合在一起,因為「怎麼回答」跟「知道什麼」本來就是一體的。
query embed → 搜 specialist_knowledge collection (threshold 0.72) → 命中的專科知識注入 context → 另外載入該專科的 sysSpecialist persona prompt
AI 有一份 skill index,列出所有可用的領域(睡眠、營養、心血管、抗老)。AI 看到使用者問題後,自己決定載入哪個 skill。Skill 本身包含知識和回答方式指引(取代獨立的 persona prompt)。
重要原則:如果 skill 知識庫沒有提到的內容,AI 仍然可以回答, 但必須標明「這是我的推論,不在目前的專業知識庫中」。 維持透明度,但不限制 AI 的能力。
Skill 吃掉 Persona。原架構的 sysSpecialist(專科 persona prompt)可以整合進 skill 的 guidance.md 裡。不需要額外維護一層 persona prompt,減少系統複雜度。
原架構把使用者的對話記憶 embed 進 Qdrant,threshold 設 0(全撈)。
建議改成結構化的 wiki 網路 — 有索引、有連結、有層次。
原架構的 user_memory collection 有一個耐人尋味的設定:score threshold 設為 0。這等於「不管相似度多低,全部都回傳」。如果向量搜尋的結果是不做篩選全部撈出來,那用向量搜尋的意義是什麼?
記憶的價值在於關聯性,不在於相似度。使用者上個月提過她在減肥,今天問「為什麼我一直好餓?」— 這兩件事的 embedding 距離可能很遠(減肥 vs. 飢餓感),但它們在語意上是高度相關的。向量搜尋不擅長處理這種跨主題的關聯。
Wiki 網路的做法是讓記憶之間有明確的連結。「減重紀錄」這份記憶連結到「飲食偏好」和「健康目標」。AI 看到使用者問飢餓感,從索引找到「減重紀錄」,順著連結就能找到飲食偏好的細節。這是結構化導航,不是機率性的相似度匹配。
而當記憶量累積到主代理沒辦法全部掃一遍的時候,子代理檢索機制可以幫忙。主代理把使用者的問題交給一個專門的記憶子代理,子代理掃描 wiki 網路後回傳最相關的幾個頁面路徑和摘要。主代理再自己決定要不要深入讀。
每段記憶存成 embedding。搜尋時 threshold 設 0,等於把所有記憶都撈出來塞進 context。隨著對話累積,context 會被不相關的記憶佔滿。
記憶存成互相連結的 Markdown 檔案(Obsidian 風格)。AI 常駐載入一個精簡的頂層索引。記憶量大時,派子代理去搜尋相關記憶,回傳路徑和摘要。
RAG 不是不能用。語意搜尋有時比關鍵字搜尋更準。
但它應該是 AI 工具箱裡的其中一把工具,由 AI 自己決定什麼時候用。
這裡要強調的是:我們不是在說 RAG 沒用。語意搜尋在某些場景確實比關鍵字搜尋更準 — 比如使用者用口語描述一個症狀,跟知識庫裡的醫學術語可能字面上完全不同,但語意是相關的。
問題是原架構把 RAG 當成唯一的資料存取方式。每次請求都跑向量搜尋,不管這次搜尋有沒有意義。使用者問「你好,今天天氣不錯」,系統照樣去搜三個 collection。
把 RAG 變成一個 tool,意味著 AI 自己判斷什麼時候需要語意搜尋。很多時候 AI 已經知道要讀哪份報告(因為它看了索引),直接讀就好,不需要繞一圈做 embedding → cosine similarity → 取 top-k。語意搜尋留給真正需要模糊匹配的場景 — 比如在大量知識庫裡找一個使用者用不精確語言描述的概念。
每次請求都跑 RAG。三個 collection 並行搜尋。embedding + vector search 是唯一的資料存取方式。
提供 semantic_search 工具,AI 覺得需要模糊語意匹配時才呼叫。結果回傳來源路徑 + 片段,AI 可以進一步讀完整檔案。跟 bash grep 並列,不是凌駕其上。
操控權的轉移:不是系統每次都自動跑 RAG 塞東西給 AI, 而是 AI 自己評估「這個問題我需要語意搜尋嗎?還是直接讀檔就好?」 把決策權還給 AI。
手動設計 4 層 system prompt,前 3 層 cache(TTL 5min),最後 1 層動態。這是自己發明的 caching 策略。
Claude 手動標 cache breakpoint + TTL。OpenAI / Google 自動做。不需要自己設計分層策略 — 按照官方文件的做法,對話到哪就 cache 到哪。
原架構有標示 SSE Stream,這是正確的。
Vercel AI SDK 等框架內建支援。不是架構設計上的挑戰。
原架構選擇 Go 語言和 Hertz 框架。
重點是架構設計思路。語言框架和資料庫的選擇可以保持彈性。
| 模組 | 原架構 | 建議方向 |
|---|---|---|
| 資料存取模式 | 系統自動 embed + 搜尋 + 注入 | AI 透過工具自主查找 |
| 基因報告 | chunk → embed → Qdrant (threshold 0.72) |
完整檔案 + llms.txt 索引 → AI 自己讀完整報告 |
| 領域知識 | specialist_knowledge collection + sysSpecialist persona |
Skill 系統(知識 + 回答指引打包) Persona 被 Skill 吸收 |
| 使用者記憶 | user_memory collection (threshold 0 = 全撈) |
Wiki 網路 + 常駐索引 + 子代理檢索 |
| RAG | 架構骨架(每次都跑) | 工具之一(AI 決定用不用) |
| Prompt Cache | 自行設計 4 層分層 | 依照供應商官方做法 |
| AI 的角色 | 被動:吃什麼由系統決定 | 主動:自己判斷需要什麼 |
| 擴展新知識 | 新 Qdrant collection + embedding pipeline | 新 skill 檔案 + 更新索引 |