Cara Bina Sistem Penilaian Kendiri (Evals) Ejen AI
Bina gelung penilaian ejen AI anda sendiri menggunakan Python dan SQLite. Ketahui cara mengumpul surih, menetapkan semakan deterministik, dan menjejak kadar lulus setiap commit.
Apakah penilaian (evals) kendiri untuk ejen AI
Penilaian kendiri untuk ejen AI terdiri daripada empat perkara yang disimpan dalam repositori anda sendiri: fail kes yang disimpan, skrip yang menjalankan ejen tersebut, set semakan yang menggred setiap jawapan, dan jadual keputusan yang boleh anda buat pertanyaan. Tiada satu pun dalam senarai itu memerlukan vendor. Keseluruhan gelung ini hanyalah beberapa ratus baris kod Python dan satu fail SQLite.
Ejen tersebut berfungsi dalam demo kerana anda memilih sendiri lima input tersebut. Ia gagal pada minggu kedua kerana baris prompt berubah, atau model berubah, atau deskripsi alat berubah, dan tiada ukuran yang meliputi mana-mana perubahan tersebut. Gelung penilaian menukarkan "ia terasa lebih teruk sekarang" kepada "kadar lulus berubah daripada 58 daripada 60 kepada 51 daripada 60 pada commit 4f1c9ab".
Gelung ini mempunyai empat langkah, dan panduan ini mengandungi satu bahagian bagi setiap langkah: kumpul surih (traces) sebenar, promosikan surih yang menarik menjadi kes, gred setiap kes pada setiap perubahan, dan simpan kadar lulus bersebelahan dengan commit yang menghasilkannya. Gelung yang sama berfungsi tidak kira apa yang anda gunakan untuk menjalankan ejen tersebut, dan rangka kerja ejen kendiri yang berbaloi untuk dijalankan kebanyakannya berbeza dari segi jumlah surih yang diberikan kepada anda secara percuma.
Mengapa ejen terhenti pada minggu kedua
Ejen terdiri daripada prompt, model, set definisi alatan, dan sebarang konteks yang diperoleh semasa runtime. Keempat-empat elemen ini boleh berubah tanpa kod aplikasi anda berubah, jadi semakan kod biasa tidak akan mengesan sebarang isu.
Punca paling kerap ialah suntingan pada prompt. Anda menambah satu ayat untuk menghentikan balasan yang kasar. Ayat tersebut mengubah kelakuan terhadap input yang tidak diuji semula, dan jejak (trace) menunjukkannya dengan jelas: jejak minggu lepas untuk soalan yang sama mengandungi panggilan alatan create_refund, manakala minggu ini tiada, dan balasannya hanyalah permohonan maaf yang sopan. Tiada ralat yang tercetus, jadi tiada amaran yang dihantar.
Punca kedua ialah model. Rekodkan rentetan model tepat yang anda hantar bagi setiap pelaksanaan, claude-haiku-4-5-20251001 dan bukannya singkatan yang anda ingat dalam kepala, kerana kadar kejayaan yang menurun pada hari anda menukar model hanya boleh didiagnosis apabila model tersebut direkodkan dalam baris data.
Punca ketiga ialah alatan. Menulis semula deskripsi alatan mengubah masa model memutuskan untuk memanggilnya. Jika alatan anda diperoleh melalui pelayan MCP yang berjalan pada VPS, skema tersebut berada dalam proses lain, jadi ia boleh berubah tanpa sebarang perbezaan (diff) dalam repositori anda. Punca keempat ialah perolehan (retrieval): soalan yang sama mencapai indeks yang dibina semula pada waktu malam, dan jawapannya mengikut dokumen yang baharu.
Bina set emas daripada jejak yang telah anda kumpul
Jangan cipta kes penilaian sendiri. Ambil kes tersebut daripada trafik. Jika anda sudah menjalankan penjejakan Langfuse layan diri untuk ejen anda, setiap permintaan disimpan bersama input, panggilan alat, dan outputnya, yang merupakan bahan mentah tepat yang diperlukan oleh sesuatu kes.
Eksport tetingkap pemerhatian akar melalui API awam. Ia menggunakan pengesahan asas, dengan kunci awam anda sebagai nama pengguna dan kunci rahsia anda sebagai kata laluan.
export LF_HOST="https://langfuse.example.com"
curl -sS -u "$LF_PUBLIC_KEY:$LF_SECRET_KEY" \
"$LF_HOST/api/public/v2/observations?limit=50&isRootObservation=true&fromStartTime=2026-07-01T00:00:00Z" \
| jq '.data[0]'Baca satu rekod sebelum anda menulis sebarang penghuraian. Baris data dikembalikan di bawah data, tetapi nama medan yang memegang soalan dan jawapan bergantung pada cara ejen anda menginstrumentasikan span-nya, jadi petakan apa yang anda lihat sebenarnya dan bukannya apa yang anda jangkakan. Kemudian, tulis kes tersebut secara manual, satu objek JSON bagi setiap baris, dalam evals/cases.jsonl:
{"id": "refund-double-charge", "tags": ["smoke"], "input": "I was charged twice for order 41822.", "must_call": ["lookup_order", "create_refund"], "must_not_include": ["I cannot help"], "rubric": "The reply confirms exactly one refund for order 41822 and states the amount."}Lima peraturan memastikan set tersebut berbaloi untuk dijalankan:
- 40 hingga 80 kes sudah memadai untuk bermula. Di bawah 20, satu kes yang tidak konsisten akan mengubah kadar lulus sebanyak 5 mata, dan angka yang melompat tanpa sebab akan diabaikan.
- Setiap pepijat pengeluaran yang anda baiki menjadi satu kes pada hari anda membaikinya. Tabiat itulah yang menjadikan set tersebut berkembang ke arah yang betul.
- Satu tingkah laku bagi setiap kes. Kes yang menyemak jumlah bayaran balik dan nada suara secara serentak tidak memberikan maklumat berguna apabila ia gagal.
idtidak boleh berubah, kerana id adalah cara larian hari ini dibandingkan dengan bulan lepas.- Lakukan penyuntingan (redact) sebelum anda melakukan commit. Fail ini dimasukkan ke dalam git, jadi buang nama pelanggan dan sebarang nombor pesanan yang bukan milik anda.
Gred dengan semakan deterministik terlebih dahulu, kerana ia percuma
Apa-apa sahaja yang mempunyai jawapan tepat akan menerima asersi mudah. Tiada panggilan model, tiada kos, dan tiada kekaburan. Semakan deterministik mengesan regresi struktur, dan inilah perkara yang merosakkan sistem di sekeliling ejen anda: JSON gagal dihurai, alat tidak pernah dipanggil, frasa terlarang muncul semula, atau jawapan tidak memetik sebarang sumber.
Satu fungsi sahaja yang mengetahui tentang ejen anda. Segala perkara lain dalam harness adalah generik.
import json, os, urllib.request
def run_agent(case):
req = urllib.request.Request(
os.environ["AGENT_URL"],
data=json.dumps({"input": case["input"]}).encode(),
headers={"content-type": "application/json"},
)
with urllib.request.urlopen(req, timeout=120) as resp:
return json.load(resp)
def deterministic(case, result):
text = result.get("output", "")
called = [c["name"] for c in result.get("tool_calls", [])]
failures = []
for tool in case.get("must_call", []):
if tool not in called:
failures.append(f"tool not called: {tool}")
for phrase in case.get("must_not_include", []):
if phrase.lower() in text.lower():
failures.append(f"forbidden phrase: {phrase}")
if len(called) > case.get("max_tool_calls", 12):
failures.append(f"too many tool calls: {len(called)}")
return failuresKekalkan bajet alat dalam senarai tersebut. Ejen yang menyelesaikan kes dalam 3 panggilan hari ini dan 11 panggilan esok telah mengalami regresi walaupun jawapan akhirnya betul, kerana anda membayar bagi setiap panggilan yang dibuatnya.
LLM sebagai penilai, dan empat cara ia boleh tersilap
Apa sahaja yang melepasi asersi memerlukan penilai yang boleh membaca. LLM sebagai penilai merupakan panggilan model kedua: ia menerima soalan, jawapan ejen, dan satu kriteria, kemudian memberikan keputusan. Ini adalah satu-satunya cara praktikal untuk menilai sama ada "jawapan tersebut menjawab apa yang ditanya oleh pengguna".
Empat peraturan menjadikan penilai boleh digunakan:
- Keputusan binari, jangan gunakan skor 1 hingga 10. Skala biasanya memberikan 7 dan 8 untuk hampir semua perkara, jadi nombor tersebut tidak berubah dan anda tidak mempelajari apa-apa daripadanya.
- Satu kriteria bagi setiap panggilan. Tanya tentang jumlah bayaran balik, atau tentang nada, jangan tanya kedua-duanya serentak.
- Berikan jawapan yang dijangkakan kepada penilai apabila kes tersebut mempunyai jawapan yang tepat. Menilai berdasarkan rujukan adalah tugas yang jauh lebih mudah daripada menilai secara abstrak.
- Paksa bentuk output dan laksanakan penghuraian (parsing) dengan ketat.
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY from the environment
def judge_prompt(case, output):
return (
"You grade one answer against one criterion.\n"
"Reply with JSON only, in this exact shape:\n"
'{"verdict": "pass", "confidence": "high", "reason": "one short sentence"}\n'
f"Criterion: {case['rubric']}\n"
f"Question: {case['input']}\n"
f"Answer: {output}\n"
"Length is not a criterion. Judge only the criterion above."
)
def judge(case, output, model):
msg = client.messages.create(
model=model,
max_tokens=200,
messages=[{"role": "user", "content": judge_prompt(case, output)}],
)
return json.loads(msg.content[0].text)Sekarang, mod kegagalan. Setiap satunya mempunyai ujian yang boleh anda jalankan petang ini, dan menjalankannya adalah penting, kerana penilai yang tidak diperiksa menghasilkan nombor yang kelihatan tepat tetapi tidak bermakna.
Bias panjang. Jawapan yang lebih panjang lebih kerap lulus. Ujinya: ambil sepuluh jawapan yang gagal dinilai oleh penilai, tambahkan dua perenggan pengisi yang yakin tetapi tidak menambah fakta baharu pada setiap jawapan, dan nilai semula. Mana-mana keputusan yang bertukar kepada lulus menunjukkan bias panjang, dan rubrik tersebut perlu diperbaiki.
Bias kendiri. Penilai sering memberikan gred yang lebih baik kepada output daripada keluarga modelnya sendiri berbanding output daripada model lain. Ujinya: nilai 30 jawapan yang sama dengan penilai daripada dua keluarga yang berbeza dan bandingkan keputusan kes demi kes. Jika terdapat percanggahan, anda perlu membaca kes tersebut sendiri.
Bias kedudukan. Jika anda menggunakan penilai untuk membandingkan dua jawapan, A dan B, tukar susunannya dan jalankan semula. Keputusan yang bertukar apabila susunan ditukar bermakna perbandingan berpasangan belum selamat untuk digunakan bagi rubrik tersebut.
Drift rubrik. Kriteria yang samar menghasilkan penilai yang terlalu bersetuju. "Adakah jawapan ini membantu" akan meluluskan hampir apa sahaja. "Adakah jawapan ini menyatakan jumlah bayaran balik dalam dolar" hanya akan meluluskan apa yang anda maksudkan. Tulis semula setiap kriteria sehingga ia menamakan fakta yang sedang diperiksa.
Satu langkah kawalan merangkumi keempat-empat perkara ini. Simpan 30 kes yang anda labelkan secara manual, dan skor penilai tersebut berdasarkan label anda setiap kali anda menukar model penilai atau prompt penilai. Jika ia tidak bersetuju dengan anda dalam lebih daripada satu kes bagi setiap sepuluh kes, perbaiki rubrik tersebut sebelum anda mempercayai sebarang kadar lulus yang dihasilkannya. Penilai adalah kod, jadi ia perlu diletakkan dalam kawalan versi dan disemak seperti kod.
Gredkan dengan model murah, tingkatkan kepada model frontier
Menilai setiap kes menggunakan model paling mahal pada setiap commit adalah punca bil penilaian (eval) melebihi kos ejen yang sedang diuji. Susun penilai mengikut harga, dan berhenti sebaik sahaja jawapan menjadi jelas.
The data behind this chart
[
{
"label": "Haiku 4.5, Batch API",
"usd_per_1000_judge_calls": "0.90"
},
{
"label": "Haiku 4.5",
"usd_per_1000_judge_calls": "1.80"
},
{
"label": "Sonnet 5",
"usd_per_1000_judge_calls": "3.60"
},
{
"label": "Opus 5",
"usd_per_1000_judge_calls": "9.00"
}
]Angka-angka tersebut mengandaikan kira-kira 1,200 token input dan 120 token output bagi setiap panggilan penilai, iaitu saiz yang realistik untuk satu soalan, satu jawapan dan satu kriteria. Menilai 1,000 kes menelan kos 1.80 dolar AS menggunakan Claude Haiku 4.5 dan 9.00 menggunakan Claude Opus 5. Jurang ini kelihatan kecil sehingga anda mendarabkannya. Set 60 kes, yang dinilai pada setiap commit, dengan 40 commit seminggu, berjumlah 2,400 panggilan penilai seminggu sebelum sesiapa pun menjalankan kerja harian (nightly job).
Dua diskaun terpakai dengan berkesan untuk kerja penilaian, dan ia boleh digabungkan. Larian penilaian tidak bersifat interaktif, jadi Batch API mengurangkan harga input dan output sebanyak separuh sebagai pertukaran untuk penghantaran tak segerak (asynchronous), yang merupakan baris pertama dalam carta. Rubrik dan arahan adalah sama bait demi bait dalam setiap panggilan, jadi prompt caching sangat sesuai: bacaan cache menelan kos sepersepuluh daripada harga input asas, dan penulisan cache lima minit menelan kos 1.25 kali ganda input asas, jadi cache akan membayar kosnya sendiri selepas satu hit. Ini adalah harga senarai Anthropic setakat Ogos 2026, dan Sonnet 5 berada pada harga pengenalan sehingga 31 Ogos 2026, jadi bar ketiga akan meningkat selepas tarikh tersebut.
Tangga penilaian, mengikut urutan:
- Semakan deterministik pada setiap kes. Tiada kos API langsung.
- Penilai model kecil pada kes yang melepasi semakan tersebut.
- Penilai frontier hanya apabila model kecil menyatakan gagal, atau menyatakan lulus dengan keyakinan rendah.
- Semakan manusia pada sampel kecil, sekali seminggu.
CHEAP = "claude-haiku-4-5-20251001"
STRICT = "claude-opus-5"
def grade(case, result):
hard = deterministic(case, result)
if hard:
return False, "deterministic", "; ".join(hard)
first = judge(case, result["output"], CHEAP)
if first["verdict"] == "pass" and first["confidence"] == "high":
return True, CHEAP, first["reason"]
second = judge(case, result["output"], STRICT)
return second["verdict"] == "pass", STRICT, second["reason"]Strategi ini menukar sedikit ketepatan penggredan dengan kos, jadi ukur pertukaran tersebut dan jangan sekadar mengandaikannya. Sekali sebulan, gredkan keseluruhan set dengan penilai yang ketat dan bandingkan kedua-dua lajur. Jika terdapat perbezaan pada lebih daripada segelintir kes, rubrik anda terlalu longgar untuk model kecil, dan rubrik itulah yang perlu anda perbaiki. Mengawal perbelanjaan ejen itu sendiri adalah tugas berasingan, yang diliputi dalam kawalan kos untuk ejen AI pada VPS.
Jejak kadar lulus dari semasa ke semasa dalam sistem milik anda
Kadar lulus yang tidak boleh dipautkan kepada commit hanyalah satu tanggapan. Simpan satu baris bagi setiap kes untuk setiap larian, dengan menyertakan commit dan model di dalam baris tersebut.
CREATE TABLE IF NOT EXISTS results (
run_id TEXT NOT NULL,
ran_at TEXT NOT NULL,
git_sha TEXT NOT NULL,
agent_model TEXT NOT NULL,
case_id TEXT NOT NULL,
passed INTEGER NOT NULL,
graded_by TEXT NOT NULL,
reason TEXT
);SELECT run_id, git_sha, agent_model,
count(*) AS cases,
round(100.0 * sum(passed) / count(*), 1) AS pass_pct
FROM results
GROUP BY run_id
ORDER BY ran_at DESC
LIMIT 10;Muatkan skema dengan sqlite3 evals/results.db < evals/schema.sql, kemudian baca trend dengan sqlite3 -box evals/results.db < evals/passrate.sql. Larian harian selama setahun bagi 60 kes adalah sekitar 22,000 baris, jadi storan ini tidak akan menjadi projek yang besar. Menjalankan SQLite dalam pengeluaran pada VPS merangkumi tetapan yang mula menjadi penting jika fail ini dikongsi antara mesin.
Runner mencetak maklumat yang sama untuk rujukan manusia:
run 2026-08-05T09:14:22Z sha 4f1c9ab model claude-sonnet-5 58/60 pass (96.7%)
FAIL refund-double-charge deterministic: tool not called: create_refund
FAIL pto-policy-question judge(opus): reply gives no dollar amountJalankan suite pada perubahan yang boleh merosakkan ejen, iaitu suntingan prompt, perubahan model dan perubahan alat, bukannya pada setiap commit di seluruh repositori. Hook pre-push merangkumi subset pantas:
cat > .git/hooks/pre-push <<'EOF'
#!/bin/sh
python3 evals/run.py --set smoke || exit 1
EOF
chmod +x .git/hooks/pre-pushLarian penuh adalah lebih perlahan dan perlu dijadualkan. Servis dan pemasa systemd pada VPS yang berjalan setiap malam akan menjalankan keseluruhan set terhadap prompt yang telah di-deploy, yang merupakan cara untuk mengesan perubahan yang datang dari luar repositori anda, seperti alat hos yang kelakuan fungsinya telah berubah.
Semakan manusia, secara sampel dan bukannya menyeluruh
Hakim ditentukur berdasarkan label manusia, jadi seseorang perlu menghasilkannya. Baca satu sampel setiap minggu: setiap kes yang gagal dinilai oleh hakim, ditambah sepuluh kes lulus yang dipilih secara rawak. Kes lulus rawak adalah separuh yang penting, kerana hakim yang secara senyap mula meluluskan jawapan yang buruk akan kelihatan sempurna pada mana-mana papan pemuka yang dibina daripada keputusan hakim itu sendiri.
Lima belas kes dengan tempoh tiga minit setiap satu mengambil masa 45 minit seminggu, dan ia memberikan pembetulan kepada rubrik di mana anda dan hakim tidak bersetuju, serta kes baharu bagi jenis kegagalan yang tidak terfikir oleh sesiapa pun. Tulis keputusan manusia ke dalam jadual yang sama dengan graded_by ditetapkan kepada human, supaya persetujuan antara hakim dan manusia menjadi satu pertanyaan (query) dan bukannya sekadar ingatan.
Perkara yang menyebabkan kegagalan dalam harness penilaian
anthropic.RateLimitError pada larian penuh pertama. Sebanyak 60 kes yang dijalankan serentak melebihi had permintaan atau token untuk tier anda. Hadkan konkurensi kepada empat pekerja, dan pindahkan larian malam ke Batch API.
json.JSONDecodeError: Expecting value: line 1 column 1 (char 0) daripada hakim. Model membalas dalam bentuk prosa, atau membungkus JSON di dalam blok kod. Cuba semula sekali, kemudian rekodkan kes tersebut sebagai ralat. Jangan sekali-kali membiarkan kegagalan penghuraian (parse failure) dikira sebagai lulus, kerana suite yang menukarkan ralat kepada lulus akan meningkat ke arah 100% sedangkan prestasi ejen semakin merosot.
Kes tidak konsisten (flaky cases). Input yang sama lulus pada satu larian tetapi gagal pada larian seterusnya, kerana ejen melakukan pensampelan terhadap outputnya. Jalankan kes yang tidak konsisten itu sebanyak tiga kali dan rekodkan pecahan (fraction) dan bukannya memadamkan kes tersebut. Kes yang lulus dua daripada tiga larian merupakan pepijat keteguhan (robustness bug) yang sebenar, dan pelanggan akan menemuinya.
Pereputan set rujukan (golden set rot). Seseorang menyunting jawapan jangkaan untuk menjadikan suite tersebut hijau. Semak diff kepada evals/cases.jsonl dengan teliti seperti anda menyemak diff kepada ejen, kerana fail tersebut merupakan definisi bertulis anda tentang perkara yang betul.
Suite yang tidak pernah gagal. Kadar lulus yang kekal pada 100% selama sebulan bermakna set tersebut telah berhenti menjejaki produk. Ambil sepuluh trace terkini, cari trace yang dikendalikan dengan buruk oleh ejen, dan tambahkannya ke dalam set.
FAQ
Berapakah jumlah kes yang diperlukan untuk set penilaian ejen AI?
Mulakan dengan 40 hingga 80 kes dan tambah set tersebut berdasarkan kegagalan sebenar. Di bawah 20 kes, satu keputusan yang tidak konsisten (flaky) akan mengubah kadar lulus sebanyak 5 mata, menyebabkan data tersebut tidak lagi bermakna. Melebihi beberapa ratus kes, setiap pelaksanaan akan memakan kos dan masa yang nyata, manakala kes tambahan hanya memberikan liputan yang minimum. Ukuran yang penting bukanlah jumlahnya: ia adalah peratusan jenis kegagalan pengeluaran (production failure) anda yang diketahui yang muncul dalam set tersebut sekurang-kurangnya sekali.
Bolehkah saya mempercayai hakim LLM untuk menilai ejen saya?
Hanya selepas anda mengukurnya berbanding label anda sendiri. Simpan 30 kes yang anda nilai secara manual, dan nilaikan hakim tersebut terhadap kes berkenaan setiap kali anda menukar model hakim atau prompt hakim. Hakim menunjukkan bias kepanjangan, di mana jawapan yang ditambah perkataan sering lulus dengan lebih kerap, dan keutamaan kendiri, di mana output daripada keluarga model mereka sendiri dinilai dengan lebih baik. Kedua-duanya boleh diuji: tambah perkataan pada jawapan yang gagal dan nilai semula, atau nilai jawapan yang sama dengan hakim daripada keluarga model yang lain. Jika hakim tidak bersetuju dengan label anda bagi lebih daripada satu kes dalam sepuluh, rubrik tersebut terlalu samar untuk digunakan.
Model manakah yang patut menilai penilaian (evals)?
Nilai dengan kos murah dan tingkatkan tahap jika perlu. Penegasan deterministik (deterministic assertions) tidak menelan kos, jadi ia dijalankan terlebih dahulu pada setiap kes. Model kecil mengendalikan kes yang jelas lulus. Hanya kegagalan dan keputusan dengan keyakinan rendah dihantar kepada model frontier. Berdasarkan harga senarai pada Ogos 2026, menilai 1,000 kes menelan kos kira-kira 1.80 dolar AS dengan Claude Haiku 4.5 dan kira-kira 9.00 dengan Claude Opus 5, dan kerana pelaksanaan penilaian adalah tak segerak (asynchronous), Batch API akan mengurangkan kedua-dua angka tersebut kepada separuh.
Adakah penilaian menggantikan pemantauan pengeluaran (production monitoring)?
Tidak, kerana ia menjawab soalan yang berbeza. Suite penilaian memberitahu anda sama ada perubahan yang bakal anda lancarkan menjadikan set kes tetap lebih baik atau lebih buruk. Penjejakan (tracing) dan pemantauan memberitahu anda apa yang dihadapi oleh pengguna sebenar sekarang, termasuk input yang tidak diliputi oleh mana-mana kes. Kedua-duanya saling melengkapi: penjejakan membekalkan kes baharu, dan suite penilaian menentukan sama ada pembetulan anda benar-benar berkesan.