Xây dựng bộ nhớ thống nhất cho AI Coding Agents: Giải pháp thoát khỏi sự phụ thuộc nhà cung cấp
Bài viết này giới thiệu cách sử dụng hooks và cơ sở dữ liệu đồ thị Neo4j để tạo ra một lớp bộ nhớ chung cho các công cụ lập trình AI như Claude Code và Cursor. Phương pháp này giúp người dùng sở hữu dữ liệu của mình, tránh bị khóa vào một nền tảng cụ thể và duy trì ngữ cảnh liên tục khi chuyển đổi công cụ.

Các Mô hình Ngôn ngữ Lớn (LLM) hiện nay đã trở nên mạnh mẽ đến mức cuộc tranh luận chính không còn là về việc khi nào mô hình tốt hơn tiếp theo sẽ ra mắt, mà là về ai sẽ xây dựng được "harness" (khung điều khiển) phù hợp xung quanh chúng. Harness chính là hệ thống bao bọc mô hình: vòng lặp tác nhân, định nghĩa công cụ, quản lý ngữ cảnh, bộ nhớ, câu lệnh (prompt) và quy trình công việc biến một LLM thô thành một sản phẩm hữu ích. Nếu ví mô hình là động cơ, thì harness là mọi thứ giúp nó có thể lăn bánh được. Các ví dụ điển hình về harness có thể kể đến là Cursor, Claude Desktop và nhiều công cụ khác.
Trong không gian của các công cụ lập trình AI, luôn tồn tại một cuộc tranh luận: việc cam kết với một harness cụ thể có đồng nghĩa với việc bị phụ thuộc vào nhà cung cấp (vendor lock-in) không? Bộ nhớ là khía cạnh nhạy cảm nhất của vấn đề này. Nếu bộ nhớ của tác nhân AI nằm bên trong một harness khép kín hoặc phía sau một API độc quyền, bạn thực sự không sở hữu nó, và chi phí chuyển đổi sẽ tăng lên rất nhanh. Tuy nhiên, mọi chuyện không nhất thiết phải như vậy.
Thiết kế bộ nhớ tác nhân thống nhất
Ý tưởng của bài viết này rất đơn giản: hãy giữ lớp bộ nhớ ở bên ngoài harness, và cho phép bất kỳ harness nào có thể cắm vào nó. Trong bài viết này, tôi sẽ chỉ ra cách bạn có thể xây dựng một lớp bộ nhớ chia sẻ duy nhất hoạt động trên ba tác nhân lập trình khác nhau — Claude Code, Codex của OpenAI và Cursor — bằng cách sử dụng hooks làm cơ chế tích hợp và Neo4j làm kho lưu trữ liên tục.
MCP tools có giới hạn trong việc quản lý bộ nhớ
MCP (Model Context Protocol - Giao thức Ngữ cảnh Mô hình) là câu trả lời phổ biến để cung cấp cho các tác nhân quyền truy cập vào các hệ thống bên ngoài. Chúng hoạt động tốt. Bạn có thể hiển thị cơ sở dữ liệu Neo4j dưới dạng công cụ MCP và để tác nhân truy vấn nó khi nó quyết định.
Tuy nhiên, các công cụ MCP được khởi xướng bởi tác nhân. Mô hình phải quyết định gọi công cụ, và nó phải biết khi nào và tại sao để làm điều đó. Điều này có nghĩa là:
- Tác nhân cần phải "nhớ rằng phải nhớ", nó phải chủ động quyết định lưu trữ một thứ gì đó đáng để nhớ lại sau này.
- Không có sự đảm bảo về tính nhất quán, một phiên có thể ghi lại mọi thứ, nhưng phiên tiếp theo có thể không ghi lại gì cả.
- Bạn đang phụ thuộc vào phán xét của mô hình về những gì quan trọng cho bộ nhớ, trong thời gian thực, trong khi nó đang bận rộn làm việc khác.
Những gì bạn thực sự cần là ghi log thụ động, xác định (deterministic), thứ mà bắt lấy mọi sự kiện của phiên bất kể mô hình đang làm gì, mà không tiêu tốn bất kỳ ngữ cảnh hay sự chú ý nào của nó. Đây chính là những gì hooks mang lại.
Sức mạnh của Hooks
Hooks là các lệnh shell tự động kích hoạt dựa trên các sự kiện trong vòng đời: khi phiên bắt đầu, khi người dùng gửi prompt, trước và sau mỗi lần sử dụng công cụ, và khi phiên kết thúc. Tác nhân không quyết định gọi chúng, chúng chạy theo chương trình.
Cấu trúc phiên trong Neo4j
Thông tin quan trọng là hooks được chuẩn hóa đáng kể giữa các nhà cung cấp. Claude Code, Codex, Cursor và các công cụ khác đều hỗ trợ về cơ bản cùng một sự kiện vòng đời:
SessionStart: Khi phiên tác nhân bắt đầu.UserPromptSubmit(hoặcbeforeSubmitPrompttrong Cursor): Khi người dùng gửi tin nhắn.PreToolUse/PostToolUse: Trước và sau mỗi lần gọi công cụ.Stop: Khi phiên kết thúc.
Hook nhận tải trọng JSON trên stdin với ID phiên, tên sự kiện, chi tiết công cụ và prompt của người dùng. Và hook có thể phát ra JSON trên stdout để tiêm thêm ngữ cảnh ngược lại vào cuộc hội thoại. Cùng một hợp đồng, cho ba harness/clients khác nhau.
Lớp bộ nhớ chia sẻ
Bây giờ chúng ta cần một nơi để lưu trữ bộ nhớ. Trong ví dụ này, chúng ta sẽ sử dụng Neo4j.
Mô hình rất đơn giản. Mỗi phiên tác nhân là một nút (node), được kết nối với một danh sách liên kết của các nút sự kiện, một cho mỗi lần gọi hook. Các sự kiện được phân loại theo sự kiện vòng đời đã kích hoạt chúng: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop. Một phiên cuối cùng trở thành một dòng thời gian có thứ tự của mọi thứ đã xảy ra trong lần chạy đó.
Tất cả năm loại sự kiện đều được ghi vào kho lưu trữ, mang lại cho bạn một đường dẫn kiểm toán hoàn chỉnh của mọi phiên trên mọi harness. Hai trong số chúng cũng là các điểm tiêm (injection points). SessionStart kích hoạt trước khi tác nhân đọc system prompt của nó, vì vậy bất kỳ thứ gì hook phát ra ở đó sẽ được thêm vào trước system prompt. Đó là cách bộ nhớ cấp độ tác nhân liên tục tìm đường vào ngữ cảnh. UserPromptSubmit kích hoạt ngay trước khi tin nhắn của người dùng được gửi, và bất kỳ thứ gì được phát ra ở đó sẽ được thêm vào prompt của người dùng. Đó là hook cho ngữ cảnh cấp độ lượt (turn-level), như kéo lại các ký ức liên quan đến những gì người dùng vừa nhập.
Giai đoạn Mơ (Dream Phase)
Công việc bộ nhớ thực tế diễn ra trong một giai đoạn mơ tách biệt: trích xuất sự thật từ các phiên, tóm tắt những gì đã xảy ra, cập nhật đồ thị. Đây chỉ là một công việc batch chạy mỗi vài giờ, đọc các sự kiện tích lũy kể từ lần chạy cuối cùng và ghi lại vào bộ nhớ.
Công việc "mơ" này kéo mọi sự kiện kể từ mốc nước (watermark) cuối cùng của phiên, chuyển chúng cho Claude cùng với bộ nhớ hiện tại, và yêu cầu nó ghi lại một tập hợp các ghi chú bền vững nhỏ. Các ghi chú này bắt chước một wiki markdown, giống như hình dạng mà Karpathy và những người khác đang hướng tới cho bộ nhớ LLM cá nhân và giống như hình dạng mà các kỹ năng (skills) của Anthropic đang sử dụng: mỗi ký ức là một tệp tại một đường dẫn ngữ nghĩa như profile/role.md, tools/bash/common-flags.md, hoặc project/neo4j-skills.md, với YAML frontmatter ở trên và văn bản ở dưới. Claude được yêu cầu hợp nhất (merge) thay vì nối thêm (append), vì vậy một đường dẫn là một tài liệu sống, không phải một nhật ký; nếu các sự kiện mới mâu thuẫn với ghi chú cũ, ghi chú cũ sẽ được viết lại.
Giai đoạn Dream thêm và chỉnh sửa ký ức
Kết quả là một cây các tệp markdown nhỏ, tự chứa mà một phiên trong tương lai có thể đọc từ đầu, không thể phân biệt được về hình thức so với một kỹ năng, chỉ được viết bởi giai đoạn mơ thay vì viết tay.
Truy cập bộ nhớ
Mảnh ghép cuối cùng của câu đố là cho phép tác nhân truy cập vào lớp bộ nhớ. Như đã đề cập, có hai cách để tiêm thông tin vào tác nhân: hooks và công cụ MCP.
Hooks mang tính xác định và chạy ở đầu mỗi phiên để điền vào system prompt. Đây là nơi thông tin hồ sơ và hướng dẫn về cách sử dụng bộ nhớ hiệu quả nên được đặt. Bạn cũng có thể thêm ngữ cảnh bổ sung khi sự kiện gửi prompt của người dùng kích hoạt, nhưng đó chỉ là thêm vào (append-only); bạn không thể thao tác các phần khác của prompt.
Mặt khác, công cụ MCP cung cấp cho LLM quyền truy cập trực tiếp vào lớp bộ nhớ theo yêu cầu. Thay vì thụ động nhận ngữ cảnh khi khởi động, tác nhân có thể tìm kiếm các ký ức liên quan, lưu trữ thông tin mới và cập nhật hoặc xóa các mục hiện có. Về cơ bản, đó là các thao tác CRUD cơ bản trên các tệp markdown trừu tượng được lưu trữ trong Neo4j.
Cuối cùng, tôi nghĩ bạn hầu như luôn cần cả hai. Trong dự án này, chúng ta chỉ có hooks, không có công cụ MCP, nhưng bạn luôn có thể cắm thêm Neo4j MCP chính thức để cho tác nhân khám phá đồ thị.
Tổng kết
Nếu bạn không sở hữu bộ nhớ của mình, bạn không sở hữu tác nhân của mình. Mọi harness ngày nay đều xây dựng khu vườn kín của riêng mình về ngữ cảnh, sở thích và lịch sử phiên. Chuyển đổi chúng và bạn bắt đầu từ con số không. Điều đó không nhất thiết phải như vậy.
Hooks phá vỡ mô hình đó. Chúng cho phép bạn viết các tích hợp cắm vào bất kỳ harness nào từ bên ngoài và giao diện là đáng kể nhất quán. Claude Code, Codex và Cursor đều kích hoạt cùng một sự kiện vòng đời: bắt đầu phiên, gửi prompt, sử dụng công cụ, kết thúc phiên. Hook nhận JSON trên stdin, tùy chọn phát ra JSON trên stdout để tiêm ngữ cảnh, và đó là toàn bộ hợp đồng. Vì hooks chạy xác định trên mọi sự kiện, chúng không tiêu tốn sự chú ý của mô hình hay dựa vào tác nhân để quyết định những gì đáng lưu. Cùng hai kịch bản Python xử lý cả ba khách hàng; các trình bao bọc shell mỏng chuyển cờ --client là keo dính duy nhất cho mỗi harness.
Kiến trúc này có ba lớp:
- Hooks (Trực tuyến): Ghi log thụ động mọi sự kiện vào Neo4j dưới dạng danh sách liên kết cho mỗi phiên. Không có gọi mô hình, không có chi phí độ trễ, chỉ là thêm vào.
- Giai đoạn Mơ (Ngoại tuyến): Một công việc batch đọc các sự kiện tích lũy, yêu cầu Claude chưng cất chúng thành các ký ức markdown bền vững và ghi lại chúng. Các ký ức được tổ chức theo chủ đề và được hợp nhất thay vì nối thêm, vì vậy chúng luôn cập nhật thay vì phát triển mãi mãi.
- Tiêm (Trực tuyến): Ở lần bắt đầu phiên tiếp theo trong bất kỳ harness nào, các ký ức hồ sơ được tải vào ngữ cảnh. Trên mỗi prompt người dùng, các ký ức liên quan được tìm kiếm và thêm vào tự động.
Kết quả là một lớp bộ nhớ nằm dưới cả ba harness, hoạt động mà không cần bất kỳ cái nào biết về những cái khác, và thuộc hoàn toàn về bạn. Bạn có thể chuyển từ Cursor sang Claude Code rồi sang Codex giữa dự án và tiếp tục chính xác nơi bạn dừng lại. Sự hiểu biết của tác nhân về bạn là ai, bạn đang làm gì và cách bạn thích làm việc sẽ đi theo bạn, không phải công cụ.
Bài viết liên quan
Phần mềm
Lo ngại về Bun: Liệu sự suy giảm của Claude Code có phải là điềm báo cho tương lai của runtime này?
04 tháng 5, 2026

Phần mềm
Google phát hành Chrome 148, vá 127 lỗ hổng bảo mật bao gồm các lỗi nghiêm trọng
07 tháng 5, 2026

Phần mềm
Tấn công chuỗi cung ứng WordPress: Kẻ tấn công mua 30 plugin trên Flippa và cài cửa sau
06 tháng 5, 2026
