Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 5 — Dựng bộ nhớ agent với pgvector từng bước

Đã đọc 0/9 mục · còn khoảng 3 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Ta sẽ xây một bộ nhớ episodic hoàn chỉnh cho agent chăm sóc khách hàng bằng pgvector — lựa chọn tuyệt vời khi bạn đã có Postgres và muốn join memory với dữ liệu người dùng.

Bước 1 — Bật extension và tạo bảng

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE agent_memory (
  id          bigserial PRIMARY KEY,
  user_id     int NOT NULL,
  content     text NOT NULL,          -- text gốc, BẮT BUỘC lưu
  embedding   vector(1536) NOT NULL,  -- khớp model text-embedding-3-small
  kind        text DEFAULT 'episodic',
  created_at  timestamptz DEFAULT now()
);

Lưu ý: content chính là payload để đưa vào prompt sau này. Đừng chỉ lưu vector.

Bước 2 — Tạo index

-- Index vector cho tìm kiếm ngữ nghĩa
CREATE INDEX ON agent_memory
  USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);

-- Index phụ cho filter theo user (bài 6 sẽ nói kỹ)
CREATE INDEX ON agent_memory (user_id);

Bước 3 — Ghi ký ức (write path)

import psycopg2

def remember(user_id, text):
    vec = embed(text)                       # list 1536 float
    cur.execute(
        "INSERT INTO agent_memory (user_id, content, embedding) "
        "VALUES (%s, %s, %s)",
        (user_id, text, vec)                # psycopg2 + pgvector adapter
    )
    conn.commit()

remember(42, "Khách phàn nàn giao hàng trễ 3 ngày, đã hoàn 50k voucher")

Bước 4 — Truy hồi ký ức liên quan (read path)

Đây là trái tim: dùng toán tử <=> (cosine distance) của pgvector, kết hợp filter user_id.

SQL = (
    "SELECT content, 1 - (embedding <=> %s) AS score "
    "FROM agent_memory "
    "WHERE user_id = %s "
    "ORDER BY embedding <=> %s "   # gần nhất trước
    "LIMIT %s"
)

def recall(user_id, query, k=5):
    qvec = embed(query)
    cur.execute(SQL, (qvec, user_id, qvec, k))
    return cur.fetchall()

hits = recall(42, "đơn hàng của tôi bị sao vậy")
# -> [("Khách phàn nàn giao hàng trễ...", 0.83), ...]

<=> là cosine distance (0 = trùng khớp). Ta đổi thành score = 1 - distance cho dễ đọc.

Bước 5 — Ghép vào prompt (augment)

def build_prompt(user_id, question):
    memories = recall(user_id, question, k=5)
    context = "\n".join(f"- {m[0]}" for m in memories)
    return (
        "Ký ức liên quan về khách hàng:\n" + context +
        f"\n\nCâu hỏi hiện tại: {question}\n"
        "Trả lời dựa trên ký ức trên, nếu không có thì nói chưa có thông tin."
    )

Bước 6 — Ngưỡng và write-back

Đừng nhét mọi mẩu tìm được. Đặt ngưỡng score (ví dụ chỉ lấy score > 0.75) để tránh nhồi ký ức không liên quan. Sau mỗi lượt, lưu lại tóm tắt đáng nhớ:

memories = [m for m in recall(uid, q) if m[1] > 0.75]
# ... sau khi trả lời:
remember(uid, summarize(conversation))  # write-back

Checklist triển khai pgvector

  • [ ] Extension vector đã bật.
  • [ ] vector(1536) khớp đúng model đang dùng.
  • [ ] Có index HNSW cho embedding.
  • [ ] Có index cho các field filter (user_id).
  • [ ] Luôn lưu content (text gốc).
  • [ ] Có ngưỡng score khi retrieve.
  • [ ] Có cơ chế write-back.

Sai lầm thường gặp

  • Quên CREATE INDEX HNSW → mọi truy vấn là brute-force, ban đầu chạy được nhưng chậm dần khi bảng lớn.
  • Không filter user_id → agent trả về ký ức của khách khác (rò rỉ dữ liệu nghiêm trọng!).
  • Đưa vector vào SQL sai định dạng — cần adapter pgvector cho psycopg2 hoặc truyền dạng chuỗi '[...]'.
  • Lấy top-k không ngưỡng → nhồi ký ức rác vào prompt khi không có mẩu nào thực sự liên quan.
  • VACUUM/ANALYZE bị bỏ quên trên bảng ghi nhiều — Postgres cần thống kê cập nhật để chọn plan đúng.
Bài sau: cùng bài toán, nhưng khai thác metadata filtering để lọc đúng ký ức của đúng người.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi