好的,以下是您提供的英文 Markdown 内容的繁體中文翻譯,已嚴格遵循您的所有指示。
問 Claude 關於你的公司、筆記或檔案,它會一片空白。它從未見過這些資料。它只知道訓練時學到的東西,而你的資料不在其中。
RAG 系統解決了這個問題。它不依賴記憶,而是先在你的文件中查找,擷取相關的部分,然後根據實際找到的內容來回答。你的資料,它的來源,完全不用猜測。
為什麼這比直接把檔案貼到對話框更好:
它具備可擴展性。 你的整個知識庫無法塞進一次對話中。RAG 會儲存所有內容,並只根據每個問題擷取所需的部份。
它更省錢。 貼上檔案意味著 Claude 每次都得重新讀取整個檔案。RAG 只讀取一次,然後僅提取相關部分。與其每次傳送一份 10,000 個 token 的手冊,它可能只傳送 500 個 token 的準確章節。實際的應用中,token 使用量可減少 80% 以上。
它更精準。 給模型一大段文字,它會遺漏中間的細節。給它幾個精確的區塊,答案會更準確。
它保持最新。 更新一次你的檔案,系統就會使用新版本。無需重新貼上。
讀完這份指南,你就能在自己的檔案上一步步建立一個 RAG 系統,不需要博士學位。
你需要準備什麼
在我們碰任何程式碼之前,這裡是完整的清單。好消息是:這個版本只需要一個 API 金鑰,其他所有東西都在你自己的機器上免費運行。
1. Python 3.9 或更新版本。 要檢查你是否已安裝,請打開你的終端機(Mac 上的 Terminal,Windows 上的 Command Prompt),然後輸入 python --version。如果你看到類似 3.11 的訊息,就沒問題。如果沒有,請從 python.org 下載並執行安裝程式。在 Windows 上,安裝時請勾選「Add Python to PATH」,否則下面的指令將無法運作。
2. 一個 Claude API 金鑰,加上一些儲值額度。 這是這份指南需要的唯一金鑰和唯一花費。以下是確切的步驟,一步步點擊:
前往 platform.claude.com,然後登入(或註冊)。
API 需要正數餘額才能運行,所以請先加值。依照提示操作,選擇額度是給個人還是公司,然後你會進入付款畫面。選擇 $5 的「Starting out」選項。這很夠用了:這份指南中的其他東西都是免費且在本機運行的,所以 Claude 是唯一需要花錢的部分,而且每個問題只會花你不到一分錢。額度在購買後一年內有效。
付款後,你會進入你的 Console 儀表板。你應該會在左上方「Organization credits」下方看到你的餘額(例如 $5.00)。
現在取得金鑰。點擊 Get API key(右上角),然後點擊 Create Key。為它取任何你喜歡的名字(例如 my-rag-key),並將工作區保留為 Default。點擊建立,然後複製顯示出來的字串。它以 sk-ant- 開頭,而且你只會看到一次,所以請先把它貼到安全的地方。
這就是整個設定。
步驟 1:加入你的金鑰並載入檔案
1. 建立專案資料夾。 在你的桌面上建立一個新資料夾,命名為 rag-project。所有東西都放在這裡。
2. 打開你的終端機。 Mac 上:Cmd+Space,輸入 Terminal,按 Enter。Windows 上:按開始按鈕,輸入 cmd,按 Enter。
3. 將終端機指向你的資料夾。 輸入 cd 和一個空格,然後將 rag-project 資料夾拖曳到終端機視窗上,並按 Enter。下面的每個指令都在這個資料夾內執行。
1cd Desktop/rag-project
4. 安裝工具。 將此貼到你的終端機並按 Enter(第一次執行可能需要一分鐘):
1pip install anthropic chromadb sentence-transformers pypdf python-dotenv
如果你看到 pip: command not found,請使用 pip3 代替 pip。當終端機顯示一個新的行且沒有紅色錯誤訊息時,就完成了。
5. 建立你的程式碼檔案。 在 rag-project 裡面,建立一個名為 [rag.py](https://rag.py/) 的空檔案。用任何文字編輯器打開它。
6. 建立你的金鑰檔案。 在同一個資料夾中,建立一個名為 .env 的檔案(以點開頭,前面沒有名稱)。將以下內容貼入,並在 = 後面放入你在設定時建立的真正金鑰,不要有空格,不要有引號:
1ANTHROPIC_API_KEY=sk-ant-paste-your-real-key-here
將金鑰放在 .env 檔案中而非程式碼中,意味著如果你分享腳本或將其放到 GitHub 上,金鑰也不會洩漏。
7. 載入金鑰。 將下列程式碼放在 [rag.py](https://rag.py/) 的最上方:
1import os2from dotenv import load_dotenv34load_dotenv() # 讀取你的 .env 檔案5api_key = os.getenv("ANTHROPIC_API_KEY")
8. 建立你的知識庫。 在 rag-project 裡面,建立一個名為 documents 的資料夾。將任何 .txt、.md 或 .pdf 檔案丟進去:你的筆記、產品文件、會議記錄,任何東西都可以。
8.1. 如果你還沒有檔案,可以使用這個測試檔案。 在 documents 資料夾中建立 notes.txt,並貼入以下內容:
Project Northstar 是我們用來追蹤客戶意見回饋的內部工具。它於 2026 年 3 月上線,由平台團隊維護。首席工程師是 Dana Reyes。意見回饋會在每週五檢視。Northstar 取代了我們在 2025 年使用的舊版試算表系統。
最後,你會問 Claude 關於 Northstar 的問題,並看著它根據這個檔案回答。
9. 加入讀取檔案的程式碼。 在步驟 7 的程式碼下方,於 [rag.py](https://rag.py/) 中:
1from pathlib import Path2from pypdf import PdfReader34def load_documents(folder="documents"):5 docs = []6 for file in Path(folder).iterdir():7 if file.suffix in [".txt", ".md"]:8 text = file.read_text(encoding="utf-8")9 docs.append({"source": file.name, "text": text})10 elif file.suffix == ".pdf":11 reader = PdfReader(str(file))12 text = "\n".join(page.extract_text() or "" for page in reader.pages)13 docs.append({"source": file.name, "text": text})14 return docs1516documents = load_documents()17print(f"已載入 {len(documents)} 個文件。")
10. 執行它。 儲存 [rag.py](https://rag.py/),然後在你的終端機中:
1python rag.py
你應該會看到:
已載入 1 個文件。
如果你看到「已載入 0 個文件」,表示 documents 資料夾是空的或在錯誤的位置。它必須直接位於 rag-project 資料夾內,與 rag.py 並列。
步驟 2:將你的檔案分割成區塊
現在每個檔案都是一大塊文字。在我們搜尋它之前,需要將其切割成稱為區塊的較小片段。原因如下:當有人提出問題時,系統會找到匹配的區塊,並只將這些區塊傳送給 Claude。如果你的區塊是整份 50 頁的文件,你會傳送太多內容。如果它們是單一句子,則會失去上下文。小段落是最佳選擇。
1. 加入分割區塊的程式碼。 在步驟 10 的程式碼下方,於 [rag.py](https://rag.py/) 中:
1def chunk_text(text, chunk_size=500, overlap=100):2 words = text.split()3 chunks = []4 start = 05 while start < len(words):6 end = start + chunk_size7 chunk = " ".join(words[start:end])8 chunks.append(chunk)9 start = end - overlap # 稍微倒退,讓區塊之間有重疊10 return chunks
這裡有兩個數字需要理解,用白話文來說:
- chunk_size=500 表示每個區塊大約 500 個字。大到足以容納一個完整的概念,小到足以保持精確。
- overlap=100 表示每個區塊會重複前一個區塊的最後 100 個字。這很重要,因為答案可能剛好落在兩個區塊的交界處。沒有重疊,一個被從中間切斷的句子可能會遺失。重疊確保沒有想法會從裂縫中掉落。
2. 將每個文件都轉換成區塊。 在下方加入:
1all_chunks = []2for doc in documents:3 for chunk in chunk_text(doc["text"]):4 all_chunks.append({"source": doc["source"], "text": chunk})56print(f"從 {len(documents)} 個文件建立了 {len(all_chunks)} 個區塊。")
請注意,每個區塊都帶有它的 source(它來自的檔案名稱)。我們會全程保持這個關聯,這樣當 Claude 稍後回答時,它可以告訴你答案來自哪個檔案。
3. 執行它。 儲存 [rag.py](https://rag.py/),然後在你的終端機中:
1python rag.py
你應該會看到類似這樣的訊息:
已載入 1 個文件。
從 1 個文件建立了 1 個區塊。
這個小的測試檔案因為很短,所以只變成一個區塊。真正的文件會產生很多區塊。如果你在資料夾中放入一個長 PDF,你可能會看到幾十個或幾百個區塊,這正是你想要的。
步驟 3:將你的區塊轉換成嵌入向量
這一步讓電腦能夠根據意義而非確切的詞彙來搜尋。每個區塊都會被轉換成一個數字列表(一個嵌入向量),用來捕捉它的主題。意義相似的區塊最終會有相似的數字。稍後,當問題進來時,我們也會將問題轉換成數字,並找到最接近的匹配項。
執行此操作的模型會在你的機器上本地運行。它會下載一次,然後離線免費運行,你的檔案永遠不會離開你的電腦。
1. 載入嵌入模型。 在步驟 2 的程式碼下方,於 rag.py 中:
1from sentence_transformers import SentenceTransformer23print("正在載入嵌入模型(首次執行會下載,約 90 MB)...")4embedder = SentenceTransformer("all-MiniLM-L6-v2")
你第一次執行此操作時,它會下載模型,所以請給它一點時間。之後每次執行都會立即完成,因為它已經在你的機器上了。
2. 將每個區塊轉換成嵌入向量。 在下方加入:
1chunk_texts = [chunk["text"] for chunk in all_chunks]2embeddings = embedder.encode(chunk_texts)34print(f"建立了 {len(embeddings)} 個嵌入向量。")5print(f"每個嵌入向量是一個包含 {len(embeddings[0])} 個數字的列表。")
embedder.encode(...) 接收你的區塊文字列表,並為每個區塊返回一個嵌入向量。就是這麼簡單。
3. 執行它。 儲存 rag.py,然後在你的終端機中:
1python rag.py
第一次執行會暫停,等待模型下載,然後你應該會看到類似這樣的訊息:
已載入 1 個文件。
從 1 個文件建立了 1 個區塊。
正在載入嵌入模型(首次執行會下載,約 90 MB)...
建立了 1 個嵌入向量。
每個嵌入向量是一個包含 384 個數字的列表。
這行「384 個數字」讓整個概念具體化:你的文字現在變成了一行數字,電腦可以進行比較。你不需要自己閱讀或理解這些數字。下一步的資料庫會為你處理所有比較工作。
如果下載因連線錯誤而失敗,只需再次執行指令即可。它會從中斷的地方繼續。
步驟 4:將所有內容儲存到你的向量資料庫
現在我們將區塊及其嵌入向量放入 Chroma,你的本地資料庫。這使得搜尋變得快速:與其每次手動將你的問題與每個區塊進行比較,Chroma 會將它們儲存好,隨時可用,並為你進行匹配。它會儲存到你機器上的一個資料夾中,所以你只需要建立一次。
1. 設定資料庫。 在步驟 3 的程式碼下方,於 rag.py 中:
1import chromadb23client = chromadb.PersistentClient(path="chroma_db")4collection = client.get_or_create_collection("my_documents")
PersistentClient(path="chroma_db") 告訴 Chroma 儲存到一個名為 chroma_db 的資料夾中(它會自動建立,就在你的腳本旁邊)。因為它儲存在磁碟上,你的資料在腳本執行完畢後仍然存在。一個 collection 只是一個命名的盒子,你的區塊就放在裡面。
2. 將你的區塊加入資料庫。 在下方加入:
1collection.add(2 ids=[str(i) for i in range(len(all_chunks))],3 embeddings=[emb.tolist() for emb in embeddings],4 documents=[chunk["text"] for chunk in all_chunks],5 metadatas=[{"source": chunk["source"]} for chunk in all_chunks],6)78print(f"已將 {collection.count()} 個區塊儲存到資料庫中。")
這裡用白話文解釋每行程式碼交給 Chroma 的內容:ids 為每個區塊提供一個唯一的標籤(0, 1, 2...),embeddings 是步驟 3 中的數字,documents 是實際的區塊文字,而 metadatas 則攜帶檔案名稱,以便我們稍後可以顯示來源。Chroma 將這四者全部綁定在一起。
3. 執行它。 儲存 rag.py,然後在你的終端機中:
1python rag.py
你應該會看到:
已將 1 個區塊儲存到資料庫中。
有一件事日後需要知道。 現在每次執行腳本時,它都會再次加入區塊,因此計數可能會在重複執行時攀升(1,然後 2,然後 3...)。這在我們建置的過程中沒問題。要從頭開始,請刪除 chroma_db 資料夾並再次執行。在最終版本中,我們會妥善處理這個問題,以避免重複。
步驟 5:搜尋你的文件
這就是 RAG 的「檢索」部分,也就是名稱中的 R。我們接受一個問題,將其轉換成嵌入向量,就像我們對區塊所做的那樣,然後要求 Chroma 提供意義最接近的區塊。這些匹配的區塊就是我們下一步要交給 Claude 的內容。
1. 加入搜尋函數。 在步驟 4 的程式碼下方,於 rag.py 中:
1def search(question, n_results=3):2 question_embedding = embedder.encode([question])[0]3 results = collection.query(4 query_embeddings=[question_embedding.tolist()],5 n_results=n_results,6 )7 return results
用白話文逐行解釋它的作用:它使用你對區塊使用的相同模型,將問題轉換成數字(這很重要,兩者必須使用相同的「數字語言」),然後要求 Chroma 提供最接近的匹配項。n_results=3 表示「給我 3 個最相關的區塊」。三個是良好的預設值:足夠的上下文,又不會浪費太多 token。
2. 嘗試搜尋。 在下方加入以下內容來測試:
1question = "誰負責 Northstar?意見回饋何時檢視?"2results = search(question)34for i, doc in enumerate(results["documents"][0]):5 source = results["metadatas"][0][i]["source"]6 print(f"\n--- 匹配 {i+1}(來自 {source})---")7 print(doc)
這會針對你的資料庫執行一個真實的問題,並列印出它找到的區塊,每個區塊都附帶其來源檔案名稱。
3. 執行它。 儲存 rag.py,然後在你的終端機中:
1python rag.py
使用 Northstar 測試檔案,你應該會看到它拉回匹配的區塊,類似於:
--- 匹配 1(來自 notes.txt)---
Project Northstar 是我們用來追蹤客戶意見回饋的內部工具。它於 2026 年 3 月上線,由平台團隊維護。首席工程師是 Dana Reyes。意見回饋會在每週五檢視。Northstar 取代了我們在 2025 年使用的舊版試算表系統。
請注意剛剛發生的事情:你的問題使用了「誰負責」和「檢視」這些詞,但檔案中寫的是「首席工程師」和「每週五檢視」。它仍然匹配了,因為搜尋是基於意義,而不是確切的詞彙。這就是嵌入向量的全部意義,也是為什麼這勝過在檔案中進行純粹的關鍵字搜尋(Ctrl+F)。
如果你有更多檔案,你會看到所有檔案中排名前 3 的區塊,根據它們與問題的匹配程度排序。
步驟 6:讓 Claude 根據找到的內容回答
這就是「生成」部分,也就是 RAG 中的 G。我們從步驟 5 取得區塊,將它們連同問題一起交給 Claude Opus 4.8,並告訴它只使用該上下文來回答。這可以防止它猜測:Claude 根據你的檔案回答,而不是根據它自己的記憶,並告訴你它使用了哪個檔案。
1. 加入回答函數。 在步驟 5 的程式碼下方,於 rag.py 中:
1import anthropic23claude = anthropic.Anthropic(api_key=api_key)45def answer(question):6 results = search(question)7 chunks = results["documents"][0]8 sources = [m["source"] for m in results["metadatas"][0]]910 context = ""11 for i, chunk in enumerate(chunks):12 context += f"[來自 {sources[i]}]\n{chunk}\n\n"1314 message = claude.messages.create(15 model="claude-opus-4-8",16 max_tokens=1024,17 system=(18 "你只能使用提供的上下文來回答問題。"19 "如果答案不在上下文中,請說你不知道。"20 "始終說明你的答案來自哪個檔案。"21 ),22 messages=[23 {24 "role": "user",25 "content": f"上下文:\n{context}\n問題: {question}",26 }27 ],28 )29 return message.content[0].text
用白話文解釋這裡發生了什麼:我們搜尋相關的區塊,將它們拼接成一個 context 區塊(每個區塊都標有其檔案名稱),然後將該區塊連同問題一起發送給 Claude。system 指令是關鍵部分。它告訴 Claude 三件事:僅根據上下文回答,當答案不在其中時要承認,並指出來源檔案。這三個規則使答案可信,而不是編造出來的。
model="claude-opus-4-8" 是確切的模型名稱(使用破折號,不是點)。max_tokens=1024 限制了答案的長度。
2. 提出一個問題。 在下方加入:
1question = "誰負責 Northstar?意見回饋何時檢視?"2print(answer(question))
3. 執行它。 儲存 rag.py,然後在你的終端機中:
1python rag.py
你應該會得到一個根據你的檔案建立的真正答案,類似於:
Dana Reyes 是負責 Project Northstar 的首席工程師,意見回饋會在每週五檢視。(來源:notes.txt)
這是一個完整的 RAG 系統在運作。Claude 在訓練期間從未見過這個檔案,它不可能知道 Dana Reyes 是誰,但它正確地回答了問題,並準確告訴你答案來自哪裡。問它一個不在你檔案中的問題,它會說不知道,而不是編造一個答案。這個「不知道」是功能,不是失敗:它正是可信賴工具與胡亂猜測工具之間的區別。
步驟 7:將其變成一個真正可用的工具
現在,每次你想問問題,都必須編輯程式碼並重新執行整個腳本。更糟的是,每次執行都會重新讀取你的檔案並重新加入資料庫,導致區塊不斷累積。讓我們解決這兩個問題:只建立一次資料庫,然後讓你在一個迴圈中提問,直接在終端機中輸入。
1. 修正重複加入的問題。 找到步驟 4 中負責加入區塊的程式碼區塊(collection.add(...) 部分),並將其替換為這個版本,它只在資料庫為空時才建立:
1if collection.count() == 0:2 collection.add(3 ids=[str(i) for i in range(len(all_chunks))],4 embeddings=[emb.tolist() for emb in embeddings],5 documents=[chunk["text"] for chunk in all_chunks],6 metadatas=[{"source": chunk["source"]} for chunk in all_chunks],7 )8 print(f"已將 {collection.count()} 個區塊儲存到資料庫中。")9else:10 print(f"資料庫已有 {collection.count()} 個區塊,跳過重建。")
現在,繁重的工作(讀取檔案、建立嵌入向量、填充資料庫)只在第一次執行時發生。之後的執行會直接跳過,進入回答階段。
2. 加入問題迴圈。 在 rag.py 的最底部,將步驟 6 中的單一測試問題替換為:
1print("\n詢問關於文件的問題(或輸入 'quit' 離開)。\n")23while True:4 question = input("你: ")5 if question.lower() in ["quit", "exit"]:6 break7 print("\nClaude: " + answer(question) + "\n")
input("你: ") 會等待你輸入問題並按 Enter。while True 讓它持續運行,這樣你就可以問任意多個問題。輸入 quit 會停止它。
3. 執行它。 儲存 rag.py,然後在你的終端機中:
1python rag.py
現在你可以直接與你的檔案對話了:
詢問關於文件的問題(或輸入 'quit' 離開)。
你: 誰是 Northstar 的首席工程師?
Claude: Project Northstar 的首席工程師是 Dana Reyes。(來源:notes.txt)
你: 它取代了什麼?
Claude: Northstar 取代了在 2025 年使用的舊版試算表系統。(來源:notes.txt)
你: quit
這就是你完成的 RAG 系統。它會讀取你的檔案一次,記住它們,並根據需求回答關於它們的問題,每次都會提供來源。
有一件事需要知道,當你加入新檔案時。 由於資料庫現在只建立一次,將新檔案放入 documents 資料夾並不會自動顯示出來。要載入新檔案,請刪除 chroma_db 資料夾,然後執行一次腳本。它會從頭開始重建,包含資料夾中的所有內容。
可選:在你的瀏覽器中給它一個聊天視窗
終端機可以運作,但如果你想要一個真正的聊天視窗,Streamlit 可以用大約 20 行程式碼就加入一個。
1. 安裝它。 在你的終端機中:
1pip install streamlit
2. 在同一個資料夾中建立 app.py 並貼入以下內容。它會重複使用你的 [rag.py](https://rag.py/) 中的 answer 函數:
1import streamlit as st2from rag import answer34st.title("與你的文件聊天")56if "history" not in st.session_state:7 st.session_state.history = []89question = st.chat_input("詢問關於你的檔案的問題...")1011if question:12 reply = answer(question)13 st.session_state.history.append((question, reply))1415for q, a in st.session_state.history:16 st.chat_message("user").write(q)17 st.chat_message("assistant").write(a)
3. 執行它。 在你的終端機中(注意:是 streamlit run,不是 python):
1streamlit run app.py
它會自動在你的瀏覽器中打開一個聊天視窗。輸入問題,獲得帶有來源的答案,就像終端機一樣,但看起來更美觀。
注意:為了讓它運作,步驟 7 中的問題迴圈在匯入時不應執行。將 rag.py 底部的該迴圈包裹在 if __name__ == "__main__": 中,這樣它只會在直接執行 rag.py 時觸發,而不會在 app.py 匯入它時觸發。
讓它也能回答一般問題
如果你希望它也能回答一般問題。 目前系統只根據你的檔案回答,所以像「委內瑞拉的首都是哪裡?」這樣的問題會得到「這不在文件中」的回答,即使 Claude 知道答案。如果你希望它退而使用自己的知識,請打開 rag.py,找到步驟 6 中的 system=(...) 區塊,並交換這一行:
1「如果答案不在上下文中,就說你不知道。」
改成這樣:
1「如果答案不在上下文中,就從你的一般知識中回答,但要說明你正在這麼做。」
儲存並重新執行。現在它會先從你的檔案中找答案,如果檔案內容不足,則回退到一般知識,並告知你使用的是哪一種來源。
總結
你剛剛建立了一個可運作的 RAG 系統。它會讀取你自己的檔案,找出相關的部分,並讓 Claude 根據這些內容回答問題,同時每次都附上精確的來源。這個架構可以從幾則筆記擴展到你的整個知識庫。
從這裡開始,你可以把它指向任何地方:你的 Obsidian 筆記庫、你的工作文件、你保存的研究資料。把檔案丟進去,重建一次,然後開始提問。你在這裡學到的一切——分段、嵌入、搜尋、回答——正是你見過的所有「與文件對話」工具背後的相同核心。
如果這篇文章對你有幫助,歡迎到我的個人檔案按追蹤。我寫關於科技、AI 和真正能運作的系統。
再會,
[@undefinedKi]([https://x.com/@undefinedKi](https://x.com/@undefinedKi))





