Mở Đầu: Khi Chatbot Của Tôi "Quên" Mọi Thứ

Tôi vẫn nhớ rõ ngày hôm đó — deadline sản phẩm còn 2 tiếng, và chatbot tôi xây dựng bằng LangChain bắt đầu trả lời như thể nó gặp khách hàng lần đầu. "Xin chào, tôi có thể giúp gì cho bạn?" — câu trả lời đó lặp đi lặp lại dù người dùng đã cung cấp đầy đủ thông tin ở 5 tin nhắn trước. Đó là khoảnh khắc tôi nhận ra: memory không phải thứ bạn có thể xem nhẹ.

Sau 3 năm làm việc với các hệ thống AI conversation tại HolySheep AI, tôi đã xử lý hàng trăm case liên quan đến context loss. Bài viết này sẽ chia sẻ toàn bộ kiến thức thực chiến của tôi — từ khái niệm cơ bản đến những pattern nâng cao mà bạn sẽ không tìm thấy trong documentation.

1. Tại Sao Memory Quan Trọng Trong LangChain?

Khi bạn gọi LLM API, mỗi request là một "trang trắng" — model không lưu lại lịch sử hội thoại. Memory chính là lớp trung gian giúp bạn:

Với HolySheep AI, độ trễ trung bình chỉ dưới 50ms giúp việc truy xuất memory trở nên gần như instant, không ảnh hưởng đến trải nghiệm người dùng.

2. Các Loại Memory Trong LangChain

2.1. ConversationBufferMemory

Đây là dạng memory đơn giản nhất — lưu trữ toàn bộ lịch sử dưới dạng messages. Tôi thường dùng nó cho các prototype vì code ngắn gọn.

import { ChatHolySheep } from "@langchain/community/chat_models/holysheep";
import { ConversationChain } from "langchain/chains";
import { BufferMemory } from "langchain/memory";

// Khởi tạo model với HolySheep AI
const model = new ChatHolySheep({
  apiKey: "YOUR_HOLYSHEEP_API_KEY",
  baseUrl: "https://api.holysheep.ai/v1",
  model: "gpt-4.1", // $8/MTok - tiết kiệm 85% so với OpenAI
});

// Tạo memory buffer
const memory = new BufferMemory({
  returnMessages: true,
  memoryKey: "history",
  outputKey: "response",
});

// Tạo conversation chain
const chain = new ConversationChain({
  llm: model,
  memory: memory,
  verbose: true,
});

// Sử dụng
const response1 = await chain.call({
  input: "Tên tôi là Minh, tôi 28 tuổi"
});
console.log(response1.response);

const response2 = await chain.call({
  input: "Tên tôi là gì?"
});
// Output: "Tên của bạn là Minh, bạn 28 tuổi."

2.2. ConversationBufferWindowMemory

Khi production, buffer memory sẽ phình to theo thời gian. Tôi đã từng để một conversation chạy 1 tuần — kết quả là 2000+ messages và chi phí API tăng vọt. BufferWindowMemory là giải pháp:

import { BufferWindowMemory } from "langchain/memory";

const windowMemory = new BufferWindowMemory({
  k: 10, // Chỉ giữ lại 10 messages gần nhất
  returnMessages: true,
  memoryKey: "chat_history",
  inputKey: "input",
});

// Trong production, kiểm tra memory size trước khi gửi
async function safeChat(chain, input, maxTokens = 4000) {
  const estimatedTokens = estimateTokens(input);
  
  if (estimatedTokens > maxTokens) {
    // Clear oldest messages nếu cần
    await chain.memory.clear();
  }
  
  return await chain.call({ input });
}

2.3. ConversationSummaryMemory

Đây là loại memory tôi sử dụng nhiều nhất. Thay vì lưu messages thuần túy, nó tự động tạo summary — đặc biệt hữu ích cho các conversation dài:

import { ConversationSummaryMemory } from "langchain/memory";
import { ChatPromptTemplate } from "@langchain/core/prompts";

// Prompt template cho việc summarize
const SUMMARY_PROMPT = ChatPromptTemplate.fromTemplate(`
Hãy tóm tắt cuộc trò chuyện sau, giữ lại thông tin quan trọng:
{context}

Tóm tắt (bằng tiếng Việt):
`);

const summaryMemory = new ConversationSummaryMemory({
  llm: model,
  memoryKey: "summary",
  prompt: SUMMARY_PROMPT,
  returnMessages: true,
});

// Với conversation 100+ messages, chỉ tốn ~200 tokens cho summary
// So với 5000+ tokens nếu dùng buffer thuần
const longConversation = await summaryMemory.loadMemoryVariables({});
console.log(longConversation.summary);
// Output: "Người dùng tên Minh, 28 tuổi, quan tâm đến sản phẩm AI..."

2.4. ConversationTokenBufferMemory

Đây là loại memory thông minh nhất — giới hạn theo token count thay vì message count. Rất hữu ích khi bạn cần kiểm soát chi phí chính xác:

import { ConversationTokenBufferMemory } from "langchain/memory";

const tokenMemory = new ConversationTokenBufferMemory({
  llm: model,
  maxTokenLimit: 2000, // Giới hạn 2000 tokens
  returnMessages: true,
});

// Kiểm tra token usage trước mỗi request
async function chatWithBudgetControl(input) {
  const currentTokens = await tokenMemory.getCurrentTokenCount();
  const inputTokens = estimateTokens(input);
  
  if (currentTokens + inputTokens > 3500) {
    // Sẽ bị cắt, notify user hoặc compress trước
    console.warn(Token limit approaching: ${currentTokens + inputTokens});
  }
  
  return await chain.call({ input });
}

3. Multi-User Memory Management

Trong production, bạn cần quản lý memory cho nhiều users. Đây là pattern tôi sử dụng tại HolySheep:

import { BufferMemory } from "langchain/memory";

// Global memory store - production nên dùng Redis
const memoryStore = new Map();

// Get hoặc create memory cho user
function getUserMemory(userId: string): BufferMemory {
  if (!memoryStore.has(userId)) {
    memoryStore.set(userId, new BufferMemory({
      returnMessages: true,
      memoryKey: history_${userId},
    }));
  }
  return memoryStore.get(userId);
}

// Xử lý request với user isolation
async function handleUserMessage(userId: string, message: string) {
  const memory = getUserMemory(userId);
  
  const chain = new ConversationChain({
    llm: model,
    memory: memory,
  });
  
  const response = await chain.call({ input: message });
  
  // Cleanup sau khi response (production: dùng TTL)
  if (memoryStore.size > 10000) {
    cleanupOldMemories();
  }
  
  return response;
}

// Cleanup function - chạy định kỳ
function cleanupOldMemories() {
  const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
  for (const [userId, memory] of memoryStore) {
    const lastAccess = memory.metadata?.lastAccess || 0;
    if (lastAccess < oneWeekAgo) {
      memoryStore.delete(userId);
    }
  }
}

4. Kết Hợp Memory Với Vector Store

Với các ứng dụng cần truy xuất thông tin từ knowledge base, tôi kết hợp memory với vector store:

import { VectorStoreRetrieverMemory } from "langchain/memory";
import { HolySheepEmbeddings } from "@langchain/community/embeddings/holysheep";
import { Milvus } from "@langchain/community/vectorstores/milvus";

const embeddings = new HolySheepEmbeddings({
  apiKey: "YOUR_HOLYSHEEP_API_KEY",
  baseUrl: "https://api.holysheep.ai/v1",
});

// Vector store cho knowledge retrieval
const vectorStore = await Milvus.fromExistingCollection(embeddings, {
  collectionName: "product_kb",
  connectionParams: { address: "localhost:19530" },
});

const retriever = vectorStore.asRetriever(3); // Top 3 results

const vectorMemory = new VectorStoreRetrieverMemory({
  retriever: retriever,
  memoryKey: "relevant_info",
  inputKey: "input",
});

// Kết hợp buffer và vector memory
class HybridMemory extends BufferMemory {
  private vectorMemory: VectorStoreRetrieverMemory;
  
  async loadMemoryVariables(values: any): Promise> {
    const bufferMemory = await super.loadMemoryVariables(values);
    const relevantInfo = await this.vectorMemory.loadMemoryVariables(values);
    
    return {
      ...bufferMemory,
      ...relevantInfo,
    };
  }
}

5. Lưu Trữ Memory Vĩnh Viễn Với Database

Để đảm bảo conversation context không bị mất khi server restart, tôi sử dụng database persistence:

import { PrismaMemory } from "langchain/memory/prisma";

// Schema: UserMemory trong database
// migration.sql:
// CREATE TABLE user_memories (
//   id SERIAL PRIMARY KEY,
//   user_id VARCHAR(255) NOT NULL,
//   session_id VARCHAR(255) NOT NULL,
//   messages JSONB NOT NULL,
//   summary TEXT,
//   created_at TIMESTAMP DEFAULT NOW(),
//   updated_at TIMESTAMP DEFAULT NOW()
// );

const prismaMemory = new PrismaMemory({
  sessionId: "session_123",
  userId: "user_456",
  prisma: prismaClient,
});

// Auto-save sau mỗi interaction
prismaMemory.saveContext = async ({ input }, { output }) => {
  await prismaClient.userMemories.upsert({
    where: {
      user_id_session_id: {
        user_id: "user_456",
        session_id: "session_123",
      },
    },
    update: {
      messages: { push: { input, output, timestamp: Date.now() } },
      updated_at: new Date(),
    },
    create: {
      user_id: "user_456",
      session_id: "session_123",
      messages: [{ input, output, timestamp: Date.now() }],
    },
  });
};

Lỗi Thường Gặp Và Cách Khắc Phục

Lỗi 1: Memory Context Bị Cắt — "context_length_exceeded"

Mô tả lỗi: Khi conversation dài, bạn sẽ gặp lỗi context length exceeded. Đây là lỗi tôi gặp thường xuyên nhất khi mới bắt đầu.

// ❌ Code gây lỗi - không kiểm soát context size
const chain = new ConversationChain({
  llm: model,
  memory: new BufferMemory(), // Không giới hạn!
});

// Sau 50+ messages sẽ crash
const response = await chain.call({ input: longMessage });

// ✅ Fix - implement token budget
import { ConversationTokenBufferMemory } from "langchain/memory";

const safeMemory = new ConversationTokenBufferMemory({
  llm: model,
  maxTokenLimit: 3000, // Giữ 1/3 context cho input mới
});

const safeChain = new ConversationChain({
  llm: model,
  memory: safeMemory,
});

// Wrapper để auto-reset khi cần
async function safeCall(input: string) {
  try {
    return await safeChain.call({ input });
  } catch (error) {
    if (error.message.includes("context_length")) {
      await safeMemory.clear(); // Reset memory
      console.log("Memory cleared due to context limit");
      return await safeChain.call({ input: "Tôi đã quên cuộc trò chuyện trước. Bạn có thể nhắc lại không?" });
    }
    throw error;
  }
}

Lỗi 2: Memory Isolation Giữa Các Users

Mô tả lỗi: User A nhìn thấy messages của User B. Lỗi bảo mật nghiêm trọng này xảy ra khi dùng shared memory instance.

// ❌ Code gây lỗi - shared memory
const sharedMemory = new BufferMemory();

app.post("/chat", async (req, res) => {
  const { userId, message } = req.body;
  
  const chain = new ConversationChain({
    llm: model,
    memory: sharedMemory, // Tất cả users dùng chung!
  });
  
  const response = await chain.call({ input: message });
  // User A và B sẽ thấy lịch sử của nhau
});

// ✅ Fix - per-user memory với proper isolation
const userMemories = new Map();

function getOrCreateMemory(userId: string): BufferMemory {
  if (!userMemories.has(userId)) {
    userMemories.set(userId, new BufferMemory({
      returnMessages: true,
    }));
  }
  return userMemories.get(userId);
}

app.post("/chat", async (req, res) => {
  const { userId, message } = req.body;
  
  if (!userId) {
    return res.status(400).json({ error: "userId required" });
  }
  
  const memory = getOrCreateMemory(userId);
  
  const chain = new ConversationChain({
    llm: model,
    memory: memory,
  });
  
  const response = await chain.call({ input: message });
  
  res.json({ response: response.response, userId });
});

// Cleanup định kỳ để tránh memory leak
setInterval(() => {
  const now = Date.now();
  for (const [userId, memory] of userMemories.entries()) {
    const lastActivity = (memory as any).lastActivity || 0;
    if (now - lastActivity > 24 * 60 * 60 * 1000) {
      userMemories.delete(userId);
    }
  }
}, 60 * 60 * 1000); // Mỗi giờ

Lỗi 3: "401 Unauthorized" — API Key Không Hợp Lệ

Mô tả lỗi: Lỗi authentication khi sử dụng memory với API. Thường do environment variable không được load đúng.

// ❌ Code gây lỗi - hardcoded key hoặc env not loaded
const model = new ChatHolySheep({
  apiKey: "sk-xxxx", // Hardcoded - có thể bị expose
  baseUrl: "https://api.holysheep.ai/v1",
});

// ❌ Hoặc env not loaded properly
require("dotenv").config(); // Load sau khi dùng
const apiKey = process.env.HOLYSHEEP_API_KEY; // Undefined!

// ✅ Fix - proper env loading và validation
import "dotenv/config";

class HolySheepModel {
  private model: any;
  
  constructor() {
    const apiKey = process.env.HOLYSHEEP_API_KEY;
    
    if (!apiKey) {
      throw new Error("HOLYSHEEP_API_KEY environment variable is required");
    }
    
    if (!apiKey.startsWith("sk-")) {
      throw new Error("Invalid API key format");
    }
    
    this.model = new ChatHolySheep({
      apiKey: apiKey,
      baseUrl: "https://api.holysheep.ai/v1",
      model: "gpt-4.1",
    });
  }
  
  createChain(memory: any) {
    return new ConversationChain({
      llm: this.model,
      memory: memory,
    });
  }
}

// .env file:
// HOLYSHEEP_API_KEY=sk-your-key-here

const aiModel = new HolySheepModel();
const memory = new BufferMemory();
const chain = aiModel.createChain(memory);

Lỗi 4: Memory Không Sync Giữa Server Instances

Mô tả lỗi: Khi chạy multi-instance (cluster), user có thể nhận responses khác nhau vì mỗi instance có memory riêng.

// ❌ Code gây lỗi - in-memory storage
const memoryStore = new Map(); // Chỉ tồn tại trong 1 process!

// Khi scale horizontally, mỗi request có thể đến server khác
// Server B không biết gì về conversation ở Server A

// ✅ Fix - dùng distributed memory store
import Redis from "ioredis";

class RedisMemoryStore {
  private redis: any;
  private ttl: number;
  
  constructor() {
    this.redis = new Redis(process.env.REDIS_URL);
    this.ttl = 7 * 24 * 60 * 60; // 7 days
  }
  
  async getMemory(userId: string): Promise {
    const key = memory:${userId};
    const data = await this.redis.get(key);
    return data ? JSON.parse(data) : [];
  }
  
  async saveMemory(userId: string, messages: any[]): Promise {
    const key = memory:${userId};
    await this.redis.setex(key, this.ttl, JSON.stringify(messages));
  }
  
  async clearMemory(userId: string): Promise {
    await this.redis.del(memory:${userId});
  }
}

class DistributedBufferMemory extends BufferMemory {
  private store: RedisMemoryStore;
  
  constructor(private userId: string) {
    super();
    this.store = new RedisMemoryStore();
  }
  
  async loadMemoryVariables(values: any): Promise> {
    const messages = await this.store.getMemory(this.userId);
    return { history: messages };
  }
  
  async saveContext(inputs: any, outputs: any): Promise {
    await super.saveContext(inputs, outputs);
    const messages = await this.getChatHistory();
    await this.store.saveMemory(this.userId, messages);
  }
}

// Sử dụng - giờ đây memory sync across all instances
const memory = new DistributedBufferMemory(userId);
const chain = new ConversationChain({
  llm: model,
  memory: memory,
});

6. Best Practices Từ Kinh Nghiệm Thực Chiến

Qua hàng trăm dự án, đây là những nguyên tắc tôi luôn tuân thủ:

Kết Luận

Memory là trái tim của bất kỳ conversational AI application nào. Không có nó, bạn chỉ có một chiếc máy trả lời câu hỏi đơn lẻ, không phải một trợ lý thông minh. Qua bài viết này, tôi đã chia sẻ những pattern mà tôi sử dụng hàng ngày tại HolySheep AI — nơi chúng tôi xử lý hàng triệu conversations với độ trễ dưới 50ms.

Nếu bạn đang xây dựng ứng dụng LangChain và cần API với chi phí thấp (từ $0.42/MTok với DeepSeek V3.2), hỗ trợ thanh toán qua WeChat/Alipay, và độ trễ cực thấp, hãy thử đăng ký tại đây để nhận tín dụng miễn phí khi bắt đầu.

👉 Đăng ký HolySheep AI — nhận tín dụng miễn phí khi đăng ký