ข้าม​ไป​ยัง​เนื้อหา

Eval-driven development (ข้อ​ถกเถียง​จริง)

เจ็ด​บท​ที่​ผ่าน​มา​สอน “วิธี” ทำ eval ให้​ถูก บท​สุดท้าย​นี้​ถาม​คำถาม​ที่​เถียง​กัน​ไม่​จบ: เมื่อไร ควร​เขียน eval — ก่อน​สร้าง agent หรือ​หลัง หัวใจ​ของ​บท​นี้​คือ EvalEvalการ​วัดผล​อย่าง​เป็น​ระบบ​ว่า output ของ AI agent (เช่น ops-copilot) ถูกต้อง​และ​มี​ประโยชน์​จริง​หรือ​ไม่ ด้วย​ชุด​เกณฑ์​และ​กรณี​ทดสอบ​ที่​ชัดเจน แทน​การ​ดู​จาก​ความ​รู้สึก​ว่า 'ตอบ​ดู​ดี' — จำเป็น​เพราะ agent เป็น​ระบบ​หลาย​ขั้นตอน​ที่​ความ​ผิดพลาด​สะสมทบ​กัน​ได้ และ​แต่ละ​รอบ​ก็​ไม่​ได้​เดินทาง​เดิม​เป๊ะ​ทุก​ครั้งProcess — การ​วัด​ความ​สำเร็จ​ของ agent อย่าง​เป็น​ระบบ​ด้วย​เกณฑ์​และ​เคส​ที่​ชัดเจน — ควร​ถูก​เขียน “ณ จุด​ไหน” ของ​วงจร​พัฒนา และ​คำ​ตอบ​ของ​แหล่ง​อ้างอิง​ชั้น​ต้น​สอง​แหล่ง​ที่​น่า​เชื่อถือ​ทั้ง​คู่ ขัด​กันตรงๆ

บท​นี้​จึง​ไม่​ได้​สอน​วิธี​เดียว​แล้ว​ปิด​จบ มัน​วางข้อ​ถกเถียง​จริง​ลง​บน​โต๊ะ ให้​เห็น​ทั้ง​สอง​ฝ่าย​โดย​ระบุ​ชื่อ แล้ว​ค่อย​ชี้​ว่า​อะไร​ทำให้​ทั้ง​คู่​ถูก​ใน​บริบท​ของ​ตัวเอง เพราะ​สิ่ง​ที่ ADVANCED ต้อง​ได้​จาก​บท​นี้​ไม่ใช่ “เชื่อ​ใคร” แต่​เป็น​วิจารณญาณ​ว่า​จะ​เลือก​วิธี​ไหน​เมื่อ​ยืน​อยู่​ใน​สถานการณ์​แบบ​ไหน

📦 code ตัวอย่าง (กำลัง​จัด​ทำ)

บท​นี้​ใช้ ops-copilot agent ที่​คืน​เงิน​ออเดอร์​ใน domain Order จาก repo kaen-food-ordering (กำลัง​จัด​ทำ) เป็น​ตัวอย่าง​เดิน​เรื่อง เป็น​บท​เชิง​วิธี​คิด (design/methodology) สเก็ตช์​ที่​เห็น — eval spec, ลำดับ​การ​ทำงาน — เป็น ภาพร่าง​เพื่อ​สื่อ​ไอเดีย ไม่ใช่ eval harness ที่​รัน​ได้​ทันที

Anthropic สนับสนุน ให้​เขียน eval ก่อน เขา​เขียนไว้ตรงๆ ว่า​ให้ “build evals to define planned capabilities before agents can fulfill them, then iterate until the agent performs well” — สร้าง eval เพื่อ นิยาม capability ที่​วางแผน​ไว้​ก่อน​ที่ agent จะ​ทำ​มัน​ได้ แล้ว​ค่อย iterate จนกว่า​จะ​ผ่าน (ดู Anthropic, “Demystifying evals for AI agents”)

วงจร​ที่​เขา​เสนอ​เดิน​ตาม​ลำดับ​นี้: นิยาม​ความ​สำเร็จ → ออกแบบ task → สร้าง grader → รัน trial → วิเคราะห์ transcript → iterate แล้ว​วน​กลับ พูด​อีก​แบบ​คือ eval มา​ก่อน ความ​สามารถ​ตามทีหลัง เหมือน​เขียน test ที่​ยัง​แดง​ไว้​รอ แล้ว​ค่อย​เขียน code ให้​มัน​เขียว

ใน​โลก ops-copilot สิ่ง​นี้​หมายความ​ว่า: ก่อน​จะ​มี tool refund ด้วย​ซ้ำ เรา​เขียน eval ที่​นิยาม​ว่า “คืน​เงิน​สำเร็จ” แปล​ว่า​อะไร​ไว้​ก่อน — และ​นิยาม​นั้น​ต้อง​วัด​ที่ outcome จริง ไม่ใช่​คำ​พูด​ของ agent:

# สเก็ตช์ eval spec (ไม่ใช่ file ที่รันได้จริง)
# เขียน 'ก่อน' มี tool refund ตามแนว eval-driven ของ Anthropic
case: "คืนเงินออเดอร์ #123 จำนวน ฿120"
input: "ลูกค้าขอคืนเงินออเดอร์ #123 เพราะร้านยกเลิก"
success: # นิยาม 'ความสำเร็จ' ล่วงหน้า — วัดที่สถานะจริงของฐานข้อมูล
db.refunds:
orderId: 123
amount: 120 # ✅ ต้องมีแถวคืนเงินของออเดอร์นี้จริง จำนวนถูกต้อง
ห้ามเชื่อ: "ข้อความ agent ว่า 'คืนเงินเรียบร้อย'" # version ดิบ — ผ่านลวงตาได้

เหตุผล​ที่​แนว​นี้​ใช้ได้: ใน​งาน greenfield เรา รู้​เป้าหมาย​ล่วงหน้า อยู่​แล้ว โจทย์ “คืน​เงิน​ให้​ถูกออเดอร์ ถูก​จำนวน” ไม่​ต้อง​รอ​เห็น production trace ก็​เขียน​เกณฑ์​ความ​สำเร็จ​ได้​ครบ eval ที่​เขียน​ไว้​ก่อน​จึง​ทำ​หน้าที่​เป็น​ทั้ง spec และ​เป้า​เล็ง​ให้การ iterate

ระวัง​ตัวเลข​พาด​หัว: 90.2% เป็น internal-eval

post multi-agent ของ Anthropic รายงาน​ว่า​ระบบ​ใหม่​ดี​ขึ้น “90.2%” เหนือ single-agent — แต่​ตัวเลข​นี้​วัด​กับ eval set ภายใน​ของ Anthropic เอง ไม่ใช่ benchmark อิสระ ให้​หยิบ post นี้​มา​อ้าง​เพื่อ วิธีการ (loop นิยาม→สร้าง grader→iterate) ไม่ใช่​เพื่อ​ยก​ตัวเลข​ไป​เคลม​ว่า agent เก่ง​ระดับ​นั้น​แบบ​พก​พา​ได้ (ดู Anthropic, “multi-agent research system”)

Hamel Husain ถูกถามตรงๆ ว่า​ควร​ทำ eval-driven development ไหม คำ​ตอบ​สั้น: “Generally no.” — ส่วน​ใหญ่​ไม่​ควร เหตุผล​ของ​เขา​คือ “LLMs have infinite surface area for potential failures. You can’t anticipate what will break.” LLM มี​พื้นที่​ผิว​ของ​ความ​ล้มเหลว​ที่​กว้าง​ไม่​สิ้นสุด คุณ​คาด​เดา​ไม่​ได้​หรอกว่า​อะไร​จะ​พัง (ดู Hamel, “Should I practice eval-driven development?”)

หัวใจ​คำ​ค้าน​ของ​เขา​อยู่​ที่​การกลับ​ลำดับ: “Write evaluators for errors you discover, not errors you imagine.” — เขียน evaluator สำหรับ error ที่​คุณ เจอ​จริง ไม่ใช่ error ที่​คุณ จินตนาการ ขึ้น​เอง เพราะ​ถ้า​เขียน eval จาก​จินตนาการ คุณ​จะ​เสียแรง​ไล่​ดัก​ปัญหา​ที่​อาจ​ไม่​เคย​เกิด ขณะ​ที่​ปัญหา​จริง​ซึ่ง​คาด​ไม่​ถึง​กลับ​หลุด​รอด

นี่​คือ Error AnalysisError Analysisการ​นั่ง​อ่าน trace ที่ agent ทำงาน​จริง​ที​ละ​เคส​เพื่อ​หา​ว่า​อะไร​พัง​จาก​อะไร เน้น​จับ 'ความ​ผิดพลาด​แรก​สุด' ใน​แต่ละ trace ก่อน​เสมอ เพราะ​ความ​ผิดพลาด​ต้นทาง​มัก​ลาก​ให้​พัง​ต่อ​เป็น​ทอด ๆ — เป็น​จุด​เริ่มต้น​ของ eval ที่​ดี ไม่ใช่​การ​มอง​แค่ dashboard ตัวเลข​รวม และ​ควร​ทำซ้ำ​ทุก 2-4 สัปดาห์Process — การ​นั่ง​อ่าน trace ที่ agent ทำงาน​จริง​ที​ละ​เคส หา “ความ​ผิดพลาด​แรก​สุด” ใน​แต่ละ trace แล้ว​เขียน evaluator ให้​จับ ปัญหา​นั้น โดย​เฉพาะ ใน​มุม​ของ Hamel eval ไม่ใช่​สิ่ง​ที่​คุณ​เขียน​ไว้​ล่วงหน้า​เพื่อ​บอกว่า agent ควร​ทำ​อะไร แต่​เป็น​สิ่ง​ที่​คุณ​สกัด​ออก​มา​จาก​สิ่ง​ที่ agent ทำ​พลาด​ไป​แล้วจริงๆ ลำดับ​จึง​เป็น “รัน​ก่อน แล้ว​ค่อย​ขุด error มา​เป็น eval” ตรง​ข้าม​กับ “เขียน eval ก่อน แล้ว​ค่อย​สร้าง”

ทั้ง​สอง​ฝ่าย​ไม่​ได้​ขัด​กัน​จริง — มัน​ขึ้น​กับ context

หัวข้อ​ที่​มีชื่อ​ว่า “ทั้ง​สอง​ฝ่าย​ไม่​ได้​ขัด​กัน​จริง — มัน​ขึ้น​กับ context”

จุด​ที่​คลี่คลาย​ข้อ​ถกเถียง​นี้​ไม่ใช่ “ใคร​ถูก” แต่​คือ context ต่าง​กัน ทั้ง​คู่​พูด​ถูก​ใน​สถานการณ์​ของ​ตัวเอง:

  • แนว Anthropic เข้า​ทาง greenfield — ระบบ​ที่​ยัง​ไม่มี​อะไร​รัน​จริง เรา รู้​เป้าหมาย​ล่วงหน้า (คืน​เงิน​ให้​ถูกออเดอร์ ถูก​จำนวน) จึง​เขียน eval “refund สำเร็จ” ไว้​ก่อน​มี tool refund ได้​เต็ม​ปาก ไม่มี production trace ให้​ขุด​ก็​ยัง​มี​เกณฑ์​ความ​สำเร็จ​ที่​ชัด​พอ​จะ​เล็ง
  • แนว Hamel เข้า​ทาง​ระบบ​ที่​รัน​จริง​แล้ว — เมื่อ​มี production trace เป็นกอง การ​เดา error ล่วงหน้า​กลาย​เป็นการ​เสียแรง เพราะ​ปัญหา​จริง​จำนวน​มาก​คาด​ไม่​ถึง ให้​ทำ error analysis ขุด​จาก trace จริง​แล้ว​เขียน evaluator ตาม​สิ่ง​ที่​เจอ จะ​ได้ eval ที่​ตรง​ปัญหา​กว่า​มาก

และ Hamel เอง​ก็​เปิด​ช่อง​ให้​แนว greenfield ใน​กรณี​ข้อ​จำกัด​แคบๆ ที่​ชัดเจน — เขา​บอกว่า​ถ้า​จะ​เพิ่ม​กฎ​อย่าง “never mention competitors” การ​เขียน evaluator สำหรับ​กฎ​นั้น​ไว้​แต่​เนิ่นๆ “may be acceptable” พูด​ง่ายๆ คือ​เมื่อ​เป้าหมาย​ชัด​และ​แคบ​พอ​จะ​เขียน​เป็น​เกณฑ์​ตายตัว​ได้​ตั้งแต่​ต้น การ​เขียน eval ก่อน​ก็​ไม่​ผิด — ซึ่ง​กลับ​ไป​บรรจบ​กับ​ตรรกะ greenfield ของ Anthropic พอดี

flowchart TB
  START["คำถาม: เขียน eval 'ก่อน' หรือ 'หลัง' สร้าง agent?"]
  START --> A["ฝ่าย Anthropic — สนับสนุน eval-driven<br/>เขียน eval นิยามความสำเร็จ ก่อน agent ทำได้<br/>แล้ว iterate จนผ่าน"]
  START --> H["ฝ่าย Hamel — 'Generally no'<br/>เขียน evaluator จาก error ที่ 'เจอจริง'<br/>ไม่ใช่ error ที่ 'จินตนาการ'"]
  A --> Q{"อยู่ในบริบทไหน?"}
  H --> Q
  Q -->|"ยังไม่มี — greenfield<br/>รู้เป้าหมายล่วงหน้า"| USEA["ใช้แนว Anthropic:<br/>เขียน eval 'refund สำเร็จ' ก่อนมี tool refund"]
  Q -->|"มีแล้ว — ระบบรันจริง"| USEH["ใช้แนว Hamel:<br/>error analysis หา error จริงก่อน แล้วค่อยเขียน evaluator"]
  Q -->|"เป้าหมายแคบ+ชัดตายตัว เช่น 'ห้ามพูดถึงคู่แข่ง'"| BOTH["เขียน evaluator ไว้แต่เนิ่น ๆ ได้<br/>(จุดที่ทั้งสองฝ่ายบรรจบกัน)"]
  classDef pos fill:#1e3a8a,stroke:#1e293b,color:#f8fafc;
  class USEA,USEH,BOTH pos;

คำ​บรรยาย​ภาพ: สอง​จุดยืน​ออก​จาก​คำถาม​เดียวกัน — เขียน eval ก่อน​หรือ​หลัง ฝ่าย Anthropic สนับสนุน eval-driven (เขียน​ก่อน) ฝ่าย Hamel ค้าน​ลำดับ​นั้น (ขุด​จาก error จริง​ก่อน) ตัว​ชี้ขาด​ว่า​ใช้​อัน​ไหน​ไม่ใช่ “ใคร​น่า​เชื่อ​กว่า” แต่​คือ context: greenfield ที่​รู้​เป้า​ล่วงหน้า​เข้า​ทาง Anthropic ระบบ​ที่​มี production trace ให้​ขุด​เข้า​ทาง Hamel และ​เมื่อ​เป้าหมาย​แคบ​และ​ชัด​ตายตัว ทั้ง​สอง​ฝ่าย​ก็​บรรจบ​กัน​ที่ “เขียน evaluator ไว้​ก่อน​ได้”

ไม่​ว่า​จะ​เข้า​ทาง​ไหน มี​กับดัก​หนึ่ง​ที่​ทั้ง​สอง​ฝ่าย​เห็น​ตรง​กัน: evaluator ที่​คุณ​เขียน​ขึ้น​ไม่ใช่​ผู้​ตัดสิน​ที่​เชื่อ​ได้​เอง​โดย​อัตโนมัติ โดย​เฉพาะ​ถ้า​มัน​เป็น LLM-as-judge ตัว judge มี​อคติ​ที่ วัด​ได้​จริง — เอนเอียง​ตาม​ลำดับ​ที่​เห็น​ก่อน (position bias) ตาม​ความ​ยาว​คำ​ตอบ (verbosity bias) และ​ชอบ output จาก model ตระกูล​เดียว​กับ​ตัวเอง (self-enhancement bias) ตาม​ที่​งาน MT-Bench รายงาน​ไว้ ดังนั้น evaluator ที่​เขียน​ไว้​ล่วงหน้า​แบบ greenfield ก็​ยัง​ต้อง calibrate ก่อน​ไว้ใจ

วิธี calibrate ที่​ถูก​คือ​ดู TPR/TNR (จับ “ผ่าน​จริง” และ “พัง​จริง” ได้​แม่น​แค่​ไหน แยก​กัน) เทียบ​กับ​ป้าย​ที่​มนุษย์​ผู้เชี่ยวชาญ​ให้​ไว้ ไม่ใช่​ดู % ที่​ตรง​กับมนุษย์รวมๆ เพราะ​ถ้า​เคส​พัง​มี​น้อย judge ที่​ตอบ “ผ่าน” ไป​เรื่อย​ก็ได้​คะแนน​ตรง​กัน​สูง​ลวงตา​ทั้ง​ที่​จับ​อะไร​ไม่​ได้​เลย และ​ไม่​ว่า​จะ calibrate ดี​แค่​ไหน มนุษย์​ที่นั่ง​อ่าน trace ก็​ยัง​จับ​ความ​ล้มเหลว​คนละ​ชนิด​ที่ automation มอง​ข้าม​ได้​เสมอ — judge คือ เครื่องมือ​ที่​ต้อง​คอย​สอบ​เทียบ ไม่ใช่ oracle ประเด็น​นี้​คือ​เหตุผล​ที่​บท 4 (LLM-as-judge) กับ​บท 6 (error analysis) ต้อง​อ้าง​ถึงกัน​ไป​มา และ​เป็น​สิ่ง​ที่​ช่วย​รักษา​คุณภาพ eval ไว้​ไม่​ให้​พึ่ง judge เพียง​ลำพัง

ทั้ง​คอร์ส​นี้​ต่อยอด​จาก​ลำดับ​ที่​ซีรีส์​บทความ​วาง​ไว้ — context → harness → loop เรา​ให้ context กับ model ห่อ​มัน​ด้วย harness ที่​ต่อ tool และ​ขอบเขต แล้ว​ปล่อย​ให้​มัน​วน​ใน loop จน​บรรลุ​เป้าหมาย บท​นี้​เติม​ชั้น​สุดท้าย: eval คือ​สิ่ง​ที่​บอกว่า​ทั้ง​สาม​ชั้น​ข้าง​ต้น​ทำงาน​ได้​จริง​หรือ​เปล่า และ​ข้อ​ถกเถียง eval-driven ก็​คือ​คำถาม​ว่า​ชั้น​นี้​ควร​มา​เมื่อไร​ใน​ไทม์​ไลน์ — คำ​ตอบ​ที่​โต​แล้ว​คือ “แล้ว​แต่ context” ไม่ใช่​สูตร​ตายตัว

จาก​จุด​นี้ ก้าว​ต่อ​ไป​คือ​ลง​ไป​สร้าง harness จริง​ให้ agent — ต่อ tool ให้​มัน​ลงมือ​กับ​โลก​ภายนอก​อย่าง​ปลอดภัย ซึ่ง​เป็น​เรื่อง​ของ​คอร์ส ออกแบบ MCP Server ที่​ดี ที่​มอง tool ทุก​ตัว​เป็น Anti-Corruption Layer เมื่อ harness แน่น​แล้ว วินัย eval ทั้ง​แปดบท​นี้​คือ​สิ่ง​ที่​ทำให้​คุณ​รู้​ว่า​มัน​เชื่อถือ​ได้​จริง ไม่ใช่​แค่​ดูเหมือน​ทำงาน


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้

ข้อ​ถกเถียง​ใน​บท​นี้​มา​จาก​แหล่ง​ชั้น​ต้น​สอง​แหล่ง​ที่​ควร​อ่าน​เอง​ทั้ง​คู่:

🔗 อ้างอิง​เพิ่มเติม​ใน DevIQ

ข้อ​ถกเถียง “เขียน eval ก่อน​หรือ​หลัง” คือ​ญาติ​สาย​ตรง​ของ​ข้อ​ถกเถียง test-first ใน​โลก code — อ่าน​การ์ด Test-Driven Development (TDD) ที่ DevIQ เทียบเคียง​ได้: eval-driven development คือ TDD version ของ agent ที่​ผลลัพธ์​ไม่ deterministic ทำให้ “เขียน test ก่อน” มี​เงื่อนไข​มากกว่า​เดิม

เช็กความเข้าใจ — บทที่ 8

ข้อ 1 / 3

จุดยืนของ Anthropic ต่อ eval-driven development คืออะไร?