Từ mảnh ghép tới hệ thống
Đến giờ bạn đã có 4 mảnh: short-term (context window), key-value (profile), vector store + RAG (kiến thức), event log (sự kiện). Bài này ghép chúng thành một agent hoàn chỉnh và làm rõ mảnh nào chịu trách nhiệm gì, được nạp vào prompt lúc nào.
Bản thiết kế: trợ lý hỗ trợ khách hàng cho Shop A
Yêu cầu: agent nhớ hội thoại trong phiên, nhớ hồ sơ khách qua các phiên, tra được chính sách/công cụ từ kho tài liệu, và ghi lại sự kiện quan trọng.
Bảng phân vai bộ nhớ:
Loại memory | Lưu ở đâu | Nạp vào prompt khi nào
----------------|------------------|-----------------------------
Short-term | RAM/session | Mỗi lượt (có cắt/tóm tắt)
Profile (KV) | SQL/Redis | Đầu phiên + khi cần
Kiến thức (RAG) | Vector store | Khi câu hỏi cần tra tài liệu
Sự kiện (log) | Event table | Khi cần nhắc lịch sử
Luồng xử lý một lượt hội thoại
1. Nhận câu hỏi user
2. Nạp profile (KV) nếu chưa có trong session
3. Quyết định: câu này có cần tra kiến thức không?
- Có -> RAG: retrieve top-K chunk
- Không -> bỏ qua
4. Dựng prompt:
[system] + [profile] + [ngữ cảnh RAG?] +
[lịch sử đã cắt/tóm tắt] + [câu hỏi]
5. Gọi LLM -> câu trả lời
6. Cập nhật short-term (thêm lượt)
7. Nếu học được fact mới -> ghi KV; nếu có sự kiện -> ghi log
Bước 3 là mấu chốt: không phải lượt nào cũng cần RAG. "Cảm ơn nhé" thì không cần tra tài liệu. Một router đơn giản (theo intent hoặc heuristic) giúp tiết kiệm token và độ trễ.
Pseudocode khung agent
def handle_turn(user_id, msg, session):
if "profile" not in session:
session["profile"] = kv.get(f"user:{user_id}")
context = ""
if needs_knowledge(msg): # router
hits = vstore.query(msg, k=4)
context = "NGỮ CẢNH:\n" + join(hits)
history = trim_or_summarize(session["history"]) # Bài 2
prompt = [SYSTEM, session["profile"], context, *history, msg]
answer = llm(prompt)
session["history"] += [msg, answer]
if fact := extract_fact(msg): # ví dụ 'tôi chuyển sang gói Pro'
kv.update(f"user:{user_id}", fact)
if event := detect_event(answer): # ví dụ 'đã tạo yêu cầu hoàn tiền'
log.append(user_id, event)
return answer
Khung tư duy: "đường đi của một sự thật"
Với mỗi thông tin, hãy vẽ được vòng đời: ghi ở đâu → sống bao lâu → nạp lại khi nào → cập nhật/xóa ra sao. Nếu bạn không trả lời được cả 4, thiết kế memory còn lỗ hổng.
Checklist kiến trúc
- [ ] Mỗi loại thông tin đã được gán đúng một loại store?
- [ ] Có router quyết định khi nào chạy RAG (tránh tra vô tội vạ)?
- [ ] Prompt có thứ tự rõ ràng: system → profile → ngữ cảnh → lịch sử → câu hỏi?
- [ ] Có bước ghi lại fact/sự kiện mới sau mỗi lượt phù hợp?
- [ ] Có giới hạn tổng token để không lượt nào làm tràn cửa sổ?
Sai lầm thường gặp
- Luôn chạy RAG mọi lượt. Tốn tiền và thêm nhiễu vào những câu không cần tài liệu.
- Nạp mọi thứ vào mọi prompt. Profile + toàn bộ log + 10 chunk mỗi lượt → tràn cửa sổ, model rối.
- Không có ranh giới trách nhiệm. Nhét profile vào vector store rồi lại tra bằng RAG cho một thứ đáng lẽ tra khóa trực tiếp.
- Ghi memory bừa. Lưu mọi câu user nói làm store phình và nhiễu; chỉ ghi fact/sự kiện có giá trị tái dùng.
- Bỏ qua thứ tự trong prompt. Đặt câu hỏi trước ngữ cảnh, hoặc để lịch sử đè lên chỉ dẫn hệ thống.
Tổng kết
Một agent thật là sự phối hợp có kỷ luật của nhiều loại memory, mỗi loại một vai trò và một thời điểm nạp. Router + thứ tự prompt rõ ràng + vòng đời dữ liệu minh bạch là ba trụ cột. Bài sau ta bàn cách vận hành, đo lường và xử lý sự cố cho hệ thống này.