Sau khi đã biết cách cài đặt và dùng Memory MCP trên từng công cụ cụ thể (Claude Code, OpenCode, Gemini CLI, Cursor), câu hỏi tiếp theo mọi team gặp phải là: làm sao để memory không biến thành một bãi rác thông tin sau vài tháng vận hành? Đây không còn là câu hỏi kỹ thuật cấu hình mà là câu hỏi về quy trình (process) — giống như việc một team viết code tốt không chỉ nhờ có Git, mà nhờ có quy trình branching, review, và release rõ ràng đi kèm. Bài này trình bày một workflow đầy đủ, từ lifecycle của memory, cách định nghĩa category và naming convention cho cả team, cách tự động hoá việc capture để không phụ thuộc vào sự tự giác của từng cá nhân, đến cách audit chất lượng memory và ngăn chặn context poisoning một cách có hệ thống.
Tổng Quan Workflow: Memory Lifecycle Từ Capture, Recall Đến Pruning
Một memory graph khoẻ mạnh trải qua vòng đời (lifecycle) lặp lại liên tục gồm ba giai đoạn, và vấn đề của hầu hết team thất bại với Memory MCP là chỉ làm tốt một trong ba, bỏ quên hai giai đoạn còn lại:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ CAPTURE │ ---> │ RECALL │ ---> │ PRUNING │
│ (ghi nhận) │ │ (truy xuất) │ │ (dọn dẹp) │
└─────────────┘ └─────────────┘ └─────────────┘
▲ │
└───────────────────────────────────────────────┘
(bài học từ pruning quay lại cải thiện
cách capture ở vòng lặp sau)
- Capture: thời điểm agent (hoặc con người, thông qua agent) ghi một fact mới vào graph. Đây là giai đoạn dễ làm nhất nhưng dễ làm SAI nhất — ghi quá nhiều thứ vụn vặt, hoặc ghi quá ít vì lười.
- Recall: thời điểm agent truy vấn lại graph để lấy context phục vụ task hiện tại. Nếu capture tốt nhưng recall không xảy ra (agent không chủ động query), memory hoàn toàn vô dụng dù dữ liệu có sẵn.
- Pruning: thời điểm loại bỏ, cập nhật, hoặc đánh dấu lỗi thời các entry không còn giá trị. Đây là giai đoạn hay bị bỏ quên nhất, và chính là nguyên nhân số một khiến memory graph "mục nát" theo thời gian.
Điểm mấu chốt của một chiến lược memory bền vững là thiết kế cả ba giai đoạn này thành quy trình có chủ đích, không phải hành vi ngẫu nhiên tuỳ vào việc agent (hay bạn) có nhớ làm hay không. Các phần tiếp theo sẽ đi vào cách hiện thực hoá từng giai đoạn.
Mẹo: Vẽ lại đúng sơ đồ lifecycle này và dán vào README hoặc
AGENTS.md/CLAUDE.mdcủa dự án — một sơ đồ trực quan giúp cả team (và các thành viên mới) hiểu ngay quy trình mong đợi, thay vì phải đọc hết tài liệu dài.
Bước 1: Định Nghĩa Memory Category Và Naming Convention Cho Cả Team
Trước khi viết một dòng memory nào, team cần thống nhất một taxonomy (hệ thống phân loại) cố định cho entityType. Không có bước này, mỗi người (và mỗi agent) sẽ tự đặt loại theo cảm tính, khiến search_nodes sau này trả kết quả không đầy đủ vì cùng một khái niệm bị gắn type khác nhau.
Một taxonomy đã được kiểm chứng hiệu quả cho dự án phần mềm cỡ vừa:
| entityType | Dùng cho | Ví dụ tên entity |
|---|---|---|
decision |
Quyết định kiến trúc/kỹ thuật đã chốt, kèm lý do | decision-async-payment-queue |
convention |
Quy tắc coding/naming/structure áp dụng lâu dài | convention-api-response-format |
constraint |
Ràng buộc kỹ thuật hoặc business bắt buộc tuân theo | constraint-api-rate-limit |
component |
Một module/service cụ thể trong hệ thống | component-order-service |
gotcha |
Cạm bẫy, hành vi không hiển nhiên, workaround cố ý | gotcha-currency-format-hydration |
person |
Ai chịu trách nhiệm/hiểu sâu nhất một phần hệ thống (không lưu thông tin cá nhân nhạy cảm, chỉ lưu vai trò) | person-oncall-payment |
Quy tắc đặt tên (naming convention) đề xuất: <entityType>-<kebab-case-slug>, luôn viết bằng tiếng Anh dù nội dung observation bằng tiếng Việt hay tiếng Anh — vì tên entity đóng vai trò như một "khoá" (key) để tìm kiếm và liên kết, cần nhất quán tuyệt đối.
Sau khi thống nhất, chốt lại taxonomy này bằng một prompt khởi tạo dùng chung, lưu sẵn trong tài liệu team (không phải chạy lại mỗi lần, chỉ dùng làm tham chiếu khi onboard thành viên mới hoặc bắt đầu dự án mới):
Từ giờ, mọi entity ghi vào memory của dự án này PHẢI dùng entityType thuộc
đúng 6 loại: decision, convention, constraint, component, gotcha, person.
Tên entity PHẢI theo format "<type>-<kebab-case-slug>" bằng tiếng Anh. Nếu
tôi yêu cầu ghi nhớ điều gì mà không rõ nên gắn type nào, hãy hỏi lại tôi
trước khi tạo entity mới.
Mẹo: In taxonomy này ra thành một bảng ngắn và dán trực tiếp vào
CLAUDE.md/AGENTS.md/GEMINI.md(rule cố định, luôn nạp) của dự án — đừng chỉ nói bằng miệng một lần, vì agent (và người mới) sẽ quên rule không được ghi lại thành văn bản.
Bước 2: Tự Động Hóa Việc Capture Memory Tại Các Task Boundary
Capture phụ thuộc vào sự tự giác của con người sẽ luôn thất bại về lâu dài — ai cũng "quên ghi lại" khi đang tập trung giải quyết vấn đề gấp. Giải pháp bền vững là gắn việc capture vào các task boundary (ranh giới công việc) tự nhiên đã có sẵn trong quy trình làm việc, biến nó thành một bước bắt buộc thay vì tuỳ chọn.
Ba task boundary phổ biến nhất để gắn capture:
1. Trước khi tạo Pull Request — thêm bước capture vào quy trình chuẩn bị PR bằng một custom slash command hoặc rule:
## Pre-PR Checklist (thêm vào AGENTS.md)
Trước khi tạo PR, LUÔN thực hiện:
1. Tóm tắt quyết định kỹ thuật quan trọng phát sinh trong quá trình làm task
này (nếu có).
2. Nếu có quyết định đáng ghi nhớ, hỏi tôi xác nhận nội dung, sau đó ghi vào
memory với entityType phù hợp.
3. Nếu phát hiện gotcha (hành vi không hiển nhiên) trong lúc code, ghi lại
ngay, đừng để "sau này nhớ ghi".
2. Ngay sau khi resolve một incident hoặc bug khó — đây là thời điểm kiến thức có giá trị cao nhất và dễ mất nhất, vì áp lực xong việc khiến người ta muốn "đóng máy nghỉ ngay":
Incident vừa được fix. Trước khi đóng ticket, tóm tắt: nguyên nhân gốc,
cách fix, và cách phát hiện sớm hơn lần sau. Ghi vào memory dưới dạng
gotcha, gắn relation tới component bị ảnh hưởng.
3. Cuối mỗi sprint/retro — một buổi tổng hợp memory theo lịch cố định, không phụ thuộc việc có ai nhớ hay không:
Đọc lại toàn bộ commit và PR đã merge trong sprint này (qua git log), tóm
tắt các quyết định kỹ thuật đáng chú ý chưa có trong memory, và đề xuất
entity cần tạo. Liệt kê đề xuất trước, tôi sẽ duyệt từng cái.
Về mặt tự động hoá kỹ thuật hơn, một số team gắn bước capture vào git hook. Ví dụ một post-merge hook đơn giản gợi nhắc (không tự động ghi, vì quyết định content vẫn cần agent/con người xử lý):
#!/usr/bin/env bash
echo "Nhắc: nếu PR vừa merge có quyết định kỹ thuật quan trọng, hãy chạy"
echo "agent để capture vào Memory MCP trước khi chuyển sang task tiếp theo."
Mẹo: Gắn capture vào một task boundary đã tồn tại sẵn trong quy trình (PR, đóng incident, retro) luôn hiệu quả hơn việc tạo thêm một "bước capture memory" độc lập mà mọi người dễ bỏ qua — nguyên tắc chung của mọi automation tốt là tận dụng thói quen đã có, không tạo thêm thói quen mới.
Bước 3: Audit Chất Lượng Memory Và Ngăn Chặn Context Poisoning
Context poisoning (ô nhiễm context) xảy ra khi memory chứa thông tin sai, mâu thuẫn, hoặc lỗi thời, và agent dùng chính thông tin đó làm "sự thật" để đưa ra quyết định hoặc code sai theo. Đây là rủi ro nghiêm trọng nhất của việc dùng memory dài hạn — một memory sai còn nguy hiểm hơn không có memory, vì nó tạo ảo tưởng về sự chắc chắn.
Quy trình audit định kỳ (đề xuất chạy đầu mỗi tháng hoặc đầu mỗi quý tuỳ tốc độ thay đổi của dự án) gồm bốn bước kiểm tra:
1. Kiểm tra mâu thuẫn nội bộ (internal consistency) — yêu cầu agent tự rà soát:
Đọc toàn bộ memory graph. Với mỗi entity, kiểm tra xem có observation nào
mâu thuẫn trực tiếp với observation khác trong cùng entity hoặc entity liên
quan không. Liệt kê từng cặp mâu thuẫn tìm được, kèm entity và nội dung cụ
thể, đừng tự sửa.
2. Kiểm tra độ mới (freshness check) — so khớp observation với trạng thái thực tế của codebase:
Với các entity type "convention" và "constraint", kiểm tra từng observation
bằng cách đọc code thực tế tương ứng (nếu observation nhắc tới file/hàm cụ
thể) và cho biết observation nào có vẻ đã lỗi thời so với code hiện tại.
3. Kiểm tra rò rỉ thông tin nhạy cảm (sensitivity check) — đây là bước bắt buộc, không được bỏ qua với bất kỳ dự án nào:
Rà soát toàn bộ observation trong memory, tìm bất kỳ nội dung nào trông
giống secret, API key, connection string, hoặc thông tin cá nhân nhạy cảm.
Liệt kê ra để tôi xử lý ngay, không tự xoá.
4. Kiểm tra độ phình (bloat check) — đo số lượng entity/observation theo thời gian, cảnh báo khi tăng bất thường:
wc -l .claude/memory.jsonl
Nếu số dòng tăng đột biến trong một khoảng thời gian ngắn mà không tương ứng với lượng công việc thực tế, đó là dấu hiệu agent đang ghi quá nhiều thông tin vụn vặt không cần thiết (over-capture) — cần siết lại rule capture ở Bước 2.
Sau khi audit phát hiện vấn đề, xử lý theo đúng ba hành động đã nói ở các bài trước: delete_observations cho thông tin sai/rác, cập nhật observation mới cho thông tin lỗi thời, hoặc đánh dấu [LỖI THỜI] cho thông tin có giá trị lịch sử nhưng không còn áp dụng.
Mẹo: Biến audit thành một task định kỳ có chủ sở hữu rõ ràng (giống việc phân công review dependency security hàng tháng) — một quy trình audit không có ai chịu trách nhiệm thực thi sẽ dần biến mất trong danh sách "việc nên làm nhưng không ai làm" của team.
Mẹo Hay Khi Xây Dựng Chiến Lược Memory Dài Hạn
- Thiết kế cả ba giai đoạn capture, recall, pruning ngay từ ngày đầu dự án — đừng chỉ triển khai capture rồi để recall và pruning "tự phát sinh sau", vì thực tế chúng không tự phát sinh.
- Taxonomy
entityTypevà naming convention phải được viết thành văn bản chính thức (trongCLAUDE.md/AGENTS.md), không truyền miệng. - Gắn capture vào task boundary đã có sẵn (PR, đóng incident, retro) để biến nó thành thói quen bắt buộc chứ không phải việc tuỳ chọn dễ quên.
- Audit định kỳ với chủ sở hữu rõ ràng, đặc biệt chú trọng bước kiểm tra rò rỉ thông tin nhạy cảm — đây là rủi ro nghiêm trọng nhất và dễ bị xem nhẹ nhất.
Mẹo: Nếu chỉ có thể áp dụng một thói quen duy nhất từ toàn bộ chiến lược này, hãy chọn: "không bao giờ để agent xoá hàng loạt (batch delete) mà không liệt kê danh sách cho con người duyệt trước" — một quy tắc đơn giản này ngăn được phần lớn thiệt hại nghiêm trọng nhất có thể xảy ra với một memory graph tích luỹ nhiều tháng công sức.