CodeGym /Các khóa học /ChatGPT Apps /Bộ nhớ và trạng thái của tác nhân: session vs persistent,...

Bộ nhớ và trạng thái của tác nhân: session vs persistent, checkpoints

ChatGPT Apps
Mức độ , Bài học
Có sẵn

1. Vì sao tác nhân cần một lớp bộ nhớ riêng

Phần này dựa trên các bài học trước của mô-đun 12 về tác nhân: chúng ta đã nói về kiến trúc cơ bản, run‑cycle và tools; ở đây tập trung vào bộ nhớ và trạng thái.

Nếu so sánh với ứng dụng web quen thuộc, LLM ở đây — giống như một CPU rất thông minh có thể thực thi các “chương trình văn bản” phức tạp. Còn trạng thái của tác nhân — là sự kết hợp giữa RAM và SSD: dữ liệu phiên sống ngắn và kho lưu trữ dài hạn.

Trong chat ChatGPT cổ điển khi không có code của bạn, “bộ nhớ” chỉ là danh sách tin nhắn system/user/assistant/tool, mà mô hình nhìn thấy trong yêu cầu hiện tại. Với tác nhân, như vậy là chưa đủ, vì:

  • nó cần ghi nhớ tiến độ của các quy trình phức tạp: bước nào của workflow đã hoàn thành, các phương án quà nào đã lọc, người dùng đã xác nhận gì;
  • nó cần biết các sự kiện dài hạn về người dùng: sở thích, địa chỉ giao hàng, lịch sử đơn hàng;
  • nó cần có khả năng vượt qua sự cố: nếu máy chủ sập giữa chừng quá trình chọn quà, người dùng không nên phải nhập lại mọi thứ từ đầu.

Nếu cố gắng nhét tất cả vào prompt‑context, bạn sẽ nhanh chóng đụng trần cửa sổ ngữ cảnh và phải trả token cho những sự kiện lặp lại. Song song, bạn gặp rủi ro bảo mật: quá nhiều dữ liệu thừa đều đặn bị gửi vào mô hình. Vì vậy trong hệ thống tác nhân luôn có trạng thái tường minh — các đối tượng sống ngoài lịch sử tin nhắn và do chính bạn quản lý.

2. Các lớp trạng thái của tác nhân: ngữ cảnh, session, persistent

Bắt đầu bằng cách phân rã theo lớp. Tác nhân thường có tối thiểu ba mức “bộ nhớ”:

  1. Lịch sử tin nhắn (dialogue context).
  2. Trạng thái phiên (session state).
  3. Trạng thái dài hạn (persistent state).

Điều quan trọng là không trộn lẫn các khái niệm này vào một mớ.

Lịch sử tin nhắn: “bộ nhớ bẩn”

Lịch sử tin nhắn là những gì LLM thấy ở mỗi bước: chỉ dẫn system, yêu cầu của người dùng, phản hồi của tác nhân, kết quả của tools.

Ưu điểm là bạn không cần tự quản lý — Agents SDK và chính nền tảng lo phần này thông qua các thực thể Session/Conversation.

Nhược điểm — đó là “bộ nhớ bẩn”: nhiều từ thừa, lặp lại, dữ liệu ngẫu nhiên từ người dùng. Những dữ liệu này đắt về token và kém cấu trúc. Bạn không muốn danh sách 200 món quà đã lọc được đọc cho mô hình mỗi lần dưới dạng plain‑text.

Session state: bộ nhớ làm việc ngắn hạn

Trạng thái Session — là một đối tượng có cấu trúc, sống trong phạm vi một phiên/cuộc trò chuyện của tác nhân. Một phép so sánh tốt cho lập trình viên frontend — useState hoặc Redux‑store sống chừng nào tab còn mở.

Ở đó chứa các thứ như:

  • bước hiện tại của quy trình (ví dụ, "collecting_profile" hoặc "filtering_candidates");
  • cache tạm kết quả của tools;
  • tham số của phiên: locale, kênh đã chọn, các cờ như “người dùng đã xác nhận điều khoản”.

Trạng thái này có thể lưu gần tác nhân — trong Redis, KV‑store in‑memory hoặc qua SessionService tích hợp sẵn của SDK cụ thể. Điều chính — đừng cố nhét tất cả vào system‑prompt.

Persistent state: dữ liệu dài hạn

Trạng thái Persistent sống lâu: giữa các phiên, các lần checkout, các thiết bị. Đó là hồ sơ người dùng, đơn hàng, danh sách mong muốn đã lưu, thiết lập.

Ý tưởng then chốt: tác nhân không “ghi nhớ” dữ liệu persistent một cách ma thuật, nó “đọc” chúng qua tools — ví dụ, get_user_profile, get_past_orders. Không có biến toàn cục ẩn trong tác nhân; luôn là lời gọi tường minh.

Bảng so sánh

Lớp Nơi lưu trữ Vòng đời Ví dụ dữ liệu
Messages Session / SDK / OpenAI Một run / cuộc hội thoại thông điệp system/user/tool
Session state KV / SessionService / Redis Trong khi phiên còn sống bước workflow, cache tạm thời
Persistent DB (Postgres/NoSQL/ACP backend) Xuyên suốt giữa các phiên và hội thoại hồ sơ, đơn hàng, danh sách đã lưu

3. Session state: nó là gì và lưu ở đâu

Hãy hình dung tác nhân GiftGenius chạy một quy trình nhiều bước:

  1. Thu thập hồ sơ người nhận quà.
  2. Tạo danh sách ứng viên.
  3. Lọc theo ngân sách, giao hàng, vùng miền.
  4. Chuẩn bị gợi ý cuối cùng.

Trong quá trình đó, nó liên tục trao đổi với người dùng và gọi tools. Mọi thứ thuộc “tiến độ của phiên chọn quà cụ thể” hợp lý là giữ trong session‑state.

Ví dụ cấu trúc session state của GiftGenius

Mô tả kiểu session state bằng TypeScript:

// Trạng thái trong phạm vi một "phiên chọn quà"
export type GiftSessionState = {
  step:
    | "collecting_profile"
    | "generating_candidates"
    | "filtering"
    | "finalizing";

  // bản nháp hồ sơ người nhận
  profileDraft?: {
    recipientType?: string;
    ageRange?: string;
    interests?: string[];
    dislikes?: string[];
  };

  // id sản phẩm ứng viên lấy từ backend
  candidateIds?: string[];

  // món quà người dùng đã chọn
  selectedGiftId?: string;

  // cờ kỹ thuật
  locale?: string;
};

Ở đây chúng ta cố ý không nhét toàn bộ đối tượng sản phẩm — chỉ lưu ID. Dữ liệu đầy đủ hãy sống trong DB; khi cần, tác nhân gọi tool get_gift_details(gift_id).

Session trong Agents SDK (mang tính khái niệm)

Trong nhiều SDK cho tác nhân có một abstraction về session, tự lo việc lưu lịch sử tin nhắn và cho phép bạn lưu thêm structured‑state. Ở pseudo‑code, nó có thể trông như sau:

import { createRunner, OpenAIConversationsSession } from "@openai/agents";
// kiểu GiftSessionState từ ví dụ ở trên

const session = new OpenAIConversationsSession<GiftSessionState>({
  sessionId: "chatgpt-thread-id-or-random",
});

const runner = createRunner({ agent });

const result = await runner.run({
  session,
  input: "Tôi muốn quà cho đồng nghiệp tới 50$",
});

SDK bên dưới sẽ:

  • lấy lịch sử tin nhắn cho session này;
  • thêm tin nhắn người dùng mới;
  • truyền cho mô hình và bộ công cụ;
  • lưu ngược lại trạng thái đã cập nhật (bao gồm session.state).

Còn bạn làm việc với session.state như một đối tượng bình thường.

Cập nhật session‑state từ tools

Mẫu điển hình: tool tính toán gì đó đồng thời cập nhật session state. Ví dụ, tool gom hồ sơ người nhận từ câu trả lời của người dùng:

export async function updateProfileDraft(
  session: GiftSessionState,
  answers: { questionId: string; value: string }
): Promise<GiftSessionState> {
  const next: GiftSessionState = { ...session };

  if (!next.profileDraft) {
    next.profileDraft = {};
  }

  if (answers.questionId === "interests") {
    next.profileDraft.interests = answers.value.split(",").map((s) => s.trim());
  }

  // ...các trường khác

  next.step = "generating_candidates";
  return next;
}

Ở đây, vào tool chúng ta truyền không phải toàn bộ Session từ SDK, mà chỉ state của nó (kiểu GiftSessionState). Trong code thực tế, nên đặt tên tham số này, chẳng hạn, là currentState để khỏi nhầm với đối tượng Session.

Tác nhân gọi tool này, nhận đối tượng trạng thái mới và lưu nó trở lại vào session.state.

4. Persistent state: bộ nhớ dài hạn của tác nhân

Giờ hãy nhớ rằng GiftGenius không chỉ chạy trong một chat. Người dùng có thể quay lại sau một tuần, từ thiết bị khác, và nói: “Chọn quà cho cùng người bạn lần trước, nhưng tăng ngân sách”.

Thông tin này không nên nằm trong session‑state, mà ở persistent store: trong cơ sở dữ liệu, commerce‑/ACP‑backend (lớp commerce, sẽ có mô‑đun riêng), v.v.

Ví dụ model persistent

Mô tả model hồ sơ người nhận trong DB (đơn giản hóa, dạng kiểu TypeScript):

// Dữ liệu lưu trong DB
export type RecipientProfile = {
  id: string;
  userId: string;
  label: string; // "đồng nghiệp ở bộ phận marketing"
  recipientType: string;
  ageRange?: string;
  interests: string[];
  dislikes: string[];
  lastUsedAt: string; // ngày ISO
};

Và repository (tạm thời dùng Map đơn giản — thực tế bạn sẽ có ORM/SQL‑layer):

const profiles = new Map<string, RecipientProfile>();

export const RecipientRepo = {
  async findByUser(userId: string): Promise<RecipientProfile[]> {
    return [...profiles.values()].filter((p) => p.userId === userId);
  },

  async save(profile: RecipientProfile): Promise<void> {
    profiles.set(profile.id, profile);
  },
};

Tác nhân truy cập persistent qua tools

Điều quan trọng là tác nhân không truy cập DB trực tiếp, mà làm việc qua tools. Như vậy nó vẫn là một thực thể “sạch”: một chỗ là LLM và logic lập kế hoạch, chỗ kia — hiện thực các tích hợp.

Ví dụ tool get_recipient_profiles:

export async function getRecipientProfilesTool(input: {
  userId: string;
}): Promise<{ profiles: RecipientProfile[] }> {
  const profiles = await RecipientRepo.findByUser(input.userId);

  return {
    profiles,
  };
}

Trong mô tả tool, tác nhân đọc: “hãy dùng tool này để lấy các hồ sơ người nhận đã lưu cho người dùng hiện tại”. Nó tự quyết định khi nào cần gọi.

Tóm lại: session‑state — nói về tiến độ của cuộc trò chuyện cụ thể và cache tạm, có thể mất mà không sao. Dữ liệu persistent — là thứ phải sống qua các phiên và thiết bị: hồ sơ, đơn hàng, danh sách mong muốn. Tác nhân luôn đọc chúng qua tools, chứ không “nhớ” một cách ma thuật.

5. Session và persistent phối hợp ra sao trong run‑cycle

Bây giờ ghép tất cả lại. Ở mỗi bước của run‑cycle tác nhân, chúng ta có trình tự ngắn:

  1. Nạp session‑state theo sessionId.
  2. Nếu cần, tải thêm dữ liệu persistent liên quan từ DB qua tools.
  3. Tạo ngữ cảnh cho mô hình (messages + trạng thái có cấu trúc).
  4. Mô hình quyết định: trả lời bằng văn bản hay gọi tools.
  5. Tools cập nhật hoặc session‑state, hoặc dữ liệu persistent (qua DB).
  6. Lưu session‑state mới và nếu cần thì tạo checkpoint (sẽ nói tiếp).
  7. Gửi phản hồi cho người dùng.

Sơ đồ bằng mermaid:

flowchart TD
    A[Nhận đầu vào người dùng] --> B["Tải Session (state + messages)"]
    B --> C{Có cần dữ liệu persistent không?}
    C -- Có --> D[Gọi tools: get_user_profile, get_recipient_profiles]
    C -- Không --> E[Tạo ngữ cảnh cho LLM]
    D --> E
    E --> F["Gọi model (LLM)"]
    F --> G{Model muốn gọi tool?}
    G -- Có --> H[Thực thi tool, cập nhật session/persistent]
    G -- Không --> I[Chuẩn bị câu trả lời cuối cùng]
    H --> J[Tạo checkpoint và lưu Session]
    I --> J
    J --> K[Phản hồi cho người dùng]

Chu trình như vậy giúp hành vi của tác nhân có thể tái lập: ở mỗi bước chúng ta rõ ràng biết trạng thái trước khi gọi mô hình và những gì đã thay đổi sau đó.

6. Checkpoints: ảnh chụp trạng thái của tác nhân

Checkpoints — là các “ảnh chụp trạng thái” đã lưu của tác nhân tại bước quan trọng. Đây không chỉ là “session‑state hiện tại”, mà là một sự kiện được ghi vào kho bên ngoài: ở bước N chúng ta có state như thế này, kết quả tools như thế kia, đầu vào của người dùng như thế nọ.

Chúng hữu ích để:

  • khôi phục sau lỗi và sụp đổ;
  • cho phép người dùng “tiếp tục sau”;
  • gỡ lỗi: tái tạo run có vấn đề;
  • kiểm toán: chính xác tác nhân đã làm gì trước khi, ví dụ, tạo đơn hàng.

Thành phần thường có trong checkpoint

Checkpoint điển hình gồm:

  • các định danh: runId, userId, workflowId, stepId;
  • session state tại thời điểm đó;
  • các định danh chính của thực thể persistent (ví dụ, id của bản nháp đơn hàng);
  • metadata: thời điểm tạo, phiên bản tác nhân.

Quan trọng là không kéo cả đống văn bản đối thoại vào đó. Bên dưới, trong phần về vệ sinh bộ nhớ, chúng ta sẽ quay lại nên lưu gì và không nên lưu gì.

Tốt hơn là lưu liên kết đến Session hoặc tóm tắt ngắn các bước.

7. Thiết kế checkpoints cho GiftGenius

Lấy quy trình chọn quà của chúng ta và quyết định nơi muốn có checkpoints. Ví dụ:

  • sau khi đã thu thập hồ sơ người nhận;
  • sau khi tạo và lọc sơ bộ danh sách ứng viên;
  • trước khi đề xuất lựa chọn cuối cùng cho người dùng.

Các kiểu cho checkpoint và trạng thái workflow

Mô tả trạng thái workflow (rất giống GiftSessionState, nhưng đây là “bản chụp” cho checkpoints):

export type GiftWorkflowStep =
  | "profile_collected"
  | "candidates_generated"
  | "filtered"
  | "final_choice_made";

export type GiftCheckpoint = {
  id: string;
  runId: string;
  userId: string;

  step: GiftWorkflowStep;

  // phần session-state
  // mà chúng ta cần để khôi phục
  sessionState: GiftSessionState;

  // các id ứng viên đã được tạo
  candidateIds: string[];

  createdAt: string; // ISO
  agentVersion: string;
};

Kho lưu checkpoints (đơn giản hóa)

Làm như trước, dùng Map đơn giản thay cho DB thực:

const checkpoints = new Map<string, GiftCheckpoint>();

export const GiftCheckpointRepo = {
  async save(cp: GiftCheckpoint) {
    checkpoints.set(cp.id, cp);
  },

  async findByRun(runId: string): Promise<GiftCheckpoint[]> {
    return [...checkpoints.values()].filter((c) => c.runId === runId);
  },

  async findLastByUser(userId: string): Promise<GiftCheckpoint | undefined> {
    return [...checkpoints.values()]
      .filter((c) => c.userId === userId)
      .sort((a, b) => b.createdAt.localeCompare(a.createdAt))[0];
  },
};

Tạo checkpoint từ code của tác nhân

Giả sử có helper chúng ta gọi sau một bước quan trọng:

import { randomUUID } from "crypto";

export async function createCheckpoint(params: {
  runId: string;
  userId: string;
  step: GiftWorkflowStep;
  sessionState: GiftSessionState;
  candidateIds: string[];
}) {
  const checkpoint: GiftCheckpoint = {
    id: randomUUID(),
    runId: params.runId,
    userId: params.userId,
    step: params.step,
    sessionState: params.sessionState,
    candidateIds: params.candidateIds,
    createdAt: new Date().toISOString(),
    agentVersion: "v1.3.0",
  };

  await GiftCheckpointRepo.save(checkpoint);
}

Tác nhân có thể gọi ở thời điểm cần thiết:

await createCheckpoint({
  runId,
  userId,
  step: "filtered",
  sessionState,
  candidateIds,
});

Khi khôi phục, chúng ta:

  1. Tìm checkpoint gần nhất theo runId hoặc userId.
  2. Khôi phục session.state từ checkpoint.sessionState.
  3. Nếu cần, kéo dữ liệu mới nhất từ DB dựa trên candidateIds.

8. Lưu trữ session, persistent và checkpoints ở đâu về mặt kỹ thuật

Ở tầng hạ tầng, bạn thường có ba lớp kho lưu trữ khác nhau:

  • In‑memory — cho dev/demos, nhanh nhưng tạm thời.
  • Redis (hoặc KV‑store khác) — cho session‑state.
  • CSDL quan hệ/NoSQL — cho dữ liệu persistent và checkpoints.

In‑memory store cho phát triển cục bộ

Cho chế độ dev cục bộ, một in‑memory store đơn giản là đủ. Ví dụ, mini‑store với TTL cho sessions:

type StoredSession<T> = {
  state: T;
  expiresAt: number;
};

const sessions = new Map<string, StoredSession<GiftSessionState>>();

export function saveSession(sessionId: string, state: GiftSessionState) {
  sessions.set(sessionId, {
    state,
    expiresAt: Date.now() + 30 * 60 * 1000, // 30 phút
  });
}

export function loadSession(sessionId: string): GiftSessionState | undefined {
  const stored = sessions.get(sessionId);
  if (!stored) return undefined;
  if (stored.expiresAt < Date.now()) {
    sessions.delete(sessionId);
    return undefined;
  }
  return stored.state;
}

Thứ này rất ổn cho local dev, nhưng lên production với mở rộng theo chiều ngang (nhiều instance) thì không dùng được nữa.

Redis cho session‑state

Trong production, lưu session‑state trong Redis là tiện lợi:

  • đọc/ghi nhanh;
  • TTL “out of the box”;
  • mọi instance của service đều truy cập được.

Ví dụ giả lập (đơn giản hóa):

// Wrapper quanh client Redis
export async function saveSessionToRedis(
  sessionId: string,
  state: GiftSessionState
) {
  const json = JSON.stringify(state);
  await redis.set(`session:${sessionId}`, json, "EX", 60 * 30); // 30 phút
}

export async function loadSessionFromRedis(
  sessionId: string
): Promise<GiftSessionState | undefined> {
  const json = await redis.get(`session:${sessionId}`);
  return json ? (JSON.parse(json) as GiftSessionState) : undefined;
}

Postgres/DB khác cho persistent và checkpoints

Persistent‑state và checkpoints — là các thực thể “nghiêm túc”, cần giao dịch, migration, index và những thứ thú vị khác. Chúng được đặt trong Postgres, MySQL, Firestore, v.v.

Mẫu kiến trúc ở đây khá đơn giản:

  • session trong Redis với TTL;
  • persistent và checkpoints trong DB không TTL (hoặc với chính sách lưu giữ phụ thuộc nghiệp vụ).

9. Vệ sinh bộ nhớ: kích thước, quyền riêng tư, phân chia trách nhiệm

Bộ nhớ của tác nhân không chỉ là “quăng đâu đó một object rồi chạy”. Có vài quy tắc quan trọng giúp tiết kiệm chi phí và giữ bạn ngủ ngon.

Đừng nhét mọi thứ vào messages

Lịch sử tin nhắn là tài nguyên đắt đỏ:

  • độ dài của nó ảnh hưởng mạnh đến chi phí request tới mô hình;
  • thường chứa nhiều “nhiễu”.

Vì vậy:

  • cố gắng kéo các sự kiện từ lịch sử sang trạng thái có cấu trúc càng sớm càng tốt;
  • dùng summarization cho phần lịch sử cũ;
  • nếu lưu lịch sử văn bản trong checkpoint, hãy làm điều này tách biệt với cái được gửi cho mô hình.

Quyền riêng tư và PII

Đặc biệt với kịch bản commerce, việc không lưu dữ liệu nhạy cảm ở nơi không cần thiết là rất quan trọng. Các tài liệu về kiến trúc bộ nhớ nhấn mạnh rằng không nên giữ dữ liệu PII trong messages hoặc checkpoints mà không làm sạch.

Quy tắc thực tiễn:

  • đừng đặt email/số điện thoại/địa chỉ thẳng vào session‑state nếu không cần cho tác nhân vận hành;
  • vào log và checkpoints, hãy ghi các định danh (userId, recipientProfileId) thay vì chuỗi thô;
  • nếu cần truyền PII qua nhiều bước — dùng các trường bảo vệ riêng trong persistent store, còn trong state chỉ chuyển khóa.

Phân tách dữ liệu nghiệp vụ và log hội thoại

Mẫu tốt — coi state là “bộ nhớ sạch”, còn messages là “bộ nhớ bẩn”.

Tức là:

  • các thực thể nghiệp vụ (hồ sơ, đơn hàng, giỏ hàng) luôn sống trong DB;
  • state/checkpoints chỉ chứa tối thiểu những gì cần để khôi phục quy trình;
  • log/lịch sử chat được lưu tách biệt (ví dụ, trong vector store) và dùng cho phân tích, chứ không trộn vào mỗi request gửi mô hình.

10. Mini‑thực hành: bạn sẽ lưu gì?

Để củng cố khác biệt giữa các lớp bộ nhớ, thử rời lý thuyết một chút và nghĩ về một case cụ thể. Không nhất thiết phải viết code — chỉ cần ước lượng cấu trúc trên giấy hoặc trong đầu là đủ.

Hãy tưởng tượng tác nhân GiftGenius của bạn đã có cuộc hội thoại sau với người dùng:

  • Người dùng: “Cần quà cho đồng nghiệp lập trình, ngân sách tới 50$, anh ấy thích board game và caffein”.
  • Tác nhân: đặt vài câu hỏi làm rõ.
  • Người dùng: “Anh ấy ghét cốc và đã có quá nhiều sổ tay”.
  • Tác nhân: tạo danh sách 10 ý tưởng, người dùng chọn một, nhưng nói: “Tôi sẽ quay lại để hoàn tất sau”.

Hãy suy nghĩ:

  1. Bạn sẽ đặt gì vào session‑state (có thể chết sau 30 phút)?
  2. Cái gì sẽ vào persistent store để người dùng quay lại sau một tuần?
  3. Checkpoint sau khi chọn ý tưởng nhưng trước khi đặt hàng sẽ trông như thế nào?

Hãy thử phác thảo các kiểu TypeScript và các hàm saveSessionState, savePersistentState, createGiftIdeaCheckpoint tương tự các ví dụ trong bài này. Nếu muốn, bạn có thể phác thảo trực tiếp các kiểu và hàm đó trong editor theo các ví dụ ở trên — đây sẽ là một mini‑checkpoint tốt trước bài giảng tiếp theo.

11. Lỗi thường gặp khi làm việc với bộ nhớ của tác nhân

Lỗi số 1: cố lưu mọi thứ chỉ trong lịch sử tin nhắn.
Lập trình viên vui mừng: “Mô hình đã thấy toàn bộ cuộc thoại rồi, cần gì thêm state nữa?”. Hệ quả là sau vài chục tin nhắn, cửa sổ ngữ cảnh đầy rác, token tốn như mua MacBook mới, và hành vi tác nhân trở nên bất ổn — nó đơn giản không thấy các sự kiện cũ quan trọng. Vấn đề này phải giải bằng cách tách tường minh session‑state và persistent store, chứ không phải tăng limit.

Lỗi số 2: trộn session và persistent thành một đối tượng.
Đôi khi cám dỗ là tạo một thực thể “to” AgentState, nhét mọi thứ vào và lưu “nguyên trạng” vào DB. Khi đó ranh giới giữa dữ liệu tạm của cuộc trò chuyện cụ thể và dữ liệu dài hạn của người dùng bị mờ. Bắt đầu xuất hiện những câu chuyện như “sau deploy, mọi session bí ẩn khôi phục từ dữ liệu năm ngoái” hoặc “session của người dùng này vô tình lấy persistent profile của người khác”. Hãy tách bạch các lớp một cách có chủ ý.

Lỗi số 3: lưu quá nhiều trong checkpoints.
Lỗi thường gặp — ghi vào checkpoint toàn bộ JSON phản hồi của tools, toàn bộ lịch sử chat, dữ liệu thô của tích hợp và các thứ khác. Sau vài tuần vận hành, DB checkpoints phình to quá mức, sao lưu mất cả giờ, truy vấn DB thì chậm. Trong checkpoint chỉ nên có những sự kiện thực sự cần để tiếp tục quy trình, cộng tối thiểu metadata.

Lỗi số 4: quên TTL và dọn dẹp session‑state.
Nếu session‑state không có hạn dùng, mọi thử nghiệm ngẫu nhiên của người dùng ở Dev Mode sẽ nằm mãi trong Redis. Vài tháng sau bạn nhìn vào monitoring và thấy cả đống session “bị quên” chiếm bộ nhớ. Tầng session cần thiết kế với TTL tường minh, còn tầng persistent — với chính sách lưu giữ hợp lý.

Lỗi số 5: lưu PII trong state và checkpoints khi không cần thiết.
Đặc biệt nguy hiểm khi session‑state bị nhét email, địa chỉ, số thẻ bừa bãi, rồi object này được serialize vào log, đẩy vào analytics và checkpoints. Điều này tạo rủi ro nghiêm trọng về quy định và bảo mật. Tốt hơn là lưu các định danh an toàn và khi cần thì resolve sang dữ liệu thật qua các tools bảo vệ riêng.

Lỗi số 6: thiếu chiến lược khôi phục từ checkpoints.
Một số nhóm ghi checkpoints rất chăm chỉ, nhưng lại không nghĩ kỹ tác nhân sẽ khôi phục từ đó như thế nào. Cuối cùng, khi “có gì đó sai”, lập trình viên nhìn vào bảng chứa các JSON đẹp đẽ, nhưng không có code nào biết tái dựng run từ chúng. Checkpointing mà không có kịch bản khôi phục — chỉ là log đắt tiền, không phải công cụ tăng độ tin cậy.

Lỗi số 7: buộc chặt tác nhân vào hiện thực lưu trữ cụ thể.
Nếu code của tác nhân đi thẳng vào Redis/Postgres, nó khó chuyển đổi, kiểm thử và phát triển hơn. Khi kiến trúc thay đổi (ví dụ, xuất hiện tài nguyên MCP hoặc service state riêng), bạn sẽ phải “đào xới” lại logic tác nhân. Tốt hơn nhiều khi tác nhân chỉ thấy abstraction Session và tập hợp tools, còn tools mới biết dữ liệu nằm ở đâu.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION