Agent 5 — Bộ nhớ agent: sống sót qua compaction và qua nhiều phiên

13 thg 7, 2026 4 lượt xem
#ai
#claude
#agent
#memory
#persistence

Vì sao agent cần bộ nhớ

Một agent về bản chất là stateless: mỗi lượt gọi model chỉ "biết" những gì nằm trong cửa sổ context lần đó. Không có gì tự động lưu lại giữa các lượt, giữa các nhiệm vụ, hay giữa các ngày làm việc. Mà cửa sổ context lại là tài nguyên hữu hạn — theo Anthropic trong "Effective context engineering for AI agents", nhồi càng nhiều không làm agent thông minh hơn, còn gây context rot (chất lượng giảm khi context quá dài, nhiễu). Khi context gần đầy, harness buộc phải compaction — nén/tóm tắt phần cũ để có chỗ đi tiếp (xem sâu Agent 3 — Context Engineering).

Hệ quả: nếu agent chỉ dựa vào context, thì mỗi lần nén là một lần có nguy cơ mất trạng thái, và mỗi phiên mới là một tờ giấy trắng. Với tác vụ ngân hàng kéo dài nhiều ngày — ví dụ rà soát một danh mục tín dụng — điều đó không chấp nhận được. Giải pháp là bộ nhớ ngoài phân tầng: đặt đúng loại thông tin vào đúng lớp lưu trữ, với đúng vòng đời.

Anthropic mô tả "augmented LLM" gồm bốn thành phần: model + retrieval + tools + memory. Memory là một trong bốn trụ. Bài này chia bộ nhớ agent thành bốn tầng, từ dễ bay hơi nhất tới bền nhất.

Tầng 1 — Working memory: scratchpad trong context

Lớp gần nhất, rẻ nhất, và cũng dễ bay hơi nhất: scratchpad ngay trong cửa sổ context. Đây là nơi agent tự viết ra danh sách việc (todo), ghi chú tiến độ, dữ kiện vừa tra được, quyết định vừa chốt. Nó không phải bộ nhớ "ngoài" — nó vẫn nằm trong context — nhưng là kỷ luật tự-tổ-chức giúp agent không lạc hướng trong một nhiệm vụ nhiều bước.

Vì sao hiệu quả? Model chú ý mạnh vào phần đầu và cuối context (hiện tượng lost-in-the-middle). Một todo list được agent cập nhật lại ở cuối mỗi cụm việc sẽ luôn nằm gần "mép nóng" của context, kéo model bám mục tiêu. Mẫu điển hình:

## Nhiệm vụ: rà soát nợ nhóm 2 chi nhánh Hà Đông
- [x] Lấy danh sách 142 khoản nợ nhóm 2 (SQL xong)
- [x] Tính DTI từng khoản → 18 khoản DTI > 50%
- [ ] Đối chiếu tài sản đảm bảo cho 18 khoản này  ← ĐANG LÀM
- [ ] Soạn ghi chú khuyến nghị
Dữ kiện chốt: kỳ dữ liệu 06/2026; ngưỡng cảnh báo DTI = 50%.

Điểm mấu chốt: scratchpad này sẽ biến mất khi compaction xảy ra nếu ta không chủ động giữ nó. Đó chính là lý do phải có Tầng 2.

Tầng 2 — Sống sót qua COMPACTION

Đây là tầng đặc thù nhất của agent so với chatbot thường, và cũng hay bị hiểu sai nhất. Khi context chạm ngưỡng (thực tế thường 70–85% cửa sổ), harness không dừng lại hỏi người dùng. Thay vào đó nó chạy compaction: gọi chính model tóm tắt phần hội thoại cũ thành một bản tóm tắt cô đọng, giữ lại những thứ không được phép mất — mục tiêu, các quyết định đã chốt, trạng thái hiện tại, việc đang dở, dữ kiện số quan trọng — rồi mở một "cửa sổ mới" bắt đầu bằng chính bản tóm tắt đó. Agent tiếp tục làm việc ngay, không cần hỏi lại người dùng đang làm gì.

Đây đúng là cách các harness thật (ví dụ Claude Code, và pattern compaction Anthropic mô tả trong "Effective context engineering") xử lý những phiên dài vượt một cửa sổ context. Người dùng thường chỉ thấy một dòng báo "đang nén hội thoại" rồi mọi thứ chạy tiếp trơn tru.

Cấu trúc một vòng lặp tự nén (minh hoạ, model claude-opus-4-8):

# MINH HOẠ — pseudocode, không phải API chính thức
MODEL = "claude-opus-4-8"      # cửa sổ tới 1M token
COMPACT_AT = 0.80              # nén khi dùng quá 80%

def step(state):
    if est_tokens(state.messages) > state.window * COMPACT_AT:
        state.messages = compact(state.messages)
    return client.messages.create(
        model=MODEL, system=state.system,
        messages=state.messages, tools=TOOLS,
    )

def compact(messages):
    head = messages[:1]           # system + mục tiêu gốc
    tail = messages[-6:]          # vài lượt gần nhất còn "nóng"
    summary = client.messages.create(
        model=MODEL,
        messages=messages[1:-6],
        system=("Tóm tắt hội thoại cho một agent sẽ TIẾP TỤC nhiệm vụ. "
                "BẮT BUỘC giữ: mục tiêu, quyết định đã chốt, trạng thái "
                "hiện tại, việc đang dở, dữ kiện SỐ quan trọng. Bỏ chi "
                "tiết thừa. Không bịa."),
    ).content[0].text
    return head + [{"role": "user",
                    "content": f"[TÓM TẮT PHIÊN TRƯỚC ĐÓ]\n{summary}"}] + tail

Hai chi tiết quyết định chất lượng compaction:

  • Giữ đầu + đuôi, nén giữa. Phần giữa là nơi context rot hay nuốt mất; đầu (mục tiêu) và đuôi (ngữ cảnh gần) phải nguyên vẹn.
  • Chỉ định rõ phải giữ gì. Câu lệnh tóm tắt phải liệt kê mục tiêu / quyết định / trạng thái / việc dở / số liệu — không nói "tóm tắt ngắn gọn" chung chung, vì như thế model dễ bỏ đúng cái ta cần.

Mẹo bổ trợ quan trọng: trước khi compaction xảy ra, cho agent ghi todo/kết luận ra file (Tầng 3). Nếu bản tóm tắt lỡ đánh rơi một dữ kiện, file vẫn còn.

Tầng 3 — Persistent: bền qua nhiều phiên

Compaction cứu ta trong một phiên. Nhưng khi người dùng đóng terminal và mai mở lại, cả context lẫn bản tóm tắt đều mất. Muốn agent "nhớ" qua nhiều phiên, phải ghi ra ngoài model — đĩa hoặc DB — rồi nạp lại ở đầu phiên.

Bộ nhớ dạng file (kiểu CLAUDE.md / thư mục memory)

Mẫu đơn giản và mạnh: một file văn bản (như CLAUDE.md) hoặc một thư mục memory/ mà agent tự đọc/ghi qua tool. Nguyên tắc vàng: mỗi ghi chú là một sự thật, ngắn, độc lập, có ngày — để dễ thêm, sửa, và loại bỏ khi lỗi thời.

memory/
├── MEMORY.md              # bảng mục lục các sự thật, mỗi dòng một fact + link
├── prefs-can-bo.md        # sở thích cán bộ: định dạng báo cáo, chi nhánh phụ trách
├── du-an-ra-soat-nhom2.md # tiến độ nhiệm vụ đang dở
└── quy-uoc-du-lieu.md     # kỳ dữ liệu, tên bảng, ngưỡng nghiệp vụ đã thống nhất

Ví dụ MEMORY.md (đúng tinh thần "mỗi ghi chú một sự thật"):

- [Sở thích báo cáo](prefs-can-bo.md) — cán bộ A thích báo cáo dạng bảng, số làm tròn 0 chữ số thập phân, đơn vị "triệu VND"
- [Chi nhánh phụ trách](prefs-can-bo.md) — cán bộ A phụ trách Hà Đông + Cầu Giấy
- [Kỳ dữ liệu](quy-uoc-du-lieu.md) — mặc định dùng snapshot cuối tháng gần nhất trên read-replica
- [Rà soát nhóm 2](du-an-ra-soat-nhom2.md) — đang dở: còn đối chiếu TSĐB 18 khoản DTI>50%

Vòng đời file memory (minh hoạ):

# MINH HOẠ — nạp đầu phiên, ghi khi có sự thật mới
def load_memory(system_prompt):
    facts = read_file("memory/MEMORY.md")      # bảng mục lục các sự thật
    return system_prompt + "\n\n[BỘ NHỚ BỀN]\n" + facts   # nạp vào system

def remember(fact: str, file: str):
    """Agent gọi khi phát hiện một sự thật đáng nhớ (qua tool write_memory)."""
    line = f"- [{today()}] {fact}\n"
    append_file(file, line)
    reindex_memory_toc()                        # cập nhật lại MEMORY.md

Ưu điểm file: người đọc được, sửa tay được, versioned bằng git, minh bạch cho audit — rất hợp môi trường ngân hàng cần giải trình được agent "biết" gì.

Bộ nhớ dạng DB

Khi bộ nhớ có cấu trúc, khối lượng lớn, cần truy vấn/lọc, dùng bảng DB thay vì file. Ví dụ: log mọi khuyến nghị agent từng đưa cho từng khoản vay, hay bảng preference của hàng trăm cán bộ.

Tiêu chíDùng file (memory/, CLAUDE.md)Dùng DB
Khối lượngÍt, vài chục–vài trăm mẩuLớn, hàng nghìn+
Cấu trúcVăn bản tự do, con người đọcCó schema, cần lọc/join/tổng hợp
Người sửaCon người sửa tay thường xuyênGhi/đọc bằng chương trình
AuditGit diff, review như codeCần bảng audit, RBAC riêng
Ví dụ NCBQuy ước dữ liệu, sở thích 1 cán bộLịch sử khuyến nghị theo khoản vay

Thực tế thường kết hợp: file cho "sự thật về cách làm việc / sở thích / ngữ cảnh dự án"; DB cho "dữ liệu vận hành có cấu trúc". Cả hai đều nạp lại ở đầu phiên (file vào system prompt, DB qua tool truy vấn).

Tầng 4 — Semantic memory: recall theo liên quan

Ba tầng trên nạp memory theo vị trí (cả file, hoặc theo khoá). Nhưng khi kho nhớ lớn, ta không thể (và không nên) nạp hết vào context. Tầng 4 giải bài đó: embed từng mẩu nhớ thành vector, lưu vào vector store, và khi cần thì recall theo độ liên quan ngữ nghĩa với tình huống hiện tại — thay vì khớp từ khoá cứng. Chi tiết cơ chế xem Embeddings & VectorRAG Retrieval.

Cần phân biệt rõ với RAG tài liệu:

RAG tài liệuSemantic memory
Nội dungTài liệu tĩnh (quy trình, thông tư, wiki)Trải nghiệm/sự thật agent tự tích luỹ khi làm việc
NguồnCon người soạn sẵnAgent ghi ra trong lúc chạy
Vòng đờiCập nhật theo bản phát hànhLớn dần theo mỗi phiên, có thể lỗi thời
Câu hỏi trả lời"Quy định nói gì?""Lần trước tôi/cán bộ này đã quyết thế nào?"

Semantic memory phù hợp trợ lý dùng lâu dài với nhiều người, nhiều dự án — recall được "ngữ cảnh giống tình huống này". Nhưng nó là tầng phức tạp và tốn kém nhất; theo tinh thần "start simple" của Anthropic (Building effective agents), chỉ thêm khi file/DB đã không đủ. Nhiều agent chạy tốt chỉ với Tầng 1–3.

Cái gì NÊN nhớ, cái gì KHÔNG

Bộ nhớ tốt là bộ nhớ có chọn lọc. Ghi bừa mọi thứ khiến memory phình to, nhiễu, và — nguy hiểm trong ngân hàng — rò rỉ dữ liệu nhạy cảm.

NÊN nhớKHÔNG nên nhớ
Sở thích cán bộ (định dạng báo cáo, đơn vị, ngôn ngữ)PII khách hàng (CMND/CCCD, số tài khoản, SĐT, địa chỉ)
Quyết định & lý do đã chốt trong dự ánBí mật/khoá: mật khẩu, API key, token, connection string
Ngữ cảnh dự án: kỳ dữ liệu, tên bảng, ngưỡng nghiệp vụSố dư/giao dịch cụ thể của một khách hàng
Bài học lỗi từng gặp (để không lặp lại)Dữ liệu chỉ đúng tại một thời điểm (dễ bịa nếu tin mù)

Hai nguyên tắc quản trị bắt buộc:

  • Xác minh lại trước khi tin. Một fact trong memory có thể đã lỗi thời (số dư đổi, chi nhánh tái cơ cấu). Với dữ liệu động, memory chỉ nên lưu cách lấy (query, tên bảng), rồi truy vấn lại từ nguồn mỗi phiên — đừng cache con số rồi tin mù. Đây là kỷ luật data quality (xem Data Quality).
  • Không đưa PII/bí mật vào memory. File memory và vector store thường không có cùng lớp kiểm soát truy cập như core banking. Ghi số CCCD vào memory/ = tạo một bản sao dữ liệu nhạy cảm ngoài vòng kiểm soát. Chặn từ tầng tool: một bộ lọc redact PII/secret trước khi ghi memory.
# MINH HOẠ — chặn PII/secret trước khi ghi memory
def write_memory_guarded(fact: str, file: str):
    if contains_pii(fact) or contains_secret(fact):
        raise MemoryPolicyError("Từ chối ghi: fact chứa PII/bí mật.")
    remember(redact(fact), file)     # vẫn redact phòng hờ

Use case thực tế

Bối cảnh: NCB triển khai một trợ lý phân tích tín dụng cho tổ giám sát danh mục. Mỗi cán bộ dùng nó nhiều ngày liên tục, mỗi phiên vài giờ, đọc read-replica (chỉ đọc), mọi hành động ghi audit log.

Nhu cầu bộ nhớ theo tầng:

  • Tầng 1 — scratchpad: trong một phiên rà soát 142 khoản nợ nhóm 2, agent giữ todo list (đã lọc DTI, đang đối chiếu TSĐB) để không lạc giữa hàng chục bước SQL.
  • Tầng 2 — compaction: phiên kéo dài, context chạm ~82%. Harness nén hội thoại thành bản tóm tắt giữ "kỳ dữ liệu 06/2026, ngưỡng DTI 50%, còn 18 khoản chờ đối chiếu TSĐB", rồi chạy tiếp — cán bộ không phải nhắc lại đang làm gì.
  • Tầng 3 — file bền: cuối ngày agent ghi memory/prefs-can-bo.md: "cán bộ A thích báo cáo bảng, đơn vị triệu VND, phụ trách Hà Đông + Cầu Giấy" và memory/du-an-ra-soat-nhom2.md: "còn đối chiếu TSĐB 18 khoản". Sáng hôm sau, load_memory() nạp lại → agent tiếp tục đúng chỗ dở, đúng gu báo cáo, không cần onboarding lại.
  • Kiểm soát nhạy cảm: bộ lọc write_memory_guarded chặn mọi số tài khoản/CCCD; memory chỉ lưu ID khoản vaycách truy vấn, số dư/DTI luôn tính lại từ read-replica mỗi phiên.

Ví dụ một truy vấn tổng hợp danh mục tính lại mỗi phiên (chạy trên read-replica Postgres):

-- ▶ Chạy được
SELECT a.currency,
       COUNT(DISTINCT a.customer_id)         AS so_khach,
       ROUND(AVG(a.balance)::numeric, 2)      AS so_du_tb
FROM accounts a
GROUP BY a.currency
ORDER BY so_du_tb DESC;

Số liệu (ước lượng nội bộ, minh hoạ): trước khi có bộ nhớ bền, mỗi sáng cán bộ mất ~10–15 phút thiết lập lại ngữ cảnh (kỳ dữ liệu, gu báo cáo, việc dở). Sau khi bật Tầng 3, thời gian "khởi động lại" xuống dưới 1 phút; số lần agent hỏi lại thông tin đã cung cấp hôm trước giảm rõ. Không có PII nào rời khỏi vùng kiểm soát vì memory chỉ chứa ID + cách truy vấn, không chứa giá trị nhạy cảm.

Ghi nhớ

  • Agent stateless; context hữu hạn và bị nén → không có bộ nhớ ngoài thì mất trạng thái. Memory là 1 trong 4 trụ của "augmented LLM".
  • Tầng 1 — scratchpad/todo trong context: rẻ, tự-tổ-chức, nhưng bay hơi khi compaction. Cập nhật ở "mép nóng" để bám mục tiêu.
  • Tầng 2 — compaction: khi context gần đầy, harness tự tóm tắt (giữ mục tiêu/quyết định/trạng thái/việc dở) rồi chạy tiếp không hỏi lại — đúng cách harness thật xử lý phiên dài. Giữ đầu+đuôi, nén giữa; chỉ định rõ phải giữ gì.
  • Tầng 3 — file / DB bền qua nhiều phiên: file (kiểu CLAUDE.md / thư mục memory/, mỗi ghi chú một sự thật) cho nội dung con-người-đọc, sửa tay, audit; DB cho dữ liệu có cấu trúc, khối lượng lớn. Luôn nạp lại đầu phiên.
  • Tầng 4 — semantic memory (vector): recall theo liên quan ngữ nghĩa; khác RAG tài liệu ở chỗ nội dung do agent tự tích luỹ. Phức tạp/tốn kém → theo "start simple", chỉ thêm khi file/DB không đủ.
  • NÊN nhớ: sở thích, quyết định, ngữ cảnh dự án, bài học lỗi. KHÔNG nhớ: PII, khoá/bí mật, số liệu động (lưu cách lấy, truy vấn lại). Xác minh trước khi tin fact cũ.
  • Ngân hàng: chặn PII/secret từ tầng tool trước khi ghi; memory versioned, giải trình được cho audit.

Nguồn tham khảo

Bài viết liên quan

Đặt nền cho chuỗi AI: phân biệt ba vòng tròn lồng nhau AI ⊃ ML ⊃ DL và khác biệt bản chất giữa lập trình truyền thống với học từ dữ liệu. Giới thiệu ba kiểu học máy (supervised, unsupervised, reinforcement), phân loại descriptive/predictive/prescriptive, quy trình ML end-to-end, chia train/validation/test, overfitting/underfitting và các thuật ngữ nền tảng, gắn với ứng dụng ngân hàng NCB.

13 thg 7, 2026 18

Harness — lớp scaffolding quanh model (vòng lặp, tool, context, memory, verify, sub-agent) — mới là thứ quyết định agent chạy được hay chỉ là demo. Bài này mổ xẻ giải phẫu một harness, 7 kỹ năng cốt lõi khi xây agent, single vs multi-agent (kèm số liệu hiệu quả/chi phí), các repo nên dùng, và một quickstart Python dựng-là-chạy cho bối cảnh ngân hàng.

13 thg 7, 2026 15

Hiểu LLM từ gốc: bản chất dự đoán token, ba giai đoạn huấn luyện (pretraining, fine-tuning, RLHF), token, context window và các tham số sinh (temperature, top-p). Nắm hiện tượng hallucination và kỹ thuật prompt engineering (vai trò, few-shot, chain-of-thought, ràng buộc đầu ra), kèm ví dụ gọi API model Claude mới nhất với adaptive thinking.

13 thg 7, 2026 14

Vì sao dữ liệu quyết định chất lượng mô hình hơn cả thuật toán. Bài này đi qua toàn bộ pipeline chuẩn bị dữ liệu: phân loại dữ liệu, làm sạch (thiếu/ngoại lai/trùng lặp), mã hoá hạng mục, scaling, feature engineering, giảm chiều, và cách phòng data leakage — soi qua bài toán chấm điểm tín dụng.

13 thg 7, 2026 14

Cảm nhận của bạn

Bình luận

Bạn cần để viết bình luận.

Chưa có bình luận. Hãy là người đầu tiên chia sẻ!