Đây là kiểu hỏng không ai cảnh báo tôi. Tôi chỉnh system prompt của một agent để sửa một chỗ, test nhanh thấy ổn, ship — rồi ba hành vi khác tôi đã kỳ công tinh chỉnh âm thầm regress. Không ai để ý cho tới khi một user đụng phải. Agent thì phi tất định, và hành vi của nó trải đều trên một prompt, một model, một temperature, và một danh sách tool. Bạn không thể nhìn bằng mắt mà biết.
Cách sửa thì cũ rích trong phần mềm và đến muộn trong agent: một bộ test. Ở thế giới này tôi gọi nó là eval harness — một golden set gồm input và thuộc tính kỳ vọng, chạy với mọi agent (và mọi workflow) mỗi lần đổi.
Một eval là một input cộng các check, chạy với một target
Đơn vị rất nhỏ. Một eval nêu một target (một agent hoặc một workflow), một input gửi tới nó, và một danh sách check mà output phải thoả. Chạy nó là chuyện máy móc:
flowchart LR
E["Eval: input + checks"] --> R["Chạy target<br/>(agent hoặc workflow)"]
R --> O["Output + chi phí + độ trễ"]
O --> C{"Áp dụng checks"}
C -->|tất cả pass| P["✅ pass"]
C -->|có cái fail| F["❌ fail + check nào"]
P --> L["Ghi log run"]
F --> LMột lần chạy bắt được nhiều hơn phần chữ: output, chi phí token, độ trễ, và lỗi (nếu có). Nhờ vậy check của tôi khẳng định được cả hành vi lẫn ngân sách — “trả lời đúng” và “dưới $0.02” và “dưới 3 giây”.
Hai loại check: tĩnh và có phán xét
Đa số thuộc tính kiểm bằng code rất rẻ. Vài cái cần phán xét, nên tôi mượn một model khác.
| Check | Khẳng định | Chi phí |
|---|---|---|
contains / not_contains | một chuỗi có (không) xuất hiện | miễn phí |
regex | khớp pattern | miễn phí |
equals / min_len | output đúng hệt / độ dài tối thiểu | miễn phí |
json_valid | output parse được thành JSON | miễn phí |
max_cost / max_ms | trần chi phí và độ trễ | miễn phí |
no_error | lần chạy không văng lỗi | miễn phí |
judge | một LLM chấm theo rubric | một lời gọi model |
Tôi với check tĩnh trước — miễn phí, nhanh, không mập mờ. Riêng json_valid đã tóm được cả một lớp lớn
regression kiểu “model gói output trong văn xuôi”. Judge chỉ dùng cho thuộc tính thật sự mờ: “đây có phải
một lời từ chối lịch sự?”, “nó có trích nguồn không?”
Judge: một người chấm nghiêm khắc, độc lập
LLM-as-judge là một model thứ hai, độc lập với agent đang test, được đưa một rubric và câu trả lời rồi yêu cầu một phán quyết duy nhất. Giữ nó nghiêm và có cấu trúc:
You are a strict evaluator. Given a RUBRIC and an ANSWER, decide whether the
answer satisfies the rubric. Reply with a single word PASS or FAIL, then a
colon and one short sentence.
Temperature 0, token budget ngắn, PASS/FAIL đứng đầu để parse dễ. Tính độc lập là mấu chốt — đừng bao
giờ để một agent tự chấm bài của chính nó bằng prompt của chính nó.
Run-on-change: tấm lưới regression
Một bộ eval mà bạn phải nhớ để chạy là một bộ eval bạn sẽ không chạy. Đòn bẩy là regression tự động: khi tôi sửa các trường ảnh hưởng hành vi của một agent — prompt, model, tool, temperature — bộ eval của nó chạy ngầm và báo cái nào vừa chuyển sang fail.
flowchart LR
Edit["Sửa agent<br/>(prompt / model / tools)"] --> Q{"có eval?"}
Q -->|có| Run["chạy golden set của nó"]
Q -->|không| Skip["bỏ qua"]
Run --> Diff["báo pass/fail<br/>+ Telegram khi regress"]Giờ golden set là một tấm lưới căng dưới mọi thay đổi, không phải một việc vặt. Một kết quả đỏ ngay lúc tôi lưu đáng giá hơn mười kết quả xanh tôi chạy tuần trước.
Chấm cả workflow, không chỉ agent
Eval một-agent là mức tối thiểu. Những lỗi thú vị nằm ở workflow đa-agent — một orchestrator giao việc cho các chuyên gia, hoặc một node graph toả ra rồi gộp lại. Chúng cần cùng cách xử lý: chạy cả workflow trên một input golden, rồi check output tổng hợp cuối cùng.
Mẹo là làm cho target đa hình. target_kind của một eval là agent hoặc workflow; harness chạy đúng
executor và áp dụng đúng bộ check lên bất kỳ thứ gì ra. Nếu eval runner của bạn chỉ biết chạy agent đơn, thì
những hệ phức tạp nhất — và mong manh nhất — lại là những hệ không được chấm. (Đó từng là một lỗ hổng thật
trong bộ của tôi cho tới gần đây; giờ workflow được chấm y như agent.)
Golden set là một bản spec sống
Phần thưởng thầm lặng: eval trở thành định nghĩa chạy-được cho việc mỗi agent dùng để làm gì. “Từ chối yêu cầu lạc đề.” “Luôn trả JSON hợp lệ.” “Trích ít nhất một nguồn.” “Chi phí dưới hai xu.” Mỗi check là một lời hứa, và cả bộ là lời hứa bạn chạy được. Hành vi mới bắt đầu bằng một eval mới; sửa một bug bắt đầu bằng viết cái eval lẽ ra đã tóm được nó.
Bạn không cần một nền tảng để bắt đầu — bạn cần ba cột (input, check, target) và kỷ luật chạy chúng mỗi lần đổi. Agent kiếm được lòng tin cùng cách code kiếm được: không phải bằng vẻ ngoài đúng, mà bằng việc pass.