FDE PulseViệc làm FDE đang mở 316Mới đăng 7 ngày qua 10Chủ đề nổi bật: Đào tạo kỹ năng FDE tại Đông Nam Á

Tờ báo của nghề Forward Deployed Engineer

Bách khoa

Thực hành: viết bộ eval 50 mẫu cho trợ lý hỏi đáp bằng Python

Năm mươi câu hỏi lấy từ lỗi thật, chấm đúng/sai bằng pytest và một LLM judge đã đối chiếu với nhãn chấm tay: đủ để biến cảm giác "có vẻ ổn hơn" thành một con số cả đội cùng tin.

Đồ hoạSo LLM judge với nhãn chấm tay: bốn ô cần đếm
Judge chấm PASSJudge chấm FAIL
Bạn chấm tay: PASSĐồng thuận: tin được điểm nàyJudge quá khắt khe: xem lại prompt judge
Bạn chấm tay: FAILfalse_pass: câu sai lọt qua, loại lỗi nguy hiểm nhấtĐồng thuận: judge bắt đúng lỗi

Ô nguy hiểm nhất là false_pass: câu bạn chấm sai nhưng judge vẫn cho qua.

Đồ hoạ: FDE Times

Tóm tắt nhanh

  • Anthropic Engineering cho rằng 20-50 tác vụ đơn giản lấy từ lỗi thật là điểm khởi đầu tốt, nên 50 mẫu là con số hợp lý cho bộ eval đầu tiên.
  • Ưu tiên chấm tự động và chấm nhị phân đúng/sai; dùng một model khác với model sinh câu trả lời để làm judge.
  • Trước khi tin LLM judge, hãy coi nhãn chấm tay là ground truth và so điểm của judge với nhãn đó.
Chia sẻLinkedInFacebookX

Theo Anthropic Engineering, 20-50 tác vụ đơn giản lấy từ những lỗi thật đã là một khởi đầu rất tốt cho bộ eval. Không cần hàng nghìn mẫu, cũng không cần một nền tảng eval đắt tiền. Con số đó cho thấy rào cản để có bộ eval đầu tiên thấp hơn nhiều người nghĩ: thứ cần bỏ ra chủ yếu là một buổi chiều ngồi viết.

Với một FDE, buổi chiều đó đáng giá hơn mọi bản demo. Khi khách hàng hỏi “bản prompt mới có tốt hơn không?”, câu trả lời “có vẻ tốt hơn” sẽ không thuyết phục được ai. Một bảng 50 câu kèm tỷ lệ pass trước và sau mới làm được việc đó.

Bài này đi từng bước để dựng một bộ eval 50 mẫu bằng Python cho trợ lý hỏi đáp trên tài liệu nội bộ. Bạn cần Python 3, pytest, và một hàm answer(question) gọi tới trợ lý của bạn. Code dưới đây là bản rút gọn để dạy, không phải framework hoàn chỉnh.

Bước 1: “Trả lời tốt” nghĩa là gì?

Tài liệu của Anthropic khuyên đặt tiêu chí thành công thật cụ thể. Ví dụ của họ là thay “hiệu năng tốt” bằng “phân loại cảm xúc chính xác”. Tiêu chí cũng phải đo được, bằng chỉ số định lượng hoặc thang định tính được định nghĩa rõ.

Áp vào trợ lý hỏi đáp, đừng viết “trả lời hay”. Hãy viết ra ba tiêu chí, chẳng hạn: câu trả lời chứa đúng dữ kiện có trong tài liệu, không bịa khi tài liệu không có thông tin, và không trả lời ngoài phạm vi. Mỗi tiêu chí phải trả lời được bằng có hoặc không.

Kiểm tra sau bước này: đưa ba tiêu chí cho một đồng nghiệp đọc. Nếu họ hỏi lại “thế nào là chính xác?”, tiêu chí của bạn còn mơ hồ.

Bước 2: 50 mẫu lấy từ đâu?

Nguồn tốt nhất là lỗi thật: log, ticket hỗ trợ, tin nhắn phàn nàn của người dùng. Anthropic nhấn mạnh bộ eval nên phản ánh phân bố tác vụ thực tế và không quên edge case. Giả sử 70% câu hỏi thật là về chính sách nghỉ phép, một bộ eval toàn câu về lương sẽ không đo được điều người dùng thực sự cần.

Cũng phải giữ cân bằng. Bài viết của Anthropic Engineering cảnh báo rằng eval một chiều sẽ dẫn tới tối ưu một chiều. Nếu cả 50 câu đều có đáp án, bạn sẽ vô tình thưởng cho một trợ lý luôn trả lời bất kể có biết hay không.

Một cách chia gợi ý, bạn chỉnh theo dữ liệu thật của mình: khoảng 30 câu có đáp án rõ trong tài liệu, 10 edge case như câu hỏi mơ hồ, sai chính tả hoặc trộn tiếng Anh, và 10 câu mà trợ lý phải nói là không có thông tin.

Mỗi mẫu phải qua được phép thử của Anthropic Engineering: hai chuyên gia trong lĩnh vực, chấm độc lập, phải cùng ra một kết luận đạt hay không đạt. Câu nào hai người còn cãi nhau thì sửa đề hoặc bỏ.

Bước 3: Ghi mẫu thành file JSONL

Mỗi dòng là một mẫu. Trường grader cho biết mẫu đó chấm bằng code hay bằng model.

{"id": "q01", "question": "Nhân viên chính thức được nghỉ phép bao nhiêu ngày?", "must_contain": ["12 ngày"], "grader": "code"}
{"id": "q41", "question": "Công ty có hỗ trợ học phí MBA không?", "expect_refusal": true, "grader": "code"}
{"id": "q47", "question": "Tóm tắt quy trình xin nghỉ không lương", "reference": "Gửi đơn cho quản lý trực tiếp...", "grader": "model"}

Nội dung các câu trên là ví dụ giả định. Kiểm tra sau bước này: đếm số dòng đúng bằng 50, và mọi id không trùng nhau.

Bước 4: Phần chấm được bằng code thì dùng pytest

Anthropic ưu tiên những câu chấm tự động được, như so khớp chuỗi hay chấm bằng code. Lý do họ đưa ra: thà có nhiều câu chấm tự động, dù mỗi câu kém chính xác hơn một chút, còn hơn ít câu do người chấm tay thật kỹ.

Hamel Husain cũng mô tả tầng đầu tiên của eval cho LLM đơn giản là các assertion, giống như khi bạn viết pytest.

# test_eval.py — bản rút gọn
import json
import pytest
from assistant import answer  # hàm gọi trợ lý của bạn

with open("eval_50.jsonl", encoding="utf-8") as f:
    CASES = [json.loads(line) for line in f if line.strip()]

CODE_CASES = [c for c in CASES if c["grader"] == "code"]
REFUSAL_MARKERS = ["không có thông tin", "không tìm thấy"]

@pytest.mark.parametrize("case", CODE_CASES, ids=lambda c: c["id"])
def test_code_graded(case):
    out = answer(case["question"]).lower()
    if case.get("expect_refusal"):
        assert any(m in out for m in REFUSAL_MARKERS)
    else:
        for s in case["must_contain"]:
            assert s.lower() in out

Chạy pytest test_eval.py -q. Mỗi mẫu hiện thành một test riêng theo id, nên khi một câu fail, bạn biết ngay là câu nào. Kết quả nào cũng chỉ là đạt hoặc không đạt. Husain nhận thấy chấm điểm theo thang chi tiết khó quản lý hơn nhiều so với chấm nhị phân.

Bước 5: Câu trả lời tự do cần một judge

Câu kiểu “tóm tắt quy trình” không so khớp chuỗi được. Bài hướng dẫn LLM-as-a-judge của Evidently AI dùng judge nhị phân, chấm mỗi câu là đúng hoặc sai, và yêu cầu judge kèm lời giải thích. Tài liệu của Anthropic cũng khuyên nên dùng một model khác với model đã sinh ra câu trả lời để chấm.

JUDGE_PROMPT = """Câu hỏi: {q}
Đáp án tham chiếu: {ref}
Câu trả lời cần chấm: {out}
Câu trả lời có khớp đáp án tham chiếu về nội dung không?
Trả về JSON: {{"verdict": "PASS" hoặc "FAIL", "reason": "một câu"}}"""

def judge(case, out):
    prompt = JUDGE_PROMPT.format(q=case["question"], ref=case["reference"], out=out)
    raw = call_judge_model(prompt)  # placeholder: gọi một model KHÁC model của trợ lý
    return json.loads(raw)

call_judge_model là hàm bạn tự viết bằng SDK của nhà cung cấp mình dùng. Kiểm tra sau bước này: đọc trường reason của 5 câu bất kỳ. Lời giải thích vô nghĩa thường là dấu hiệu prompt judge chưa ổn.

Bước 6: Judge cũng cần được chấm

Đây là bước hay bị bỏ qua nhất. Cách của Evidently AI rất rõ: coi nhãn bạn chấm tay là ground truth, coi nhãn của LLM là dự đoán, rồi so hai bên như đánh giá một bộ phân loại.

human = ["PASS", "FAIL", "PASS", ...]   # bạn tự chấm
judged = ["PASS", "PASS", "PASS", ...]  # judge chấm cùng các câu
agree = sum(h == j for h, j in zip(human, judged)) / len(human)
false_pass = sum(h == "FAIL" and j == "PASS" for h, j in zip(human, judged))
print(agree, false_pass)

Hãy để ý false_pass: những câu sai mà judge cho qua là loại lỗi nguy hiểm nhất. Muốn chấm tay nhanh, Husain nhấn mạnh phải loại bỏ mọi ma sát khi nhìn dữ liệu. Chỉ cần xuất câu hỏi, câu trả lời, verdict và lý do ra một file CSV rồi mở bằng bảng tính là đủ để bắt đầu.

Một lần sửa prompt trông thế nào trên bảng điểm?

Thử hình dung một lần chạy giả định. Bản prompt gốc đạt 31/50. Bạn thêm một chỉ dẫn: nếu tài liệu không có thông tin thì nói rõ là không có, đừng tự suy ra. Chạy lại cả hai phần, bảng điểm thành thế này:

# Số liệu giả định, chỉ để minh họa cách đọc
                   trước    sau
code  (40 câu)     26/40    31/40
model (10 câu)      5/10     7/10
tổng               31/50    38/50

Đừng vội ghi 38/50 vào báo cáo. Bạn chấm tay lại 10 câu do judge chấm và thấy một ca false_pass:

q47  judge: PASS   bạn: FAIL
reason của judge: "Nêu đúng bước gửi đơn cho quản lý trực tiếp."
lỗi thật: câu trả lời bịa thêm một bước duyệt không có trong tài liệu

Nhóm model vì thế thực chất là 6/10, và con số trung thực là 37/50. Lần sửa prompt vẫn là một tiến bộ rõ, nhưng giờ bạn còn biết thêm một việc: prompt của judge cần được dặn kiểm tra cả những chi tiết thừa, không chỉ chi tiết thiếu.

Ba lỗi khiến bộ eval vô dụng

Lỗi đầu tiên là tự nghĩ ra câu hỏi thay vì lấy từ log. Câu do kỹ sư viết thường sạch sẽ và dễ hơn câu người dùng thật gõ, nên tỷ lệ pass đẹp nhưng không nói lên điều gì.

Lỗi thứ hai là thiếu những câu trợ lý phải từ chối trả lời. Thiếu nhóm này, mọi lần sửa prompt cho trợ lý “mạnh dạn hơn” đều trông như tiến bộ. Lỗi thứ ba là dùng chính model của trợ lý làm judge, rồi không bao giờ so judge với nhãn tay.

Tại site khách hàng, bộ eval là hợp đồng

Ở một deployment thật, 50 câu này nên được viết cùng người của khách hàng, vì họ mới là “chuyên gia trong lĩnh vực” đủ tư cách quyết định câu nào đạt. Khi hai bên đã thống nhất bộ câu hỏi, mọi tranh luận kiểu “bot dạo này kém đi” sẽ chuyển thành việc cùng xem câu nào vừa từ pass sang fail.

Ba thành phần trong bài này cũng là ba loại grader mà Anthropic Engineering nói thường được kết hợp: code, model và con người. Nếu bạn đang nhắm tới vị trí FDE, hãy đưa một repo nhỏ như vậy lên GitHub, kèm README ghi bảng điểm trước và sau một lần sửa prompt, và cả ca false_pass bạn đã bắt được.

Khi đọc JD, bạn có thể tìm các cụm như “evaluation” hay “LLM-as-a-judge” để xem vị trí đó có cần kỹ năng này không, rồi đưa repo trên lên đầu phần dự án trong CV.

Lần tới khi ai đó hỏi bản mới có tốt hơn không, bạn sẽ không phải đoán. Chạy bộ eval là có câu trả lời.

4 nguồn
Đọc tiếp trên lộ trình · Chặng 6: Đo lườngĐo ROI cho khách hàng: đếm việc đạt chuẩn, trừ công kiểm tra và sửa lỗiKhi CFO của khách hàng hỏi dự án có đáng tiền không, câu "người dùng thấy nhanh hơn" sẽ không thuyết phục được ai.