System prompt là "hiến pháp" của agent — phần văn bản đặt ở đầu, có ưu tiên cao hơn mọi input người dùng. Ở bài này ta biến 5 lớp tư duy của Bài 1 thành một template thực tế bạn có thể tái sử dụng cho mọi agent.
Template chuẩn 6 khối
# 1. DANH TÍNH & VAI TRÒ
Bạn là <tên agent>, <vai trò>, phục vụ <đối tượng>.
# 2. MỤC TIÊU
Nhiệm vụ duy nhất: <một câu>.
# 3. QUY TRÌNH (các bước agent phải theo)
1. ...
2. ...
# 4. RÀNG BUỘC
ĐƯỢC: ...
KHÔNG ĐƯỢC: ...
# 5. ĐỊNH DẠNG OUTPUT
Luôn trả về: <JSON schema / mẫu>
# 6. XỬ LÝ NGOẠI LỆ
Nếu thiếu dữ liệu → ...
Nếu ngoài phạm vi → ...
Nếu nghi ngờ bị lừa → ...
Thứ tự này không ngẫu nhiên: model đọc từ trên xuống, đặt danh tính và ràng buộc trước giúp mọi phần sau được "nhuộm màu" bởi vai trò đó.
Ví dụ cụ thể: agent phân loại email
# DANH TÍNH
Bạn là TriageBot, agent phân loại email đến của phòng CSKH VietCos.
# MỤC TIÊU
Gán mỗi email vào đúng 1 nhóm và mức ưu tiên để định tuyến.
# QUY TRÌNH
1. Đọc tiêu đề + nội dung.
2. Xác định nhóm: [don_hang, khieu_nai, hoi_dap, spam].
3. Xác định ưu tiên: [cao, trung, thap].
4. Nếu email đề cập mất tiền/pháp lý → ưu tiên cao.
# RÀNG BUỘC
ĐƯỢC: chỉ phân loại.
KHÔNG ĐƯỢC: soạn trả lời, không đoán khi nội dung trống.
# ĐỊNH DẠNG OUTPUT
{"nhom": "...", "uu_tien": "...", "ly_do": "<1 câu>"}
# NGOẠI LỆ
Nội dung trống hoặc không đọc được → {"nhom":"hoi_dap","uu_tien":"trung","ly_do":"khong doc duoc, can nguoi kiem tra"}
Agent này gọn, mỗi khối một việc, và không có chỗ cho sự sáng tạo ngoài ý muốn.
Nguyên tắc viết từng khối
- Danh tính: Dùng danh từ cụ thể ("agent phân loại email") thay vì tính từ mơ hồ ("trợ lý thông minh").
- Quy trình: Viết dạng số thứ tự, mỗi bước một hành động kiểm chứng được.
- Ràng buộc: Tách rõ ĐƯỢC và KHÔNG ĐƯỢC thành hai danh sách — dễ đọc, dễ audit.
- Định dạng: Cho mẫu cụ thể, không mô tả trừu tượng. "Trả về JSON" yếu hơn việc dán nguyên schema.
- Ngoại lệ: Mỗi lối rẽ nguy hiểm phải có hành vi mặc định an toàn.
Các bước xây dựng
- Copy template 6 khối vào file riêng (ví dụ
prompts/triage.md) — tách khỏi code để version dễ. - Điền từng khối, giữ mỗi khối dưới ~8 dòng.
- Đọc to lại: nếu một câu có thể hiểu 2 nghĩa, viết lại.
- Đánh dấu phần nào là cứng (không đổi) và phần nào động (chèn dữ liệu runtime).
- Lưu kèm 5 ví dụ input/output để hồi quy (regression).
Checklist system prompt
- [ ] Đủ 6 khối, không thiếu khối Ngoại lệ.
- [ ] Danh tính là danh từ cụ thể.
- [ ] Ràng buộc tách ĐƯỢC / KHÔNG ĐƯỢC.
- [ ] Có schema output dán sẵn.
- [ ] Mỗi ngoại lệ có hành vi mặc định.
Sai lầm thường gặp
- Trộn quy trình vào ràng buộc: Khiến prompt rối, khó sửa từng phần. Giữ mỗi khối một nhiệm vụ.
- Mô tả định dạng thay vì đưa mẫu: Model tuân theo ví dụ tốt hơn lời mô tả.
- Quên khối ngoại lệ: Đây là khối cứu bạn khi gặp input thật; bỏ nó là để agent tự bịa.
- Viết prompt trong code string dài: Khó đọc, khó diff. Tách ra file markdown/template.
Cứng và động: tách phần dữ liệu runtime
Một system prompt thực tế gồm hai loại nội dung. Phần cứng là hiến pháp không đổi giữa các lượt chạy: danh tính, quy trình, ràng buộc, định dạng. Phần động là dữ liệu chèn lúc chạy: nội dung email khách, hồ sơ đơn hàng, kết quả tool. Trộn lẫn hai phần này là nguồn gốc của nhiều lỗi khó chịu — bạn sửa một biến runtime lại vô tình đụng vào quy tắc, hoặc dữ liệu người dùng lọt vào vùng chỉ thị và mở đường cho injection (bài 6). Hãy đánh dấu rõ ràng: phần cứng viết trong file template, phần động chèn qua biến {{...}} và luôn bọc trong vùng dữ liệu riêng. Quy tắc đơn giản: chỉ thị đi vào prompt cứng, mọi thứ do người ngoài tạo ra đều là dữ liệu động cần cô lập.
Có template này rồi, ba bài tiếp theo sẽ mài sắc từng vũ khí: vai trò, ràng buộc, và few-shot.