0

Phân tích Agent Memory trong Claude Code: Kiến trúc thực tế và so sánh với LangGraph, OpenAI Codex

Bài viết này phân tích kiến trúc memory mà Claude Code sử dụng để quản lý bộ nhớ agent, từ in-memory buffer đến disk persistence. Đồng thời so sánh trực tiếp với hai hệ thống phổ biến: LangGraphOpenAI Codex CLI.

Mục tiêu: giúp bạn hiểu rõ trade-off thực tế để chọn đúng chiến lược memory cho dự án của mình.

Memory là vấn đề cốt lõi của AI Agent

Một AI Agent không có memory tốt cũng giống như một lập trình viên bị mất trí nhớ ngắn hạn. Mỗi bước họ làm rất logic và xử lý rất giỏi, nhưng liên tục quên ngữ cảnh và không biết mình đang đứng ở đâu trong bức tranh tổng thể.

Dù context window của các mô hình LLM ngày càng lớn (128k–1M tokens), vấn đề memory của Agent vẫn chưa được giải quyết. Một coding agent có thể sinh ra hàng trăm tool call chỉ trong 20–30 phút (đọc file, chạy code, check output, debug…). Nếu giữ nguyên toàn bộ lịch sử, context window sẽ nhanh chóng bị đầy và dẫn đến hai rủi ro lớn:

  1. API reject request vì prompt quá dài.
  2. Thông tin quan trọng ở đầu conversation bị đẩy ra khỏi attention window → agent “quên” task ban đầu.

Đây chính là bài toán mà mọi hệ thống Agent production đều phải giải quyết theo cách riêng. Hãy cùng xem Claude Code đã làm thế nào.

1. Claude Code: Kiến trúc Memory 4 lớp

Nhờ source code của Claude Code bị lộ và được cộng đồng phân tích qua project claw-code của Sigrid Jin, chúng ta có cái nhìn rõ nét chưa từng có về cách Anthropic thiết kế memory cho một coding agent thực tế.

Layer 1 – TranscriptStore: In-memory buffer (Working Memory)

Đây là working memory trong một session đang chạy. Nó lưu toàn bộ lịch sử lượt hội thoại dưới dạng một danh sách strings đơn giản, kèm một flag flushed

# transcript.py
class TranscriptStore:
    entries: list[str]          # Danh sách các lượt hội thoại (string)
    flushed: bool = False       # True = đã sync với disk

    def append(self, entry: str):
        self.entries.append(entry)
        self.flushed = False

    def compact(self, keep_last: int):
        # Cắt bớt, chỉ giữ N entries gần nhất
        ...

    def replay(self) -> tuple:
        # Trả về snapshot không thể mutate
        ...

    def flush(self):
        # Đánh dấu đã sync xuống disk
        self.flushed = True

Cần chú ý ở đây là flag flushed, giúp đảm bảo caller luôn biết rõ trạng thái nào cần được persist xuống disk. Không có auto-sync ngầm, tất cả đều explicit.

Layer 2 - QueryEnginePort: Agent Loop & Compaction

Đây là nơi logic thực sự xảy ra. Mỗi khi agent submit một message, tiến trình sẽ diễn ra như sau:

  1. Append prompt vào TranscriptStore
  2. Kiểm tra nếu số turn vượt compact_after_turns
  3. Nếu vượt, gọi compact() — cắt cả transcript lẫn mutable_messages xuống và keep_last turns
  4. Ghi kết quả vào HistoryLog

Compaction trong Claude Code là pure sliding window và không dùng LLM để summarize, không dùng vector search. Đây là lựa chọn có chủ ý: đơn giản, zero dependency, hoàn toàn predictable.

Layer 3 – SessionStore: Disk persistence (JSON)

# session_store.py
@dataclass(frozen=True)
class StoredSession:
    session_id: str
    messages: tuple[str, ...]
    input_tokens: int
    output_tokens: int

# Lưu tại: .port_sessions/{session_id}.json

Sessions sẽ được lưu dưới dạng JSON file, đặt tên {session_id}.json. Để recovery, QueryEnginePort.from_saved_session() đọc lại file, dựng lại TranscriptStore với flushed=True, rồi populate lại toàn bộ state. Đây là pattern tương tự checkpoint-restore, tuy không phức tạp, nhưng đủ để agent tiếp tục từ đúng nơi nó dừng.

Layer 4 – HistoryLog: Audit trail append-only

Khác với ba lớp trên, HistoryLog không phục vụ agent trong việc trả lời câu hỏi. Nó là nhật ký sự kiện nội bộ, gọi tool nào, lúc nào compact, session được persist ở đâu. Append-only, không bao giờ xóa entry cũ.

Lifecycle hoàn chỉnh:

Session chạy → tool calls, conversations → session kết thúc → extractMemories (background, không block) → Session mới bắt đầu → memoryScan + findRelevantMemories → loadMemoryPrompt inject vào system prompt → Agent "nhớ" relevant facts từ sessions trước.

2. LangGraph: Memory là một phần của Graph State

LangGraph thay đổi hoàn toàn paradigm. Thay vì memory là một object độc lập, state của toàn bộ graph chính là memory. Mỗi node trong graph đọc và ghi vào shared state, và state đó được persist qua checkpointer.

from langgraph.checkpoint.sqlite import SqliteSaver

checkpointer = SqliteSaver.from_conn_string("checkpoints.db")
graph = builder.compile(checkpointer=checkpointer)

# Resume session
config = {"configurable": {"thread_id": "session-abc"}}
graph.invoke({"messages": [...]}, config=config)

Điểm mạnh của LangGraph là khả năng xử lý multi-agent và workflow phức tạp, mỗi agent node có state riêng nhưng có thể share global state với nhau. Điểm yếu là learning curve cao hơn đáng kể so với các approach đơn giản hơn.

3. OpenAI Codex CLI: Stateless by design

Codex CLI chọn một hướng đi khác biệt hoàn toàn: stateless request model. Mỗi API call đều gửi toàn bộ conversation history từ đầu, không dùng server-side session storage. Điều này có nghĩa là mọi turn, context window đều tăng lên cho đến khi đạt ngưỡng compaction.

Khi số token vượt threshold, Codex thực hiện compaction tương tự Claude, nhưng thay vì chỉ cắt bớt, Codex dùng Responses API để tạo ra một summary rồi thay thế conversation history bằng summary đó.

4. So sánh trực tiếp

Tiêu chí Claude Code LangGraph (v0.3+) OpenAI Codex CLI
Chiến lược chính Sliding-window compact Graph state + checkpointer Token-threshold + summary
Lưu trữ disk JSON file (.port_sessions/) SqliteSaver / InMemorySaver Stateless (full resend)
Audit log HistoryLog append-only Graph event log Không có mặc định
Cross-session recovery from_saved_session() load_checkpoint(thread_id) Không hỗ trợ
Compaction trigger Turn count threshold Trimming node trong graph Token count threshold

5. Nên chọn chiến lược nào?

Không có một giải pháp nào là “đúng” cho mọi trường hợp. Tất cả đều là bài toán trade-off, và lựa chọn phụ thuộc vào việc bạn ưu tiên điều gì trong hệ thống của mình.

Nếu bạn đang xây một coding agent đơn luồng, cần hệ thống đơn giản, dễ kiểm soát và không phụ thuộc vào nhiều thành phần bên ngoài, thì sliding-window compaction là đủ. Bạn đánh đổi khả năng ghi nhớ dài hạn để lấy sự ổn định và predictability.

LangGraph checkpointing lại là lựa chọn mạnh hơn cho các workflow phức tạp, đặc biệt là multi-agent. Nó cho phép “time-travel”, quay lại trạng thái trước đó, rất hữu ích khi cần debug, rollback hoặc xây dựng các hệ thống production yêu cầu persistence rõ ràng.

Trong khi đó, stateless + server-side compaction (Codex) phù hợp với các hệ thống cloud-native. Cách tiếp cận này đặc biệt có lợi khi bạn cần ZDR compliance hoặc phải xử lý nhiều tác vụ song song mà không muốn bị ràng buộc bởi state phía client.

Tài liệu tham khảo

  1. instructkr/claw-code
  2. OpenAI: Unrolling the Codex agent loop
  3. LangGraph memory
  4. Tài liệu: Giải phẫu Agentic OS

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí