Cải thiện LLM agent: độ chính xác, chi phí, cascade & routing
TL;DR
- Không có evaluator + CI thì không biết một thay đổi giúp hay hại. Mọi cải thiện đều qua đo lường.
- Accuracy trước, cost sau. Accuracy đi theo thang công sức: prompt → kiến trúc → model. Vắt kiệt bậc rẻ trước.
- Cost: model vừa đủ cho từng bước, cắt token, prompt thân thiện cache, batch việc offline, distillation.
- Cascade: model rẻ (proxy) trả lời trước, chỉ đẩy lên model đắt (oracle) khi độ tự tin dưới ngưỡng — ngưỡng hiệu chỉnh offline trên tập held-out.
- Routing: classifier chọn model ngay từ đầu — nhanh hơn, nhưng route sai thì không có cơ hội thứ hai. Kết hợp: router cho ca rõ, cascade cho vùng mơ hồ.
12.1Nguyên tắc: đo rồi mới sửa
Evaluator (Ch.5) và monitoring/CI (Ch.9) là thước đo cho mọi thay đổi ở chương này.
Chỉ tối ưu chi phí khi chất lượng đã đạt yêu cầu — và kiểm lại chất lượng sau mỗi lần cắt giảm.
Sửa lỗi này có thể đẻ ra lỗi khác. Mỗi thay đổi phải chạy lại toàn bộ bộ eval.
Thang công sức cho accuracy
12.2Công sức thấp: tinh chỉnh prompt
Sửa lỗi specification trước tiên
Nhiều lỗi thuộc Gulf of Specification: prompt không nói rõ, model đoán hợp lý nhưng sai. Ví dụ Người dùng hỏi "homes gần trung tâm", agent trả cả căn hộ lẫn nhà liền kề — vì prompt không định nghĩa "homes". Thêm một dòng mặc định (homes = nhà riêng, trừ khi người dùng nói khác) là xong.Sửa spec trước khi xây evaluator: đo khả năng generalize trên một tác vụ chưa định nghĩa rõ là lãng phí.
Mẹo từ tác giả: trước khi viết evaluator nào, lướt 10 trace tệ nhất và tự hỏi prompt có thực sự nói điều mình muốn không. Theo kinh nghiệm của họ, khoảng một nửa lỗi giai đoạn đầu biến mất chỉ với một dòng sửa prompt.
2–3 cặp input/output nhắm vào lỗi đã thấy — nhưng không trùng test case CI, nếu không điểm eval phồng lên mà hành vi thật không đổi.
Có thư viện ví dụ lớn → mỗi query lấy vài ví dụ giống nhất theo embedding, thay vì nhồi tất cả.
"Bạn là cố vấn thuế cẩn trọng" — định hướng giọng và cách suy luận, nhất là với tác vụ mở.
Giúp tác vụ nhiều bước. Với reasoning model (o-series, R1…) thì đừng bảo "think step by step" — chỉnh thinking budget thay vì vậy.
12.3Công sức trung bình: đổi cách agent vận hành
| Kỹ thuật | Ý tưởng | Đánh đổi / lưu ý |
|---|---|---|
| Chia nhỏ tác vụ | Thay một lời gọi làm tất cả bằng chuỗi bước: trích ràng buộc → truy vấn → lọc/tóm tắt → sinh câu trả lời | Mỗi bước eval riêng được → lỗi khoanh vùng tới đúng bước |
| Tinh chỉnh RAG | Viết lại query, đổi cách chunk, số chunk, thêm reranker nhẹ | Retrieval thường quyết định chất lượng nhiều hơn generation (đo theo Ch.7) |
| Sửa mô tả tool | date: string → ghi rõ định dạng YYYY-MM-DD kèm ví dụ | Một chỗ sửa, hết lỗi tham số cho mọi query đặt lịch |
| Self-consistency | Sample nhiều lần (temperature > 0), lấy đáp án đa số | Tốn nhiều lời gọi → dành cho quyết định stakes cao |
| Bước verify | Lời gọi thứ hai kiểm output lần một (khớp dữ liệu? đúng persona?) | Gấp đôi latency/cost → chỉ cho output quan trọng |
| Guardrail | Check đồng bộ trước khi tới người dùng: regex bắt chuỗi giống số an sinh xã hội, parser từ chối SQL sai cú pháp | Rẻ, nhanh, chặn những lỗi xấu hổ nhất |
Case study: DoorDash — sắp xếp lại thứ model đã có
12.4Công sức cao: can thiệp ở mức model
Khi nào: có hàng trăm ví dụ chất lượng và prompt không cứu được.
- LoRA: chỉ train các ma trận adapter hạng thấp thay vì toàn bộ tham số → một GPU (A100/H100), vài giờ.
- QLoRA: lượng tử hoá base model xuống 4-bit khi train → fine-tune model 70B trên một GPU 48GB.
- Tác vụ hẹp: thường 50–200 ví dụ tốt đã tạo khác biệt đáng kể.
Chi phí ẩn phải serve model (vLLM/TGI tự host, hoặc Together, Fireworks, Modal). Tiết kiệm từ model nhỏ phải lớn hơn chi phí train + serve + bảo trì.
DPO, KTO: train trên cặp output — một cái được ưa hơn cái kia.
Hợp với lỗi phong cách/chất lượng ("đúng nhưng dài dòng") hơn là lỗi đúng/sai.
Coi prompt là tham số tối ưu được: sinh biến thể, chấm trên golden set held-out, giữ cái tốt nhất. DSPy, TextGrad, OPRO tự động hoá việc tìm kiếm, dùng metric eval (Ch.5) làm hàm mục tiêu.
Điều kiện hàm eval rõ ràng + đủ ví dụ gán nhãn. Tác giả khuyên chờ tới khi đã vắt kiệt các cách nhanh — khó debug, khó bảo trì.
Stakes cao hoặc lỗi khó phát hiện tự động → giao diện gọn (Ch.10) để người review gán nhãn một mẫu nhỏ đều đặn.
Nhãn vừa thành dữ liệu fine-tune, vừa lộ ra failure mode mà evaluator tự động bỏ sót.
12.5Tối ưu chi phí
Cùng công thức như accuracy: tìm nguồn tốn tiền lớn nhất, sửa có mục tiêu, rồi dùng evaluator xác nhận chất lượng không tụt.
Model nhỏ cho phân loại intent, trích field, sentiment. Model lớn cho reasoning phức tạp, stakes cao.
Instruction gọn; bớt tài liệu RAG; model rẻ tóm tắt tài liệu dài một lần cho nhiều lời gọi sau; output ngắn, định dạng gọn (YAML ít ký tự hơn JSON).
Phần cố định lên đầu, phần thay đổi xuống cuối (xem sơ đồ dưới).
Việc không gấp (chạy evaluator cả ngày, chấm dataset CI) → batch API, vd OpenAI rẻ một nửa, trả trong 24h.
Model lớn sinh nhãn trên dữ liệu chưa gán → fine-tune model nhỏ bắt chước.
Vì sao thứ tự trong prompt quyết định cache hit
Provider lưu KV state (key/value mà Transformer tính cho từng token) của các prefix đã gặp. Request mới bắt đầu bằng đúng y hệt chuỗi token đó thì dùng lại, chỉ tính phần đuôi. Anthropic công bố cache hit tốn 10% giá input và giảm tới 85% latency với prompt dài.
- Instruction cố định lên trước, dữ liệu người dùng/biến đổi xuống cuối.
- Giữ định dạng, cách diễn đạt, khoảng trắng nhất quán giữa các request.
- Tra bảng giá cache của providerMức giảm cho token cached khác nhau giữa các nhà cung cấp.
Giới hạn của distillation
Student không vượt teacher và học luôn lỗi của teacher; thường calibration kém trên input hiếm hoặc lệch phân bố (vì chúng ít xuất hiện trong dữ liệu teacher sinh ra). Eval student chặt như mọi thay đổi khác và so hồ sơ lỗi với teacher. Cần hạ tầng serving → hợp với khối lượng lớn; khối lượng nhỏ thì cascade có thể kinh tế hơn vì không phải train/host model riêng.12.6Model cascade
Bối cảnh Một LLM-as-judge tốn $0.02/trace × 10.000 trace/ngày = $200/ngày. Muốn giữ chất lượng mà chi ít hơn.
Trả lời trước. Phải cho ra verdict nhị phân + điểm tự tin để đặt ngưỡng.
Không cần hoàn hảo — chỉ là hệ thống tốt nhất hiện có. Cascade cố tái tạo câu trả lời của nó với chi phí gần proxy.
Tỷ lệ đồng thuận tối thiểu giữa cascade và oracle, vd 95%. Mục tiêu: đạt r với ít lời gọi oracle nhất.
Ước lượng độ tự tin (confidence)
Đừng tin confidence theo mặt chữ
Dù lấy từ logprobs hay để model tự khai, confidence chỉ là một tín hiệu nhiễu, không phải thước đo đúng/sai. Câu hỏi cần trả lời: khi proxy báo tự tin cao, nó có thường khớp oracle không? Tin tốt: quy trình tìm ngưỡng tự bảo vệ — nếu confidence không tách được khớp/không khớp thì sẽ không tìm ra ngưỡng, và mọi thứ đi oracle (không tiết kiệm, nhưng không hỏng gì).Với tác vụ đáp án là một token (true/false), API trả logprob (log tự nhiên của xác suất) cho top 5–20 token. Đổi về xác suất bằng exp(), chuẩn hoá cho tổng bằng 1, lấy giá trị lớn hơn làm confidence.
| Token | logprob | exp() | Chuẩn hoá |
|---|---|---|---|
true | −0.105 | 0.90 | 0.90 ← confidence |
false | −2.303 | 0.10 | 0.10 |
import math
top = {"true": -0.105, "false": -2.303} # logprob của 2 nhãn hợp lệ
p = {k: math.exp(v) for k, v in top.items()}
z = sum(p.values())
label = max(p, key=p.get)
confidence = p[label] / z # ≈ 0.90 → rất tự tin
Giới hạn Output dài nhiều token → không có "xác suất của cả câu trả lời" để so; cách này không dùng được.
Yêu cầu structured output hai trường: answer (câu trả lời mở) và confidence (số nguyên 0–10), ép bằng JSON Schema qua API structured output của provider.
- Tác giả thấy tương đối ổn với phân loại; kém tin cậy với output mở.
- Model đã qua RLHF/instruction-tuning thường tự tin quá mức; base model calibrate tốt hơn (Tian et al. 2023).
- Output mở còn thiếu một thứ: biết khi nào proxy "khớp" oracle — so chuỗi không được. Cần một LLM-judge đã được kiểm (Ch.5) để chấm độ khớp offline, hoặc nhãn người.
12.7Hiệu chỉnh ngưỡng cascade offline
- Chuẩn bịTập held-out N ví dụ, target đồng thuận r (vd 0.95).
- Chạy proxy trên cả NLưu câu trả lời + confidence.
- Chạy oracle trên cả NLưu câu trả lời — coi là "ground truth" cho mục đích cascade.
- Đánh dấu khớp / không khớpcho từng ví dụ.
- Sắp theo confidence của proxy, cao → thấpĐi dần xuống, tính tỷ lệ khớp cộng dồn từ đầu tới vị trí i.
- Ngưỡng t= confidence tại vị trí cuối cùng mà tỷ lệ khớp cộng dồn vẫn ≥ r.
Viết thành code (minh hoạ, tự viết):
def calibrate(conf, proxy_ans, oracle_ans, r=0.95):
rows = sorted(zip(conf, proxy_ans, oracle_ans), key=lambda x: -x[0])
hits, t = 0, None
for i, (c, p, o) in enumerate(rows, start=1):
hits += (p == o) # output mở: thay bằng judge(p, o)
if hits / i >= r:
t = c # vẫn đạt target tới vị trí i
return t # None → không có ngưỡng: gửi hết cho oracle
# runtime: answer = proxy if proxy_conf >= t else oracle(query)
Xếp chuỗi từ rẻ đến đắt; mỗi bậc một ngưỡng, hiệu chỉnh cùng cách. Dừng ở bậc đầu tiên đủ tự tin hoặc tới oracle.
FrugalGPT (Chen et al. 2023) — cascade LLM đạt chất lượng GPT-4 với một phần nhỏ chi phí. Task cascades (Shankar et al. 2026) — đổi cả việc được giao ở mỗi bậc (bậc rẻ trả lời phiên bản đơn giản của câu hỏi).
12.8Routing
Mọi query qua model rẻ trước, escalate nếu confidence thấp.
- ✓ Có "lưới an toàn": proxy không chắc → oracle vẫn xử lý.
- ✗ Query bị escalate đi qua ≥2 model → chậm hơn và tốn thêm lượt proxy.
- Cần: confidence được hiệu chỉnh.
Classifier quyết định trước gửi query tới model nào — không thử model rẻ trước. Vd "giá căn này bao nhiêu?" → model nhanh, rẻ; "so sánh 3 căn theo trường học, thời gian đi làm, xu hướng giá" → model mạnh.
- ✓ Một lượt → không có phạt latency.
- ✗ Cần classifier đáng tin; route sai không có cơ hội thứ hai.
Router xử lý ca hiển nhiên, cascade xử lý vùng lưng chừng — tận dụng ưu điểm của cả hai.
Giải pháp có sẵn: RouteLLM (Ong et al. 2024, mã nguồn mở, train trên dữ liệu preference của Chatbot Arena; báo cáo giảm 85% chi phí trên MT-Bench mà giữ 95% chất lượng GPT-4), Model Router của Microsoft Foundry, Martian.
12.9Chọn chiến lược cải thiện
| Chiến lược | Công sức | Dữ liệu cần | Hợp nhất cho |
|---|---|---|---|
| Tinh chỉnh prompt | Thấp | Vài ví dụ lỗi | Lỗi spec, định dạng, thiếu ràng buộc |
| Đổi kiến trúc (chia bước, retrieval, tool) | Trung bình | Hiểu pattern lỗi | Lỗi suy luận nhiều bước, thiếu thông tin, workflow phức tạp |
| Tối ưu prompt tự động (DSPy, TextGrad) | Trung bình | ≥50 ví dụ gán nhãn + hàm eval | Khi sửa prompt thủ công chững lại |
| PEFT (LoRA/QLoRA) | Cao | 50–200+ ví dụ | Tác vụ hẹp, prompt không cứu được lỗi generalization |
| Full fine-tune / DPO | Cao | 500+ ví dụ hoặc cặp preference | Style, tone, format trên nhiều tác vụ; khi PEFT chưa đủ |
| Distillation + cascade | Cao | Dataset lớn + oracle | Giảm cost trên tác vụ khối lượng lớn, đã hiểu rõ |
→ Đi từ trên xuống. Mỗi hàng tốn công và dữ liệu hơn hàng trên — vắt kiệt lựa chọn rẻ trước.
12.10Vòng đời tiếp diễn & pitfalls
Mọi cải thiện có thể dịch chuyển phân bố lỗi: sửa prompt cho lỗi này làm nảy lỗi khác; route sang model rẻ giảm chất lượng một cách âm thầm. Dataset eval cũ dần, taxonomy cần xem lại, evaluator mới ra đời khi lỗi mới lộ diện — và mọi thay đổi đi qua CI. Ngay cả một vòng lặp thô (mỗi tuần đọc vài trace, cập nhật golden set, sửa prompt tệ nhất) cũng cộng dồn giá trị theo thời gian.
- Dùng test case CI làm few-shot. Điểm eval phồng lên, hành vi trên input mới không đổi.
- Bảo reasoning model "think step by step". Có thể phá suy luận nội tại — chỉnh thinking budget thay vào.
- Nhảy thẳng lên fine-tune. Chưa vắt kiệt prompt và kiến trúc; tốn dữ liệu, hạ tầng serving, khó debug.
- Tin confidence theo mặt chữ. Luôn hiệu chỉnh ngưỡng offline so với oracle.
- Đặt dữ liệu biến đổi trước instruction. Mất toàn bộ lợi ích prefix cache.
- Coi student distill là "bằng teacher". Nó thừa hưởng lỗi của teacher và yếu ở input hiếm — so hồ sơ lỗi.
- Tối ưu cost mà không chạy lại eval. Model rẻ có thể làm tụt chất lượng ở một phân khúc mà trung bình không lộ ra.
Áp dụng cho dự án model-routing của bạn
Chương này gần như là bản thiết kế cho bộ eval của bạn. Ý cốt lõi: eval router = bài toán hiệu chỉnh offline. Nếu bạn có sẵn một ma trận offline — mỗi request trong tập held-out đã chạy qua mọi model ứng viên, lưu output, token, cost, latency, điểm chất lượng — thì bất kỳ chính sách nào (router, cascade ở mọi ngưỡng, lai) đều mô phỏng được bằng tra bảng, không cần gọi lại API, và tái lập được hoàn toàn.
Quy trình đề xuất
- Tập held-out phân tầngLấy mẫu request thật theo loại tác vụ / độ khó / độ dài (dùng kỹ thuật Ch.11 để tìm phân khúc). Tách hẳn khỏi dữ liệu dùng để train/tune router.
- Chạy ma trận model × requestMọi model ứng viên trên mọi request, cố định prompt/temperature/seed nếu có; lưu output, token vào/ra, cost, latency.
- Định nghĩa "đạt"Verdict nhị phân từ một LLM-judge đã được kiểm với nhãn người (Ch.5): "output này đạt yêu cầu?" hoặc "tương đương output của oracle?". Output mở không so chuỗi được.
- Mô phỏng chính sáchBaseline: luôn-rẻ, luôn-đắt, random cùng tỷ lệ trộn. Ứng viên: router, cascade quét ngưỡng t, lai router + cascade. Thêm router lý tưởng (với mỗi request chọn model rẻ nhất vẫn đạt) làm cận trên.
- Hiệu chỉnh ngưỡngDùng đúng thuật toán 12.7 cho điểm tự tin của router ("gửi xuống model rẻ") và của proxy trong cascade. Không tìm được ngưỡng đạt r → mặc định route lên model mạnh.
- Chọn trên đường ParetoChính sách rẻ nhất vượt sàn chất lượng đã thống nhất, báo cáo theo từng phân khúc chứ không chỉ trung bình.
- Canh gác productionTheo dõi % traffic mỗi bậc, tỷ lệ escalate, drift phân bố request; hiệu chỉnh lại khi đổi model, đổi giá, hoặc phân bố request dịch chuyển.
Bảng metric nên báo cáo
| Metric | Định nghĩa | Vì sao cần |
|---|---|---|
| Quality retention | Tỷ lệ đạt của chính sách ÷ tỷ lệ đạt của luôn-đắt | Con số "giữ X% chất lượng" so được với RouteLLM |
| Agreement với oracle | % request mà output chính sách được judge coi là tương đương oracle | Chính là target r của cascade |
| Cost / 1K request & % tiết kiệm | So với luôn-đắt | Trục ngang của đường Pareto |
| Misroute xuống | Gửi model rẻ, model rẻ trượt trong khi model mạnh đạt | Lỗi chất lượng — với router thuần là không cứu được |
| Misroute lên | Gửi model mạnh dù model rẻ cũng đạt | Tiền lãng phí; khoảng cách tới cận trên |
| Latency p50 / p95 | Tính cả lượt proxy của query bị escalate | Cascade có thể làm p95 xấu đi dù trung bình tốt |
| Phân theo phân khúc | Mọi metric trên, tách theo loại request / độ khó | Trung bình giấu lỗi ở phân khúc hiếm |
Bẫy riêng cho eval routing
- Rò dữ liệu: router được train/tune trên chính tập dùng để đánh giá → kết quả đẹp giả. Giữ held-out tách biệt (giống quy tắc "few-shot ≠ test case CI").
- Judge thiên vị model lớn: judge có thể ưu ái văn phong dài, trau chuốt của model đắt. Kiểm judge với nhãn người trên chính các cặp (rẻ vs đắt).
- Oracle không phải chân lý: agreement đo "giống oracle", không phải "đúng". Với phân khúc quan trọng, kèm thêm tỷ lệ đạt tuyệt đối.
- Confidence của router chưa calibrate: điểm "độ khó" của classifier cũng nhiễu như confidence của proxy — tìm ngưỡng theo 12.7, đừng đặt tay 0.5.
- Ma trận cũ: đổi model, đổi giá, hay phân bố request dịch chuyển → chạy lại ma trận; đường Pareto sẽ khác.
Tự kiểm tra
Agent trả cả căn hộ khi người dùng hỏi "homes". Nên xây evaluator đo tần suất lỗi này ngay không?
Vì sao đặt tên người dùng ở đầu prompt, trước instruction dài, lại làm tăng chi phí?
Proxy trả true với logprob −0.105 và false với −2.303. Confidence bằng bao nhiêu?
true.