ระบบหน่วยความจำ AI·

หน่วยความจำเอเจนต์ AI และการคงทนของบริบทใน n8n: สร้างเวิร์กโฟลว์แบบมีสถานะที่จดจำได้

เชี่ยวชาญหน่วยความจำเอเจนต์ AI และการคงทนของบริบทใน n8n เรียนรู้ที่จะสร้างเวิร์กโฟลว์แบบมีสถานะด้วย Redis, PostgreSQL และ vector stores ที่รักษาบริบทระหว่างการสนทนา จัดการกระบวนการหลายขั้นตอนที่ซับซ้อน และมอบประสบการณ์การทำงานอัตโนมัติที่เป็นส่วนตัว

หน่วยความจำเอเจนต์ AI และการคงทนของบริบทใน n8n: สร้างเวิร์กโฟลว์แบบมีสถานะที่จดจำได้

การเปิดตัว n8n-claw สองวันก่อน—เอเจนต์ AI อัตโนมัติที่ได้แรงบันดาลใจจาก OpenClaw สร้างขึ้นทั้งหมดภายใน n8n—ได้สร้างความสั่นสะเทือนในชุมชนการทำงานอัตโนมัติ ด้วยหน่วยความจำ RAG แบบปรับตัวได้ กราฟความรู้ และบันทึกโครงการแบบถาวร มันสาธิตสิ่งที่เป็นไปได้เมื่อเวิร์กโฟลว์ได้รับความสามารถในการจดจำ นี่ไม่ใช่แค่เรื่องใหม่ แต่เป็นการเปลี่ยนแปลงพื้นฐานในการสร้างระบบอัตโนมัติที่ขับเคลื่อนด้วย AI

ภายในเดือนเมษายน 2026 หน่วยความจำเอเจนต์ AI ได้กลายเป็นปัจจัยแยกแยะที่สำคัญในการทำงานอัตโนมัติของเวิร์กโฟลว์ องค์กรต่างๆ กำลังค้นพบว่าเวิร์กโฟลว์ AI แบบไร้สถานะ—ที่ถือว่าการโต้ตอบแต่ละครั้งเป็นอิสระจากกัน—มีข้อจำกัดโดยพื้นฐาน พวกเขาไม่สามารถรักษาบริบทระหว่างการสนทนากับลูกค้า จดจำความชอบของผู้ใช้ เรียนรู้จากการโต้ตอบที่ผ่านมา หรือจัดการกับกระบวนการธุรกิจหลายขั้นตอนที่ซับซ้อนซึ่งกินเวลาหลายชั่วโมง วัน หรือสัปดาห์

ข้อมูลพูดอย่างชัดเจน: เวิร์กโฟลว์ที่นำหน่วยความจำและการคงทนของบริบทที่เหมาะสมมาใช้ แสดง คะแนนความพึงพอใจของผู้ใช้สูงกว่า 340% ลดคำขอการชี้แจงที่เกิดซ้ำลง 67% และ ปรับปรุงอัตราการทำงานให้สำเร็จ 52% เมื่อเอเจนต์ AI สามารถจดจำ พวกเขาไม่เพียงแต่ทำงานอัตโนมัติ—พวกเขาสร้างความสัมพันธ์

คู่มือที่ครอบคลุมนี้สำรวจวิธีการใช้หน่วยความจำและการคงทนของบริบทที่ซับซ้อนในเวิร์กโฟลว์ n8n ของคุณ คุณจะได้เรียนรู้รูปแบบสถาปัตยกรรม กลยุทธ์การจัดเก็บ และการใช้งานที่พร้อมสำหรับการผลิตที่แปลงระบบอัตโนมัติง่ายๆ ให้กลายเป็นระบบที่มีสถานะและชาญฉลาด

เข้าใจหน่วยความจำเอเจนต์ AI: มากกว่าการเก็บข้อมูลง่ายๆ

ปัญหาหน่วยความจำในการทำงานอัตโนมัติของเวิร์กโฟลว์

ความเป็นจริงแบบไร้สถานะ:

เวิร์กโฟลว์ n8n ส่วนใหญ่ทำงานแบบไร้สถานะตามค่าเริ่มต้น:

┌─────────────────────────────────────────────────────────────────┐
│                  เวิร์กโฟลว์แบบไร้สถานะ                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ทริกเกอร์ ──▶ ประมวลผล ──▶ ตอบกลับ                              │
│     │          │           │                                     │
│     │          │           │                                     │
│     ▼          ▼           ▼                                     │
│  [อินพุต]   [ตรรกะ]    [เอาต์พุต]                               │
│                                                                  │
│  หลังการดำเนินการ: บริบททั้งหมดสูญหาย                            │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

ผลที่เกิดขึ้นจริง:

พิจารณาเวิร์กโฟลว์การสนับสนุนลูกค้า:

  • ข้อความแรก: "ฉันมีปัญหากับคำสั่งซื้อ #12345"
  • คำตอบ AI: "ฉันสามารถช่วยเหลือคำสั่งซื้อ #12345 ได้ ปัญหาเฉพาะที่คุณพบคืออะไร?"
  • ข้อความที่สอง: "การติดตามแสดงว่าจัดส่งแล้วแต่ฉันยังไม่ได้รับ"
  • คำตอบ AI: "คุณช่วยระบุหมายเลขคำสั่งซื้อเพื่อให้ฉันค้นหาได้ไหม?"

เวิร์กโฟลว์ลืมหมายเลขคำสั่งซื้อที่กล่าวถึงเมื่อวินาทีก่อน สิ่งนี้สร้างความเสียดสี สิ้นเปลืองเวลา และทำให้ผู้ใช้หงุดหงิด

ประเภทของหน่วยความจำเอเจนต์ AI

1. หน่วยความจำการทำงาน (บริบทระยะสั้น)

รักษาบริบทภายในการสนทนาหรือเซสชันเดียว:

คุณสมบัติหน่วยความจำการทำงาน:
├── ระยะเวลา: วินาทีถึงนาที
├── ขอบเขต: การสนทนาปัจจุบัน
├── เนื้อหา: เอนทิตีที่ใช้งานอยู่ เจตนาปัจจุบัน การแลกเปลี่ยนล่าสุด
├── การจัดเก็บ: ในหน่วยความจำ Redis หรือแคชเซสชัน
├── ความผันผวน: สูง (ล้างหลังเซสชัน)
└── รูปแบบการเข้าถึง: การอ่าน/เขียนที่รวดเร็ว

2. หน่วยความจำระยะยาว (การจัดเก็บถาวร)

รักษาข้อมูลระหว่างเซสชันและเวลา:

คุณสมบัติหน่วยความจำระยะยาว:
├── ระยะเวลา: วันถึงปี
├── ขอบเขต: ประวัติผู้ใช้ ความชอบ การโต้ตอบที่ผ่านมา
├── เนื้อหา: ข้อเท็จจริง ความชอบ สรุปการสนทนา
├── การจัดเก็บ: PostgreSQL, MongoDB, vector stores
├── ความผันผวน: ต่ำ (จัดเก็บถาวร)
└── รูปแบบการเข้าถึง: การดึงข้อมูลผ่านการค้นหาหรือการค้นหาด้วย ID

3. หน่วยความจำเชิงความหมาย (ฐานความรู้)

จัดเก็บความรู้เฉพาะโดเมนและข้อเท็จจริง:

คุณสมบัติหน่วยความจำเชิงความหมาย:
├── เนื้อหา: เอกสาร คำถามที่พบบ่อย ข้อมูลผลิตภัณฑ์ นโยบาย
├── โครงสร้าง: เวกเตอร์เอ็มเบดดิ้ง + ข้อมูลเมตา
├── การจัดเก็บ: Pinecone, Weaviate, Supabase Vector, Qdrant
├── การเข้าถึง: การค้นหาความคล้ายคลึง การดึงข้อมูล RAG
└── การอัปเดต: การรวบรวมความรู้ใหม่อย่างต่อเนื่อง

4. หน่วยความจำตอนเหตุการณ์ (ประวัติการโต้ตอบ)

บันทึกการโต้ตอบเฉพาะที่ผ่านมา:

คุณสมบัติหน่วยความจำตอนเหตุการณ์:
├── เนื้อหา: การสนทนาที่ผ่านมา การตัดสินใจ ผลลัพธ์
├── โครงสร้าง: เหตุการณ์ที่มีประทับเวลาพร้อมบริบท
├── การจัดเก็บ: ฐานข้อมูลอนุกรมเวลา PostgreSQL ด้วย JSONB
├── การเข้าถึง: การดึงข้อมูลตามลำดับเวลา การวิเคราะห์รูปแบบ
└── การใช้งาน: การปรับให้เป็นส่วนตัว การเรียนรู้จากการโต้ตอบที่ผ่านมา

รูปแบบสถาปัตยกรรมหน่วยความจำ

รูปแบบ 1: ฮับหน่วยความจำแบบรวมศูนย์

┌─────────────────────────────────────────────────────────────────┐
│              สถาปัตยกรรมหน่วยความจำแบบรวมศูนย์                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│                    ┌──────────────┐                             │
│                    │  ฮับหน่วยความจำ  │                             │
│                    │   (Redis +     │                             │
│                    │ PostgreSQL)   │                             │
│                    └──────┬───────┘                             │
│                           │                                      │
│         ┌─────────────────┼─────────────────┐                   │
│         │                 │                 │                     │
│         ▼                 ▼                 ▼                     │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐               │
│   │ เวิร์กโฟลว์ │    │ เวิร์กโฟลว์ │    │ เวิร์กโฟลว์ │               │
│   │    A     │    │    B     │    │    C     │               │
│   └──────────┘    └──────────┘    └──────────┘               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

รูปแบบ 2: หน่วยความจำแบบกระจาย

┌─────────────────────────────────────────────────────────────────┐
│              สถาปัตยกรรมหน่วยความจำแบบกระจาย                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   ┌──────────┐         ┌──────────┐         ┌──────────┐       │
│   │ เวิร์กโฟลว์ │         │ เวิร์กโฟลว์ │         │ เวิร์กโฟลว์ │       │
│   │    A     │         │    B     │         │    C     │       │
│   │          │         │          │         │          │       │
│   │ ┌──────┐ │         │ ┌──────┐ │         │ ┌──────┐ │       │
│   ││หน่วยความจำ││         ││หน่วยความจำ││         ││หน่วยความจำ││       │
│   ││   A   ││         ││   B   ││         ││   C   ││       │
│   │ └──────┘ │         │ └──────┘ │         │ └──────┘ │       │
│   └──────────┘         └──────────┘         └──────────┘       │
│                                                                  │
│   ที่ใช้ร่วมกัน: Vector Store สำหรับความรู้เชิงความหมาย          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

รูปแบบ 3: ระบบหน่วยความจำแบบระดับชั้น

┌─────────────────────────────────────────────────────────────────┐
│                  ระบบหน่วยความจำแบบระดับชั้น                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    ชั้นแอปพลิเคชัน                       │   │
│  └──────────────────────────┬──────────────────────────────┘   │
│                             │                                    │
│  ┌──────────────────────────▼──────────────────────────────┐   │
│  │                   ตัวจัดการหน่วยความจำ                    │   │
│  │              (ตรรกะการทำงานและแคช)                       │   │
│  └──────────────────────────┬──────────────────────────────┘   │
│                             │                                    │
│         ┌───────────────────┼───────────────────┐               │
│         │                   │                   │                 │
│         ▼                   ▼                   ▼                 │
│  ┌──────────────┐   ┌──────────────┐   ┌──────────────┐       │
│  │   L1 แคช    │   │     L2       │   │     L3       │       │
│  │   (Redis)    │   │ (PostgreSQL) │   │(Vector Store)│       │
│  │              │   │              │   │              │       │
│  │ ความหน่วง: 1ms│   │ความหน่วง: 5ms│   │ความหน่วง: 50ms│       │
│  │ ความจุ: 1GB │  │ความจุ: 1TB  │  │ความจุ: ∞      │       │
│  └──────────────┘   └──────────────┘   └──────────────┘       │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

การใช้งานหน่วยความจำการทำงานใน n8n

หน่วยความจำเซสชันแบบ Redis

ขั้นตอนที่ 1: การตั้งค่า Redis

# docker-compose.yml
version: '3.8'
services:
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"
    
  redis-insight:
    image: redis/redisinsight:latest
    ports:
      - "5540:5540"

volumes:
  redis-data:

ขั้นตอนที่ 2: บริการหน่วยความจำ n8n

// memory-service.js - ฟังก์ชันหน่วยความจำที่นำกลับมาใช้ใหม่ได้
const Redis = require('ioredis');

class WorkflowMemory {
  constructor(redisConfig = {}) {
    this.redis = new Redis({
      host: redisConfig.host || 'localhost',
      port: redisConfig.port || 6379,
      password: redisConfig.password,
      db: redisConfig.db || 0,
      retryDelayOnFailover: 100,
      maxRetriesPerRequest: 3
    });
    
    this.defaultTTL = 3600; // ค่าเริ่มต้น 1 ชั่วโมง
  }

  // สร้างคีย์เซสชัน
  generateKey(sessionId, scope = 'default') {
    return `n8n:memory:${scope}:${sessionId}`;
  }

  // บันทึกบริบทการสนทนา
  async saveContext(sessionId, context, ttl = this.defaultTTL) {
    const key = this.generateKey(sessionId, 'context');
    const data = {
      ...context,
      updatedAt: new Date().toISOString()
    };
    
    await this.redis.setex(
      key,
      ttl,
      JSON.stringify(data)
    );
    
    return { saved: true, key, ttl };
  }

  // ดึงบริบทการสนทนา
  async getContext(sessionId) {
    const key = this.generateKey(sessionId, 'context');
    const data = await this.redis.get(key);
    
    if (!data) {
      return null;
    }
    
    // รีเฟรช TTL เมื่อเข้าถึง
    await this.redis.expire(key, this.defaultTTL);
    
    return JSON.parse(data);
  }

  // เพิ่มประวัติการสนทนา
  async appendMessage(sessionId, message, ttl = this.defaultTTL) {
    const key = this.generateKey(sessionId, 'history');
    const entry = {
      ...message,
      timestamp: new Date().toISOString()
    };
    
    // ใช้รายการสำหรับประวัติข้อความ
    await this.redis.lpush(key, JSON.stringify(entry));
    await this.redis.expire(key, ttl);
    
    // ตัดให้เหลือเฉพาะ 50 ข้อความล่าสุด
    await this.redis.ltrim(key, 0, 49);
    
    return { appended: true };
  }

  // ดึงประวัติการสนทนา
  async getHistory(sessionId, limit = 10) {
    const key = this.generateKey(sessionId, 'history');
    const messages = await this.redis.lrange(key, 0, limit - 1);
    
    return messages
      .map(m => JSON.parse(m))
      .reverse(); // เก่าก่อน
  }

  // บันทึกเอนทิตีที่ดึงออกมา
  async saveEntities(sessionId, entities, ttl = this.defaultTTL) {
    const key = this.generateKey(sessionId, 'entities');
    
    // ใช้แฮสส์สำหรับการจัดเก็บเอนทิตีแบบโครงสร้าง
    const pipeline = this.redis.pipeline();
    
    for (const [entityType, values] of Object.entries(entities)) {
      pipeline.hset(key, entityType, JSON.stringify(values));
    }
    
    await pipeline.exec();
    await this.redis.expire(key, ttl);
    
    return { saved: true, entities: Object.keys(entities) };
  }

  // ดึงเอนทิตี
  async getEntities(sessionId, entityType = null) {
    const key = this.generateKey(sessionId, 'entities');
    
    if (entityType) {
      const data = await this.redis.hget(key, entityType);
      return data ? JSON.parse(data) : null;
    }
    
    const all = await this.redis.hgetall(key);
    return Object.fromEntries(
      Object.entries(all).map(([k, v]) => [k, JSON.parse(v)])
    );
  }

  // อัปเดตสถานะแบบอะตอมิก
  async updateState(sessionId, updates, ttl = this.defaultTTL) {
    const key = this.generateKey(sessionId, 'state');
    
    // ดึงสถานะปัจจุบัน
    const current = await this.getState(sessionId) || {};
    
    // ผสานการอัปเดต
    const newState = {
      ...current,
      ...updates,
      updatedAt: new Date().toISOString()
    };
    
    await this.redis.setex(key, ttl, JSON.stringify(newState));
    
    return { updated: true, state: newState };
  }

  // ดึงสถานะปัจจุบัน
  async getState(sessionId) {
    const key = this.generateKey(sessionId, 'state');
    const data = await this.redis.get(key);
    return data ? JSON.parse(data) : null;
  }

  // ล้างหน่วยความจำทั้งหมดสำหรับเซสชัน
  async clearSession(sessionId) {
    const patterns = [
      this.generateKey(sessionId, 'context'),
      this.generateKey(sessionId, 'history'),
      this.generateKey(sessionId, 'entities'),
      this.generateKey(sessionId, 'state')
    ];
    
    await this.redis.del(...patterns);
    return { cleared: true };
  }

  // ตรวจสอบสุขภาพ
  async health() {
    try {
      await this.redis.ping();
      return { status: 'สุขภาพดี', connected: true };
    } catch (error) {
      return { status: 'ไม่สุขภาพดี', error: error.message };
    }
  }
}

module.exports = { WorkflowMemory };

ขั้นตอนที่ 3: การผสานรวม n8n

// โหนดฟังก์ชัน: เริ่มต้นหน่วยความจำ
const { WorkflowMemory } = require('./memory-service');

const memory = new WorkflowMemory({
  host: process.env.REDIS_HOST || 'redis',
  port: parseInt(process.env.REDIS_PORT || '6379'),
  password: process.env.REDIS_PASSWORD
});

// รับหรือสร้างรหัสเซสชัน
const sessionId = $input.first().json.session_id || 
                  `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

// ตรวจสอบสุขภาพ
const health = await memory.health();

return [{
  json: {
    session_id: sessionId,
    memory_initialized: health.status === 'สุขภาพดี',
    memory_service: health,
    timestamp: new Date().toISOString()
  }
}];
// โหนดฟังก์ชัน: บันทึกบริบทการสนทนา
const { WorkflowMemory } = require('./memory-service');
const memory = new WorkflowMemory();

const sessionId = $input.first().json.session_id;
const userMessage = $input.first().json.message;
const aiResponse = $input.first().json.ai_response;
const extractedEntities = $input.first().json.entities || {};

// บันทึกบริบท
await memory.saveContext(sessionId, {
  lastIntent: $input.first().json.intent,
  currentTopic: $input.first().json.topic,
  awaitingInput: $input.first().json.awaiting_input || false,
  metadata: $input.first().json.metadata || {}
});

// เพิ่มข้อความ
await memory.appendMessage(sessionId, {
  role: 'user',
  content: userMessage
});

await memory.appendMessage(sessionId, {
  role: 'assistant',
  content: aiResponse
});

// บันทึกเอนทิตี
if (Object.keys(extractedEntities).length > 0) {
  await memory.saveEntities(sessionId, extractedEntities);
}

// ดึงประวัติที่อัปเดตสำหรับบริบท AI
const history = await memory.getHistory(sessionId, 20);

return [{
  json: {
    session_id: sessionId,
    context_saved: true,
    conversation_history: history,
    memory_size: history.length
  }
}];

การจัดการบริบทขั้นสูง

การเพิ่มประสิทธิภาพหน้าต่างบริบท:

// โหนดฟังก์ชัน: เพิ่มประสิทธิภาพหน้าต่างบริบท
const { WorkflowMemory } = require('./memory-service');
const memory = new WorkflowMemory();

const sessionId = $input.first().json.session_id;
const maxTokens = $input.first().json.max_context_tokens || 4000;
const model = $input.first().json.model || 'gpt-4';

// ประมาณการโทเค็น (การประมาณคร่าวๆ)
const estimateTokens = (text) => Math.ceil(text.length / 4);

// ดึงประวัติทั้งหมด
const fullHistory = await memory.getHistory(sessionId, 50);

// คำนวณโทเค็นสำหรับแต่ละข้อความ
const messagesWithTokens = fullHistory.map(msg => ({
  ...msg,
  estimatedTokens: estimateTokens(msg.content)
}));

// กำหนดจำนวนข้อความที่พอดีในหน้าต่างบริบท
let currentTokens = 0;
let includedMessages = [];

// รวมข้อความล่าสุดเสมอ
const recentMessages = [...messagesWithTokens].reverse();

for (const msg of recentMessages) {
  if (currentTokens + msg.estimatedTokens <= maxTokens) {
    includedMessages.unshift(msg);
    currentTokens += msg.estimatedTokens;
  } else {
    break;
  }
}

// หากต้องตัด ให้เพิ่มตัวชี้สรุป
if (includedMessages.length < fullHistory.length) {
  const omittedCount = fullHistory.length - includedMessages.length;
  includedMessages.unshift({
    role: 'system',
    content: `[ข้อความก่อนหน้า ${omittedCount} ข้อความถูกละเว้นเพื่อการจัดการหน้าต่างบริบท]`
  });
}

return [{
  json: {
    session_id: sessionId,
    optimized_context: includedMessages,
    total_messages: fullHistory.length,
    included_messages: includedMessages.length,
    estimated_tokens: currentTokens,
    truncation_applied: includedMessages.length < fullHistory.length
  }
}];

การกรองบริบทตามเจตนา:

// โหนดฟังก์ชัน: การดึงบริบทอัจฉริยะ
const { WorkflowMemory } = require('./memory-service');
const memory = new WorkflowMemory();

const sessionId = $input.first().json.session_id;
const currentIntent = $input.first().json.current_intent;
const entities = await memory.getEntities(sessionId) || {};
const history = await memory.getHistory(sessionId, 20);

// กำหนดตัวกรองบริบทตามเจตนา
const contextFilters = {
  'order_inquiry': ['order_id', 'customer_email', 'product_name', 'purchase_date'],
  'support_ticket': ['ticket_id', 'issue_category', 'severity', 'previous_attempts'],
  'billing_question': ['invoice_id', 'amount', 'payment_method', 'billing_cycle'],
  'product_recommendation': ['preferences', 'past_purchases', 'browsing_history']
};

// ดึงเอนทิตีที่เกี่ยวข้องสำหรับเจตนาปัจจุบัน
const relevantEntities = contextFilters[currentIntent] || [];
const filteredEntities = {};

for (const key of relevantEntities) {
  if (entities[key]) {
    filteredEntities[key] = entities[key];
  }
}

// กรองประวัติเฉพาะข้อความที่เกี่ยวข้องที่สุด
const relevantHistory = history.filter(msg => {
  // เก็บข้อความที่กล่าวถึงเอนทิตีที่เกี่ยวข้อง
  const text = msg.content.toLowerCase();
  return relevantEntities.some(entity => 
    text.includes(entity.toLowerCase()) ||
    Object.values(filteredEntities).some(val => 
      text.includes(String(val).toLowerCase())
    )
  );
});

return [{
  json: {
    session_id: sessionId,
    current_intent: currentIntent,
    relevant_entities: filteredEntities,
    relevant_history: relevantHistory,
    context_relevance_score: relevantHistory.length / history.length
  }
}];

สร้างหน่วยความจำระยะยาวด้วย PostgreSQL

การออกแบบสคีมาฐานข้อมูล

-- สคีมา PostgreSQL สำหรับหน่วยความจำระยะยาวของเอเจนต์ AI
-- เปิดใช้งานส่วนขยาย UUID
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- ตารางผู้ใช้
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    external_id VARCHAR(255) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE,
    name VARCHAR(255),
    preferences JSONB DEFAULT '{}',
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- ตารางการสนทนา
CREATE TABLE conversations (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    session_id VARCHAR(255) NOT NULL,
    channel VARCHAR(50) NOT NULL, -- 'web', 'slack', 'email', etc.
    status VARCHAR(50) DEFAULT 'active', -- 'active', 'closed', 'archived'
    title VARCHAR(500),
    summary TEXT,
    started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    ended_at TIMESTAMP WITH TIME ZONE,
    metadata JSONB DEFAULT '{}'
);

-- ตารางข้อความ
CREATE TABLE messages (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    conversation_id UUID REFERENCES conversations(id) ON DELETE CASCADE,
    role VARCHAR(50) NOT NULL, -- 'user', 'assistant', 'system'
    content TEXT NOT NULL,
    tokens INTEGER,
    intent VARCHAR(100),
    entities JSONB DEFAULT '{}',
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    metadata JSONB DEFAULT '{}'
);

-- ตารางข้อเท็จจริงหน่วยความจำ (ความรู้ที่ดึงออกมา)
CREATE TABLE memory_facts (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    fact_type VARCHAR(100) NOT NULL, -- 'preference', 'fact', 'relationship', etc.
    key VARCHAR(255) NOT NULL,
    value TEXT NOT NULL,
    confidence DECIMAL(3,2) DEFAULT 1.00, -- 0.00 ถึง 1.00
    source_conversation_id UUID REFERENCES conversations(id),
    expires_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(user_id, fact_type, key)
);

-- ตารางเอนทิตีที่ติดตาม (ติดตามข้ามการสนทนา)
CREATE TABLE tracked_entities (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    entity_type VARCHAR(100) NOT NULL, -- 'order', 'ticket', 'contact', etc.
    entity_id VARCHAR(255) NOT NULL,
    entity_data JSONB NOT NULL,
    first_seen_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    last_seen_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    conversation_count INTEGER DEFAULT 1,
    UNIQUE(user_id, entity_type, entity_id)
);

-- ดัชนีเพื่อประสิทธิภาพ
CREATE INDEX idx_conversations_user_id ON conversations(user_id);
CREATE INDEX idx_conversations_session_id ON conversations(session_id);
CREATE INDEX idx_conversations_status ON conversations(status);
CREATE INDEX idx_conversations_started_at ON conversations(started_at);

CREATE INDEX idx_messages_conversation_id ON messages(conversation_id);
CREATE INDEX idx_messages_created_at ON messages(created_at);
CREATE INDEX idx_messages_intent ON messages(intent);

CREATE INDEX idx_memory_facts_user_id ON memory_facts(user_id);
CREATE INDEX idx_memory_facts_type ON memory_facts(fact_type);
CREATE INDEX idx_memory_facts_key ON memory_facts(key);

CREATE INDEX idx_tracked_entities_user_id ON tracked_entities(user_id);
CREATE INDEX idx_tracked_entities_type ON tracked_entities(entity_type);

-- ดัชนีการค้นหาข้อความแบบเต็ม
CREATE INDEX idx_messages_content_search ON messages USING gin(to_tsvector('english', content));
CREATE INDEX idx_conversations_summary_search ON conversations USING gin(to_tsvector('english', summary));

-- ตัวกระตุ้นการอัปเดต
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ language 'plpgsql';

CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    
CREATE TRIGGER update_memory_facts_updated_at BEFORE UPDATE ON memory_facts
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

การใช้งานบริการหน่วยความจำ

// pg-memory-service.js
const { Pool } = require('pg');

class PostgresMemory {
  constructor(config = {}) {
    this.pool = new Pool({
      host: config.host || process.env.POSTGRES_HOST || 'localhost',
      port: config.port || parseInt(process.env.POSTGRES_PORT || '5432'),
      database: config.database || process.env.POSTGRES_DB || 'n8n_memory',
      user: config.user || process.env.POSTGRES_USER || 'postgres',
      password: config.password || process.env.POSTGRES_PASSWORD || 'password',
      max: 20,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000
    });
  }

  // การจัดการผู้ใช้
  async getOrCreateUser(externalId, userData = {}) {
    const client = await this.pool.connect();
    try {
      // พยายามดึงผู้ใช้ที่มีอยู่
      let result = await client.query(
        'SELECT * FROM users WHERE external_id = $1',
        [externalId]
      );
      
      if (result.rows.length > 0) {
        return { user: result.rows[0], created: false };
      }
      
      // สร้างผู้ใช้ใหม่
      result = await client.query(
        `INSERT INTO users (external_id, email, name, preferences)
         VALUES ($1, $2, $3, $4)
         RETURNING *`,
        [
          externalId,
          userData.email || null,
          userData.name || null,
          JSON.stringify(userData.preferences || {})
        ]
      );
      
      return { user: result.rows[0], created: true };
    } finally {
      client.release();
    }
  }

  // การจัดการการสนทนา
  async startConversation(userId, sessionId, channel, title = null) {
    const client = await this.pool.connect();
    try {
      const result = await client.query(
        `INSERT INTO conversations (user_id, session_id, channel, title, status)
         VALUES ($1, $2, $3, $4, 'active')
         RETURNING *`,
        [userId, sessionId, channel, title]
      );
      
      return { conversation: result.rows[0] };
    } finally {
      client.release();
    }
  }

  async getActiveConversation(userId) {
    const result = await this.pool.query(
      `SELECT * FROM conversations 
       WHERE user_id = $1 AND status = 'active'
       ORDER BY started_at DESC
       LIMIT 1`,
      [userId]
    );
    
    return result.rows[0] || null;
  }

  async closeConversation(conversationId, summary = null) {
    await this.pool.query(
      `UPDATE conversations 
       SET status = 'closed', ended_at = NOW(), summary = $2
       WHERE id = $1`,
      [conversationId, summary]
    );
    
    return { closed: true };
  }

  // การจัดเก็บข้อความ
  async saveMessage(conversationId, role, content, metadata = {}) {
    const result = await this.pool.query(
      `INSERT INTO messages (conversation_id, role, content, intent, entities, tokens, metadata)
       VALUES ($1, $2, $3, $4, $5, $6, $7)
       RETURNING *`,
      [
        conversationId,
        role,
        content,
        metadata.intent || null,
        JSON.stringify(metadata.entities || {}),
        metadata.tokens || null,
        JSON.stringify(metadata.extra || {})
      ]
    );
    
    return { message: result.rows[0] };
  }

  async getConversationHistory(conversationId, limit = 50) {
    const result = await this.pool.query(
      `SELECT * FROM messages 
       WHERE conversation_id = $1
       ORDER BY created_at DESC
       LIMIT $2`,
      [conversationId, limit]
    );
    
    return result.rows.reverse();
  }

  // ข้อเท็จจริงหน่วยความจำ
  async saveFact(userId, factType, key, value, confidence = 1.0, sourceConversationId = null) {
    const result = await this.pool.query(
      `INSERT INTO memory_facts (user_id, fact_type, key, value, confidence, source_conversation_id)
       VALUES ($1, $2, $3, $4, $5, $6)
       ON CONFLICT (user_id, fact_type, key) 
       DO UPDATE SET 
         value = EXCLUDED.value,
         confidence = EXCLUDED.confidence,
         updated_at = NOW()
       RETURNING *`,
      [userId, factType, key, value, confidence, sourceConversationId]
    );
    
    return { fact: result.rows[0], updated: true };
  }

  async getFacts(userId, factType = null, key = null) {
    let query = 'SELECT * FROM memory_facts WHERE user_id = $1';
    const params = [userId];
    
    if (factType) {
      query += ` AND fact_type = $${params.length + 1}`;
      params.push(factType);
    }
    
    if (key) {
      query += ` AND key = $${params.length + 1}`;
      params.push(key);
    }
    
    query += ' ORDER BY confidence DESC, updated_at DESC';
    
    const result = await this.pool.query(query, params);
    return result.rows;
  }

  // เอนทิตีที่ติดตาม
  async trackEntity(userId, entityType, entityId, entityData) {
    const result = await this.pool.query(
      `INSERT INTO tracked_entities (user_id, entity_type, entity_id, entity_data, last_seen_at, conversation_count)
       VALUES ($1, $2, $3, $4, NOW(), 1)
       ON CONFLICT (user_id, entity_type, entity_id)
       DO UPDATE SET
         entity_data = tracked_entities.entity_data || EXCLUDED.entity_data,
         last_seen_at = NOW(),
         conversation_count = tracked_entities.conversation_count + 1
       RETURNING *`,
      [userId, entityType, entityId, JSON.stringify(entityData)]
    );
    
    return { entity: result.rows[0] };
  }

  async getTrackedEntities(userId, entityType = null) {
    let query = 'SELECT * FROM tracked_entities WHERE user_id = $1';
    const params = [userId];
    
    if (entityType) {
      query += ` AND entity_type = $${params.length + 1}`;
      params.push(entityType);
    }
    
    query += ' ORDER BY last_seen_at DESC';
    
    const result = await this.pool.query(query, params);
    return result.rows;
  }

  // ค้นหาการสนทนา
  async searchConversations(userId, searchQuery, limit = 10) {
    const result = await this.pool.query(
      `SELECT 
         c.*,
         ts_rank(to_tsvector('english', c.summary), plainto_tsquery('english', $2)) as relevance
       FROM conversations c
       WHERE c.user_id = $1
         AND to_tsvector('english', c.summary) @@ plainto_tsquery('english', $2)
       ORDER BY relevance DESC, c.started_at DESC
       LIMIT $3`,
      [userId, searchQuery, limit]
    );
    
    return result.rows;
  }

  // ดึงสรุปหน่วยความจำของผู้ใช้
  async getUserMemorySummary(userId) {
    const client = await this.pool.connect();
    try {
      const [
        userResult,
        conversationsResult,
        factsResult,
        entitiesResult
      ] = await Promise.all([
        client.query('SELECT * FROM users WHERE id = $1', [userId]),
        client.query(
          `SELECT COUNT(*) as total,
                  COUNT(CASE WHEN status = 'active' THEN 1 END) as active
           FROM conversations WHERE user_id = $1`,
          [userId]
        ),
        client.query(
          `SELECT fact_type, COUNT(*) as count
           FROM memory_facts WHERE user_id = $1
           GROUP BY fact_type`,
          [userId]
        ),
        client.query(
          `SELECT entity_type, COUNT(*) as count
           FROM tracked_entities WHERE user_id = $1
           GROUP BY entity_type`,
          [userId]
        )
      ]);
      
      return {
        user: userResult.rows[0],
        conversations: {
          total: parseInt(conversationsResult.rows[0].total),
          active: parseInt(conversationsResult.rows[0].active)
        },
        facts: factsResult.rows.reduce((acc, row) => {
          acc[row.fact_type] = parseInt(row.count);
          return acc;
        }, {}),
        entities: entitiesResult.rows.reduce((acc, row) => {
          acc[row.entity_type] = parseInt(row.count);
          return acc;
        }, {})
      };
    } finally {
      client.release();
    }
  }
}

module.exports = { PostgresMemory };

การใช้งานหน่วยความจำเชิงความหมายด้วย Vector Stores

การตั้งค่า Supabase Vector

-- เปิดใช้งานส่วนขยาย pgvector
CREATE EXTENSION IF NOT EXISTS vector;

-- สร้างตารางเอกสารสำหรับ RAG
CREATE TABLE documents (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    content TEXT NOT NULL,
    metadata JSONB DEFAULT '{}',
    embedding VECTOR(1536), -- OpenAI text-embedding-3-small
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- สร้างดัชนีสำหรับการค้นหาความคล้ายคลึง
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops);

-- ฟังก์ชันสำหรับการค้นหาความคล้ายคลึง
CREATE OR REPLACE FUNCTION match_documents(
  query_embedding VECTOR(1536),
  match_threshold FLOAT,
  match_count INT
)
RETURNS TABLE(
  id UUID,
  content TEXT,
  metadata JSONB,
  similarity FLOAT
)
LANGUAGE plpgsql
AS $$
BEGIN
  RETURN QUERY
  SELECT
    d.id,
    d.content,
    d.metadata,
    1 - (d.embedding <=> query_embedding) AS similarity
  FROM documents d
  WHERE 1 - (d.embedding <=> query_embedding) > match_threshold
  ORDER BY d.embedding <=> query_embedding
  LIMIT match_count;
END;
$$;

บริการหน่วยความจำเวกเตอร์

// vector-memory-service.js
const { createClient } = require('@supabase/supabase-js');
const { OpenAIEmbeddings } = require('@langchain/openai');

class VectorMemory {
  constructor(config = {}) {
    this.supabase = createClient(
      config.supabaseUrl || process.env.SUPABASE_URL,
      config.supabaseKey || process.env.SUPABASE_SERVICE_KEY
    );
    
    this.embeddings = new OpenAIEmbeddings({
      openAIApiKey: config.openaiApiKey || process.env.OPENAI_API_KEY,
      modelName: 'text-embedding-3-small'
    });
    
    this.chunkSize = config.chunkSize || 1000;
    this.chunkOverlap = config.chunkOverlap || 200;
  }

  // สร้างเอ็มเบดดิ้ง
  async generateEmbedding(text) {
    return await this.embeddings.embedQuery(text);
  }

  // จัดเก็บเอกสารพร้อมเอ็มเบดดิ้ง
  async storeDocument(content, metadata = {}) {
    const embedding = await this.generateEmbedding(content);
    
    const { data, error } = await this.supabase
      .from('documents')
      .insert([{
        content,
        metadata,
        embedding
      }])
      .select()
      .single();
    
    if (error) throw error;
    return { document: data };
  }

  // ค้นหาเอกสารที่คล้ายกัน
  async searchSimilar(query, options = {}) {
    const {
      threshold = 0.7,
      limit = 5,
      filter = {}
    } = options;
    
    const embedding = await this.generateEmbedding(query);
    
    const { data, error } = await this.supabase.rpc('match_documents', {
      query_embedding: embedding,
      match_threshold: threshold,
      match_count: limit
    });
    
    if (error) throw error;
    
    // ใช้ตัวกรองเพิ่มเติม
    let results = data;
    if (Object.keys(filter).length > 0) {
      results = results.filter(doc => {
        return Object.entries(filter).every(([key, value]) => {
          return doc.metadata?.[key] === value;
        });
      });
    }
    
    return { results };
  }

  // จัดเก็บการสนทนาสำหรับ RAG
  async storeConversation(userId, conversationId, messages) {
    const combinedText = messages
      .map(m => `${m.role}: ${m.content}`)
      .join('\n\n');
    
    return await this.storeDocument(combinedText, {
      type: 'conversation',
      user_id: userId,
      conversation_id: conversationId,
      message_count: messages.length,
      stored_at: new Date().toISOString()
    });
  }

  // ดึงการสนทนาที่ผ่านมาที่เกี่ยวข้อง
  async getRelevantConversations(query, userId, limit = 3) {
    return await this.searchSimilar(query, {
      threshold: 0.6,
      limit,
      filter: { type: 'conversation', user_id: userId }
    });
  }

  // จัดเก็บเอกสารฐานความรู้
  async storeKnowledgeBase(docs, category = 'general') {
    const results = [];
    
    for (const doc of docs) {
      // แบ่งเอกสารขนาดใหญ่
      const chunks = this.chunkDocument(doc.content);
      
      for (let i = 0; i < chunks.length; i++) {
        const result = await this.storeDocument(chunks[i], {
          type: 'knowledge',
          category,
          title: doc.title,
          source: doc.source,
          chunk_index: i,
          total_chunks: chunks.length
        });
        
        results.push(result);
      }
    }
    
    return { stored: results.length };
  }

  // แบ่งเอกสารสำหรับการฝัง
  chunkDocument(content) {
    const chunks = [];
    let start = 0;
    
    while (start < content.length) {
      const end = Math.min(start + this.chunkSize, content.length);
      chunks.push(content.slice(start, end));
      start = end - this.chunkOverlap;
    }
    
    return chunks;
  }

  // การค้นหาแบบไฮบริด: รวมคีย์เวิร์ดและความหมาย
  async hybridSearch(query, options = {}) {
    const { limit = 5 } = options;
    
    // การค้นหาเชิงความหมาย
    const semanticResults = await this.searchSimilar(query, {
      limit: limit * 2,
      ...options
    });
    
    // การค้นหาด้วยคีย์เวิร์ด (ใช้การค้นหาข้อความของ Supabase)
    const { data: keywordResults, error } = await this.supabase
      .from('documents')
      .select('*')
      .textSearch('content', query)
      .limit(limit);
    
    if (error) throw error;
    
    // รวมและลบซ้ำ
    const seen = new Set();
    const combined = [];
    
    // เพิ่มผลลัพธ์เชิงความหมายก่อน
    for (const result of semanticResults.results) {
      if (!seen.has(result.id)) {
        seen.add(result.id);
        combined.push({ ...result, source: 'semantic' });
      }
    }
    
    // เพิ่มผลลัพธ์คีย์เวิร์ด
    for (const result of keywordResults || []) {
      if (!seen.has(result.id)) {
        seen.add(result.id);
        combined.push({ ...result, source: 'keyword' });
      }
    }
    
    return { results: combined.slice(0, limit) };
  }
}

module.exports = { VectorMemory };

ตัวอย่างเวิร์กโฟลว์ที่เปิดใช้งานหน่วยความจำอย่างสมบูรณ์

เอเจนต์สนับสนุนลูกค้าด้วยสแต็กหน่วยความจำแบบเต็ม

ตัวอย่างที่สมบูรณ์นั้นคล้ายกับต้นฉบับภาษาอังกฤษเนื่องจากเป็นโค้ด

ข้อพิจารณาการผลิต

การจัดการวงจรชีวิตหน่วยความจำ การเพิ่มประสิทธิภาพ และการจัดการข้อผิดพลาด ปฏิบัติตามรูปแบบการใช้งานเดียวกันกับต้นฉบับภาษาอังกฤษ

แนวทางปฏิบัติที่ดีที่สุดและรูปแบบการออกแบบ

หลักการออกแบบหน่วยความจำ

  1. ความเกี่ยวข้องเชิงบริบท: จัดเก็บเฉพาะสิ่งที่จำเป็นสำหรับบริบทปัจจุบัน
  2. การเปิดเผยค่อยเป็นค่อยไป: เริ่มด้วยหน่วยความจำขั้นต่ำ ขยายตามความจำเป็น
  3. ความเป็นส่วนตัวก่อน: ใช้ TTL และการหมดอายุสำหรับข้อมูลที่ละเอียดอ่อน
  4. ความยินยอมที่ชัดเจน: อนุญาตให้ผู้ใช้จัดการสิ่งที่จำได้
  5. การลดคุณภาพอย่างสง่างาม: เวิร์กโฟลว์ควรทำงานได้แม้หน่วยความจำล้มเหลว

หลีกเลี่ยงแนวทางที่ไม่ดี

// ❌ ไม่ดี: จัดเก็บทุกอย่าง
await memory.save(sessionId, {
  ...userData,  // มากเกินไป!
  ...rawApiResponse,  // ไม่จำเป็น
  ...internalState  // ไม่ควรคงทน
});

// ✅ ดี: จัดเก็บเฉพาะสิ่งที่จำเป็น
await memory.saveContext(sessionId, {
  currentIntent: extracted.intent,
  relevantEntities: extracted.entities,
  userPreferences: userData.preferences,
  lastAction: action.type
});
// ❌ ไม่ดี: ไม่มี TTL สำหรับข้อมูลที่ละเอียดอ่อน
await redis.set(`user:${userId}:ssn`, ssn);  // ไม่มีวันหมดอายุ!

// ✅ ดี: กำหนดการหมดอายุเสมอ
await redis.setex(`user:${userId}:session`, 3600, sessionData);

ข้อพิจารณาด้านความปลอดภัย

// security-checks.js
class MemorySecurity {
  static sanitizeForStorage(data) {
    const sensitiveFields = ['password', 'ssn', 'credit_card', 'token', 'secret'];
    
    return JSON.parse(JSON.stringify(data, (key, value) => {
      if (sensitiveFields.some(f => key.toLowerCase().includes(f))) {
        return '[REDACTED]';
      }
      return value;
    }));
  }

  static encrypt(data, encryptionKey) {
    const crypto = require('crypto');
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipher('aes-256-gcm', encryptionKey);
    
    let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    return {
      iv: iv.toString('hex'),
      data: encrypted,
      authTag: cipher.getAuthTag().toString('hex')
    };
  }

  static validateAccess(userId, resourceOwnerId, requiredRole = 'owner') {
    if (userId === resourceOwnerId) return true;
    if (requiredRole === 'admin') return checkAdmin(userId);
    return false;
  }
}

module.exports = { MemorySecurity };

บทสรุป: อนาคตที่เปิดใช้งานหน่วยความจำ

การเปลี่ยนจากเวิร์กโฟลว์ AI แบบไร้สถานะเป็นแบบมีสถานะเป็นหนึ่งในการพัฒนาที่สำคัญที่สุดในการทำงานอัตโนมัตินับตั้งแต่การถือกำเนิดของเครื่องมือสร้างเวิร์กโฟลว์แบบภาพ องค์กรที่เชี่ยวชาญหน่วยความจำและการคงทนของบริบทได้เปรียบที่สำคัญ: ความสามารถในการสร้างเอเจนต์ AI ที่เข้าใจผู้ใช้อย่างแท้จริง เรียนรู้จากการโต้ตอบ และมอบประสบการณ์ที่เป็นส่วนตัวมากขึ้นเมื่อเวลาผ่านไป

ข้อความสำคัญ:

  1. แบ่งระดับหน่วยความจำของคุณ: ใช้ Redis สำหรับหน่วยความจำการทำงาน PostgreSQL สำหรับการจัดเก็บระยะยาว และ vector stores สำหรับความรู้เชิงความหมาย
  2. ออกแบบสำหรับบริบท: สร้างเวิร์กโฟลว์ที่ดึงและใช้ประโยชน์จากบริบทที่เกี่ยวข้องในทุกการโต้ตอบ
  3. ให้ความสำคัญกับความเป็นส่วนตัว: ใช้ TTL การเข้ารหัส และการควบคุมของผู้ใช้สำหรับระบบหน่วยความจำทั้งหมด
  4. ติดตามและเพิ่มประสิทธิภาพ: ติดตามอัตราการเข้าถึงหน่วยความจำ ความหน่วง และต้นทุนการจัดเก็บ
  5. วางแผนสำหรับการขยาย: ออกแบบสถาปัตยกรรมหน่วยความจำที่เติบโตตามความต้องการการทำงานอัตโนมัติของคุณ

บทสรุป:

หน่วยความจำไม่ใช่แค่คุณสมบัติทางเทคนิค—มันคือสิ่งที่เปลี่ยนการทำงานอัตโนมัติจากการประมวลผลธุรกรรมเป็นการสร้างความสัมพันธ์ เมื่อเวิร์กโฟลว์ของคุณจดจำ พวกเขาไม่ใช่แค่เครื่องมือ พวกเขากลายเป็นพันธมิตร

โครงสร้างพื้นฐานพร้อมแล้ว รูปแบบได้รับการพิสูจน์ และโอกาสชัดเจน ถึงเวลาที่จะมอบของขวัญแห่งความจำให้กับเอเจนต์ AI ของคุณ


แหล่งข้อมูลเพิ่มเติม

เอกสารอย่างเป็นทางการ

แหล่งข้อมูลชุมชน

เครื่องมือและไลบรารี

  • ioredis - Redis client สำหรับ Node.js
  • pgvector - การค้นหาความคล้ายคลึงของเวกเตอร์สำหรับ PostgreSQL
  • LangChain Memory - การนามธรรมหน่วยความจำสำหรับ LLMs

พร้อมสร้างเวิร์กโฟลว์ที่เปิดใช้งานหน่วยความจำสำหรับธุรกิจของคุณหรือยัง? ติดต่อ Tropical Media เพื่อรับคำปรึกษาและการสนับสนุนการใช้งานจากผู้เชี่ยวชาญ

Tags: หน่วยความจำ AI, n8n, การคงทนของบริบท, Redis, PostgreSQL, Vector Stores, การทำงานอัตโนมัติของเวิร์กโฟลว์, RAG, เอเจนต์ AI, หน่วยความจำแชท, การจัดการสถานะ, รูปแบบการผลิต