Zero-shot, few-shot, CoT hay ReAct: thử cả bốn trên một bộ ticket để biết lúc nào cần kỹ thuật nặng hơn
Model đời mới không làm prompt engineering lỗi thời. Chúng chỉ khiến thói quen bắt đầu bằng kỹ thuật nặng nhất trở nên tốn kém.
- 1Zero-shotChạy 20 ticket lấy baseline. Đạt ngưỡng khách chấp nhận? Dừng. Chưa đạt: sang few-shot
- 2Few-shotVí dụ đa dạng, nhãn cân bằng. Điểm tăng đủ? Dừng. Không tăng: thay ví dụ trước đã
- 3Chain-of-thoughtCho bài suy luận. So kết luận với hàm kiểm tra. Lệch nhiều? Đưa phép tính ra code
- 4ReActGọi tool thay vì tự tính. Đọc history ca sai: tool có đúng, observation có hiểu đúng?
- 5Context engineeringHistory dài ra: chọn giữ, tóm tắt hay truy xuất gì vào context window
Sau mỗi kỹ thuật, chạy lại bộ eval: đạt ngưỡng thì dừng, chưa đạt mới sang bước nặng hơn.
Đồ hoạ: FDE Times
Tóm tắt nhanh
- Hãy bắt đầu bằng zero-shot, vì instruction tuning đã giúp model đời mới làm tốt nhiều tác vụ đơn giản mà không cần ví dụ.
- Few-shot vẫn đáng dùng nếu ví dụ ít, đa dạng và đúng định dạng. CoT hữu ích cho bài toán suy luận, nhưng bạn phải tự kiểm chứng kết quả.
- ReAct là nền của agent loop ngày nay. Khi đã có agent, việc chọn đưa gì vào context quan trọng hơn câu chữ trong prompt.
Tháng 9/2025, Anthropic viết rằng context engineering là bước phát triển tự nhiên của prompt engineering. Thế nhưng ngay trong bài đó, họ vẫn dành chỗ cho một kỹ thuật rất cũ là few-shot, và gọi ví dụ là những “bức tranh” đáng giá ngàn lời đối với LLM.
Vì thế, hỏi kỹ thuật nào đã lỗi thời là hỏi chưa trúng. Với một FDE, câu hỏi thực tế hơn là khi nào nên chuyển sang kỹ thuật nặng hơn. Bước nào cũng tốn thêm token, độ trễ và công bảo trì, nên bạn chỉ nên chuyển khi có số liệu cho thấy bước trước không đủ.
Bài này hướng dẫn bạn tự trả lời câu hỏi đó trên laptop. Bạn sẽ dựng một bộ thử nhỏ, chạy lần lượt bốn kỹ thuật trên cùng một tác vụ và quyết định dựa vào điểm số chứ không dựa vào cảm giác.
Bạn cần chuẩn bị những gì?
Hãy hình dung một khách hàng thương mại điện tử nhờ bạn phân loại ticket hỗ trợ vào bốn nhãn: hoan_tien, giao_hang, tai_khoan, khac. Đây là tình huống giả định, nhưng rất giống việc bạn sẽ gặp trong tuần đầu ở một khách hàng.
Đồ nghề gồm 20 ticket đã gán nhãn tay, quyền gọi một model bất kỳ qua API, và một hàm call_model(prompt) tự viết bọc quanh API đó. Code trong bài là bản đơn giản hoá: call_model và các tool là hàm của bạn, không phải tên API của nhà cung cấp nào.
eval/
tickets.jsonl # 20 dòng: {"text": ..., "label": ...}
prompts/
zero_shot.txt
few_shot.txt
cot.txt
run.py # gọi call_model, so với label, in độ chính xác
Bước 1: zero-shot thường đã đủ cho tác vụ đơn giản
Zero-shot nghĩa là prompt không kèm ví dụ hay minh hoạ nào. Kỹ thuật này dùng được vì model đã qua instruction tuning và RLHF. Instruction tuning được chứng minh là cải thiện khả năng zero-shot, nên các model đời mới thường xử lý tốt tác vụ đơn giản mà không cần bạn chỉ mẫu.
Phân loại ticket hỗ trợ sau vào đúng MỘT nhãn:
hoan_tien, giao_hang, tai_khoan, khac.
Chỉ trả về tên nhãn, không giải thích.
Ticket: {text}
Kiểm tra: chạy cả 20 ticket và ghi lại số câu đúng. Con số này là baseline. Nếu kết quả đã đạt mức khách hàng chấp nhận được, bạn dừng ở đây. Đọc riêng từng câu sai, vì các câu sai mới cho biết bạn có cần sang bước 2 hay không.
Bước 2: few-shot vẫn đáng dùng, nếu ví dụ được chọn kỹ
Few-shot là cách tạo in-context learning: các ví dụ trong prompt định hướng câu trả lời của model. Anthropic khuyên nên chọn một bộ ví dụ đa dạng và chuẩn mực, mô tả rõ hành vi mong muốn, thay vì nhồi mọi trường hợp biên vào prompt.
Phân loại ticket vào đúng MỘT nhãn: hoan_tien, giao_hang, tai_khoan, khac.
Ticket: Đã trả hàng tuần trước mà chưa thấy tiền về.
Nhãn: hoan_tien
Ticket: Đơn báo đã giao nhưng chưa nhận được hàng.
Nhãn: giao_hang
Ticket: Không đăng nhập được dù đã đổi mật khẩu.
Nhãn: tai_khoan
Ticket: {text}
Nhãn:
Lỗi hay gặp nhất là chọn ví dụ một cách lười biếng. Tài liệu tổng hợp nghiên cứu của Min và cộng sự (2022) cho thấy cả định dạng lẫn phân phối nhãn trong ví dụ đều ảnh hưởng đáng kể đến kết quả. Thử nghĩ xem: nếu cả ba ví dụ đều mang nhãn hoan_tien, model sẽ dễ nghiêng về nhãn đó.
Nếu ví dụ này viết “Nhãn:”, ví dụ kia viết “Label -”, đầu ra cũng sẽ lộn xộn theo.
IBM cũng lưu ý rằng hiệu quả của few-shot phụ thuộc phần lớn vào chất lượng thiết kế prompt. Kiểm tra: so điểm với baseline và xem chính xác những ticket nào đổi kết quả. Nếu điểm không tăng, hãy thay ví dụ trước khi kết luận rằng few-shot vô ích.
Bước 3: CoT dành cho bài toán suy luận, kèm điều kiện phải kiểm chứng
Giờ khách hàng muốn biết thêm ticket hoàn tiền nào còn trong hạn. Đến đây few-shot thường không còn đáng tin. Đây chính là loại bài toán suy luận khiến người ta phát minh ra chain-of-thought.
Chính sách: khách được hoàn tiền trong 7 ngày kể từ ngày nhận hàng.
Ngày nhận hàng: {received}. Ngày gửi yêu cầu: {requested}.
Hãy suy luận từng bước, rồi ghi kết luận cuối cùng trên một dòng
dạng: KET_LUAN: hop_le hoặc KET_LUAN: khong_hop_le
IBM cảnh báo rằng CoT có thể tạo ra chuỗi suy luận nghe hợp lý nhưng sai. Thử với một trường hợp giả định: nhận hàng ngày 3/10, yêu cầu hoàn tiền ngày 12/10. Khoảng cách là 9 ngày, nên kết luận phải là không hợp lệ. Một đoạn lập luận trơn tru nhưng đếm nhầm ngày vẫn có thể ra kết luận ngược lại.
Vì vậy, phần nào tính được bằng code thì hãy để code tính. Đoạn dưới đây là bản đơn giản hoá:
from datetime import date
def check_refund(received: date, requested: date, days: int = 7) -> bool:
return (requested - received).days <= days
# so với KET_LUAN của model; đếm số lần lệch
assert check_refund(date(2026, 10, 3), date(2026, 10, 12)) is False
Kiểm tra: đếm số lần kết luận của model lệch với hàm kiểm tra. Nếu thường xuyên lệch, đừng cố viết prompt hay hơn. Hãy chuyển phép tính ra khỏi model, và đó cũng là lý do để sang bước 4.
Bước 4: ReAct là nền của agent, nhưng có cái giá phải trả
ReAct cho LLM tạo ra cả chuỗi suy luận lẫn hành động cụ thể theo kiểu xen kẽ, và đây là nền tảng của agent loop hiện nay. Trong ví dụ đang làm, thay vì tự đếm ngày, model sẽ gọi tool tra đơn hàng rồi gọi hàm check_refund ở bước 3.
# Bản đơn giản hoá: call_model, parse_action và TOOLS đều do bạn tự viết
history = [f"Ticket: {text}"]
for _ in range(5): # luôn đặt giới hạn số vòng
step = call_model(REACT_PROMPT + "\n".join(history))
history.append(step) # Thought + Action
action = parse_action(step)
if action.name == "finish":
break
observation = TOOLS[action.name](**action.args)
history.append(f"Observation: {observation}")
Cái được là khả năng diễn giải: từng bước Thought, Action, Observation đều đọc lại được. Cái mất là cấu trúc cứng của ReAct làm giảm độ linh hoạt khi model xây dựng các bước suy luận. Đừng dùng ReAct cho việc phân loại ở bước 1. Hãy dành nó cho những tác vụ thực sự cần dữ liệu bên ngoài.
Kiểm tra: in toàn bộ history của các ticket bị sai. Vì từng bước đều đọc lại được, nên xem trước model có gọi nhầm tool hay hiểu sai observation không, rồi mới tính chuyện sửa câu chữ trong prompt.
Khi đã có agent, nội dung context quan trọng hơn câu chữ
Elastic tách hai khái niệm khá gọn. Prompt engineering là cách bạn giao tiếp với model. Context engineering là những thông tin model có trong tay khi trả lời, gồm tài liệu truy xuất, kết quả tool và lịch sử hội thoại, kèm việc chủ động chọn lọc những gì được đưa vào context window.
Nhìn lại vòng lặp ở bước 4: history cứ dài thêm sau mỗi vòng. Vì thế việc đáng làm tiếp theo không phải là sửa thêm vài từ trong REACT_PROMPT. Bạn nên quyết định observation nào cần giữ nguyên, observation nào chỉ cần tóm tắt, và đoạn chính sách nào cần truy xuất cho từng ticket.
Kỹ năng này giúp gì khi làm việc với khách hàng?
Tuần đầu ở khách hàng, thứ thuyết phục được người ta là một file tickets.jsonl với 20 dòng gán nhãn bằng dữ liệu của họ, kèm bảng điểm so sánh các kỹ thuật. Một demo agent hào nhoáng ít tác dụng hơn thế. Bộ thử đó còn trả lời được câu hỏi khách sẽ hỏi bạn: tại sao không dùng cách đơn giản hơn?
Nếu bạn đang chuẩn bị ứng tuyển FDE, hãy đọc kỹ JD để tìm các cụm như “evaluation”, “prompt and context design” hay “agent reliability”. Trong CV, thay câu “thành thạo prompt engineering” bằng một dòng có số liệu, chẳng hạn: dựng bộ eval 20 ca, chứng minh zero-shot đủ dùng cho bước phân loại, và chuyển phép tính hạn hoàn tiền từ CoT sang code kiểm tra.
Model càng mạnh thì zero-shot càng đi được xa. Phần việc còn lại của FDE nằm ở chỗ biết dừng đúng bước, và có số liệu để chứng minh với khách hàng vì sao dừng ở đó.