13. Time Travel - Kiểm soát và Chỉnh sửa lịch sử

Nam

Nam Hoang / Sep 13, 2025

7 min read

Làm việc với các hệ thống không xác định (non-deterministic) như LLM thường rất khó khăn: Agent có thể đưa ra một quyết định sai ở bước 3 khiến toàn bộ quy trình ở bước 10 thất bại. Time Travel (Du hành thời gian) là tính năng cho phép bạn quay lại bước 3, sửa lại "suy nghĩ" của Agent và cho nó chạy tiếp theo một hướng đúng đắn hơn.

I. 3 Khả năng của Time Travel

  1. Hiểu lập luận (Understand): Xem lại chính xác các bước dẫn đến kết quả hiện tại.
  2. Gỡ lỗi (Debug): Xác định node nào đã làm hỏng dữ liệu hoặc tại sao LLM lại đưa ra quyết định sai.
  3. Thử nghiệm (Explore): Thay đổi dữ liệu ở quá khứ để xem Agent sẽ phản ứng thế nào (Rẽ nhánh - Forking).

II. Truy vấn lịch sử và Sửa đổi trạng thái

Mỗi khi một Node chạy xong, một Checkpoint được lưu lại. Bạn có thể sử dụng hai công cụ chính:

  • getStateHistory: Liệt kê tất cả các checkpoint đã lưu để xem "dòng thời gian" của Agent.
  • updateState: Cho phép bạn tạo ra một checkpoint mới (một nhánh mới) dựa trên một checkpoint cũ với dữ liệu đã được thay đổi.

III. Mã nguồn: 13-time-travel-debugging.ts

Ví dụ dưới đây minh họa cách một Agent tự chọn chủ đề "Vacuum cleaner" (Máy hút bụi). Chúng ta sẽ "du hành thời gian" về thời điểm sau khi Node 1 chạy xong để đổi chủ đề thành "Cat" (Con mèo) trước khi Node 2 thực hiện viết lời đùa.

// path: 13-time-travel-debugging.ts

import { END, MemorySaver, START, StateGraph } from '@langchain/langgraph';
import { generateImage } from '@workspace/util-langchain';
import z from 'zod';

const State = z.object({
  topic: z.string(),
  joke: z.string().optional(),
});

const checkpointer = new MemorySaver();

const workflow = new StateGraph(State)
  .addNode('node1', async (state) => {
    console.log('run node 1');
    return { topic: 'Vacuum cleaner' };
  }) // Agent automatically chooses
  .addNode('node2', async (state) => {
    console.log('run node 2');
    return {
      joke: `A joke about ${state.topic}`,
    };
  })
  .addEdge(START, 'node1')
  .addEdge('node1', 'node2')
  .addEdge('node2', END)
  .compile({ checkpointer });

async function run() {
  await generateImage(
    workflow,
    'graph-ignore/scripts-13-time-travel-debugging.jpg',
  );

  const config = { configurable: { thread_id: 'test_1' } };

  // 1. Agent runs automatically
  console.log('--- Initial Execution ---');
  await workflow.invoke({}, config);

  // 2. Retrieve history to find the right point in time
  let history = [];
  for await (const s of workflow.getStateHistory(config)) {
    history.push(s);
  }

  // We want to go back to the checkpoint after node1 finished
  // In history (from newest to oldest), this is usually index 1
  const checkpointAfterNode1 = history[1];
  console.log(`Current topic in history: ${checkpointAfterNode1.values.topic}`);

  console.log("--- Performing Time Travel: Changing topic to 'Cat' ---");

  // 3. Modify the topic to 'Cat' at that specific checkpoint
  // This creates a NEW fork in the history
  const newConfig = await workflow.updateState(checkpointAfterNode1.config, {
    topic: 'Cat',
  });

  // 4. Continue execution from the new state
  const finalResult = await workflow.invoke(null, newConfig);

  console.log('\n=== FINAL RESULT AFTER TIME TRAVEL ===');
  console.log(finalResult);
}

run();

IV. Giải thích cơ chế Forking

  1. checkpoint_id: Mỗi trạng thái trong lịch sử có một ID duy nhất. Đây là "tọa độ" để bạn quay về.
  2. updateState(config, values): Khi bạn truyền config của một checkpoint cũ, LangGraph không xóa lịch sử cũ. Nó tạo ra một "nhánh" (fork) mới bắt đầu từ đó.
  3. Tiếp tục luồng: Khi gọi invoke(null, newConfig), Agent sẽ không chạy lại Node 1. Nó bắt đầu ngay từ Node 2 vì Node 1 đã được đánh dấu là hoàn thành trong checkpoint đó, nhưng với giá trị topic mới là "Cat".

Sơ đồ hoạt động:

Sơ đồ Time Travel

V. Tổng kết

  • Time Travel biến LangGraph thành một công cụ gỡ lỗi cực kỳ mạnh mẽ cho các hệ thống Agent phức tạp.
  • getStateHistory giúp bạn quan sát toàn bộ quá trình Agent "suy nghĩ".
  • Forking cho phép bạn thử nghiệm nhiều kịch bản khác nhau trên cùng một Thread mà không làm mất dữ liệu gốc.

Ở bài học tới, chúng ta sẽ học về Memory, cách kết hợp Persistence và Store để Agent không chỉ nhớ trong một Thread mà còn có trí nhớ dài hạn xuyên suốt nhiều Thread khác nhau!

👉 Bài tiếp theo: 14. Memory - Quản lý ngữ cảnh ngắn hạn và dài hạn