Các agent của LangGraph cuối cùng đã có một cách đáng tin cậy để duy trì trạng thái sau nhiều tuần bị mất dữ liệu một cách âm thầm. Sau ba nỗ lực lưu checkpoint thất bại—sử dụng SQLite, lưu trữ đối tượng (object storage) thô, và một phiên bản lỗi của cả hai—tác giả đã tìm ra mô hình atomic-update giúp ngăn các agent phải khởi động lại từ đầu mỗi khi có một yêu cầu mới đến.

Tại sao checkpointing lại quan trọng đối với LangGraph

LangGraph cho phép các nhà phát triển kết nối các lời gọi LLM thành các "agent" có thể tái sử dụng, giúp chúng có thể ghi nhớ những gì đã xảy ra trước đó trong một cuộc hội thoại. Các agent này chia nhỏ yêu cầu của người dùng thành các tác vụ con, lưu trữ các kết quả trung gian và tiếp tục từ nơi chúng đã dừng lại trong lần gọi tiếp theo. Nếu trạng thái đã lưu bị mất, agent sẽ phải tính toán lại mọi thứ, gây lãng phí tài nguyên tính toán, làm tăng độ trễ và mang lại trải nghiệm người dùng kém. Trong một bot chạy thực tế (production) xử lý tin nhắn Telegram, việc mất dữ liệu này đã xóa sạch lịch sử trò chuyện trong nhiều tuần.

Giải pháp đầu tiên: SQLite saver

SqliteSaver tích hợp sẵn hoạt động tốt khi chỉ có một instance duy nhất chạy agent. Nó ghi mỗi checkpoint dưới dạng một khối JSON trong một tệp SQLite cục bộ. Rắc rối bắt đầu khi nhà phát triển thêm một trường mới vào kiểu AgentState và triển khai lại (redeploy). Các checkpoint hiện có, được tạo trước khi thay đổi schema, thiếu trường mới này. Vì SqliteSaver không bao giờ thực hiện migration (di trú dữ liệu), LangGraph đã tải tệp JSON không đầy đủ, loại bỏ dữ liệu bị thiếu, và agent lại phải khởi động lại từ đầu.

Điểm mấu chốt: Lưu trữ SQLite là một công cụ dùng để demo, không phải là một giải pháp sẵn sàng cho môi trường production khi cần có sự tiến hóa về schema.

Giải pháp thứ hai: Object storage

Để kiểm soát định dạng tuần tự hóa (serialization), tác giả đã viết một bộ lưu trữ tùy chỉnh để tải checkpoint JSON lên Oracle Cloud Object Storage. Việc chuyển đổi này mang lại sự linh hoạt trong việc quản lý phiên bản schema một cách thủ công, nhưng nó lại dẫn đến một kiểu lỗi mới. Khi hai yêu cầu cùng tác động vào một luồng hội thoại cùng một lúc, cả hai đều cố gắng ghi đè lên cùng một đối tượng. Các dịch vụ object storage được tối ưu hóa cho mô hình "ghi một lần, đọc nhiều lần"; chúng không cung cấp các ngữ nghĩa ghi đè nguyên tử (atomic overwrite). Tình trạng tranh chấp (race condition) đã tạo ra các tệp JSON bị lỗi định dạng hoặc bị cắt cụt, và agent lại một lần nữa mất đi ngữ cảnh.

Điểm mấu chốt: Việc ghi đè thông thường trong object storage không an toàn khi có nhiều worker có thể tác động vào cùng một key tại cùng một thời điểm.

Giải pháp thứ ba: Atomic updates với versioning

Thiết kế cuối cùng và ổn định kết hợp hai ý tưởng: số phiên bản (version number) rõ ràng và ghi có điều kiện (conditional writes) dựa trên ETag của đối tượng (mã định danh checksum của dịch vụ lưu trữ).

  1. Đọc (Read) checkpoint hiện tại và lấy ETag của nó.
  2. Tăng (Increment) một trường phiên bản bên trong lớp vỏ (envelope) của checkpoint.
  3. Ghi (Write) checkpoint đã cập nhật bằng một yêu cầu có điều kiện, yêu cầu này chỉ thành công nếu ETag khớp với ETag đã đọc trước đó.
  4. Thử lại (Retry) toàn bộ vòng lặp đọc-tăng-ghi nếu việc ghi có điều kiện thất bại do một tiến trình khác đã thay đổi đối tượng.

Vì việc ghi chỉ thành công khi không có tiến trình nào khác thay đổi tệp, nên tại một thời điểm chỉ có một worker có thể commit trạng thái mới. Trường phiên bản cũng giúp dễ dàng phát hiện các checkpoint lỗi thời và di trú chúng khi schema thay đổi.

Mô hình này hoạt động với các dịch vụ object storage hỗ trợ ghi có điều kiện dựa trên ETag.

Bài học cho các kỹ sư AI

  • Chỉ sử dụng SQLite cho các bản mẫu (prototype). Các agent chạy production cần một kho lưu trữ có thể xử lý các thay đổi schema và các thao tác ghi đồng thời.
  • Tự lập kế hoạch di trú schema. Các Typed dictionaries mô tả hình dạng dữ liệu để phân tích tĩnh nhưng không bắt buộc cấu trúc lúc thực thi (runtime).
  • Coi trạng thái là một tài nguyên dùng chung. Các lỗi đồng thời (concurrency bugs) thường xuất hiện dưới dạng mất dữ liệu âm thầm; chúng khó debug hơn nhiều so với các ngoại lệ (exceptions) rõ ràng.
  • Sử dụng các thành phần cơ bản của đám mây (cloud primitives). Ghi có điều kiện dựa trên ETag cung cấp cơ chế khóa lạc quan (optimistic locking) chi phí thấp mà không cần một dịch vụ khóa (lock service) riêng biệt.
  • Ghi log mọi bước. Những lỗi âm thầm—như một trường bị thiếu mà LangGraph bỏ qua—là những lỗi khó truy vết nhất.

Bước tiếp theo cho checkpointing của LangGraph là gì?

Đối với các đội ngũ đã gặp phải những rào cản tương tự, công thức atomic-update cung cấp một giải pháp khắc phục nhanh chóng và ít tốn kém. Nó cho thấy rằng một pipeline production đáng tin cậy không nhất thiết đòi hỏi một kho lưu trữ trạng thái cồng kềnh—chỉ cần xử lý cẩn thận vấn đề đồng thời và quản lý phiên bản.

Bài học rút ra: Một lớp vỏ có phiên bản đơn giản kết hợp với ghi có điều kiện sẽ biến một hệ thống chập chờn thành một hệ thống đáng tin cậy, cho phép các kỹ sư AI tập trung vào logic của agent thay vì phải debug việc mất dữ liệu vô tận.