Xây Coding Agent AI chạy Local với Mem0 và Ollama

Bài viết dựa trên engineering blog của Mem0 (tháng 9/2026), viết lại bằng tiếng Việt đơn giản cho người đọc muốn hiểu và tự làm theo.

Câu chuyện khiến bạn phải để ý

Hãy tưởng tượng một mô hình AI nhỏ — chỉ 4 tỷ tham số (4B), chạy hoàn toàn trên laptop của bạn — được giao cùng một việc: sinh ra code dạng JSON.

  • Lần 1: đổ toàn bộ 407 token lịch sử hội thoại vào prompt → JSON sai, không đọc được. Ba lần chạy, sai cả ba.
  • Lần 2: chỉ đưa 247 token "tin đã chưng cất" từ Mem0 → JSON đúng. Ba lần chạy, đúng cả ba.

Cùng một mô hình, cùng một máy, cùng một phút. Không gì thay đổi ngoài cách ngữ cảnh đến với mô hình. Chênh lệch 160 token — nhưng đó là ranh giới giữa một agent chạy được và một agent hỏng.

Bài này giải thích vì sao, và hướng dẫn bạn dựng lại toàn bộ hệ thống đó ngay trên máy mình.

Vấn đề: "bộ nhớ ngắn hạn" của AI quá đầy

Mô hình AI có cái gọi là context window — coi như bộ nhớ làm việc, mọi thứ AI "nhìn thấy" ở lượt hiện tại. Cách viết agent phổ biến (gọi là naive — nghĩa là "thô sơ") là nhét toàn bộ lịch sử hội thoại vào prompt mỗi lượt gọi.

Khi hội thoại dài lên, ba chuyện xấu xảy ra cùng lúc:

  1. Chi phí token tăng theo độ dài hội thoại, không theo độ khó công việc. Chat 20 lượt thì lượt thứ 20 trả tiền cho 19 lượt trước đó — dù nội dung cũ chẳng liên quan gì tới việc đang làm.
  2. AI dễ sai cấu trúc output. Mô hình phải "bơi" qua đống nhiễu (lời xã giao, hỏi lại, sửa sai cũ…) trước khi chạm tới chỉ dẫn quan trọng. Đến được chỗ cần thì "sự chú ý" của nó đã bị phân tán.
  3. Hết phiên là mất sạch. Đóng terminal, mọi thứ AI "biết" về bạn biến mất. Phiên sau lại bắt đầu từ con số 0.

Mem0 phá cả ba vấn đề này cùng lúc — bằng một ý tưởng đơn giản: thay vì nhớ nguyên hội thoại, chỉ nhớ các dữ kiện (facts) thật sự quan trọng, và mỗi lần chỉ lấy đúng những dữ kiện liên quan đến việc đang làm.

Hai viên gạch chính

Hệ thống chỉ cần bốn thành phần, nhưng hai viên gạch chính là:

  • Ollama — phần mềm chạy mô hình AI ngay trên máy bạn (macOS/Linux/Windows), không cần internet, không tốn phí API. Nó mở một API tương thích chuẩn OpenAI tại http://localhost:11434/v1, nên code Python viết cho OpenAI chạy được luôn.
  • Mem0 — "lớp trí nhớ" cho AI. Bạn gởi dữ kiện vào, Mem0 lưu lại; lần sau bạn hỏi, nó trả về đúng mấy dữ kiện liên quan. Có hai chế độ: dùng MemoryClient ( dịch vụ quản lý — có gói miễn phí, khỏi lo hạ tầng) hoặc Memory tự host (Mem0 chạy trên máy bạn, tự chọn vector store).

Mô hình sinh code: Gemma 3 4B (nhẹ, chạy tốt trên laptop). Mô hình embedding (dùng để "hiểu" ngữ nghĩa câu hỏi khi tra trí nhớ): nomic-embed-text.

Điều thú vị: toàn bộ requirements.txt chỉ có ba dòngopenai, mem0ai, ollama. Không cần Docker, không cần server riêng.

Kiến trúc prompt 3 khối — trái tim của bài toán

Mỗi lần gọi agent, prompt được ghép từ đúng ba khối, theo thứ tự cố định:

Khối Nội dung Vì sao ở vị trí đó
1. Luật tĩnh Quy tắc ứng xử, format output, ràng buộc cứng Không bao giờ đổi → tận dụng được prefix caching (server KV cache tái dùng phần đầu giống nhau, tiết kiệm tính toán)
2. Khối Mem0 Vài dữ kiện liên quan truy xuất từ trí nhớ Đặt sau phần đầu ổn định để giữ cache hit; phần này đổi theo từng request
3. Yêu cầu người dùng Chỉ việc đang làm, KHÔNG kèm lịch sử Đứng cuối để "sự chú ý" của mô hình dồn hết vào việc hiện tại

Hãy hình dung như một nhân viên: quy trình làm việc (không đổi), phiếu ghi chú ngắn về sở thích khách hàng (đổi theo từng khách), và yêu cầu cụ thể hôm nay. Không ai đi mang theo cả cuốn sổ tay hội thoại ba năm nay đi làm cả — nhưng đó chính xác là cách agent naive đang làm.

Lưu và truy xuất trí nhớ

Vòng đời trí nhớ chỉ có hai phép toán:

  • Trước khi gọi AI: search() tìm tối đa 5 dữ kiện liên quan nhất đến câu hỏi hiện tại, sắp xếp lại cho ổn định, rồi nhét vào prompt dưới nhãn [Persistent Context].
  • Sau khi AI trả lời: add() ghi lại dữ kiện mới. Phần hay ở đây: Mem0 tự khử trùng lặp — khi dữ kiện mới mâu thuẫn dữ kiện cũ ("dùng flexbox" → "chuyển sang grid layout"), nó cập nhật chứ không thêm chồng lên.

Cái gì đáng nhớ? Sở thích và pattern lặp lại: "User thích async/await hơn .then()", "Project dùng React 18 + TypeScript strict". Còn nguyên các lượt hội thoại thô thì KHÔNG đáng nhớ — thêm token mà không thêm thông tin, tức là tái tạo đúng vấn đề đang muốn giải.

Trong demo, Mem0 được "gieo sẵn" 6 sở thích của user. Trong thực tế, các dữ kiện này tự tích lũy dần qua mỗi lần bạn sửa lỗi, gợi ý thêm, hoặc phản đối cách AI làm — trí nhớ càng dùng càng giá trị.

Bắt JSON chuẩn không cần "khung sắt"

Nhiều hệ thống lớn (vLLM) có guided JSON — ép mô hình chỉ sinh ra token hợp lệ JSON, sai không được. Nhưng Ollama không có tính năng này. Giải pháp thay thế chỉ gồm hai bước:

  1. Nhét "hợp đồng JSON" vào cuối system message — vị trí cuối cùng là thứ mô hình đọc ngay trước khi bắt đầu sinh, nên hiệu lực mạnh nhất: "Chỉ trả về JSON object hợp lệ — không markdown, không văn xuôi. Đúng các key: code, language, file_path, explanation."
  2. Thử lại đúng một lần, kiểu "tự sửa bài": nếu JSON sai, gởi lại chính câu trả lời sai của nó kèm yêu cầu sửa — thay vì bảo nó làm lại từ đầu. Nói "câu này của em sai chỗ X, sửa lại" luôn tin cậy hơn "quên đi, làm lại cái khác" vì mô hình chỉ cần sửa trong phạm vi hẹp.

Kết quả benchmark chứng minh cách này đủ dùng: naive sai 0/3, tối ưu đúng 3/3.

Kết quả đối đầu: naive vs Mem0

Cùng một task ("sinh React TypeScript component dashboard hiển thị metrics TTFT và token count"), cùng máy, chạy 3 lần mỗi bên:

Chỉ số Naive (đầy lịch sử) Tối ưu (Mem0)
Context tokens 407 247 (−39%)
Thời gian token đầu (TTFT) 374 ms 311 ms (nhanh 1.2×)
Tổng latency 6.95 s 5.13 s
JSON parse thành công 0/3 3/3

Đáng chú ý: 39% giảm token và 1.2× nhanh hơn là con số khiêm tốn — máy càng nhanh, mô hình càng nhỏ thì khoảng cách latency càng hẹp. Nhưng khoảng cách độ tin cậy JSON (0% vs 100%) thì không hẹp lại dù phần cứng tốt đến đâu. Đó mới là con số đáng nhớ.

Và token còn có hiệu ứng lũy tiến: naive thì mỗi lượt dài hơn lượt trước (lịch sử phình ra), còn Mem0 giữ chi phí gần như phẳng mỗi lượt — vì chỉ truy xuất cửa sổ cố định (5 dữ kiện) thay vì cả cuộc hội thoại. Agent chạy 20 lượt không phải trả 20 lần tiền của lượt đầu.

Chạy thử trên máy bạn (5 bước)

Toàn bộ code có trong repo demo của Mem0. Bạn cần:

  1. Lấy API key miễn phí của Mem0, đặt vào biến môi trường MEM0_API_KEY
  2. Cài Ollama rồi kéo 2 mô hình: ollama pull gemma3:4bollama pull nomic-embed-text
  3. pip install openai mem0ai
  4. Clone repo demo
  5. python benchmark.py --runs 3 — chạy là ra lại đúng các con số trên

Muốn "air-gapped" hoàn toàn (không gởi gì ra ngoài)? Đổi từ MemoryClient sang class Memory tự host — mang theo vector store (Qdrant) và embedding model của bạn. Ba hàm add(), search(), get_all() giữ nguyên chữ ký, chỉ đổi cấu hình, không phải viết lại code.

Bài học rút ra

  • Hệ thống AI tốt hay dở không chỉ nằm ở mô hình. Cùng mô hình 4B: cách đưa ngữ cảnh quyết định nó là agent chạy được hay agent hỏng.
  • Ít mà chất thắng nhiều mà loãng. 5 dữ kiện đúng chỗ giá trị hơn 400 token lịch sử.
  • Trí nhớ cho AI là hạ tầng, không phải tính năng "nice to have". Không có nó, agent của bạn mỗi ngày đều là một người mới — dù bạn đã dạy nó cả trăm lần.
  • Tự host hoàn toàn khả thi. Stack này chạy trong-process trên một laptop thường, không cloud, không GPU đắt tiền.

Với ai đang mày mò homelab AI ở Việt Nam (chạy Ollama trên máy để bàn hoặc LXC như chúng tôi từng chia sẻ), đây là bước tiến tự nhiên tiếp theo: từ "chạy được mô hình" lên "có agent thực sự nhớ được bạn".

Nguồn gốc: Build a Local Coding Agent with Mem0 and Ollama — Aashi Dutt, Mem0 Engineering Blog, cập nhật 03/09/2026. Số liệu benchmark do tác giả gốc thực hiện; bài Việt Nam viết lại giữ nguyên số liệu.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *