
使用 Ollama 在本地執行嵌入模型:我測試了 GPU 冷啟動與熱啟動的差異
你可以使用 Ollama 在本地執行嵌入模型,不再需要為索引的每個區塊支付 OpenAI 每百萬 Token 0.02 美元的費用。代價是:你需要擁有 GPU、處理冷啟動問題以及負責維運工作。Ollama 會在埠 11434 上提供服務,且無需 API Key。以下是完整的工作流程,從 ollama pull 到能夠回答查詢的熱機向量搜尋。
重點摘要
- Ollama 透過
POST /api/embed在http://localhost:11434上本地提供嵌入服務,無需 API Key 且每 Token 成本為 0 美元。 - 請使用
/api/embed(當前版本,支援批次陣列);/api/embeddings已過時,通常是導致 404 錯誤的原因。 - 熱門的本地模型包括:
nomic-embed-text(768 維)、mxbai-embed-large(1024 維)、bge-m3(1024 維)、embeddinggemma(768 維)。 - 確保嵌入維度與向量資料庫欄位相符,並使用
keep_alive鎖定模型以跳过冷啟動延遲。
使用 Ollama 在本地執行嵌入模型需要什麼?
在本地執行嵌入所需的一切僅需三部分:嵌入模型、運行在埠 11434 上的 Ollama 伺服器,以及用於儲存輸出的 向量儲存庫。Ollama 會下載並提供模型服務;你的程式碼將文字發送至 /api/embed;向量則存入如 pgvector、Qdrant 或 Chroma 等資料庫中。無需雲端往返,也無每 Token 帳單。
只需兩個指令,你就能在一分鐘內獲得可用的嵌入功能:
ollama pull nomic-embed-text
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "The quick brown fox"
}'這就是快速入門的全部內容。本教學的其餘部分將補充模型選擇、儲存庫設定,以及兩個常讓人踩坑的問題:端點混淆和冷啟動懲罰。
步驟 1:安裝 Ollama 並拉取嵌入模型
安裝 Ollama,確認伺服器正在監聽埠 11434,然後拉取一個嵌入模型。Ollama 作為背景服務運行,因此 ollama pull nomic-embed-text 會下載權重,後續的 /api/embed 呼叫即可使用這些權重。與聊天模型相比,嵌入模型非常小巧,因此下載速度很快。
# macOS / Linux install
curl -fsSL https://ollama.com/install.sh | sh
# Make sure the server is up (background service on :11434)
ollama serve # only if it isn't already running
# Pull an embedding model and health-check the server
ollama pull nomic-embed-text
curl http://localhost:11434 # should return "Ollama is running"精彩的部分在於:像 nomic-embed-text 這樣的嵌入模型僅有 1.37 億參數,下載大小約為 274 MB,相比之下聊天模型則高達數 GB。它大約只需一秒鐘就能載入 VRAM。如果你希望建立完整的本地 LLM 設定,讓聊天模型與嵌入模型並存,我們的為本地 LLM 設定 Ollama 指南涵蓋了該路徑;如果你偏好點擊操作而非使用 curl,也可以參考本地 Ollama 模型的 UI 介面。
專業提示:伺服器必須在發出任何請求前啟動。如果在 :11434 出現連接被拒絕的情況,幾乎總是意味著 ollama serve 未啟動。
應該拉取哪種本地嵌入模型?
對於大多數本地 RAG 應用,768 維的 nomic-embed-text 是安全的預設選擇。它的表現優於 OpenAI 舊版的 ada-002,且幾乎能在任何硬體上運行。當你需要多語言或長上下文檢索時,請選擇 bge-m3 或 qwen3-embedding;若在小型硬體上追求速度,則選擇 all-minilm;而 embeddinggemma 則是較新的 Google 選項。下表涵蓋了目前的 Ollama 嵌入模型庫,作為服務決策參考,而非質量排行榜。
| 模型(確切標籤) | 參數量 | 輸出維度 | 上下文 | 備註 |
|---|---|---|---|---|
| nomic-embed-text | 1.37 億 | 768 | 預設 2048(原生 8192,需提高 num_ctx) | 最受歡迎的本地嵌入器;勝過 ada-002 |
| embeddinggemma | 3 億 | 768 (MRL 512/256/128) | ~2K | Google 出品;現為 Ollama 推薦模型 |
| mxbai-embed-large | 3.35 億 | 1024 | 512 | mixedbread.ai;媲美更大規模模型 |
| bge-m3 | 5.67 億 | 1024 | 8192 | BAAI;支援密集、稀疏、多向量及多語言 |
| snowflake-arctic-embed | 2200 萬 - 3.35 億 | 最高 1024 | 512 | Snowflake;尺寸範圍廣 |
| granite-embedding | 3000 萬 / 2.78 億 | 384 / 768 | 512 | IBM;微型和小型 |
| qwen3-embedding | 0.6b/4b/8b | 1024/2560/4096(用戶可定義) | 32K | 最佳開源多語言及代碼 RAG |
| all-minilm | 2200 萬 / 3300 萬 | 384 | 256 | 最快且最小 |
在「最佳 ollama 嵌入模型 reddit」討論串中,反覆達成的共識是:一般 RAG 使用 nomic-embed-text,多語言場景使用 bge-m3,這與我們部署的情況相符。如果你想要查看包含評分的跨供應商排名視圖,那是模型中心的工作:為 RAG 選擇哪種嵌入模型。我們在此刻意略過 MTEB 數據;我們關於 MTEB 評分如何影響 RAG 的配套文章解釋了為何僅看排行榜可能會產生誤導。
步驟 2:透過 /api/embed 生成嵌入
將文字發送至 POST /api/embed,Ollama 會返回 L2 標準化向量,這意味著每個向量都是單位長度,因此可以直接使用餘弦相似度。根據 Ollama 嵌入文件,當前端點接受一個 input 欄位,該欄位可以是單一字符串或用於批次的陣列,並返回 {"embeddings": [[...]]}。
原始 HTTP 呼叫如下:
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": ["first chunk", "second chunk", "third chunk"]
}'在 Python 中,官方客戶端每次批次處理只需一行代碼:
import ollama
resp = ollama.embed(
model="nomic-embed-text",
input=["first chunk", "second chunk", "third chunk"],
options={"num_ctx": 8192}, # raise context for long chunks
)
vectors = resp["embeddings"] # list of 768-float lists, L2-normalized透過 input 陣列進行批次處理是你提升吞吐量的主要手段。一次包含 64 個區塊的請求遠勝於 64 次單獨請求,因為你只需支付一次每次呼叫的開銷。注意 num_ctx 的提升:nomic-embed-text 預設為 2048 Token 窗口,儘管它原生支援 8192,因此如果不提高該值,長區塊會被靜默截斷。嵌入只是其所饋入的完整 RAG 管道中的一個階段;分塊和檢索邏輯位於那裡,而非此處。
/api/embed 與 /api/embeddings 與 /v1/embeddings:有什麼區別?
/api/embed 是當前端點;/api/embeddings 是已棄用的端點,也是大多數「Ollama 嵌入無法運作」貼文背後的原因。舊版路由使用單數 prompt 欄位並返回 embedding(無 s),而當前路由使用 input,接受批次並返回 embeddings。第三個路由 /v1/embeddings 相容於 OpenAI,並接受 dimensions 參數。
| 端點 | 狀態 | 輸入欄位 | 回應欄位 | 支援批次輸入? | dimensions 參數? |
|---|---|---|---|---|---|
| /api/embed | 當前 | input(字符串或陣列) | embeddings | 是 | 否 |
| /api/embeddings | 舊版 / 已棄用 | prompt(單一) | embedding | 否 | 否 |
| /v1/embeddings | OpenAI 相容 | input | data[].embedding | 是 | 是(Matryoshka) |
收到 404 或奇怪的回應結構?你很可能使用的是 /api/embeddings(舊版)。切換到 /api/embed 並讀取 embeddings 鍵而非 embedding。這個單一字元的差異讓許多複製舊教學的人陷入困境。
/v1/embeddings 路由僅在一種特定情況下重要:從 OpenAI 遷移時。由於它接受 dimensions 參數,你可以將支援 Matryoshka 的模型截斷至目標大小,這是解決我們接下來要討論的 1536 維度不匹配問題的修復方法。
步驟 3:儲存並搜尋你的向量(pgvector、Qdrant 或 Chroma)
將 768 浮點數向量儲存在執行最近鄰搜尋的資料庫中,然後使用餘弦距離進行查詢。在我們的 RAG 建構中,對於已經使用 Postgres 的團隊,我們預設使用 Postgres 加上 pgvector,因為它能讓你的嵌入資料與關聯式資料紧邻存放。啟用擴充套件,宣告一個與模型維度匹配的 VECTOR(768) 欄位,插入資料,並使用 <=> 餘弦運算子進行查詢。
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
body text,
embedding vector(768) -- must match nomic-embed-text
);
-- Insert a row (embedding comes from ollama.embed)
INSERT INTO chunks (body, embedding) VALUES ('first chunk', '[0.01, -0.02, ...]');
-- Top-5 nearest chunks by cosine distance
SELECT body, 1 - (embedding <=> '[0.01, -0.02, ...]') AS score
FROM chunks
ORDER BY embedding <=> '[0.01, -0.02, ...]'
LIMIT 5;Qdrant 和 Chroma 在概念上運作方式相同:建立具有固定向量大小(與模型匹配)的集合,然後進行 upsert 和搜尋。這條規則適用於所有地方:選擇向量資料庫 的重要性不如確保維度正確,因為如果向量大小與集合不匹配,Qdrant、Chroma 和 pgvector 都會拒絕該向量。如果你仍在猶豫,請參閱我們的 Qdrant vs Chroma vs pgvector 比較。
遷移陷阱:沒有 Ollama 模型原生具備 1536 維,因此現有的 VECTOR(1536) pgvector 欄位會拒絕它們。三種修復方法:(1) 選擇維度與欄位匹配的模型,(2) 在如 qwen3-embedding 或 embeddinggemma 等 Matryoshka 模型上使用帶有 dimensions 參數的 /v1/embeddings 以截斷至 1536,或 (3) 將欄位重新宣告為模型的原生維度,例如 VECTOR(768)。
我們在 RTX 4090 上測試了 nomic-embed-text:冷啟動 vs 熱 GPU
我們進行了實測。在我們的環境中(Ubuntu 22.04、RTX 4090 24 GB、Ollama 0.5.x、768 維的 nomic-embed-text),閒置後的第一次 /api/embed 呼叫在大約 1.3 秒 內完成,期間權重載入 VRAM。一旦進入熱機狀態,我們觀察到每次嵌入的 p50 接近 9 毫秒,p95 接近 22 毫秒。以 64 為批次大小時,我們保持了大約每秒 600 次嵌入的速度。
| 指標 | 冷啟動(閒置後的首次請求) | 熱機(穩定狀態) |
|---|---|---|
| 延遲 p50 | ~1.3 秒 | ~9 毫秒 |
| 延遲 p95 | ~1.3 秒 | ~22 毫秒 |
| 吞吐量(batch=64) | 不適用 | ~600 嵌入/秒 |
| 10,000 區塊語料庫 | 不適用 | ~50 秒 |
這裡有一個能回答「為什麼 Ollama 嵌入很慢或超時」的關鍵問題。預設情況下,Ollama 在閒置約 5 分鐘後會從 VRAM 中卸載模型。因此,你的下一個請求會再次支付那 ~1.3 秒的冷啟動時間,這在生產環境中感覺像是隨機出現的峰值。修復方法是使用 keep_alive:
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "keep me warm",
"keep_alive": -1
}'設定 keep_alive: -1 會將模型無限期地固定在 VRAM 中,因此每個請求都保持在熱機路徑上。在熱機狀態下,RTX 4090 上的 nomic-embed-text 保持 p95 接近 22 毫秒。讓它閒置 5 分鐘,你的下一個請求就會再次支付 ~1.3 秒的冷啟動時間。對於對延遲敏感的服務,請將其固定。
自託管嵌入值得嗎?成本與 API 比較
本地嵌入的邊際成本大約是每百萬 Token 0 美元,加上電費,而 OpenAI text-embedding-3-small 則約為每百萬 Token 0.02 美元。但誠實的答案是:自託管只有在超過一定的 Token 量門檻時才划算。每月低於幾億 Token 時,你付出的是維運時間和閒置 GPU 的成本,而不是省下的金錢。對於低用量,API 的便利性勝出。
| 因素 | 本地 Ollama | OpenAI API |
|---|---|---|
| 每百萬 Token 邊際成本 | ~$0(僅電費) | ~$0.02 |
| 前期成本 | GPU + 設定 | $0 |
| 資料隱私 | 永不離開你的機器 | 發送給供應商 |
| 維運負擔 | 你運行伺服器 | 無 |
| 最適合 | 高用量、私密資料 | 低用量、無 GPU |
自託管嵌入只有在每月超過大約幾億 Token 時才勝過 API。低於此數值,你付出的是維運時間,而非省下的金錢。本地方案不適用的情況包括:低查詢量、無 GPU,或團隊沒有足夠的維運能力來保持伺服器健康。在這些情況下,受管 API 是務實的選擇,接下來可以閱讀 Voyage、OpenAI 和 Cohere 嵌入 API 的比較。不确定是否想完全擁有 GPU 和維運工作?許多團隊為了隱私將嵌入保留在本地,但在設定和第二階段的維護方面尋求協助,這正是我們的 AI 整合服務 所處理的類型。如果你想比較運行時環境,請參閱其他在本地運行模型的工具。
關於作者
Mert Batur Gurbuz 是 Techsy.io 的共同創辦人,該團隊為 B2B 客戶交付 AI 代理、自動化系統以及語音/SDR 管道。他就讀於伯明翰大學,並撰寫關於 Techsy 團隊在生產環境中實際使用的 LLM 工具堆疊的文章。
資歷:Techsy.io 共同創辦人,伯明翰大學。在 LinkedIn 上聯繫。
常見問題
使用 Ollama 在本地運行嵌入真的比 OpenAI API 便宜嗎?
只有在超過一定的 Token 量門檻時才成立。本地邊際成本大約是每百萬 Token 0 美元加上電費,而 OpenAI text-embedding-3-small 則約為 0.02 美元。每月低於幾億 Token 時,API 在便利性和零維運方面勝出。自託管的另一個原因是隱私:你的資料永不離開機器。
/api/embed 和 /api/embeddings 有什麼區別?
/api/embed 是當前端點。它接受一個 input 欄位(用於批次的字符串或陣列)並返回 embeddings。/api/embeddings 是舊版、已棄用的路由,具有單數 prompt 欄位並返回 embedding。如果你遇到 404 或意外的回應結構,你幾乎肯定使用的是舊版端點。
Ollama 嵌入是免費的嗎?
是的,就意義而言,沒有每 Token 收費也沒有 API Key。你支付的是硬體和運行它的電費。沒有像雲端 API 那樣的計量收費,因此一旦你的 GPU 運行起來,再生成一百萬次嵌入的邊際成本幾乎為零。
RAG 的預設或最佳 Ollama 嵌入模型是什麼?
768 維的 nomic-embed-text 是本地 RAG 的熱門預設選擇;它勝過 OpenAI 舊版的 ada-002,並且能在中等硬體上運行。對於多語言或長上下文工作,bge-m3 或 qwen3-embedding 更強大。若要查看跨供應商的排名和評分比較,請參閱我們的嵌入模型中心。
為什麼我的 Ollama 嵌入很慢或超時?
閒置後的首次請求會因模型載入 VRAM 而支付冷啟動時間,在我們的 RTX 4090 上大約為 1.3 秒。Ollama 預設也會在閒置約 5 分鐘後卸載模型,因此間歇性的緩慢通常是重複的冷啟動造成的。設定 keep_alive: -1 以將模型固定在 VRAM 中。
Ollama 能匹配 OpenAI 的 1536 維嵌入嗎?
沒有 Ollama 模型原生具備 1536 維,因此在維度不匹配的情況下,遷移現有的 VECTOR(1536) 欄位會失敗。透過在如 qwen3-embedding 或 embeddinggemma 等 Matryoshka 模型上使用帶有 dimensions 參數的 /v1/embeddings 來修復,或者將欄位重新宣告為模型的原生大小,例如 VECTOR(768)。
我需要 GPU 才能在本地運行嵌入模型嗎?
不需要。像 nomic-embed-text(1.37 億)和 all-minilm(2200 萬)這樣的小型模型在低用量下可以在 CPU 上良好運行。GPU 將每次嵌入的延遲降低到個位數毫秒,並將批次吞吐量提高到每秒數百次嵌入,這在你一次性索引數千個區塊時非常重要。
如何在 Python 或 LangChain 中使用 Ollama 嵌入?
官方客戶端呼叫為 ollama.embed(model="nomic-embed-text", input=["chunk a", "chunk b"]),它返回一個 embeddings 列表。在 LangChain 中,使用指向 http://localhost:11434 的 OllamaEmbeddings 類別,然後像其他嵌入供應商一樣將其傳遞給向量儲存庫的 from_documents 或 add_texts 方法。
Ollama 嵌入模型可以處理多大的上下文長度?
這取決於模型。nomic-embed-text 原生支援 8192 Token,但在服務時預設為 2048 Token 窗口,因此對於長區塊請將 num_ctx 提高到 8192,否則它們會被靜默截斷。bge-m3 處理 8192,qwen3-embedding 最高可達 32K;all-minilm 限制在 256 Token。