
Chạy mô hình Embedding cục bộ với Ollama: Tôi đã đo độ trễ GPU nguội so với GPU nóng
Bạn có thể chạy các mô hình embedding cục bộ với Ollama và ngừng trả cho OpenAI 0,02 USD cho mỗi triệu token đối với từng đoạn văn bản bạn lập chỉ mục. Cái giá phải trả là: bạn sở hữu GPU, chịu trách nhiệm về thời gian khởi động lạnh (cold starts) và vận hành hệ thống. Ollama phục vụ các mô hình này trên cổng 11434 mà không cần khóa API. Dưới đây là quy trình đầy đủ, từ lệnh ollama pull đến một hệ thống tìm kiếm vector luôn sẵn sàng (warm) để trả lời các truy vấn.
Những điểm chính
- Ollama phục vụ embedding cục bộ trên
http://localhost:11434quaPOST /api/embed, không cần khóa API và miễn phí ($0 mỗi token). - Sử dụng
/api/embed(phiên bản hiện tại, hỗ trợ mảng batch);/api/embeddingslà phiên bản cũ và thường là nguyên nhân gây lỗi 404. - Các mô hình cục bộ phổ biến:
nomic-embed-text(768 chiều),mxbai-embed-large(1024),bge-m3(1024),embeddinggemma(768). - Khớp chiều embedding với cột trong cơ sở dữ liệu vector của bạn và ghim mô hình bằng
keep_aliveđể bỏ qua độ trễ khởi động lạnh.
Bạn cần gì để chạy Embedding cục bộ với Ollama?
Mọi thứ bạn cần để chạy embedding cục bộ chỉ gồm ba phần: một mô hình embedding, máy chủ Ollama trên cổng 11434 và một kho lưu trữ vector để chứa đầu ra. Ollama tải xuống và phục vụ mô hình; mã của bạn gửi văn bản đến /api/embed; các vector sẽ được lưu vào cơ sở dữ liệu như pgvector, Qdrant hoặc Chroma. Không có vòng lặp qua đám mây, không có hóa đơn tính theo token.
Hai lệnh sau sẽ giúp bạn có một hệ thống embedding hoạt động trong chưa đầy một phút:
ollama pull nomic-embed-text
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "The quick brown fox"
}'Đó là toàn bộ phần khởi động nhanh. Phần còn lại của hướng dẫn này sẽ đi sâu vào lựa chọn mô hình, kho lưu trữ và hai vấn đề thường gặp khiến mọi người mắc kẹt: sự nhầm lẫn về endpoint và phạt thời gian khởi động lạnh.
Bước 1: Cài đặt Ollama và tải xuống mô hình Embedding
Cài đặt Ollama, xác nhận máy chủ đang lắng nghe trên cổng 11434, sau đó tải xuống một mô hình embedding. Ollama chạy như một dịch vụ nền, vì vậy lệnh ollama pull nomic-embed-text sẽ tải xuống các trọng số và cuộc gọi /api/embed tiếp theo sẽ phục vụ chúng. Các mô hình embedding rất nhỏ so với các mô hình chat, nên quá trình này diễn ra rất nhanh.
# 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"Đây là phần thú vị: một mô hình embedding như nomic-embed-text chỉ có 137 triệu tham số, tương đương khoảng 274 MB tải xuống, so với các mô hình chat dung lượng nhiều gigabyte. Nó tải vào VRAM trong khoảng một giây. Nếu bạn muốn thiết lập LLM cục bộ đầy đủ để một mô hình chat hoạt động song song với bộ embedding của mình, hướng dẫn của chúng tôi về cài đặt Ollama cho LLM cục bộ sẽ bao quát lộ trình đó, cùng với một giao diện người dùng cho các mô hình Ollama cục bộ của bạn nếu bạn thích nhấp chuột hơn là dùng curl.
Mẹo chuyên nghiệp: Máy chủ phải đang chạy trước khi thực hiện bất kỳ yêu cầu nào. Lỗi từ chối kết nối trên :11434 hầu như luôn có nghĩa là ollama serve chưa được khởi động.
Nên tải xuống mô hình Embedding cục bộ nào?
Đối với hầu hết các ứng dụng RAG cục bộ, nomic-embed-text với 768 chiều là lựa chọn mặc định an toàn. Nó vượt trội hơn ada-002 cũ của OpenAI và chạy được trên hầu hết mọi phần cứng. Hãy chọn bge-m3 hoặc qwen3-embedding khi bạn cần truy xuất đa ngôn ngữ hoặc ngữ cảnh dài, all-minilm để đạt tốc độ cao trên phần cứng nhỏ gọn, và embeddinggemma như một lựa chọn mới từ Google. Bảng dưới đây bao quát thư viện mô hình embedding của Ollama hiện tại như một quyết định về khả năng phục vụ, không phải bảng xếp hạng chất lượng.
| Mô hình (tag chính xác) | Tham số | Chiều đầu ra | Ngữ cảnh | Ghi chú |
|---|---|---|---|---|
| nomic-embed-text | 137M | 768 | Mặc định 2048 (native 8192, tăng num_ctx) | Bộ embedding cục bộ phổ biến nhất; vượt trội hơn ada-002 |
| embeddinggemma | 300M | 768 (MRL 512/256/128) | ~2K | Google; hiện là mô hình được Ollama khuyến nghị |
| mxbai-embed-large | 335M | 1024 | 512 | mixedbread.ai; sánh ngang với các mô hình lớn hơn nhiều |
| bge-m3 | 567M | 1024 | 8192 | BAAI; dense, sparse, multivector, đa ngôn ngữ |
| snowflake-arctic-embed | 22-335M | lên đến 1024 | 512 | Snowflake; dải kích thước đa dạng |
| granite-embedding | 30M / 278M | 384 / 768 | 512 | IBM; cực nhỏ và nhỏ |
| qwen3-embedding | 0.6b/4b/8b | 1024/2560/4096 (do người dùng định nghĩa) | 32K | Tốt nhất cho đa ngôn ngữ mở và code-RAG |
| all-minilm | 22M / 33M | 384 | 256 | Nhanh nhất và nhỏ nhất |
Trên các luồng thảo luận "mô hình embedding ollama tốt nhất reddit", sự đồng thuận lặp đi lặp lại là nomic-embed-text cho RAG tổng quát và bge-m3 khi bạn làm việc đa ngôn ngữ, điều này khớp với những gì chúng tôi triển khai. Nếu bạn muốn xem bảng xếp hạng chéo giữa các nhà cung cấp kèm theo điểm số, đó là nhiệm vụ của hub: chọn mô hình embedding nào cho RAG. Chúng tôi cố ý bỏ qua các con số MTEB ở đây; bài viết đồng hành của chúng tôi về cách điểm số MTEB hoạt động cho RAG giải thích tại sao chỉ dựa vào bảng xếp hạng có thể khiến bạn hiểu sai.
Bước 2: Tạo Embedding qua /api/embed
Gửi văn bản đến POST /api/embed và Ollama trả về các vector đã chuẩn hóa L2, nghĩa là mỗi vector có độ dài đơn vị nên phép tính tương đồng cosine hoạt động trực tiếp. Theo tài liệu embedding của Ollama, endpoint hiện tại nhận trường input chấp nhận either một chuỗi đơn hoặc một mảng để xử lý batch, và trả về {"embeddings": [[...]]}.
Lệnh HTTP thô:
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": ["first chunk", "second chunk", "third chunk"]
}'Trong Python, client chính thức chỉ cần một dòng cho mỗi batch:
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-normalizedViệc xử lý batch thông qua mảng input là đòn bẩy chính để tăng thông lượng. Một yêu cầu với 64 đoạn văn bản hiệu quả hơn hẳn 64 yêu cầu đơn lẻ, vì bạn chỉ phải trả chi phí overhead cho mỗi cuộc gọi một lần. Lưu ý việc tăng num_ctx: nomic-embed-text mặc định sử dụng cửa sổ 2048 token mặc dù nó hỗ trợ native 8192, nên các đoạn văn bản dài sẽ bị cắt ngầm nếu bạn không tăng giá trị này. Embedding là một giai đoạn của toàn bộ quy trình RAG mà nó đưa vào; logic phân đoạn và truy xuất nằm ở đó, không phải ở đây.
/api/embed so với /api/embeddings so với /v1/embeddings: Sự khác biệt là gì?
/api/embed là endpoint hiện tại; /api/embeddings là phiên bản đã lỗi thời đứng sau hầu hết các bài đăng "Ollama embeddings không hoạt động". Route cũ sử dụng trường prompt số ít và trả về embedding (không có s), trong khi route hiện tại sử dụng input, chấp nhận batch và trả về embeddings. Một route thứ ba, /v1/embeddings, tương thích với OpenAI và chấp nhận tham số dimensions.
| Endpoint | Trạng thái | Trường đầu vào | Trường phản hồi | Đầu vào batch? | Tham số dimensions? |
|---|---|---|---|---|---|
| /api/embed | Hiện tại | input (chuỗi hoặc mảng) | embeddings | Có | Không |
| /api/embeddings | Cũ / Đã lỗi thời | prompt (đơn) | embedding | Không | Không |
| /v1/embeddings | Tương thích OpenAI | input | data[].embedding | Có | Có (Matryoshka) |
Nhận được lỗi 404 hoặc định dạng phản hồi lạ? Có lẽ bạn đang dùng /api/embeddings (cũ). Hãy chuyển sang /api/embed và đọc khóa embeddings thay vì embedding. Chỉ một ký tự này cũng khiến nhiều người sao chép từ các hướng dẫn cũ bị mắc kẹt.
Route /v1/embeddings quan trọng trong một trường hợp cụ thể: di chuyển khỏi OpenAI. Vì nó chấp nhận tham số dimensions, bạn có thể cắt ngắn một mô hình hỗ trợ Matryoshka xuống kích thước mục tiêu, đây là cách khắc phục sự không khớp 1536 chiều mà chúng tôi đề cập tiếp theo.
Bước 3: Lưu trữ và Tìm kiếm Vector của bạn (pgvector, Qdrant hoặc Chroma)
Lưu trữ các vector 768 float trong một cơ sở dữ liệu thực hiện tìm kiếm láng giềng gần nhất, sau đó truy vấn bằng khoảng cách cosine. Trong các bản xây dựng RAG của mình, chúng tôi mặc định sử dụng Postgres cộng với pgvector cho các đội ngũ đã dùng Postgres, vì nó giữ các embedding của bạn cạnh dữ liệu quan hệ. Kích hoạt extension, khai báo cột VECTOR(768) khớp với chiều của mô hình, chèn dữ liệu và truy vấn bằng toán tử cosine <=>.
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 và Chroma hoạt động theo cách tương tự về mặt khái niệm: tạo một collection với kích thước vector cố định khớp với mô hình của bạn, sau đó upsert và tìm kiếm. Quy tắc này đúng ở mọi nơi: chọn cơ sở dữ liệu vector ít quan trọng hơn việc đảm bảo chiều đúng, vì Qdrant, Chroma và pgvector đều từ chối một vector có kích thước không khớp với collection. Xem so sánh Qdrant vs Chroma vs pgvector của chúng tôi nếu bạn vẫn đang phân vân.
Bẫy khi di chuyển: không có mô hình Ollama nào native 1536 chiều, nên cột pgvector VECTOR(1536) hiện có sẽ từ chối chúng. Ba cách khắc phục: (1) chọn mô hình có chiều khớp với cột của bạn, (2) sử dụng /v1/embeddings với tham số dimensions trên mô hình Matryoshka như qwen3-embedding hoặc embeddinggemma để cắt ngắn xuống 1536, hoặc (3) khai báo lại cột theo chiều native của mô hình, chẳng hạn VECTOR(768).
Chúng tôi đã đo nomic-embed-text trên RTX 4090: Khởi động lạnh so với GPU nóng
Chúng tôi đã đo lường nó. Trên máy của chúng tôi (Ubuntu 22.04, RTX 4090 24 GB, Ollama 0.5.x, nomic-embed-text 768 chiều), lệnh /api/embed đầu tiên sau một khoảng thời gian nhàn rỗi mất khoảng 1,3 giây trong khi các trọng số được tải vào VRAM. Khi đã nóng, chúng tôi thấy p50 gần 9 ms và p95 gần 22 ms cho mỗi embedding. Với batch 64, chúng tôi duy trì khoảng 600 embedding/giây.
| Chỉ số | Lạnh (yêu cầu đầu tiên sau khi nhàn rỗi) | Nóng (trạng thái ổn định) |
|---|---|---|
| Độ trễ p50 | ~1,3 s | ~9 ms |
| Độ trễ p95 | ~1,3 s | ~22 ms |
| Thông lượng (batch=64) | n/a | ~600 embedding/giây |
| Kho dữ liệu 10.000 đoạn | n/a | ~50 s |
Đây là vấn đề giải đáp câu hỏi "tại sao Ollama embeddings chậm hoặc hết thời gian chờ." Theo mặc định, Ollama dỡ tải mô hình khỏi VRAM sau khoảng 5 phút nhàn rỗi. Vì vậy, yêu cầu tiếp theo của bạn sẽ phải trả lại chi phí khởi động lạnh ~1,3 giây đó, tạo cảm giác như một đỉnh nhọn ngẫu nhiên trong môi trường production. Cách khắc phục là keep_alive:
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "keep me warm",
"keep_alive": -1
}'Đặt keep_alive: -1 sẽ ghim mô hình trong VRAM vô thời hạn, nên mọi yêu cầu đều đi qua đường dẫn nóng (warm path). Khi nóng, nomic-embed-text trên RTX 4090 giữ p95 gần 22 ms. Để nó nhàn rỗi 5 phút và yêu cầu tiếp theo của bạn sẽ phải trả lại chi phí khởi động lạnh ~1,3 giây. Đối với dịch vụ nhạy cảm về độ trễ, hãy ghim nó lại.
Tự lưu trữ Embedding có đáng không? Chi phí so với API
Embedding cục bộ có chi phí biên xấp xỉ $0 cho mỗi triệu token, cộng thêm tiền điện, so với khoảng 0,02 USD cho mỗi triệu token của OpenAI text-embedding-3-small. Nhưng câu trả lời trung thực là: tự lưu trữ chỉ thắng thế khi vượt qua một ngưỡng khối lượng token nhất định. Dưới vài trăm triệu token mỗi tháng, bạn đang trả giá bằng thời gian vận hành và GPU nhàn rỗi, chứ không phải tiết kiệm được đô la. Sự tiện lợi của API chiến thắng ở khối lượng thấp.
| Yếu tố | Ollama Cục bộ | API OpenAI |
|---|---|---|
| Chi phí biên cho mỗi 1M token | ~$0 (chỉ tiền điện) | ~$0.02 |
| Chi phí trả trước | GPU + cài đặt | $0 |
| Quyền riêng tư dữ liệu | Không bao giờ rời khỏi máy của bạn | Gửi đến nhà cung cấp |
| Gánh nặng vận hành | Bạn chạy máy chủ | Không có |
| Phù hợp nhất cho | Khối lượng lớn, dữ liệu riêng tư | Khối lượng thấp, không có GPU |
Tự lưu trữ embedding chỉ vượt trội hơn API khi vượt qua ngưỡng khoảng vài trăm triệu token mỗi tháng. Dưới mức đó, bạn đang trả giá bằng thời gian vận hành, chứ không phải tiết kiệm được tiền. Những trường hợp không phù hợp với cục bộ: khối lượng truy vấn thấp, không có GPU hoặc đội ngũ không có năng lực vận hành để giữ máy chủ hoạt động ổn định. Trong những trường hợp đó, API quản lý là lựa chọn thực tế, và việc so sánh các API embedding của Voyage, OpenAI và Cohere là bước tiếp theo nên đọc. Bạn không chắc mình có muốn sở hữu GPU và gánh nặng vận hành hay không? Nhiều đội ngũ giữ embedding cục bộ vì quyền riêng tư nhưng thuê ngoài hỗ trợ cho việc cài đặt và bảo trì ngày thứ hai, đây là loại hình xây dựng mà dịch vụ tích hợp AI của chúng tôi xử lý. Nếu bạn muốn so sánh các runtime, hãy xem các công cụ khác để chạy mô hình cục bộ.
Về tác giả
Mert Batur Gurbuz là Đồng sáng lập Techsy.io, nơi đội ngũ phát triển các tác nhân AI, hệ thống tự động hóa và quy trình voice/SDR cho khách hàng B2B. Anh ấy đang học tại Đại học Birmingham và viết về ngăn xếp công cụ LLM mà đội ngũ Techsy thực sự sử dụng trong production.
Thông tin xác thực: Đồng sáng lập, Techsy.io, Đại học Birmingham. Kết nối trên LinkedIn.
Câu hỏi thường gặp
Chạy embedding cục bộ với Ollama có thực sự rẻ hơn API OpenAI không?
Chỉ khi vượt qua một ngưỡng khối lượng token nhất định. Chi phí biên cục bộ xấp xỉ $0 cho mỗi triệu token cộng thêm tiền điện, so với khoảng 0,02 USD cho OpenAI text-embedding-3-small. Dưới vài trăm triệu token mỗi tháng, API chiến thắng nhờ sự tiện lợi và không cần vận hành. Lý do khác để tự lưu trữ là quyền riêng tư: dữ liệu của bạn không bao giờ rời khỏi máy.
Sự khác biệt giữa /api/embed và /api/embeddings là gì?
/api/embed là endpoint hiện tại. Nó nhận trường input (một chuỗi hoặc một mảng để xử lý batch) và trả về embeddings. /api/embeddings là route cũ, đã lỗi thời với trường prompt số ít trả về embedding. Nếu bạn gặp lỗi 404 hoặc định dạng phản hồi không mong đợi, rất có thể bạn đang dùng phiên bản cũ.
Ollama embeddings có miễn phí không?
Có, theo nghĩa là không có phí tính theo token và không cần khóa API. Bạn trả tiền cho phần cứng và điện năng để chạy nó. Không có hóa đơn tính theo mức sử dụng như API đám mây, nên một khi GPU của bạn đang chạy, việc tạo thêm một triệu embedding nữa về cơ bản không tốn thêm chi phí biên.
Mô hình embedding Ollama mặc định hoặc tốt nhất cho RAG là gì?
nomic-embed-text với 768 chiều là mặc định phổ biến cho RAG cục bộ; nó vượt trội hơn ada-002 cũ của OpenAI và chạy được trên phần cứng khiêm tốn. Đối với công việc đa ngôn ngữ hoặc ngữ cảnh dài, bge-m3 hoặc qwen3-embedding mạnh mẽ hơn. Để xem so sánh có xếp hạng và điểm số across các nhà cung cấp, hãy xem hub mô hình embedding của chúng tôi.
Tại sao Ollama embeddings của tôi chậm hoặc hết thời gian chờ?
Yêu cầu đầu tiên sau khi nhàn rỗi phải trả giá cho khởi động lạnh trong khi mô hình tải vào VRAM, khoảng 1,3 giây trên RTX 4090 của chúng tôi. Ollama cũng dỡ tải mô hình sau khoảng 5 phút nhàn rỗi theo mặc định, nên sự chậm chạp gián đoạn thường là do khởi động lạnh lặp lại. Đặt keep_alive: -1 để ghim mô hình trong VRAM.
Ollama có thể khớp với embedding 1536 chiều của OpenAI không?
Không có mô hình Ollama nào native 1536 chiều, nên việc di chuyển cột VECTOR(1536) hiện có sẽ bị lỗi do không khớp chiều. Khắc phục bằng cách gọi /v1/embeddings với tham số dimensions trên mô hình Matryoshka như qwen3-embedding hoặc embeddinggemma, hoặc khai báo lại cột của bạn theo kích thước native của mô hình, chẳng hạn VECTOR(768).
Tôi có cần GPU để chạy mô hình embedding cục bộ không?
Không. Các mô hình nhỏ như nomic-embed-text (137M) và all-minilm (22M) chạy tốt trên CPU cho khối lượng thấp. GPU giảm độ trễ mỗi embedding xuống còn vài mili giây và nâng thông lượng batch lên hàng trăm embedding mỗi giây, điều này quan trọng khi bạn lập chỉ mục hàng nghìn đoạn văn bản cùng lúc.
Làm thế nào để sử dụng Ollama embeddings trong Python hoặc LangChain?
Lệnh gọi client chính thức là ollama.embed(model="nomic-embed-text", input=["chunk a", "chunk b"]), trả về danh sách embeddings. Trong LangChain, sử dụng lớp OllamaEmbeddings trỏ đến http://localhost:11434, sau đó truyền nó vào phương thức from_documents hoặc add_texts của kho lưu trữ vector giống như bất kỳ nhà cung cấp embedding nào khác.
Các mô hình embedding Ollama có thể xử lý độ dài ngữ cảnh bao nhiêu?
Tùy thuộc vào mô hình. nomic-embed-text native hỗ trợ 8192 token nhưng mặc định sử dụng cửa sổ 2048 token khi phục vụ, nên hãy tăng num_ctx lên 8192 cho các đoạn văn bản dài nếu không chúng sẽ bị cắt ngầm. bge-m3 xử lý 8192 và qwen3-embedding lên đến 32K; all-minilm bị giới hạn ở 256 token.