SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-21

Phân loại bộ nhớ cho AI Agent và chi phí vận hành

Tìm hiểu chi phí lưu trữ và tái tạo cho semantic, episodic và procedural memory của AI agent. Bảng so sánh thực tế về tài nguyên VPS khi triển khai vector store cho hệ thống.

Ba loại bộ nhớ của agent

Bộ nhớ của agent được chia làm ba loại và mỗi loại ảnh hưởng khác nhau đến phần cứng bạn phải chi trả: bộ nhớ ngữ nghĩa (semantic memory) lưu trữ các sự kiện, bộ nhớ tình tiết (episodic memory) lưu trữ những gì đã xảy ra, và bộ nhớ thủ tục (procedural memory) lưu trữ cách thực hiện một công việc. Bảng dưới đây định nghĩa từng loại kèm theo một ví dụ trên máy chủ. Mọi nội dung sau bảng này là phần thường bị bỏ qua, đó là chi phí lưu trữ và chi phí tái tạo của từng loại.

ChartThe three agent memory types, with one server example of each
The data behind this chart
[
  {
    "label": "Semantic",
    "what_it_holds": "Facts the agent should treat as currently true",
    "server_example": "The database listens on 10.8.0.4:5432 and the nightly dump runs at 03:15 UTC"
  },
  {
    "label": "Episodic",
    "what_it_holds": "A record of one past event or session",
    "server_example": "On 2026-08-11 the deploy failed because the disk was full, and rotating logs fixed it"
  },
  {
    "label": "Procedural",
    "what_it_holds": "How to carry out a task, as steps the agent can run",
    "server_example": "The restore runbook: stop the service, load the dump, run migrations, start the service"
  }
]

Các tên gọi này được mượn từ tâm lý học con người nhưng chỉ mang tính tương đối. Việc phân chia này hữu ích vì một lý do thực tế: ba loại bộ nhớ có kích thước và quy trình khôi phục khác nhau, nên việc gộp tất cả vào một vector store sẽ làm giảm hiệu năng của từng loại.

Semantic memory có dung lượng nhỏ, và bạn sẽ muốn tự tay chỉnh sửa nó

Vài trăm sự kiện về các server của chính bạn chỉ chiếm vài chục kilobyte văn bản. Lưu trữ không phải là vấn đề ở đây. Việc hiệu chỉnh mới là vấn đề. Một sự kiện sai trong semantic memory sẽ khiến mọi câu trả lời sau đó của agent đều sai, vì vậy kho lưu trữ phải cho phép bạn tìm một sự kiện theo tên, thay đổi nó và đảm bảo giá trị cũ đã bị xóa bỏ.

Điều đó dẫn đến việc sử dụng keyed store: một bảng Postgres với primary key, hoặc một thư mục chứa các file markdown nhỏ trong git. Cả hai đều cho phép bạn chạy một truy vấn, xem giá trị và chỉnh sửa nó tại chỗ. Similarity search không làm được điều này, vì bạn truy xuất dựa trên sự tương đồng thay vì theo khóa. "Thay đổi cổng database" trở thành "tìm mọi chunk có nhắc đến cổng database", và bạn không thể chứng minh mình đã tìm thấy tất cả. Hãy giữ các sự kiện ở dạng có khóa (keyed). Bạn cũng có thể nhúng (embed) chúng nếu muốn truy xuất lỏng lẻo, nhưng hãy coi bản sao có khóa là nguồn sự thật duy nhất.

Các sự kiện cũ (stale facts) không tự báo hiệu cho bạn biết. Cổng đã đổi nhưng dòng dữ liệu vẫn còn đó, nên agent vẫn tiếp tục trả lời bằng một con số vốn chỉ đúng vào tháng 6. Chính sách về độ cũ và dọn dẹp bộ nhớ cho agent là nửa còn lại của trang này, và việc thiết kế nó sẽ rẻ hơn nhiều khi bảng dữ liệu vẫn còn nhỏ.

Tại sao bộ nhớ episodic tăng trưởng không giới hạn

Bộ nhớ episodic là một log, và log thì luôn tăng trưởng. Mỗi phiên làm việc, mỗi lần gọi tool và mỗi lệnh thất bại đều là một episode tiềm năng. Một agent ghi lại một dòng cho mỗi lượt tương tác sẽ tạo ra số lượng dòng nhiều hơn mức bất kỳ ai có thể đọc trong một tháng, và chi phí không chỉ nằm ở dung lượng đĩa: mỗi episode được nhúng (embedded) còn tham gia vào chỉ mục (index) mà quá trình tìm kiếm phải duyệt qua.

Hãy quyết định quy tắc lưu giữ (retention rule) ngay từ ngày bạn tạo bảng, khi việc xóa dữ liệu vẫn còn dễ dàng. Hai câu hỏi sẽ giải quyết hầu hết vấn đề này. Thứ nhất, nội dung nào đáng để ghi lại: bản tóm tắt của một phiên làm việc thường là đáng, còn toàn bộ output của một ls -la thì thường là không. Thứ hai, mỗi loại episode sẽ tồn tại trong bao lâu: ví dụ như các episode thô trong 30 ngày và bản tóm tắt phiên làm việc trong một năm.

Hãy gán cho mỗi dòng episode một timestamp created_at và một cột source. Nếu không có created_at, bạn không thể xóa dữ liệu theo thời gian. Nếu không có source, bạn không thể xóa toàn bộ dữ liệu đến từ một nguồn lỗi, đây chính xác là thứ bạn cần vào ngày mà một trang web hoặc một ticket bị phát hiện là đang ghi các chỉ dẫn độc hại vào bộ nhớ.

DELETE FROM episodes WHERE created_at < now() - interval '30 days';

Hãy chạy lệnh đó từ một systemd timer, sau đó theo dõi số lượng dòng và kích thước bảng thực tế thay đổi sau đó. Một chính sách lưu giữ mà không ai thực thi thì chỉ là một dòng chú thích.

psql -d agentmem -c "SELECT count(*) FROM episodes;"
psql -d agentmem -c "SELECT pg_size_pretty(pg_total_relation_size('episodes'));"

Bộ nhớ quy trình cần được lưu trong repository

Bộ nhớ quy trình là cách agent thực hiện công việc: một shell script, một file kỹ năng, hoặc một runbook với các bước được đánh số. Đó là code, và code phải nằm ở nơi dành cho code, trong một git repository với quy trình review, quản lý phiên bản và diff mà bạn có thể đọc được.

Lưu trữ runbook dưới dạng các đoạn dữ liệu nhúng (embedded chunks) chỉ cho bạn kết quả là một bản sao xấp xỉ. Việc truy xuất trả về các đoạn có điểm số cao nhất, vì vậy agent có thể thực hiện bước 2 và bước 5 trong khi bước 3 không bao giờ xuất hiện, và không có gì ghi lại phiên bản nào của quy trình đã chạy. Trong git, git log trả lời được cả hai câu hỏi đó. Chi phí lưu trữ gần như bằng không, đó cũng là lý do khác để không phải trả phí cho vector database chỉ để lưu trữ những thứ này.

Chi phí lưu trữ thực tế của một embedding trên ổ đĩa

ChartVector storage in pgvector, at 4 bytes per dimension plus an 8 byte header
The data behind this chart
[
  {
    "label": "bge-small-en-v1.5 (384 dims)",
    "bytes_per_vector": 1544,
    "mib_per_100k_rows": 147.2
  },
  {
    "label": "bge-base-en-v1.5 (768 dims)",
    "bytes_per_vector": 3080,
    "mib_per_100k_rows": 293.7
  },
  {
    "label": "bge-large-en-v1.5 (1024 dims)",
    "bytes_per_vector": 4104,
    "mib_per_100k_rows": 391.4
  },
  {
    "label": "text-embedding-3-small (1536 dims)",
    "bytes_per_vector": 6152,
    "mib_per_100k_rows": 586.7
  },
  {
    "label": "text-embedding-3-large (3072 dims)",
    "bytes_per_vector": 12296,
    "mib_per_100k_rows": 1172.6
  }
]

pgvector lưu trữ một vector với 4 byte cho mỗi chiều cộng thêm 8 byte header, vì vậy phép tính này là cố định và bạn có thể lập kế hoạch trước khi nạp dữ liệu. Một vector 384 chiều tốn 1544 byte, nghĩa là 100.000 chunk sẽ tốn 147.2 MiB cho các vector. Cùng một tập dữ liệu đó nhưng với 3.072 chiều sẽ tốn 1172.6 MiB, với 12296 byte mỗi hàng. Cùng một nội dung văn bản, nhưng tốn gần gấp tám lần dung lượng lưu trữ.

Đó mới chỉ là cột vector. Nội dung văn bản của chunk, khóa chính, overhead của hàng và index đều cộng dồn vào, và index là phần mọi người hay quên. HNSW (hierarchical navigable small world, cấu trúc index dạng đồ thị mà pgvector xây dựng) giữ một bản sao riêng của các vector mà nó liên kết, vì vậy một kho lưu trữ có index sẽ tốn hơn gấp đôi các con số nêu trên. Hãy tự đo lường thay vì đoán.

SELECT pg_size_pretty(pg_total_relation_size('memories')) AS total,
       pg_size_pretty(pg_relation_size('memories_embedding_idx')) AS idx;

RAM quyết định tốc độ tìm kiếm, vì đồ thị chỉ duyệt nhanh khi nó nằm trong bộ nhớ. Khi index lớn hơn dung lượng RAM mà Postgres có thể chứa, các truy vấn bắt đầu đọc từ ổ đĩa và độ trễ sẽ tăng lên. Quá trình build index có giới hạn riêng, maintenance_work_mem, và khi đồ thị vượt quá giới hạn này, quá trình build sẽ báo lỗi và chậm lại:

NOTICE:  hnsw graph no longer fits into maintenance_work_mem after 61440 tuples
HINT:  Increase maintenance_work_mem to speed up builds.

Có hai cách để thu nhỏ cùng một tập dữ liệu. Hãy chọn model nhỏ hơn, vì 384 chiều chỉ tốn một phần tư so với 1.536 chiều và đối với việc tìm kiếm ghi chú cá nhân, sự khác biệt về độ chính xác thường không đáng kể. Hoặc lưu trữ ở độ chính xác một nửa (half precision): kiểu dữ liệu halfvec tốn 2 byte cho mỗi chiều cộng với 8 byte header tương tự, giúp giảm gần một nửa dung lượng của cột và index đi kèm.

Có một giới hạn cần biết trước khi bạn chọn model. Tính đến tháng 8 năm 2026, một cột vector có thể được đánh index tối đa 2.000 chiều, vì vậy một embedding 3.072 chiều sẽ được chấp nhận khi lưu vào cột nhưng bị từ chối khi tạo index:

ERROR:  column cannot have more than 2000 dimensions for hnsw index

halfvec hỗ trợ index lên đến 4.000 chiều, vì vậy cách khắc phục thông thường là đánh index trên kết quả cast:

CREATE INDEX ON memories USING hnsw ((embedding::halfvec(3072)) halfvec_cosine_ops);

Khi nào pgvector hiệu quả hơn một dịch vụ lưu trữ bộ nhớ riêng biệt

Nếu máy chủ đã chạy Postgres, các vector chỉ là một gói cài đặt và một câu lệnh.

psql -V
sudo apt install postgresql-16-pgvector
CREATE EXTENSION vector;

Con số trong tên gói là phiên bản chính của Postgres, ví dụ 16 trên Ubuntu 24.04, vì vậy hãy đọc psql -V trước khi bạn nhập lệnh.

Việc lưu trữ bộ nhớ trong cơ sở dữ liệu đó giúp bạn có một bản backup duy nhất cho cả bộ nhớ và dữ liệu ứng dụng tại cùng một thời điểm, một connection pool duy nhất và các transaction: một sự kiện và dòng dữ liệu mô tả nó sẽ cùng commit hoặc cùng thất bại. Một dịch vụ riêng biệt không thể đảm bảo điều này.

Hãy chuyển sang một dịch vụ bộ nhớ chuyên dụng khi một trong các điều kiện sau xảy ra. Tải tìm kiếm cạnh tranh với ứng dụng của bạn và cần máy chủ riêng. Nhiều agent trên nhiều host chia sẻ chung một bộ nhớ. Hoặc bạn muốn logic trích xuất và khử trùng lặp mà một sản phẩm hoàn chỉnh cung cấp, đây cũng là lý do để chọn tự host Mem0 memory server. Đối với một agent và tập dữ liệu có quy mô vài triệu chunk, pgvector trên máy chủ hiện tại của bạn dễ vận hành hơn và ít lỗi hơn. Việc lựa chọn engine và yêu cầu RAM của từng loại được đề cập trong chạy vector database trên VPS.

Chi phí re-embedding khi thay đổi model

Vector từ hai model khác nhau không thể so sánh được, vì vậy bạn không thể nhúng các memory mới bằng model mới trong khi vẫn giữ nguyên các hàng cũ. Một bảng dữ liệu hỗn hợp sẽ trả về kết quả vô nghĩa, vì khoảng cách được tính toán giữa hai hệ tọa độ khác nhau là một con số không có giá trị. Thay đổi model đồng nghĩa với việc phải re-embedding toàn bộ corpus.

Hóa đơn đó bao gồm bốn phần: số lượng token (phí API, hoặc thời gian CPU và GPU trên máy chủ của bạn), thời gian thực (wall clock time) trong khi chạy, dung lượng ổ đĩa cho cả hai cột cùng lúc trong quá trình backfill, và việc rebuild index ở bước cuối. Quy trình an toàn là thêm một cột mới, backfill theo từng batch, chuyển đổi truy vấn, sau đó xóa cột cũ và index của nó.

Hãy đo lường tốc độ trên phần cứng của chính bạn thay vì tin vào các con số được công bố, vì việc embedding chỉ bằng CPU trên một VPS nhỏ sẽ chậm hơn rất nhiều so với cùng model đó chạy trên GPU. Hãy đo thời gian cho một phần dữ liệu đại diện, sau đó nhân với tổng kích thước corpus.

ollama pull nomic-embed-text
time curl -s http://localhost:11434/api/embed \
  -d '{"model": "nomic-embed-text", "input": "one representative chunk of your corpus"}' > /dev/null

Một embedding model chạy local cũng lưu các trọng số (weights) trên cùng ổ đĩa với kho lưu trữ memory, và nơi Ollama lưu các model đã tải về giải thích nơi không gian đó được sử dụng.

Một yêu cầu giúp mọi việc này khả thi: hãy giữ văn bản gốc bên cạnh mỗi vector. Một kho lưu trữ chỉ chứa vector thì không thể re-embedding được, vì không còn gì để đưa vào model mới. Nếu bạn không thể trả lời câu hỏi "văn bản nào đã tạo ra hàng này", thì lộ trình di chuyển duy nhất của bạn là rebuild toàn bộ từ nguồn dữ liệu gốc.

Những điều cần theo dõi khi bộ nhớ đầy

Chi phí không phải là thứ duy nhất thay đổi khi kho lưu trữ đầy dần. Các dữ kiện cũ trở nên lỗi thời và các tập dữ liệu cũ lấn át những kết quả hữu ích, đây lại là vấn đề về cắt tỉa (pruning). Kho lưu trữ bộ nhớ cũng là một đầu vào có thể ghi được, ảnh hưởng đến hành vi tương lai của agent, vì vậy bất kỳ thứ gì được phép ghi vào đó đều có thể điều hướng agent sau này. Nếu văn bản từ các trang web hoặc ticket đi vào bộ nhớ, hãy đọc cách thức hoạt động của việc đầu độc bộ nhớ agent trước khi bạn mở rộng phạm vi những gì được phép ghi. Kích thước truy xuất cũng đẩy chi phí token lên trong mỗi request, và đó là nơi bắt đầu của việc giữ cho chi phí vận hành của agent ở mức dự đoán được.

FAQ

Tôi có cần vector database cho bộ nhớ của agent không?

Không cần nếu chỉ lưu dữ kiện. Semantic memory thường nhỏ và bạn cần chỉnh sửa theo tên, vì vậy một bảng có khóa (keyed table) hoặc thư mục chứa các file markdown trong git sẽ hiệu quả hơn, vì bạn có thể xem và sửa trực tiếp từng giá trị. Embeddings chỉ thực sự phát huy giá trị khi bạn cần truy xuất theo ý nghĩa trên một tập dữ liệu quá lớn để liệt kê thủ công, thường là cho episodic memory và tài liệu. Nếu bạn đang chạy Postgres, CREATE EXTENSION vector sẽ giải quyết vấn đề này mà không cần thêm một service nào khác để vận hành.

Bộ nhớ của agent sẽ tốn bao nhiêu dung lượng đĩa?

Kích thước các vector là có thể dự đoán được, với 4 byte cho mỗi chiều cộng thêm 8 byte header trong pgvector. Với 768 chiều, con số này là 293.7 MiB cho mỗi 100.000 dòng, và với 384 chiều là 147.2 MiB. Sau đó, hãy cộng thêm dung lượng của đoạn văn bản (chunk text), overhead của dòng và HNSW index (vốn lưu trữ bản sao riêng của các vector), vì vậy hãy dự trù ít nhất gấp đôi con số vector và đo lường con số thực tế bằng pg_total_relation_size.

Procedural memory nên được lưu ở đâu?

Trong một git repository, dưới dạng các script hoặc file kỹ năng mà agent chạy trực tiếp. Một quy trình cần khả năng truy xuất chính xác và lịch sử phiên bản, trong khi tìm kiếm theo độ tương đồng (similarity search) không cung cấp được cả hai. Một runbook được chia nhỏ khi truy xuất sẽ trả về các phần có điểm số cao nhất, điều này có thể dẫn đến việc bước 2 và bước 5 xuất hiện trong khi bước 3 bị thiếu, và không có gì ghi lại phiên bản nào đã được thực thi.

Việc thay đổi embedding model tốn kém như thế nào?

Bạn sẽ phải thực hiện re-embedding toàn bộ tập dữ liệu, vì các vector từ những model khác nhau không thể so sánh với nhau. Hãy dự trù chi phí token hoặc thời gian GPU, dung lượng đĩa để lưu trữ đồng thời cả cột cũ và cột mới, cùng với thời gian xây dựng lại index. Hãy thêm cột mới, backfill theo từng batch, chuyển đổi truy vấn, sau đó mới xóa cột cũ. Tất cả những việc này phụ thuộc vào việc bạn đã lưu trữ văn bản gốc bên cạnh mỗi vector hay chưa.

#agent-memory#semantic-memory#episodic-memory#pgvector#lưu trữ