Thư viện của Lâm
AI and more Chủ đề ngoài sách

Prompt Evaluation

Đánh giá & đo chất lượng prompt cho ứng dụng LLM

Tinh chỉnh prompt bằng cảm giác ("vibe check") không scale được. Eval biến chất lượng đầu ra thành con số đo được, lặp lại được — để biết một thay đổi prompt là tiến bộ hay thụt lùi, trước khi nó tới tay người dùng.

01 Prompt Evaluation là gì & vì sao cần?

Prompt evaluation là việc đo chất lượng đầu ra của một prompt một cách có hệ thống, trên một tập đầu vào cố định và theo tiêu chí rõ ràng — thay vì đọc vài ví dụ rồi "thấy ổn". Nó là test tự động cho prompt: cùng vai trò mà unit test đảm nhận với code.

Không đo thì không cải thiện được. LLM là phi xác định (non-deterministic) — đổi một câu trong prompt có thể sửa được case này nhưng làm hỏng case khác. Không có eval, bạn đang sửa mù & không biết mình tiến hay lùi.

Lặp lại được

reproducible

Cùng bộ test → cùng cách chấm. So sánh phiên bản prompt A vs B trên đúng một thước đo.

Khách quan

objective

Thay "tôi thấy hay" bằng điểm số & tỉ lệ đạt. Cả nhóm cùng nhìn một bảng kết quả.

Chống thụt lùi

regression-safe

Giữ bộ eval như một "lưới an toàn" — mỗi lần sửa prompt chạy lại để bắt hồi quy.

Ví dụ xuyên suốt: hệ Hỏi–đáp tài liệu (RAG)

Cả trang neo vào một hệ RAG thật: Python lo pipeline (chunk → embed → truy hồi → sinh câu trả lời), PHP lo web/API. Eval đứng bên ngoài pipeline: cho cùng một bộ câu hỏi đi qua hệ, rồi chấm điểm câu trả lời theo tiêu chí, lặp cho tới khi đạt ngưỡng.

flowchart LR
  D[("Eval set: câu hỏi + đáp án vàng")]:::db --> R
  subgraph SUT["Hệ đang đánh giá (RAG)"]
    direction TB
    R["Retrieve (Python)"]:::m --> G["Generate (LLM)"]:::m
  end
  G --> J{{"Chấm điểm: code-based · LLM-judge · human"}}:::core
  J --> S["Điểm số & báo cáo lỗi"]:::out
  S -.->|"sửa prompt rồi chạy lại"| R
  classDef m fill:#dbeee8,stroke:#0f7d72,color:#1c1a14;
  classDef db fill:#e2edf3,stroke:#2f6d93,color:#1c1a14;
  classDef out fill:#e2edf3,stroke:#2f6d93,color:#1c1a14;
  classDef core fill:#14233b,stroke:#14233b,color:#f3ede0;
            
Eval là một vòng lặp: bộ test cố định đi qua hệ → chấm → đọc lỗi → sửa prompt → chạy lại. Bộ test là "hằng số", prompt là "biến".

02 Ba cách chấm điểm

Mỗi cách có vùng dùng tốt riêng — thường kết hợp cả ba: code-based cho cái đo được chính xác, LLM-judge cho cái mở, human để hiệu chuẩn.

Code-based

deterministic / heuristic

Khớp chính xác, regex, JSON hợp lệ, chứa từ khóa, sai số số học. Rẻ, nhanh, khách quan tuyệt đối — nhưng chỉ hợp output có cấu trúc rõ.

LLM-as-judge

model-graded

Dùng một model mạnh chấm theo rubric. Linh hoạt cho output mở (tóm tắt, giải thích) & scale tốt — nhưng cần rubric chặt & có thể lệch (bias).

Human eval

chuẩn vàng

Người chấm là tiêu chuẩn cao nhất nhưng đắt & chậm. Dùng để hiệu chuẩn judge & xử các phán đoán tinh tế, không chạy cho mọi lần.

Cách chấmChi phíTốc độHợp với
Code-basedRất thấpTức thìOutput có cấu trúc: JSON, nhãn phân loại, con số, định dạng.
LLM-as-judgeTrung bìnhNhanhOutput mở: chất lượng văn bản, mức độ bám ngữ cảnh, giọng văn.
HumanCaoChậmCa tinh tế, gây tranh cãi, hoặc để hiệu chuẩn LLM-judge.

RAG  Code-based grader chạy ngay trong harness — không tốn token, không mơ hồ:

Python · grader xác định (deterministic)
def grade_structured(answer: str, expect: dict) -> bool:
    data = json.loads(answer)                     # 1) đúng JSON?
    assert set(data) >= {"answer", "sources"}     # 2) đủ field?
    return (expect["keyword"] in data["answer"]   # 3) chứa ý chính?
            and len(data["sources"]) > 0)         # 4) có trích nguồn?
Mọi thứ đo được bằng luật nên để code chấm — vừa rẻ vừa hết mơ hồ. Đừng phí LLM-judge cho việc json.loads() làm được.

03 Thiết kế bộ eval (eval set)

Bộ eval là tập các cặp (đầu vào, kỳ vọng). Chất lượng eval phụ thuộc vào chất lượng tập này hơn là vào cách chấm. Một bộ tốt phủ bốn loại ca:

Happy path

Câu hỏi thường gặp, có câu trả lời rõ trong tài liệu — phải đúng gần như 100%.

Edge cases

Câu mơ hồ, nhiều phần, hoặc tài liệu chỉ trả lời được một phần — kiểm khả năng "biết thì nói, không thì thừa nhận".

Adversarial

Câu ngoài phạm vi tài liệu, gài bẫy, hoặc cố moi thông tin sai — phải từ chối / nói không biết, không bịa.

Regression

Mỗi lỗi thật từ production được "đóng băng" thành một ca, để không bao giờ tái diễn.

Bắt đầu nhỏ nhưng thật. 20 ca được chọn lọc kỹ phủ đúng các thất bại bạn quan tâm còn giá trị hơn 1.000 ca sinh tự động na ná nhau. Tăng dần khi gặp lỗi mới.

RAG  Mỗi ca khai báo cách chấm riêng — một bộ eval trộn nhiều kiểu grader:

Python · một ca trong eval set
cases = [
  {"q": "Chính sách hoàn tiền bao nhiêu ngày?",
   "expect": {"keyword": "14 ngày"}, "grader": "code"},     # happy path
  {"q": "Giá cổ phiếu công ty hôm nay?",
   "expect": {"must_refuse": True}, "grader": "judge"},      # adversarial: phải từ chối
]
Tách dữ liệu (ca test) khỏi logic (grader). Thêm ca = thêm một dòng, không sửa harness.

04 Tiêu chí chất lượng — "RAG triad"

Với hệ RAG, chất lượng tách thành hai tầng: truy hồi (retrieval) đưa đúng ngữ cảnh chưa, và sinh (generation) dùng ngữ cảnh đó đúng chưa. Ba tiêu chí lõi tạo thành "RAG triad":

Context relevance

truy hồi đúng?

Đoạn tài liệu lấy về có liên quan câu hỏi không. Lỗi ở đây thì khâu sinh "có cố mấy cũng sai".

Groundedness

không bịa

Câu trả lời bám sát ngữ cảnh, không thêm thông tin ngoài tài liệu. Đây là tuyến phòng thủ chính chống hallucination.

Answer relevance

trả đúng câu hỏi?

Câu trả lời thực sự giải quyết câu hỏi, không lạc đề dù vẫn "đúng & có căn cứ".

Bảng tiêu chí thường dùng & trọng số

Không phải tiêu chí nào cũng quan trọng như nhau — gán trọng số theo rủi ro của ứng dụng (càng nhiều chấm càng nặng cân):

Tiêu chíĐo cái gìTrọng số điển hình
Groundedness / FaithfulnessKhông bịa, bám ngữ cảnh.
Answer relevanceTrả đúng trọng tâm câu hỏi.
Safety / từ chối đúng chỗKhông trả lời cái ngoài phạm vi / nguy hại.
Format complianceĐúng JSON / độ dài / cấu trúc yêu cầu.
Tone / styleGiọng văn, mức độ chi tiết phù hợp.
Cost & latencyToken tiêu thụ & thời gian p95.

Đừng quên chi phí & độ trễ. Một prompt "chính xác hơn 2%" nhưng dài gấp đôi & chậm gấp đôi có thể là nước lùi trên thực tế. Luôn đặt chất lượng cạnh token/latency.

05 LLM-as-judge đúng cách & quy trình lặp

LLM-as-judge mạnh nhưng dễ sai nếu làm cẩu thả. Bốn nguyên tắc giữ cho điểm số đáng tin:

Rubric cụ thể

thang điểm rõ

Định nghĩa từng mức điểm bằng tiêu chí quan sát được (1 = bịa, 5 = bám 100% ngữ cảnh), không để judge tự hiểu "tốt".

Bắt giải thích trước

reasoning rồi mới chấm

Yêu cầu judge nêu lý do trước khi cho điểm — chấm "bộc phát" kém tin cậy hơn hẳn.

Hiệu chuẩn với người

calibrate

So điểm judge với điểm người trên một mẫu — chỉ tin judge khi nó đồng thuận cao với con người.

Tránh bias

position / verbosity

Khi so cặp, đảo vị trí A/B; cảnh giác judge thiên vị câu dài hơn hoặc câu do chính model mình sinh ra.

RAG  Judge bằng Claude — chấm groundedness theo rubric, có lý do kèm điểm:

Python · LLM-as-judge (Anthropic SDK)
judge_prompt = f"""Cho NGỮ CẢNH và CÂU TRẢ LỜI. Chấm groundedness 1–5
(1=bịa hoàn toàn, 5=bám 100% ngữ cảnh). Nêu lý do TRƯỚC, rồi 'SCORE: n'.
NGỮ CẢNH: {context}
TRẢ LỜI: {answer}"""
msg = client.messages.create(model="claude-opus-4-8",   # judge nên mạnh hơn model bị chấm
    max_tokens=512, messages=[{"role": "user", "content": judge_prompt}])
score = parse_score(msg.content[0].text)                 # tách 'SCORE: n'
Judge giải thích trước, chấm sau & dùng model mạnh hơn model đang đánh giá → giảm self-preference bias.

RAG  Ranh giới service: web (PHP) chỉ kích hoạt một lần chạy eval & đọc điểm tổng — không tự chấm:

PHP · gọi eval qua HTTP rồi hiển thị
// Web chỉ ra lệnh "chạy bộ eval v3", logic chấm nằm trọn bên Python
$report = $http->post('/eval/run', ['suite' => 'v3'])->json();

echo "Đạt: {$report['pass_rate']}%  ·  Groundedness TB: {$report['groundedness']}";
PHP không nhúng logic chấm — chỉ ở ranh giới: ra lệnh chạy & đọc kết quả. Toàn bộ harness sống trong Python.

Vòng lặp eval-driven

Lập baseline

Chạy prompt hiện tại trên bộ eval, ghi lại điểm gốc để mọi thay đổi đều so được.

Đọc ca lỗi

Soi các ca trượt để tìm nguyên nhân chung — đây là nơi học được nhiều nhất.

Sửa một thứ

Đổi một biến (một câu trong prompt / một tham số) để biết cái gì gây ra khác biệt.

Chạy lại & gác hồi quy

Re-run toàn bộ; chỉ giữ thay đổi khi điểm tổng tăng mà không làm tụt ca nào đang đạt.

Cạm bẫy thường gặp

Nên tránh

  • Overfit eval set: tỉa prompt vừa khít bộ test nhỏ → giỏi trên test, dở ngoài đời.
  • Judge = generator: cùng một model sinh & chấm → thiên vị chính mình.
  • Rubric mơ hồ: "chấm độ hữu ích" mà không định nghĩa → điểm nhiễu, không lặp lại.
  • Chỉ nhìn điểm trung bình: giấu các ca tệ thảm. Luôn xem phân bố & ca xấu nhất.

Nên làm

  • Version hóa eval set: coi nó như tài sản, review khi thêm/sửa ca.
  • Tự động trong CI: chạy eval mỗi lần đổi prompt, chặn merge nếu tụt ngưỡng.
  • Trộn grader: code-based cho cái đo được, judge cho cái mở.
  • Hiệu chuẩn định kỳ: đối chiếu judge với người để nó không "trôi".

06 Ghi nhớ nhanh

Eval là test cho prompt — không đo được thì không cải thiện được; "vibe check" không scale.

Trộn ba cách chấm — code-based cho cái đo chính xác, LLM-judge cho output mở, human để hiệu chuẩn.

Bộ eval > cách chấm — 20 ca chọn lọc phủ đúng thất bại quan trọng hơn 1.000 ca na ná. Mỗi bug thật → một ca regression.

Với RAG, ưu tiên groundedness — chống bịa là tuyến phòng thủ số một; tách eval truy hồi khỏi eval sinh.

Đổi một biến mỗi lần — lập baseline, sửa một thứ, chạy lại, gác hồi quy. Đưa eval vào CI.

Tham khảoTổng hợp từ thực hành đánh giá LLM — Anthropic: Building evals & Test & evaluate, khái niệm "RAG triad" (context relevance · groundedness · answer relevance) & mẫu hình LLM-as-judge. Biên tập tiếng Việt cô đọng cho mục đích ôn tập.