แสดงภาพหน่วยความจำของผู้ช่วย AI ด้วย Gemini และ pgvector ของ Cloud SQL

1. บทนำ

ใน Codelab นี้ คุณจะได้เรียนรู้วิธีสร้าง Living Memory Demo ซึ่งเป็นผู้ช่วยที่ขับเคลื่อนด้วย AI ที่ติดตาม "ความทรงจำ" ของการสนทนาเพื่อมอบประสบการณ์การใช้งานที่ปรับเปลี่ยนในแบบของคุณ

อินเทอร์เฟซผู้ใช้ของ Living Memory

แอปพลิเคชันใช้ Gemini สำหรับความเข้าใจภาษาธรรมชาติ และ Cloud SQL สำหรับ PostgreSQL ที่มีส่วนขยาย pgvector เพื่อจัดเก็บและดึงข้อมูลความทรงจำเหล่านี้ตามความคล้ายคลึงเชิงความหมาย

Codelab นี้เหมาะสำหรับนักพัฒนาซอฟต์แวร์ทุกระดับทักษะที่สนใจ AI และฐานข้อมูล และควรใช้เวลาประมาณ 60 นาทีจึงจะเสร็จสมบูรณ์ ทรัพยากรที่สร้างขึ้นควรมีค่าใช้จ่ายไม่เกิน $5

สิ่งที่คุณจะได้ทำ

  • วิธีตั้งค่าอินสแตนซ์ Cloud SQL สำหรับ PostgreSQL ที่รองรับ pgvector
  • วิธีใช้ Gemini เพื่อดึงข้อมูล "ความทรงจำ" จากข้อความของผู้ใช้แบบโต้ตอบ
  • วิธีค้นหาเวกเตอร์ใน PostgreSQL เพื่อดึงข้อมูลบริบทที่เกี่ยวข้องสำหรับคำตอบของ AI

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

สิ่งที่คุณต้องมี

  • โปรเจ็กต์ Google Cloud ที่เปิดใช้การเรียกเก็บเงิน
  • ความรู้พื้นฐานเกี่ยวกับบรรทัดคำสั่งและ Node.js

2. ก่อนเริ่มต้น

การตั้งค่าโปรเจ็กต์

สร้างโปรเจ็กต์ Google Cloud

  1. ใน Google Cloud Console บนหน้าตัวเลือกโปรเจ็กต์ ให้เลือกหรือสร้างโปรเจ็กต์ Google Cloud
  2. ตรวจสอบว่าได้เปิดใช้การเรียกเก็บเงินสำหรับโปรเจ็กต์ที่อยู่ในระบบคลาวด์แล้ว ดูวิธีตรวจสอบว่าได้เปิดใช้การเรียกเก็บเงินในโปรเจ็กต์แล้ว

เริ่มต้น Cloud Shell

Cloud Shell คือสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานใน Google Cloud ซึ่งโหลดเครื่องมือที่จำเป็นไว้ล่วงหน้า

  1. คลิกเปิดใช้งาน Cloud Shell ที่ด้านบนของคอนโซล Google Cloud
  2. เมื่อเชื่อมต่อกับ Cloud Shell แล้ว ให้ยืนยันการตรวจสอบสิทธิ์โดยทำดังนี้
    gcloud auth list
    
  3. ยืนยันว่าได้กำหนดค่าโปรเจ็กต์แล้ว
    gcloud config get project
    
  4. หากโปรเจ็กต์ไม่ได้ตั้งค่าตามที่คาดไว้ ให้ตั้งค่าดังนี้
    export PROJECT_ID=<YOUR_PROJECT_ID>
    gcloud config set project $PROJECT_ID
    

เปิดใช้ API

เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อเปิดใช้ API ที่จำเป็น

gcloud services enable sqladmin.googleapis.com \
                       aiplatform.googleapis.com

3. โคลนที่เก็บการสาธิต

ตอนนี้ให้รับโค้ดสำหรับการสาธิต Living Memory

  1. โคลนที่เก็บไปยังสภาพแวดล้อมของ Shell ใน Cloud Shell โดยทำดังนี้
    git clone https://github.com/GoogleCloudPlatform/devrel-demos.git
    cd devrel-demos/codelabs/visual-memory-postgres-demo
    
  2. ติดตั้งการอ้างอิงโดยทำดังนี้
    npm install
    

4. สร้างและกำหนดค่าฐานข้อมูล Cloud SQL

ในส่วนนี้ คุณจะได้สร้างอินสแตนซ์ Cloud SQL เริ่มต้นฐานข้อมูล และตั้งค่าสคีมา

  1. แอปพลิเคชันใช้ตัวแปรสภาพแวดล้อมสำหรับการกำหนดค่า เรียกใช้บล็อกต่อไปนี้ในเทอร์มินัล Cloud Shell เพื่อตั้งค่าตัวแปรที่จำเป็นสำหรับเซสชันนี้
    export REGION="us-central1"
    export INSTANCE_NAME="living-memory-db"
    export DB_HOST=127.0.0.1
    export DB_PORT=5432
    export DB_USER=memory_app
    export DB_PASS=memory_app_password
    export DB_NAME=living_memory
    export PGPASSWORD=$DB_PASS
    
  2. สร้างอินสแตนซ์ ขั้นตอนนี้มักใช้เวลา 5-10 นาที
    gcloud sql instances create $INSTANCE_NAME \
        --database-version=POSTGRES_16 \
        --cpu=1 \
        --memory=3840MB \
        --region=$REGION \
        --root-password=$DB_PASS \
        --edition=ENTERPRISE
    
    ขณะที่ระบบกำลังสร้างอินสแตนซ์ ให้ใช้เวลาสักครู่เพื่อทำความเข้าใจสคีมาฐานข้อมูลที่คุณจะใช้ สคริปต์จะเปิดใช้ส่วนขยาย vector และสร้างตารางหลายตารางเพื่อรองรับแอปพลิเคชัน:แผนภาพสคีมาหน่วยความจำภาพ
    • users, conversations, messages: ตารางมาตรฐานสำหรับจัดเก็บโปรไฟล์ผู้ใช้และประวัติการสนทนา
    • memories: ตารางหลักสำหรับการสร้างผลลัพธ์ที่เสริมด้วยการดึงข้อมูล (Retrieval-Augmented Generation หรือ RAG) แต่ละแถวแสดงถึงข้อมูลที่ดึงออกมาจากการสนทนา (เช่น "ผู้ใช้ชอบเดินป่า") โดยจะจัดเก็บข้อมูลต่อไปนี้
      • content: ข้อความของความทรงจำ
      • memory_type: ประเภทความทรงจำ (FACT, PREF หรือ IMPLICIT)
      • embedding: คอลัมน์ vector ขนาด 768 มิติที่มีการแสดงความหมายของเนื้อหา ซึ่งสร้างโดย Gemini
    • pgvector Index: ระบบจะสร้างดัชนี HNSW (Hierarchical Navigable Small World) ในคอลัมน์ embedding ซึ่งมีความสำคัญอย่างยิ่งต่อการเพิ่มประสิทธิภาพการค้นหา k-Nearest Neighbor (k-NN) ทำให้ pgvector ค้นหาความทรงจำที่คล้ายคลึงกันมากที่สุดในเชิงความหมายได้อย่างรวดเร็วโดยใช้ตัวดำเนินการระยะทางโคไซน์ (<=>)
  3. สร้างฐานข้อมูล
    gcloud sql databases create $DB_NAME --instance=$INSTANCE_NAME
    
  4. สร้างผู้ใช้แอปพลิเคชัน
    gcloud sql users create $DB_USER --instance=$INSTANCE_NAME --password=$DB_PASS
    
  5. เริ่มพร็อกซีการตรวจสอบสิทธิ์ Cloud SQL พร็อกซีจะให้สิทธิ์เข้าถึงอินสแตนซ์อย่างปลอดภัยโดยไม่จำเป็นต้องกำหนดค่าการอนุญาตพิเศษตาม IP
    (cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:us-central1:living-memory-db &) && sleep 2 && echo ""
    
    คุณควรเห็นข้อความ เช่น The proxy has started successfully and is ready for new connections!
  6. ใช้ schema.sql เพื่อเปิดใช้ส่วนขยาย vector และสร้างตารางที่จำเป็น เนื่องจากพร็อกซีทำงานอยู่ ตอนนี้คุณจึงเชื่อมต่อกับอินสแตนซ์ได้ที่ 127.0.0.1
    psql -h 127.0.0.1 -U $DB_USER -d $DB_NAME < schema.sql
    
  7. ตรวจสอบว่าสร้างสคีมาสำเร็จ
    psql -h 127.0.0.1 -U $DB_USER -d $DB_NAME -c "\dt"
    
    คุณควรเห็นเอาต์พุตที่แสดงตาราง conversations, memories, messages และ users
                      List of relations
    Schema |     Name      | Type  |   Owner    
    --------+---------------+-------+------------
    public | conversations | table | memory_app
    public | memories      | table | memory_app
    public | messages      | table | memory_app
    public | queries_log   | table | memory_app
    public | users         | table | memory_app
    (5 rows)
    

5. ทำความเข้าใจการดึงข้อมูลเชิงความหมายด้วย pgvector

ในส่วนนี้ คุณจะได้ดูวิธีที่แอปพลิเคชันดึงข้อมูลบริบทที่เกี่ยวข้องสำหรับ AI ก่อนที่จะสร้างคำตอบ ข้อมูลโค้ดต่อไปนี้จาก server.js แสดงโค้ดที่รับผิดชอบในปลายทาง /api/chat

// Retrieve Similar Memories for Context (Using pgvector)
const promptEmbeddingRes = await ai.models.embedContent({
  model: 'gemini-embedding-001',
  contents: message,
  config: { outputDimensionality: 768 },
});

const promptEmbedding = promptEmbeddingRes.embeddings[0].values;
const embeddingStr = `[${promptEmbedding.join(',')}]`;

// Query DB for top 5 closest memories
const relevantMemories = await pool.query(
  `SELECT id, content, memory_type, category 
   FROM memories 
   WHERE user_id = $1 
   ORDER BY embedding <=> $2::vector 
   LIMIT 5`,
  [userId, embeddingStr]
);

วิธีการทำงาน

  • Gen AI (การฝัง): แอปพลิเคชันจะรับข้อความขาเข้าของผู้ใช้และใช้โมเดล gemini-embedding-001 เพื่อแปลงข้อความเป็นเวกเตอร์ขนาด 768 มิติ เวกเตอร์นี้แสดงถึงความหมายเชิงความหมายของข้อความ
  • Cloud SQL (pgvector): แอปพลิเคชันจะส่งเวกเตอร์นั้นไปยัง Cloud SQL Cloud SQL จะใช้ตัวดำเนินการ <=> (ระยะทางโคไซน์) ที่ส่วนขยาย pgvector มีให้เพื่อค้นหาความทรงจำ 5 รายการที่คล้ายคลึงกับพรอมต์มากที่สุดในเชิงความหมาย
  • ผลลัพธ์: นี่คือการสร้างผลลัพธ์ที่เสริมด้วยการดึงข้อมูล (Retrieval-Augmented Generation หรือ RAG) AI จะเข้าถึงความทรงจำที่เฉพาะเจาะจงและเกี่ยวข้องจากฐานข้อมูลเพื่อปรับเปลี่ยนคำตอบในแบบของคุณ โดยไม่จำเป็นต้องโหลดประวัติทั้งหมด

6. ทำความเข้าใจการดึงข้อมูลความทรงจำ

จากนั้นดูวิธีที่แอปพลิเคชันเรียนรู้จากการสนทนา ข้อมูลโค้ดต่อไปนี้มาจากฟังก์ชัน extractMemoriesAsync ใน server.js

// MEMORY EXTRACTION LOGIC
async function extractMemoriesAsync(userMessage, userId, messageId) {
  const extractionPrompt = `
    Analyze the following user message. A memory profile is being built for this user.
    Extract ANY explicit facts (Facts), preferences (Pref), or implicit behavioral traits/styles (Implicit).
    Return the result as a raw JSON array of objects (NO Markdown blocks, just the JSON array).
    Format: [{"content": "string fact/sentence", "type": "FACT|PREF|IMPLICIT", "category": "General|Travel|Hobby|Persona"}]
    If nothing is found, return [].
    Message: "${userMessage}"
    `;

  const result = await ai.models.generateContent({
    model: 'gemini-2.5-flash',
    contents: extractionPrompt
  });
  let rawJson = result.text.replace(/^```json/g, '').replace(/```$/g, '').trim();

  let extracted;
  try {
    extracted = JSON.parse(rawJson);
  } catch (e) {
    console.warn("Could not parse extracted JSON:", rawJson);
    return;
  }

  if (Array.isArray(extracted) && extracted.length > 0) {
    // Compute embeddings and save each to the DB
    for (const memory of extracted) {
      const embedRes = await ai.models.embedContent({
        model: 'gemini-embedding-001',
        contents: memory.content,
        config: { outputDimensionality: 768 },
      });
      const vectorData = `[${embedRes.embeddings[0].values.join(',')}]`;

      await pool.query(
        `INSERT INTO memories (user_id, content, memory_type, category, embedding, source_message_id)
                 VALUES ($1, $2, $3, $4, $5, $6)`,
        [userId, memory.content, memory.type.toUpperCase(), memory.category, vectorData, messageId]
      );
      console.log(`Saved new memory: ${memory.content}`);
    }
  }
}

วิธีการทำงาน

  • Gen AI (เอาต์พุตแบบมีโครงสร้าง): แอปพลิเคชันใช้โมเดล gemini-2.5-flash ที่รวดเร็วเป็นพิเศษเพื่อวิเคราะห์ข้อความของผู้ใช้และดึงข้อมูลและความชอบแบบมีโครงสร้างออกมาเป็นอาร์เรย์ JSON
  • Cloud SQL (พื้นที่เก็บข้อมูลแบบไฮบริด): หลังจากสร้างการฝังสำหรับข้อมูลใหม่เหล่านี้แล้ว ระบบจะจัดเก็บข้อมูลไว้ใน Cloud SQL โปรดทราบว่าข้อมูลเชิงสัมพันธ์มาตรฐาน (รหัสผู้ใช้ เนื้อหาข้อความ หมวดหมู่) จะจัดเก็บไว้ข้างๆ ข้อมูลเวกเตอร์แบบหลายมิติในแถวเดียว
  • ผลลัพธ์: แอปจะสร้างโปรไฟล์ความทรงจำที่อัปเดตตัวเองแบบเรียลไทม์ โดยใช้ประโยชน์จากทั้งความสามารถในการวิเคราะห์ของ Gemini และความสามารถในการจัดเก็บข้อมูลของ Cloud SQL

7. เรียกใช้แอปพลิเคชันแชท

  1. ป้อนข้อมูลผู้ใช้ตัวอย่าง 2-3 รายการลงในฐานข้อมูล
    npm run seed
    
    หน่วยความจำเริ่มต้น
  2. จากนั้นเรียกใช้เซิร์ฟเวอร์
    node server.js
    
  3. ใน Cloud Shell ให้คลิกการแสดงตัวอย่างเว็บ ที่ด้านขวาบนของแถบเครื่องมือเทอร์มินัล แล้วเลือกเปลี่ยนพอร์ต ป้อน 3000 เป็นหมายเลขพอร์ต แล้วคลิกเปลี่ยนและแสดงตัวอย่าง

โต้ตอบกับผู้ช่วย

เมื่อแอปพลิเคชันเปิดขึ้นในเบราว์เซอร์ คุณจะเห็นอินเทอร์เฟซการแชทของ Living Memory ทางด้านขวา AI Cortex Data Visualizer จะแสดงความทรงจำเป็นโหนดในพื้นที่เวกเตอร์ โดยมีการเข้ารหัสสีตามประเภท (ข้อมูล ความชอบ ลักษณะโดยนัย) ข้อความในโหนดความทรงจำอาจมีขนาดเล็ก ทั้งนี้ขึ้นอยู่กับความละเอียดหน้าจอของคุณ ให้ใช้เมาส์หรือแทร็กแพดเพื่อซูมและเลื่อนดูใกล้ๆ

อินเทอร์เฟซผู้ใช้ของ Living Memory

ค้นหาความทรงจำที่มีอยู่

สคริปต์ seed ที่คุณเรียกใช้ก่อนหน้านี้ได้สร้างผู้ใช้ตัวอย่าง 2 รายการที่มีความทรงจำที่ป้อนไว้ล่วงหน้า

  1. เลือกผู้ใช้จากเมนูแบบเลื่อนลงของผู้ใช้ที่ด้านซ้ายบน
  2. ใช้ปุ่มแชทด่วนปุ่มใดปุ่มหนึ่งหรือพิมพ์ Give me restaurant recommendations in New York City ลงในอินพุตแชท แล้วกดส่ง
  3. เมื่อผู้ช่วยตอบกลับ คุณสามารถคลิกข้อความของผู้ช่วยเพื่อดูว่าผู้ช่วยใช้ความทรงจำใด ความทรงจำเหล่านั้นจะไฮไลต์เป็นสีเขียว และคุณสามารถซูมดูความทรงจำเหล่านั้นและดูว่าความทรงจำเหล่านั้นช่วยสร้างคำตอบได้อย่างไร

สร้างผู้ใช้ใหม่

ตอนนี้มาสร้างผู้ใช้ใหม่กัน

  1. คลิกปุ่ม + ข้างเมนูแบบเลื่อนลงของผู้ใช้เพื่อเริ่มเซสชันการแชทใหม่
  2. ใช้ชื่อและคำอธิบายที่สร้างขึ้นหรือแก้ไขเพื่ออธิบายตัวเอง
  3. คลิกสร้าง แล้วดูแอปพลิเคชันเริ่มดึงข้อมูลความทรงจำ หลังจากผ่านไปประมาณ 30 วินาที คุณควรเห็นโหนดใหม่ปรากฏใน Visualizer ทางด้านขวา โหนดเหล่านี้แสดงถึงข้อมูลและความชอบที่ Gemini ดึงออกมาจากข้อความของคุณและจัดเก็บไว้ในฐานข้อมูล Cloud SQL
  4. ถามคำถามต่อเนื่อง เช่น What food do I like? เพื่อดูว่าผู้ช่วยใช้ความทรงจำที่เพิ่งได้รับมาในการสนทนาหรือไม่

8. ล้างข้อมูล

หากต้องการหลีกเลี่ยงการเรียกเก็บเงินอย่างต่อเนื่องจากบัญชี Google Cloud สำหรับทรัพยากรที่ใช้ใน Codelab นี้ คุณควรลบทรัพยากรที่สร้างขึ้น

  1. ลบอินสแตนซ์ Cloud SQL โดยทำดังนี้
    gcloud sql instances delete $INSTANCE_NAME --quiet
    
  2. นำที่เก็บการสาธิตออกโดยทำดังนี้
    rm -rf ~/devrel-demos
    

9. ขอแสดงความยินดี

คุณสร้างและติดตั้งใช้งานผู้ช่วย AI "Living Memory" สำเร็จแล้ว

สิ่งที่คุณได้เรียนรู้

  • วิธีใช้ Cloud SQL pgvector สำหรับการค้นหาเชิงความหมาย
  • วิธีใช้ Gemini สำหรับการดึงข้อมูลความทรงจำแบบไดนามิก

ขั้นตอนถัดไป

สนุกกับการสร้างด้วย Living Memory