Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 3 — Xây bộ Evals: chấm điểm agent tự động

Đã đọc 0/10 mục · còn khoảng 4 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

, lặp lại được

Eval (evaluation) là "unit test cho agent": một bộ kịch bản cố định + cách chấm điểm tự động, chạy lại được mỗi khi bạn đổi prompt/model/tool. Không có eval, mỗi thay đổi là canh bạc — bạn sửa prompt cho ca A, vô tình làm hỏng ca B mà không biết. Bài này dựng bộ eval đầu tiên.

Cấu trúc một eval

Một eval gồm 3 phần:

  • Dataset: danh sách các ca thử (test cases), mỗi ca có input + kỳ vọng.
  • Runner: chạy agent trên từng input, thu trace + output.
  • Scorer (grader): chấm mỗi ca đạt/không đạt theo tiêu chí.

3 kiểu chấm điểm — chọn đúng kiểu cho đúng việc

  • Exact / rule-based: so khớp cứng (agent có gọi tool X không? output có chứa mã đơn không?). Rẻ, chắc chắn, dùng cho tín hiệu rõ ràng.
  • LLM-as-judge: dùng một LLM khác chấm câu trả lời theo rubric (dùng khi output tự do, không so cứng được). Mạnh nhưng đắt và có thể lệch — cần rubric chặt.
  • Human: người chấm, chỉ dùng cho mẫu nhỏ để hiệu chỉnh 2 kiểu trên.
Nguyên tắc: ưu tiên rule-based tối đa, chỉ dùng LLM-judge cho phần thực sự cần.

Ví dụ: dataset + scorer bằng code

DATASET = [
  {"id":"c1","input":"Đơn A123 bao giờ giao?",
   "expect":{"must_call":"lookup_order","must_contain":"A123","no_bias_date":True}},
  {"id":"c2","input":"Giá cổ phiếu ngày mai?",
   "expect":{"must_refuse":True}},  # ca gài bẫy: không được bịa
]

def score(case, trace, answer):
    e = case["expect"]
    if e.get("must_call") and e["must_call"] not in tools_used(trace):
        return 0, "thiếu gọi tool bắt buộc"
    if e.get("must_contain") and e["must_contain"] not in answer:
        return 0, "thiếu thông tin bắt buộc"
    if e.get("must_refuse") and not looks_like_refusal(answer):
        return 0, "đáng lẽ phải từ chối/không bịa"
    return 1, "đạt"

Ví dụ: rubric cho LLM-as-judge

Chấm câu trả lời theo 3 tiêu chí, mỗi tiêu chí 0 hoặc 1:
- correct : thông tin có khớp dữ liệu tool trong trace không?
- grounded: có KHÔNG bịa thông tin ngoài trace không?
- helpful : có trả lời đúng ý người hỏi không?
Trả JSON: {"correct":0/1,"grounded":0/1,"helpful":0/1,"reason":"..."}

Các bước xây bộ eval đầu tiên

  • Gom 10-30 ca từ log thật (bài 2) + tự thêm ca gài bẫy (câu mơ hồ, câu nên từ chối, câu cần nhiều bước).
  • Với mỗi ca, viết kỳ vọng máy chấm được (must_call, must_contain, must_refuse, max_steps...).
  • Viết runner chạy agent trên toàn dataset, lưu trace mỗi ca.
  • Viết scorer ưu tiên rule-based; chỉ ca output tự do mới dùng LLM-judge.
  • Chạy → in bảng kết quả: pass rate tổng, và danh sách ca fail kèm lý do.
  • Chốt "baseline": lưu điểm hiện tại làm mốc để so cho lần sau.

Vì sao chạy mỗi ca nhiều lần (n-run)

Vì agent phi tất định, chạy một ca một lần có thể may hoặc rủi. Với ca quan trọng, chạy 3-5 lần và tính pass rate của ca đó (VD: 4/5 = 0.8). Chỉ số này lộ ra sự bất ổn (flakiness): một ca 60% pass nguy hiểm hơn một ca fail hẳn, vì nó đúng lúc demo rồi sai lúc production. Đặt temperature thấp cho phần cần ổn định, nhưng đừng che giấu sự bất ổn — hãy đo nó.

Khung tư duy: cân bằng bộ dataset

Một dataset tốt không chỉ có ca dễ. Cân theo tỷ lệ:

  • ~50% ca phổ biến (happy path).
  • ~30% ca khó/nhiều bước.
  • ~20% ca gài bẫy (nên từ chối, dữ liệu thiếu, input độc hại, câu mơ hồ cần hỏi lại).
Mẹo: đừng ngồi bịa dataset từ đầu. Khai thác log thật (bài 2) là nguồn ca tốt nhất — chúng phản ánh đúng cách người dùng thật diễn đạt, gồm cả những câu bạn không bao giờ tự nghĩ ra.

Sai lầm thường gặp

  • Dataset toàn ca dễ: Pass rate 100% nhưng vô nghĩa — không có ca khó thì eval không bắt được lỗi thật.
  • Lạm dụng LLM-as-judge: Đắt, chậm, và judge cũng ảo giác. Dùng rule-based khi có thể.
  • Rubric mơ hồ cho judge: "Câu trả lời có tốt không?" → điểm dao động lung tung. Rubric phải nhị phân, cụ thể.
  • Không lưu baseline: Không biết thay đổi làm tốt lên hay tệ đi.
  • Eval không lặp lại được: Dùng dữ liệu sống (giá real-time) làm kỳ vọng → hôm nay đúng mai sai. Cố định (freeze) dữ liệu test.

Checklist kết bài

  • [ ] Dataset 10-30 ca, có ca gài bẫy.
  • [ ] Mỗi ca có kỳ vọng máy chấm được.
  • [ ] Scorer ưu tiên rule-based.
  • [ ] Có baseline pass rate đã lưu.
Bài sau: đo cụ thể task completion — chỉ số quan trọng nhất, và các cạm bẫy khi đo nó.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi