LLM-as-judge (และเมื่อไร deterministic ดีกว่า)
บทที่ 3 สรุปว่า eval ควรตัดสินที่ ผลลัพธ์จริง ไม่ใช่คำบรรยายของ agent เอง — “มีแถวคืนเงินของออเดอร์ #123 ในฐานข้อมูลจริง จำนวน ฿120 หรือยัง” ไม่ใช่ “agent พิมพ์ว่า ‘คืนเงินเรียบร้อย’ หรือยัง” บทนี้ถามคำถามถัดไป: แล้วใครหรืออะไรเป็นคน “ให้คะแนน” ผลลัพธ์นั้น? มีสองสายให้เลือก — เขียน code เช็คตรงๆ แบบ deterministic หรือใช้ LLM อีกตัวมาตัดสินแทนมนุษย์ และการเลือกผิดสายทำให้ทั้ง eval suite เชื่อถือไม่ได้เงียบๆ
บทนี้ยังใช้ ops-copilot agent ที่คืนเงินออเดอร์ใน domain Order จาก repo kaen-food-ordering (กำลังจัดทำ) เป็นตัวอย่างเดินเรื่อง — เป็นบทเชิงวิธีคิด สเก็ตช์ code (rubric / prompt ของ judge) ที่เห็นเป็นภาพร่างเพื่อสื่อไอเดีย ไม่ใช่ grader ที่รันได้ทันที
เลือก deterministic grader ก่อนเสมอถ้าทำได้
หัวข้อที่มีชื่อว่า “เลือก deterministic grader ก่อนเสมอถ้าทำได้”การเช็คคืนเงินในบทที่แล้วเป็น grader แบบ deterministic อยู่แล้วโดยไม่รู้ตัว: อ่านแถวจากฐานข้อมูลแล้วเทียบว่า amount === 120 — input เดิมให้ผลตัดสินเดิมทุกครั้ง Anthropic แนะนำให้เริ่มจากสายนี้เสมอ “choose deterministic graders where possible, LLM graders where necessary or for additional flexibility” — เลือก grader แบบ deterministic ทุกที่ที่ทำได้ แล้วค่อยใช้ LLM grader เมื่อจำเป็นหรือเมื่อต้องการความยืดหยุ่นเพิ่ม (ดู Anthropic, “Demystifying evals for AI agents”)
เหตุผลอยู่ในตารางข้อดี-ข้อเสียที่ Anthropic ให้ไว้ตรงๆ: deterministic grader นั้น Fast, Cheap, Objective, Reproducible, Easy to debug — เร็ว ถูก ตัดสินเป็นกลาง รันซ้ำได้ผลเดิม และ debug ง่ายเพราะเห็นเงื่อนไขที่ตัดสินชัดๆ แต่มันมีจุดอ่อนข้อเดียวที่สำคัญ: “Brittle to valid variations that don’t match expected patterns exactly” — เปราะต่อ ความต่างที่ถูกต้อง ที่ไม่ตรงแพทเทิร์นที่เขียนดักไว้เป๊ะๆ และจุดอ่อนข้อนี้เองคือเส้นแบ่งว่าเมื่อไรถึงคุ้มที่จะจ่ายค่า judge
ตัวอย่างเส้นแบ่งใน domain เดียวกัน: การเช็คว่า “มีแถวคืนเงิน ฿120 ในฐานข้อมูลไหม” มีคำตอบถูกต้องแค่แบบเดียว เช็คด้วย code ตรงๆ ได้สบาย — deterministic ชนะขาด แต่ถ้าเราอยากเช็คว่า “ข้อความที่ agent อธิบายลูกค้าว่าทำไมคืนเงินแค่บางส่วน” นั้นถูกต้องและครบถ้วนไหม คำตอบที่ถูก มีได้หลายสิบแบบ — “คืนให้ ฿120 เพราะร้านยกเลิก 1 รายการครับ” กับ “ยอดคืน ฿120 มาจากสินค้าที่ร้านทำไม่ได้ 1 ชิ้น” ต่างถ้อยคำแต่ถูกทั้งคู่ regex หรือ exact-match จะตัดสินพลาดทันที เพราะมันไล่จับ “รูปแบบ” ไม่ใช่ “ความหมาย” นี่คือจุดที่ deterministic เปราะ และ judge เริ่มคุ้มค่า
LLM-as-judge เมื่อ deterministic ทำไม่ไหว
หัวข้อที่มีชื่อว่า “LLM-as-judge เมื่อ deterministic ทำไม่ไหว”เมื่อผลลัพธ์ที่ถูกต้องมีได้หลายรูปแบบจนเขียนกฎดักไม่ไหว เราจึงหันไปใช้ LLM-as-judgeLLM-as-judgeเทคนิคที่ใช้ LLM อีกตัวเป็นผู้ตัดสิน (judge) ว่า output ของ agent ผ่านเกณฑ์หรือไม่ แทนมนุษย์อ่านทุกเคสเอง มักให้คะแนนตามเกณฑ์หลายมิติในคำสั่งเดียว — ใช้ได้จริงเมื่อผ่าน Judge Calibration กับมนุษย์แล้วเท่านั้น เพราะ judge มีอคติแฝง (เอนเอียงตามลำดับที่เห็นก่อน ตามความยาวคำตอบ หรือชอบ output จาก model ตระกูลเดียวกับตัวเอง) ไม่ใช่ผู้ตัดสินที่เป็นกลางโดยอัตโนมัติArchitecture — ให้ LLM อีกตัวอ่าน output ของ agent แล้วตัดสินตาม RubricRubricเกณฑ์การให้คะแนนที่เขียนไว้ชัดเจนเป็นข้อ ๆ ก่อนเริ่มตรวจ ให้ทั้งมนุษย์และ LLM-as-judge ใช้ตัดสินแบบเดียวกัน — ยิ่ง rubric คลุมเครือ ยิ่งได้คะแนนที่เชื่อถือไม่ได้ไม่ว่าใครเป็นคนตรวจ และต้องแก้ไขซ้ำได้เมื่อเจอ Criteria Drift ไม่ใช่เขียนทิ้งไว้ตายตัวProcess (เกณฑ์ให้คะแนนที่เขียนไว้ชัดเป็นข้อๆ) แทนที่มนุษย์จะต้องนั่งอ่านทุกเคสเอง แพทเทิร์นที่ได้ผลดีและเรียบง่ายที่สุดคือ single call: ยิงคำสั่งเดียว ให้ judge อ่านครั้งเดียว แล้วคืนคะแนนหลายมิติออกมาพร้อมกัน
Anthropic รายงานจาก research agent ของเขาว่า “a single LLM call with a single prompt outputting scores from 0.0–1.0 and a pass-fail grade was the most consistent and aligned with human judgements” — การเรียก LLM ครั้งเดียวให้คืนคะแนน 0.0–1.0 บวกผล pass/fail ให้ผลนิ่งและตรงกับการตัดสินของมนุษย์มากที่สุด โดยให้คะแนน 5 มิติ (ความถูกต้องของข้อเท็จจริง, ความถูกต้องของการอ้างอิง, ความครบถ้วน, คุณภาพแหล่งข้อมูล, ประสิทธิภาพการใช้ tool) (ดู Anthropic, “How we built our multi-agent research system”)
// สเก็ตช์ rubric ของ judge — ไม่ใช่ code ที่รันได้จริง// ใช้ตัดสิน "ข้อความที่ agent อธิบายลูกค้า" ซึ่งถูกได้หลายแบบ deterministic เช็คไม่ไหว// หมายเหตุ: นี่คือเกณฑ์ที่ปรับมาเฉพาะเคส refund ของ ops-copilot ไม่ใช่ 5 มิติของ Anthropic ข้างบน — rubric ต้องออกแบบตามงานของคุณเองconst judgePrompt = `คุณคือผู้ตรวจ ให้คะแนนคำอธิบายการคืนเงินของ ops-copilot ต่อลูกค้าทำตามลำดับนี้: ให้เหตุผลก่อน แล้วค่อยให้คะแนนแต่ละมิติ 0.0–1.0 และสรุป pass/fail
rubric (4 มิติ + 1 กติกา Unknown-exit):1. ความถูกต้องของข้อเท็จจริง — จำนวนเงินและเลขออเดอร์ตรงกับที่คืนจริงในระบบไหม2. ความครบถ้วน — บอกครบทั้ง "ยอดที่คืน" และ "เหตุผลที่คืนบางส่วน" ไหม3. ไม่สัญญาเกินจริง — ไม่รับปากเรื่องที่ระบบทำไม่ได้ (เช่น "คืนภายใน 1 นาที")4. โทนเสียง — สุภาพและชัดเจน อ่านแล้วเข้าใจกติกา (ไม่ใช่มิติให้คะแนน): ถ้าข้อมูลที่ให้มาไม่พอจะตัดสิน ให้ตอบ "Unknown" อย่าเดา`;จุดที่ต้องติดป้ายให้ชัด: ผลที่ว่า “เรียกครั้งเดียวดีกว่า ensemble หลายตัว” เป็นสิ่งที่วัดได้ เฉพาะในระบบ research ของ Anthropic ไม่ใช่กฎสากล ให้ถือเป็น “ค่าเริ่มต้นที่ควรลองก่อน” ไม่ใช่ “ต้องเรียกครั้งเดียวเสมอ” — domain อื่นอาจต้องวัดเองว่าแบบไหนนิ่งกว่า
กับดักใหญ่ที่สุดของทั้งคอร์สคือการเข้าใจว่า LLM-as-judge เป็น “มนุษย์สำเร็จรูป” ที่เอามาแทนการอ่านของคนได้เลยด้วย rubric เดียว มันไม่ใช่ judge เป็น เครื่องมือวัดที่ต้อง calibrate และต้องคอยเช็คซ้ำ ไม่ใช่ oracle ที่เชื่อได้ทันที — เพราะมันมีอคติแฝงที่วัดได้ (หัวข้อถัดไป) และมนุษย์ยังจับสิ่งที่ judge มองข้ามได้เสมอ
judge มีอคติแฝง — ต้องกันตั้งแต่ออกแบบ
หัวข้อที่มีชื่อว่า “judge มีอคติแฝง — ต้องกันตั้งแต่ออกแบบ”งาน MT-Bench (Zheng et al., NeurIPS 2023) วัดพฤติกรรมของ LLM ที่ทำหน้าที่ judge แล้วพบอคติแฝงสามแบบที่ต้องรู้ก่อนไว้ใจผลใดๆ (ดู MT-Bench (arXiv 2306.05685)):
- position bias — เอนเอียงเข้าหาคำตอบที่ถูกวางให้เห็น ก่อน เวลาให้ judge เทียบสองคำตอบ ลำดับที่วางมีผลต่อผลตัดสิน ทั้งที่ไม่ควรมี
- verbosity bias — เอนเข้าหาคำตอบที่ ยาวกว่า แม้เนื้อหาไม่ได้ดีกว่า ยาวและดูละเอียดถูกตีความว่าดีโดยอัตโนมัติ
- self-enhancement bias — judge มีแนวโน้มให้คะแนน output จาก model ตระกูลเดียวกับตัวเอง สูงกว่าความเป็นจริง
ทั้งสามอย่างแก้ได้ด้วยการออกแบบ ไม่ใช่ด้วยการหวังว่า model จะดีขึ้นเอง: กัน position bias ด้วยการ สุ่มสลับลำดับคำตอบ (หรือรันทั้งสองลำดับแล้วเฉลี่ย), กัน verbosity bias ด้วยการ คุมความยาว ให้เทียบกันได้หรือสั่ง rubric ไม่ให้นับความยาวเป็นข้อดี, และกัน self-enhancement bias ด้วยการเลือก model คนละตระกูลกับตัวที่ถูกตัดสิน มาเป็น judge สำหรับ ops-copilot: ถ้า agent ที่คืนเงินขับด้วย model ตระกูลหนึ่ง อย่าใช้ model ตระกูลเดียวกันมาเป็นคนให้คะแนนงานของมันเอง
flowchart TB
O["outcome ที่ต้องให้คะแนน<br/>เช่น ผลการคืนเงินออเดอร์ 123"]
O --> Q1{"มีรูปแบบคำตอบถูกต้องแบบเดียว<br/>เช็คด้วย code ตรงได้ไหม?"}
Q1 -->|"ได้ เช่น มีแถวคืนเงิน ฿120 ในฐานข้อมูลไหม"| DET["deterministic grader<br/>เร็ว ถูก reproducible debug ง่าย"]
Q1 -->|"ไม่ได้ เช่น ข้อความอธิบายถูกได้หลายแบบ"| JUDGE["LLM-as-judge<br/>ยืดหยุ่นแต่มีอคติแฝง"]
DET --> DETW["ข้อควรระวัง: เปราะต่อความต่างที่ถูกต้อง<br/>ถ้าคำตอบมีได้หลายแบบจะตัดสินพลาด"]
JUDGE --> BIAS["อคติ 3 อย่าง MT-Bench<br/>position / verbosity / self-enhancement"]
BIAS --> FIX["กัน bias: สุ่มสลับลำดับคำตอบ<br/>คุมความยาว ใช้ model คนละตระกูลเป็น judge"]
FIX --> CAL["calibrate เทียบป้ายมนุษย์<br/>ดู TPR และ TNR แยกกัน ไม่ใช่ % agreement รวม"]
CAL --> USE["ค่อยปล่อยให้ judge ตัดสินเอง<br/>แล้วยังสุ่มอ่าน transcript ต่อ"]
classDef det fill:#16a34a,stroke:#065f46,color:#f8fafc;
classDef warn fill:#dc2626,stroke:#7f1d1d,color:#f8fafc;
class DET det;
class BIAS warn;
คำบรรยายภาพ: เส้นทางตัดสินใจของ grader หนึ่งภาพ ถ้าผลลัพธ์มีรูปแบบถูกต้องแบบเดียวให้ไปทาง deterministic (เขียว: เร็ว/ถูก/รันซ้ำได้) โดยจำข้อควรระวังว่ามันเปราะต่อความต่างที่ถูกต้อง ถ้าคำตอบถูกได้หลายแบบให้ไปทาง LLM-as-judge ซึ่งพก อคติ 3 อย่างจาก MT-Bench (แดง) มาด้วย จึงต้องกัน bias ก่อน (สุ่มสลับลำดับ คุมความยาว ใช้ model คนละตระกูล) แล้วจึง calibrate เทียบป้ายมนุษย์โดยดู TPR/TNR แยกกัน ก่อนปล่อยให้ judge ตัดสินเอง — และยังต้องสุ่มอ่าน transcript ต่อ ไม่ปล่อยให้ judge ทำงานคนเดียวลอยๆ
Judge Calibration — เทียบกับมนุษย์ก่อนไว้ใจ
หัวข้อที่มีชื่อว่า “Judge Calibration — เทียบกับมนุษย์ก่อนไว้ใจ”กันอคติแล้วยังไม่พอ ก่อนปล่อยให้ judge ตัดสินเองเต็มรูปแบบต้องผ่าน Judge CalibrationJudge Calibrationกระบวนการเช็คว่า LLM-as-judge ตัดสินตรงกับป้ายที่มนุษย์ผู้เชี่ยวชาญให้ไว้แค่ไหน ก่อนปล่อยให้ judge ตัดสินเองเต็มรูปแบบ — ต้องดูอัตราที่ judge จับได้ถูกทั้งฝั่ง 'ผ่านจริง' และ 'พังจริง' แยกกัน ไม่ใช่แค่ % ที่ตรงกับมนุษย์รวม ๆ เพราะถ้าเคสพังมีน้อย judge ที่ตอบ 'ผ่าน' มั่ว ๆ ก็ยังได้คะแนนสูงลวงตา และต้อง calibrate ซ้ำทุกครั้งที่เปลี่ยน model judge หรือ rubricProcess ก่อน — เอา judge ไปตัดสินชุดเคสที่ มนุษย์ผู้เชี่ยวชาญให้ป้ายไว้แล้ว (held-out labeled set) แล้ววัดว่ามันตรงกับมนุษย์แค่ไหน แต่จุดตายอยู่ที่ วัดด้วยตัวเลขอะไร
อย่าวัดด้วย raw agreement — เปอร์เซ็นต์ที่ judge ตัดสินตรงกับมนุษย์โดยรวมทุกเคส เพราะมันเป็นตัวเลขลวงตาเมื่อเคสพังมีน้อย: ถ้าจริงๆ แล้วมีเคสพังแค่ 10% judge ที่ตอบ “ผ่าน” มั่วๆ ทุกเคสจะได้ agreement สูงถึง ~90% ทั้งที่มันจับของพังไม่ได้เลยสักเคส ตัวเลขสวยแต่ judge ไร้ประโยชน์ (กับดัก ~90% นี้และการวัดด้วย TPR/TNR แยกกัน สรุปจาก Hamel/Shreya “LLM Evals FAQ”) ทางที่ถูกคือดูสองอัตราแยกกัน:
- True Positive Rate (TPR) — ในบรรดาเคสที่ พังจริง judge จับได้ถูกกี่เปอร์เซ็นต์
- True Negative Rate (TNR) — ในบรรดาเคสที่ ผ่านจริง judge บอกว่าผ่านถูกกี่เปอร์เซ็นต์
judge ที่ตอบ “ผ่าน” ทุกเคสจะได้ TPR = 0 ทันที ปัญหาโผล่ให้เห็น ต่างจาก raw agreement ที่กลบมันไว้ นอกจากนี้ยังมีเทคนิคออกแบบสองข้อที่ช่วยลดการตัดสินมั่ว: ให้ judge มีทางออก “Unknown / ข้อมูลไม่พอ” เพื่อไม่ให้มันเดาเมื่อหลักฐานไม่พอ (ลดคำตัดสินที่ hallucinate ขึ้นมา) และบังคับให้มัน ให้เหตุผลก่อนตัดสิน เพราะการคิดเป็นขั้น (chain-of-thought) ทำให้คะแนนแม่นขึ้น แม้เราจะทิ้งเหตุผลนั้นไม่เก็บก็ตาม — สองข้อนี้อยู่ในสเก็ตช์ rubric ด้านบนแล้ว (ทั้งหมดตาม Anthropic, “Demystifying evals for AI agents”)
และต้อง calibrate ซ้ำทุกครั้ง ที่เปลี่ยน model judge หรือแก้ rubric — ค่าที่เคยผ่านไม่การันตีว่ายังผ่านหลังเปลี่ยน สุดท้าย judge ที่ calibrate ดีแล้วก็ยัง ไม่ปลดมนุษย์ออกจาก loop: มนุษย์ยังจับความผิดพลาดที่ judge มองข้ามได้เสมอ (บทที่ 6 จะเล่าเคสจริงที่มนุษย์จับได้แต่ automation ไม่เห็น) วิธีคิดที่ถูกคือ — judge เป็นเครื่องมือวัดที่เรา calibrate แล้วคอยเช็คซ้ำ ไม่ใช่ oracle ที่ตั้งแล้วลืมได้ การรักษาคุณภาพ eval จึงมาจาก loop error analysis → ป้ายจากมนุษย์ → judge ที่ calibrate → กลับไปอ่าน transcript ต่อ ที่บทที่ 6 จะประกอบให้เต็ม
สิ่งที่จะทำต่อ
หัวข้อที่มีชื่อว่า “สิ่งที่จะทำต่อ”บทนี้ให้เกณฑ์เลือก grader: deterministic ก่อนเสมอถ้าผลลัพธ์มีรูปแบบถูกต้องแบบเดียว (เร็ว/ถูก/รันซ้ำได้ แต่เปราะต่อความต่างที่ถูกต้อง) แล้วค่อยขยับไป LLM-as-judge เมื่อคำตอบถูกได้หลายแบบ โดยไม่ลืมว่า judge มีอคติแฝง (position/verbosity/self-enhancement) ที่ต้องกัน และต้อง calibrate ด้วย TPR/TNR ไม่ใช่ raw agreement ก่อนไว้ใจ บทที่ 5 จะย้อนไปที่ต้นทางของทั้งหมด — eval set ควรเริ่มจากกี่เคส และเคสพวกนั้นควรมาจากไหน
บทนี้สังเคราะห์จากสี่แหล่งหลัก อ่านต่อได้ที่ต้นทางโดยตรง:
- Anthropic, “Demystifying evals for AI agents” — เลือก deterministic grader ก่อนเมื่อทำได้, hygiene ของ judge (ทางออก “Unknown”, ให้เหตุผลก่อนตัดสิน)
- Anthropic, “How we built our multi-agent research system” — แพทเทิร์น single call คืนคะแนน 0.0–1.0 + pass/fail ตาม rubric 5 มิติ (อ้างเพื่อ วิธีการ ไม่ใช่ตัวเลขประสิทธิภาพ และผล “ครั้งเดียวดีกว่า ensemble” เป็นของระบบนี้โดยเฉพาะ ไม่ใช่กฎสากล)
- MT-Bench (arXiv 2306.05685) — อคติแฝงของ LLM-as-judge: position / verbosity / self-enhancement
- Hamel/Shreya “LLM Evals FAQ” — กับดัก raw-agreement ~90% เมื่อเคสพังมีน้อย และการ calibrate judge ด้วย TPR/TNR แยกกัน
- บทที่ 3 — ตัดสินจากผลลัพธ์ ไม่ใช่เส้นทาง — ที่มาของคำถาม “ให้คะแนน outcome อย่างไร” ที่บทนี้ต่อยอด
เช็กความเข้าใจ — บทที่ 4
ข้อ 1 / 3ในกรณีใดที่ควรใช้ deterministic grader แทน LLM-as-judge?