作者:@hi_im_isaac_、@learnwdaniel、@gaozenghao
備註:完整技術部落格的互動版本請見:https://www.cerebras.ai/blog/how-we-built-our-knowledge-base
員工每天向我們的內部知識庫提出超過 15,000 個問題。自三個月前推出以來,它已成為公司內部最廣泛使用的內部工具之一,被員工、自動化流程和 Agent 所使用。
在 Cerebras,我們的團隊涵蓋數據中心營運、晶片設計、硬體、訓練、推理、雲端平台等多個領域。隨著每年數百名新員工加入,我們的溝通渠道不斷被相同的問題淹沒:
- 「我在哪裡可以找到 X?」
- 「誰是 Y 領域的專家?」
- 「Z 是什麼?」

我們打造了 Cerebras Knowledge,幫助人們將人員和系統與有用的資訊連結起來。
讓數據在所在之處被運用
在組織內部尋找資訊並非易事。數據分散在各個工具中,每隔一段時間就會有人提出同樣的絕妙解決方案:把一切記錄在同一個平台上,讓所有資訊集中在一處。然而,單一事實來源的夢想,在實務上幾乎從未實現過。
資訊總是在最方便、最符合人體工學的地方產生:文件中的建議編輯、Slack 中的討論串、GitHub 中的程式碼引用、Jira 中的狀態元數據。這些平台專為特定領域量身打造,經過多年的產品工程和分析最佳化。在 Google Docs 中討論 pull request 會是一種糟糕的體驗。
因此,我們著手設計一個系統,要求對現有行為的改變降到最低。在數據收集方面,這意味著直接從每個平台提取數據。
知識庫的結構
我們的知識庫提供三項功能:
- 一個用於收集和儲存內部數據的平台。
- 一個用於查詢這些數據的平台。
- 一個強制執行身份驗證和授權的層級,並附帶稽核和分析功能。
核心是一個單一的 Postgres 資料表,其中儲存來自多個來源的嵌入向量、原始摘要和元數據。系統不斷從公司各處攝取數據,並維護一個可供查詢的數據儲存庫。
我們想要一個簡單但能處理大多數數據形式的數據介面。我們也希望 Cerebras 的其他開發人員能夠建立自訂連接器。結果非常簡單:每個來源(從 Slack 討論串到 netlist)都會進入同一個嵌入向量表,而該表中的任何內容都可以透過同一個介面立即查詢:

每個數據來源定義了數據是什麼、如何連接到它以及應多久擷取一次。每個產生的嵌入向量行都遵循相同的介面,無論它來自 Slack、程式碼儲存庫、文件系統還是自訂資料庫。
我們如何處理非結構化的 Slack 對話
Slack 是我們需要設計的最重要的數據來源。它是公司內最新工程討論發生的地方。

我們最初測試了單純對原始文字進行嵌入向量處理是否足夠。我們很快意識到,單靠向量搜尋不足以匹配所有相關數據。
Slack 訊息帶來了幾個挑戰:
- 資訊密度差異極大:「嘿,好啊,Mike」和一篇詳細的 kernel 說明都是訊息。
- 訊息長度各異,較短的訊息在餘弦相似度上常常勝過較長、更詳細的訊息。
- 訊息的意義通常取決於其所在對話的上下文。
我們需要一種混合方法。我們建立了 Slack 攝取機制,使每個討論串可以同時透過多種搜尋技術進行檢索,每種技術都彌補了其他技術的弱點:
- 全文搜尋捕捉嵌入向量模糊處理的確切詞彙:錯誤字串、標誌名稱、主機名稱。當工程師貼出一個具體的錯誤訊息時,精確的詞彙匹配幾乎總是最好的證據,任何程度的語義相似度都不應超越它。
- 嵌入向量搜尋捕捉同義表述。問「restore 在 manifest 載入後卡住」的人和回答「checkpoint 在 NFS 掛載點上停滯」的人,可能從未使用過相同的詞彙。向量相似度正是將一個問題與用不同詞語寫成的答案連結起來的方式。(1)
- 反向文件頻率將訊號與雜訊分離。一條圍繞罕見詞彙(例如一個晦澀的配置標誌)建立的簡短訊息,理應獲得較高排名。「聽起來不錯,謝謝!」在嵌入空間中與許多查詢相近,但一旦考慮詞彙罕見度,其分數就接近於零。
- 時效衰減編碼了 Slack 答案會過期的事實。兩個討論串可能回答同一個問題,而六個月前的那個可能描述了已不存在的基礎設施。當相關性相同時,較新的討論串勝出。

沒有任何單一評分器是獨立可信的。每種技術都會對同一個語料庫產生自己的排名視圖,這些視圖在查詢時會融合在一起(請參閱「重排序」)。
Socket 模式
為了即時收集數據,我們在 workspace 中安裝了一個 Slack 機器人,並以 Socket 模式運行。Slack 透過持續的 WebSocket 將每個訊息事件推送給我們,因此我們無需輪詢 Web API 並耗盡其速率限制,即可獲得即時更新。
當事件到達時,我們立即確認它,使用穩定的事件 ID 進行去重,並將該訊息標記為待攝取。
攝取消費者不會孤立地儲存新訊息。它會解析該訊息所屬的討論串,並從 Slack API 重新擷取整個對話,包括父訊息和所有回覆。然後它將整個討論串作為一行寫回。因此,對現有討論串的回覆會重新拉取父訊息和所有兄弟訊息,使儲存的內容、參與者列表和最後活動時間戳始終反映完整的對話。
我們系統中的每個 Slack 頻道都有自己的數據來源。這提供了數據新鮮度的精細調整。例如,團隊可以選擇更頻繁地攝取一個繁忙的事件頻道。
討論串與訊息
原始 Slack 文字在落地後即可進行關鍵字搜尋,因為我們在原始內容上維護了一個 Postgres 全文(GIN)索引。然而,為了實現有用的向量搜尋,我們還需要進行一些額外處理。(8)
在提煉過程中,LLM 從完整討論串中提取結構化數據:
- 工程師實際會搜尋的一行問題。
- 簡短摘要。
- 解決方案。
- 提到的系統和程式碼引用。

我們將這些數據點嵌入並寫入共享的嵌入向量表。原始對話記錄不會直接被嵌入。在我們的實驗中,當討論串被標準化為一致格式時,準確率顯著提高。(7,9) 額外的元數據也為語義匹配提供了更有用的訊號。
爆發
此時 Slack 搜尋已經不錯,但我們不斷遇到同一個問題:長討論串中的重要訊息並不總能在討論串層級的摘要中得到體現。
為了增強單一訊息的訊號,我們使用了爆發。爆發是指來自同一作者的一連串連續訊息。我們將帶有討論串主題作為前綴上下文的個別爆發嵌入,(2) 因為有時答案存在於某個偏離主題的訊息中,而該訊息的詞彙從未出現在討論串摘要中。爆發嵌入使該訊息能夠獨立被找到。
為了防止低訊號數據進入資料庫,每個爆發會根據加權訊號組合進行評分,並且必須超過閾值才會被嵌入:
- 它包含一個在語料庫中相對罕見的詞彙,IDF 至少為 4.0。
- 組合爆發至少包含 200 個字元。
- 爆發中的一則或多則訊息包含表情符號反應,提供社交加成。

提煉後,合格的爆發會被嵌入並儲存在嵌入向量表中,與討論串層級的記錄放在一起。
程式碼儲存庫
最初我們曾辯論嵌入程式碼儲存庫是否有必要。隨著 Claude Code 和其他命令列工具的興起,在「grep 就夠了」的時代,建立程式碼嵌入感覺有些違反直覺。在與業界其他人士交流並閱讀了 Cursor 關於大型程式碼庫語義搜尋的發現後,我們決定一試。
我們有許多內部儲存庫,有些超過 40 GB。我們主要擔心的是如何有效地保持它們的最新狀態。
使用 @cocoindex_io 維護程式碼嵌入
經過幾次實驗後,我們選擇了 CocoIndex,這是一個開源文件嵌入框架,專門用於將程式碼庫向量化。
對於每個儲存庫,我們使用語言特定的正則表達式邊界(由粗到細)來分割程式碼。分割器會先嘗試較高層級的邊界,例如類別。如果產生的區塊仍然太大,它會回退到方法邊界,然後是更小的區塊。我們嵌入產生的區塊,並將向量寫入 Postgres。一個檔案可能產生多個不同特異性層級的嵌入,例如檔案層級和函式層級的記錄。

CocoIndex 在 Postgres 中追蹤同步元數據。在每次提交時,它只重新嵌入和重新匯出已變更的程式碼區塊,而不是重新計算整個儲存庫。這對我們特別有效,因為同步狀態和嵌入儲存位於同一個資料庫中。
隨著程式碼庫數量的增加,我們將儲存庫加入流程轉移到組態檔中,團隊可以自行提交,包括檔案路徑層級的允許清單和拒絕清單。
自訂數據來源
有些團隊已經有自己的資料庫,不想為了加入知識庫而將數據移到 Slack 或文件系統中。他們希望在其現有資料表上獲得相同的查詢界面。
為此,我們將自訂來源視為插件腳本。團隊提交一個 pull request,其中包含一個小型 Python 模組,該模組知道如何從其系統讀取數據並產生形狀類似於我們嵌入向量表的行,以及一個匹配的數據來源條目。
只要腳本使用與其他嵌入向量行相同的 schema 寫入共享資料庫,其餘的技術堆疊就無需更改。該數據會與 Slack、程式碼和文件一起變得可查詢,無需在系統其他部分進行特殊處理。
規劃與工具分發
對於每個查詢,我們首先進行一次簡短的規劃過程,LLM 在此過程中決定哪些工具和數據來源可能相關。主要工具包括:
- subsystem_index:每個檔案的 LLM 摘要。
- search:跨 Slack、wiki、程式碼和其他索引來源的統一向量管道,內部進行合併和重排序。
- search_slack:直接 Slack 檢索。
- search_code:對來源儲存庫進行 ripgrep 搜尋。
- recent_prs:與問題相關的近期 pull request。
- who_knows:在特定主題上具有公認專業知識的人員。
規劃器基於我們已索引的內容的簡潔描述進行運作:有哪些專案、每個專案中有哪些可用來源、以及每個來源擅長回答什麼問題。根據使用者的查詢和有效範圍,它會發出工具選擇,由執行器並行分發,標準化為通用的證據格式,然後傳遞給最終的綜合 LLM。(4)

重排序
一個文件可能僅僅因為與查詢共享詞彙而出現在頂部,但實際上回答的是不同的問題。在重排序之前,我們使用互惠排名融合(RRF)將檢索器不相容的結果列表合併。對於每個文件,我們在其出現的每個列表中加上 weight / (60 + rank),預設權重為 1.0,平滑常數為 60。

平滑常數使共識比單一強烈投票更重要:一個在多個檢索器中都出現在頂部的文件,可以勝過僅在其中一個檢索器中排名第一的文件。然後,我們將重複的區塊合併回一個來源,限制每個檔案可以貢獻的結果數量,最終得到一個更多樣化的前二十名。
我們將原始查詢和這些候選項目發送給一個小型重排序模型。它為每個文件打分,範圍從 0 到 10,我們保留前 10 名。(6)
排定最終名次後,我們為勝出的文件添加上下文。例如,如果我們匹配到一個 wiki 章節,我們會拉入相鄰的兩個章節,這樣因區塊分割而分開的標題、前提條件和注意事項就不會遺失。這為讀者提供了一個完整的片段,而不是一個缺少重要上下文的孤立段落。
因此,搜尋的輸出是一個豐富的證據包:來自不同檢索器的結果融合,在來源層級去重,根據實際問題進行重排序,最後才用周圍的上下文進行擴充。
MCP
在 MCP 整合中,我們將檢索建構區塊作為直接工具公開,而不是將它們隱藏在一個「回答這個問題」的端點後面。這些工具刻意保持簡單,盡可能不依賴 LLM,以便客戶端可以快速且低成本地查詢它們。(5)
每個 MCP 工具對應一個底層檢索原語,例如 search_slack、search_code、search 或 who_knows。工具輸入和輸出是狹窄、結構化且穩定的,使得任何客戶端或 Agent 都可以輕鬆呼叫它們,而無需在工具內部嵌入額外的編排邏輯。
大多數工具運行一個查詢管道,例如向量搜尋、詞彙搜尋或 ripgrep,應用輕量級評分啟發式,並返回原始證據行。
Claude Code 或任何與 MCP 相容的 Agent,成為編排引擎。它決定呼叫哪些工具、以什麼順序,以及如何將結果組裝成最終答案或程式碼編輯。檢索層本身不依賴這些 LLM 決策來提供服務。
Web UI
在 Web UI 中,同樣的工具也存在,但它們連接到一個完整的查詢管道,該管道為每個使用者的問題端到端地運行。UI Agent 擁有規劃器和執行器步驟。
- 規劃器:一個輕量級 LLM 過程檢查查詢和活躍專案,然後選擇要呼叫哪些檢索工具,例如 search、search_slack 和 subsystem_index。
- 執行器:系統將這些工具呼叫並行分發,收集結果,並將它們標準化為共享的證據模式,包含分數、新近度和來源提示。
- 綜合:一個最終的 LLM 過程接受帶有類型的證據包和原始問題,然後產生 UI 中顯示的答案,包括引用、注意事項和跨來源綜合。
從使用者的角度來看,Web UI 就是「提出問題並獲得答案」。在底層,它運行著與 MCP 客戶端可以明確重現的相同規劃器 → 執行器 → 綜合器模式。

組織
隨著語料庫的增長,「到處搜尋所有東西」很快就變得不實用。編譯器團隊的工程師不希望看到基礎設施操作手冊出現在他們的結果中,反之亦然。專案是我們讓搜尋預設相關的方式。
專案與範圍限定搜尋
我們引入了專案作為組織查詢所運行工作區的主要方式。專案是一個命名的數據來源捆綁:與特定團隊或計劃相關的特定 Slack 頻道、程式碼儲存庫、內部資料庫和文件空間。
專案刻意保持輕量。同一個數據來源(例如共享的事件頻道或中央平台儲存庫)可以被多個專案引用,而不是複製。

入職與預設
在入職期間,系統會提示使用者選擇或建立一個與其工作方式相符的預設專案,例如 ML 訓練基礎設施、編譯器或數據中心營運。
該預設專案儲存在使用者設定檔中,並自動限定查詢範圍。新進工程師無需先了解哪些 Slack 頻道、儲存庫或文件空間是重要的,就能獲得高訊號的答案。
最終想法
歸根結底,這個知識庫之所以有效,是因為它在資訊原本所在之處滿足人們的需求,而不是強迫所有東西進入一個僵化的系統。透過結合多種搜尋技術,我們能夠快速找到證據。結果是一個保持足夠靈活以應對真實公司數據、但結構化程度足以在 Cerebras 持續成長時保持有用的搜尋體驗。
如果你讀到這裡並且對此感興趣,ai/growth 團隊正在招募,如果你有興趣,請聯繫 @learnwdaniel
參考文獻
- Malkov 與 Yashunin,Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs,arXiv:1603.09320 / IEEE TPAMI 2018。
- Anthropic,Introducing Contextual Retrieval,2024。
- Cormack、Clarke 與 Büttcher,Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods,SIGIR 2009。
- Li 等人,Search-o1: Agentic Search-Enhanced Large Reasoning Models,arXiv:2501.05366,2025。
- Anthropic,Code Execution with MCP,2025。
- Liu 等人,Lost in the Middle: How Language Models Use Long Contexts,arXiv:2307.03172,2023。
- Anthropic,Use XML Tags。
- Salesforce/Slack Engineering,How Slack AI Processes Billions of Messages。
- Improving Agents,Best Nested Data Format。
- Cursor,Improving Agent with Semantic Search,2025。





