Tôi đang vận hành SRE K8s Agent (một chatbot AI được xây dựng cho đội ngũ SRE tại GreenNode). Nhưng có một vấn đề khá rõ ràng: mỗi khi mở một tab mới, agent gần như bắt đầu lại từ đầu. Người dùng phải liên tục cung cấp lại context, đang theo dõi cluster nào, vấn đề gì đang xảy ra và những gì đã trao đổi trước đó.
Bài toán cần giải quyết: agent "quên" sau mỗi lần mở tab mới
SRE K8s Agent tại GreenNode có khả năng query metrics, đọc log, phân tích node health và chạy runbook tự động trên các cụm VKS, chạy trên nền LLM qwen/qwen3-5-27b qua MaS API. Về mặt năng lực, agent đủ mạnh để trả lời các câu hỏi vận hành phức tạp.
Tuy nhiên, khả năng reasoning của LLM không giải quyết được một vấn đề cơ bản: agent không có long-term memory giữa các session.
Ví dụ, người dùng đã trao đổi với agent về: cluster prod-01 đang gặp memory leak tại namespace payments.
Nhưng khi mở một tab mới, toàn bộ context này biến mất. Người dùng phải cung cấp lại cluster, namespace và vấn đề đang xảy ra.
Đây là friction point rất thực trong công việc hàng ngày. Người dùng gõ lại "cluster prod-01 đang có vấn đề với memory leak ở namespace payments": thông tin mà agent đã biết từ hôm qua, chỉ vì họ mở một tab mới.
Câu hỏi đặt ra không mới, nhưng lời giải thì không hiển nhiên: có cách nào để agent nhớ được lịch sử hội thoại xuyên suốt các session mà không cần nhồi toàn bộ chat history vào prompt, vừa tốn token, vừa chậm?
TencentDB-Agent-Memory là gì?
TencentDB-Agent-Memory (với 17.6k star trên GitHub) là một team-level memory hub cho AI agent. Thay vì lưu raw chat log rồi nhét ngược vào prompt như cách RAG thông thường vẫn làm, nó chiết lọc hội thoại, tài liệu và code thành bốn loại memory asset có thể tái sử dụng: Chat Memory (lịch sử hội thoại, preferences, facts, decisions xuyên session), Skill (quy trình tái sử dụng như troubleshooting, code review, release checklist), LLM-Wiki (tài liệu, design spec, runbook tổ chức thành structured page có link graph), và CodeGraph (index code symbol, file, call relationship và impact path).
Điểm khác biệt cốt lõi so với RAG phẳng nằm ở kiến trúc bốn lớp, với độ trừu tượng tăng dần từ raw text đến profile dài hạn:
| Layer | Lưu trữ gì | Cơ chế chính | Dùng để làm gì |
|---|---|---|---|
| L0: Raw Conversation | Toàn bộ cuộc hội thoại, timestamp, source | BM25 full-text search | Kiểm tra chính xác từ ngữ, truy nguồn gốc |
| L1: Atom | Facts, preferences, constraints trích từ hội thoại | LLM distillation (30s idle timeout) | Recall thông tin hành động cụ thể |
| L2: Scenario | Knowledge block theo project/chủ đề | Scene clustering sau L1 30s | Khôi phục nhanh working context |
| L3: Persona | Long-term profile, stable pattern | Persona build sau L2 | Agent nhanh chóng hiểu người dùng, team |
Về mặt thiết kế, pipeline L1→L2→L3 chạy bất đồng bộ và không block response stream. L1 được kích hoạt sau một khoảng thời gian idle, sau đó L2 và L3 tiếp tục xây dựng context ở mức trừu tượng cao hơn. Tuy nhiên, trong quá trình triển khai thực tế với hội thoại tiếng Việt, pipeline này không hoạt động như kỳ vọng. Phần này sẽ được phân tích chi tiết ở các vấn đề triển khai bên dưới.
Về mặt hiệu quả, benchmark PersonaMem — đo khả năng agent hiểu và áp dụng thông tin người dùng sau nhiều lượt tương tác — cho kết quả 48% khi không có memory, so với 76% khi có TencentDB-Agent-Memory, tương đương cải thiện +59%.
Kiến trúc triển khai trên VM GreenNode
Toàn bộ SRE Agent chạy trên một VM, các service được containerize và kết nối qua Docker bridge network sre-net. Container giao tiếp với nhau qua container name như hostname — không cần IP tĩnh. sre-backend gọi tdai-memory qua http://tdai-memory:3701, Docker DNS tự resolve container name trên cùng bridge network; port 3701 chỉ expose ra 127.0.0.1 trên host, không public ra ngoài.
Bốn endpoint được dùng thực tế trong production:
| Endpoint | Mô tả | Sử dụng thực tế |
|---|---|---|
POST /capture | Lưu một lượt hội thoại vào L0 | Gọi sau khi agent trả lời (background thread) |
POST /search/conversations | BM25 full-text search trực tiếp L0 | Thay thế /recall — nhanh hơn, không cần LLM |
POST /recall | Truy xuất memory từ L1+ (LLM-based) | Không dùng — L1 thất bại với tiếng Việt |
GET /health | Health check gateway | Monitoring, readiness probe |
Đáng chú ý nhất trong bảng trên là dòng thứ ba: endpoint được thiết kế làm tính năng chính của package lại là endpoint bị loại khỏi production flow — lý do nằm ở phần gotcha bên dưới.
6 bước để AI Agent truy xuất và lưu memory trong mỗi request
Mỗi lượt hội thoại đi qua bốn giai đoạn chính, trong đó giai đoạn capture chạy nền để không chặn response stream:
POST /api/v1/agent-ops/chat- frontend gửi{message, history, session_id}POST /search/conversations- sre-backend gọi BM25 search lấy memory liên quan (timeout 3s)- Inject vào system prompt - nếu có kết quả, thêm vào section "Lịch sử hội thoại liên quan"
- Chat completion streaming - gọi MaaS LLM với system prompt đã được augment
- Render response - SSE stream token về client, render trong UI
POST /capture(background thread) - lưuuser_contentvàassistant_contentvào L0, không block response
Luồng xử lý có thể tóm tắt như sau: User → Search Memory → Inject Context → LLM → Stream Response → Capture Conversation. Việc capture được chạy ở background để không ảnh hưởng đến response stream của agent.
Cách triển khai: code thực tế
Bước 1: deploy container tdai-memory:
# Tạo network nếu chưa có
docker network create sre-net
docker run -d \
--name tdai-memory \
--network sre-net \
-p 127.0.0.1:3701:3701 \
-v /opt/tdai-data:/data/tdai-memory \
-e TDAI_LLM_BASE_URL=https://maas-llm-aiplatform-hcm.api.vngcloud.vn/v1 \
-e TDAI_LLM_API_KEY=<maas-api-key> \
-e TDAI_LLM_MODEL=qwen/qwen3-5-27b \
tencentdb-agent-memory:latest⚠️ Gateway tìm config tại
./tdai-gateway.yaml(CWD =/opt/tdai-gateway/), không phảiTDAI_DATA_DIR. Đây là gotcha quan trọng nhất — đặt file nhầm chỗ làm config bị ignore hoàn toàn, không có error message nào cảnh báo.
Bước 2: tdai-gateway.yaml, phần cấu hình quyết định:
memory:
recall:
strategy: keyword # keyword=BM25; hybrid=cần EmbeddingService (không dùng)
maxResults: 5
scoreThreshold: 0.1
extraction:
enabled: true
maxMemoriesPerSession: 20
pipeline:
everyNConversations: 3
l1IdleTimeoutSeconds: 30 # giảm từ 600s mặc định
l2DelayAfterL1Seconds: 30
l2MinIntervalSeconds: 60
bm25:
enabled: true
language: en # en vì BM25 tiếng Việt chưa optimize tốtBước 3: helper function phía Python, chỉ dùng urllib stdlib, không thêm dependency:
_TDAI_URL = "http://tdai-memory:3701"
def _tdai_search(query: str, session_key: str, limit: int = 3) -> str:
try:
data = json.dumps({
"query": query, "limit": limit, "session_key": session_key
}).encode()
req = urllib.request.Request(
f"{_TDAI_URL}/search/conversations",
data=data, headers={"Content-Type": "application/json"}, method="POST"
)
with urllib.request.urlopen(req, timeout=3) as resp:
return json.loads(resp.read().decode()).get("results", "")
except Exception as e:
log.debug(f"[tdai] search: {e}")
return ""
def _tdai_capture_bg(user_content: str, assistant_content: str, session_key: str):
try:
data = json.dumps({
"user_content": user_content,
"assistant_content": assistant_content,
"session_key": session_key,
}).encode()
req = urllib.request.Request(
f"{_TDAI_URL}/capture",
data=data, headers={"Content-Type": "application/json"}, method="POST"
)
with urllib.request.urlopen(req, timeout=5) as resp:
resp.read()
except Exception as e:
log.debug(f"[tdai] capture: {e}")Bước 4: tích hợp vào agent_chat endpoint:
# Search memory trước khi gọi LLM
_mem_context = await loop.run_in_executor(None, _tdai_search, message, session_id)
if _mem_context:
system_prompt += f"\n\n## Lịch sử hội thoại liên quan\n{_mem_context}"
log.info(f"[tdai] injected len={len(_mem_context)} session={session_id!r}")
# Capture async sau khi agent done (background thread)
finally:
if _response_parts:
threading.Thread(
target=_tdai_capture_bg,
args=(message, "".join(_response_parts), session_id),
daemon=True,
).start()Kết quả thực tế
Sau khi deploy, log từ sre-backend xác nhận memory injection và capture hoạt động đúng như thiết kế:
[tdai] injected memory context len=342 session="prod-session-01"
[tdai] captured session="prod-session-01"Các con số đo được: ~3ms cho memory search latency (BM25 chạy local, không qua mạng), HTTP 200 cho mọi response /capture, 0 dependency thêm ngoài urllib stdlib, và capture chạy qua background thread nên không ảnh hưởng đến response stream.
Kết quả: agent giờ có "ký ức" — một cải thiện UX rất rõ ràng khi vận hành production K8s hàng ngày. Backend log xác nhận [tdai] injected memory context và [tdai] captured ở mỗi request. Nhưng con đường đến kết quả này không thẳng — sáu gotcha dưới đây là phần tốn thời gian nhất.
6 vấn đề thực tế khi tích hợp TencentDB-Agent-Memory
| # | Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|---|
| 1 | Config lookup path ≠ TDAI_DATA_DIR | Gateway tìm config tại ./tdai-gateway.yaml (CWD), không phải thư mục data | Đặt file đúng thư mục /opt/tdai-gateway/ |
| 2 | Strategy hybrid yêu cầu EmbeddingService | Default config dùng hybrid. Gọi /recall trả về code 10001 | Đổi sang keyword (BM25) trong tdai-gateway.yaml |
| 3 | L1 extraction thất bại với tiếng Việt | LLM chạy 48s, 977 input token, chỉ sinh 77 token — memory list rỗng | Bypass L1, dùng /search/conversations (L0 BM25) trực tiếp |
| 4 | /capture API format: user_content/assistant_content | Tài liệu và code example có inconsistency | Dùng flat object: user_content, assistant_content, session_key |
| 5 | Memory recall cần đặt SAU khi loop khởi tạo | await loop.run_in_executor cần biến loop tồn tại trước | Kiểm tra thứ tự khởi tạo biến trong hàm async |
| 6 | Container settings bị override khi rebuild | /root/.sre-agent/settings.json trong image có URL cũ | Mount settings.json qua Docker volume thay vì bake vào image |
Gotcha số 3 là điểm đáng nói nhất, vì nó không phải lỗi cấu hình mà là giới hạn thực sự của pipeline: LLM distillation cho L1 chạy mất 48 giây, tiêu tốn 977 input token, nhưng chỉ sinh ra 77 token đầu ra — và danh sách memory kết quả rỗng. Nói cách khác, pipeline L1→L2→L3 không hoạt động với hội thoại tiếng Việt trong thực tế. Memory recall production hiện tại chỉ là BM25 keyword match trên L0 raw storage, không phải semantic understanding như thiết kế ban đầu của package. Đây là một compromise thực dụng: hệ thống vẫn có context, nhưng chưa có "hiểu" thật sự. Để fix triệt để, cần customize extraction prompt hoặc dùng model tốt hơn cho tiếng Việt.
Hướng cải thiện tiếp theo
- Mount settings.json qua Docker volume để không bị mất khi rebuild container
- Customize L1 extraction prompt cho tiếng Việt và domain SRE (tên cluster, tên pod, thuật ngữ K8s)
- Thử strategy embedding với local embedding model để có semantic recall thay vì chỉ BM25 keyword
- Implement memory TTL — tự động xoá conversation cũ hơn 30 ngày để tránh L0 phình to
- Thêm memory namespace theo từng cluster để recall có thể filter theo VKS cluster cụ thể
- Xem xét TencentDB-Agent-Memory v2.0+ với Memory Hub đầy đủ — hỗ trợ Skill, Wiki, CodeGraph và team sharing
Kết luận
Kết quả cuối cùng không phải là "memory hoàn hảo" như tên package hứa hẹn, mà là một compromise chấp nhận được: BM25 keyword search trên raw conversation, đủ để agent không còn là trang giấy trắng ở mỗi tab mới, với chi phí gần như bằng không - 3ms latency, không thêm dependency, không block response stream. Phần LLM distillation nhiều lớp phía sau vẫn còn đó như một hướng cải thiện, không phải một tính năng đang chạy. Với một hệ thống production tiếng Việt, đây có lẽ là bài học chung: đọc kỹ log trước khi tin vào tài liệu, và sẵn sàng dùng lớp đơn giản nhất hoạt động được thay vì lớp phức tạp nhất được quảng cáo.

