- DeepEval เน้น pytest-native และเมตริก LLM-as-a-Judge กว่า 50 ตัว เหมาะกับ Quality Gate ใน CI/CD ส่วน Promptfoo เน้น YAML และ Red-teaming 40+ Attack Plugin
- Golden Dataset ควรสร้างจาก Production Failure จริง ไม่ใช่ตัวอย่างสังเคราะห์ ขนาดเริ่มต้นที่ 200–500 เคส
- LLM Judge ต้อง Calibrate ให้เห็นด้วยกับ Human Annotator ที่ 85–90% ก่อนใช้ตัดสิน PR
- Threshold ที่ตั้งใน Dev มักพังใน Production เพราะ Distribution ต่างกัน ต้อง Re-baseline หลังใช้จริง 2 สัปดาห์
- Pipeline 4 ขั้น (Local Dev, PR Gate, Deploy Gate, Production Monitoring) เชื่อมกันเป็นวงจร Feedback
- งานวิจัยปี 2026 พบว่า 40% ขององค์กรที่ปล่อย LLM Application เจอ Quality Regression ภายใน 90 วันแรกหลัง Deploy
LLM Evaluation คืออะไร และทำไมสำคัญกว่าที่คิด
LLM Evaluation คือการวัดว่าเอาต์พุตของโมเดล (หรือ Chain, Agent, RAG Pipeline) ตรงตามคุณสมบัติที่เราต้องการหรือไม่ ต้องแม่นยำ, ตรงประเด็น, ไม่แต่งเรื่อง, ตอบตาม Format ที่กำหนด ผมทำงานกับ LLM Integration มาระยะหนึ่งและสังเกตซ้ำ ๆ ว่าทีมส่วนใหญ่ใช้เวลาสร้าง RAG หรือ Agent เป็นเดือน แต่มีแค่ Notebook หนึ่งไฟล์กับ "ตัวอย่างที่รันแล้วดูโอเค" เป็นวิธีตรวจสอบก่อนขึ้น Prod. ผลก็คือ Prompt เปลี่ยนบรรทัดเดียว, Model Provider ปล่อย Snapshot ใหม่ หรือ Retriever ปรับ top_k แล้วคุณภาพถดถอยแบบเงียบ ๆ กว่าจะรู้ตัวคือมี Ticket จากผู้ใช้กองอยู่แล้ว
ในปี 2026 การประเมินไม่ใช่ "Nice to Have" อีกต่อไป จากรายงานของ งานวิจัยด้านเลือก Threshold สำหรับ LLM Metric และรายงานอุตสาหกรรมล่าสุด องค์กรที่ปล่อย LLM Application ราว 40% พบ Quality Regression ภายใน 90 วันแรก สาเหตุคือทุกอย่างในระบบ AI เป็น Non-deterministic ทั้งโมเดลอัปเดต, Embedding Space เลื่อน, Retriever ดึงข้อมูลใหม่ หรือ Prompt Template ถูก Refactor และไม่มีอะไรจับได้ถ้าไม่มี Eval Pipeline อัตโนมัติ
วิธีคิดที่ผมใช้: Eval คือ Unit Test สำหรับ LLM ต่างกันตรงที่ Assertion ไม่ใช่ assert x == 5 แต่เป็น "Faithfulness > 0.7" หรือ "Answer Relevancy > 0.8" ซึ่งได้จาก LLM Judge อีกตัว หลักการยังเดิม คือถ้า Metric ตก PR ก็ไม่ Merge และ Deploy ก็ไม่ผ่าน
DeepEval vs Promptfoo: เลือกอย่างไรในปี 2026
สอง Framework นี้เป็น Open Source ที่โต้กันในเวทีเดียวกัน แต่ปรัชญาต่างขั้ว DeepEval คือ Python-first, pytest-native (เขียน Eval แบบเดียวกับที่คุณเขียน Unit Test อยู่แล้ว) รัน pytest แล้ว CI บอกได้เลยว่าผ่านหรือไม่ผ่าน มีเมตริกกว่า 50 ตัวรวมถึง Component-level Metric สำหรับ Agent (Tool Correctness, Argument Correctness, Plan Adherence) ส่วน Promptfoo คือ YAML-first, CLI-driven เก่งด้าน Side-by-side Comparison ระหว่าง Prompt หรือ Model และมี Red-team Plugin กว่า 40 ตัวสำหรับทดสอบ Jailbreak, Prompt Injection, Excessive Agency
| คุณสมบัติ | DeepEval | Promptfoo |
| รูปแบบการเขียน | Python / pytest | YAML / CLI |
| จุดแข็งหลัก | Quality Gate + RAG Metric + Agent Trace | Model Comparison + Red-teaming |
| จำนวนเมตริก Built-in | 50+ (G-Eval, Faithfulness, Answer Relevancy ฯลฯ) | Assertion-based (contains, equals, LLM-rubric) |
| Red-team Plugin | จำกัด | 40+ (Jailbreak, BOLA, SQL Injection via LLM) |
| ต้นทุนโดยประมาณ (10k evals/วัน) | ~$300/วัน (ทุก Metric ใช้ LLM Judge) | ~$100/วัน (Assertion หลายตัวไม่ต้องใช้ Judge) |
| เวลา Setup ครั้งแรก | ~30 นาที | ~15 นาที |
| เหมาะสำหรับ | RAG Diagnosis, Regression Gate | Model Selection, Security Testing |
คำตอบที่ผมให้ทีมส่วนใหญ่คือ ใช้ทั้งคู่. Promptfoo ตอบคำถามว่า "attacker เจาะระบบเราได้ไหม" ส่วน DeepEval ตอบว่า "คุณภาพเราตกกว่า Baseline หรือเปล่า" ทั้งสองรันเป็น PR Check ก่อน Deploy. ปี 2026 มี News สำคัญคือ OpenAI ตกลงเข้าซื้อ Promptfoo แล้วประกาศจะรวมเทคโนโลยีนี้เข้า OpenAI Frontier ตัว Codebase ยังเป็น MIT License เหมือนเดิม แต่ Roadmap ต่อไปจะ Vendor-neutral น้อยลง ทีมที่ต้อง Evaluate หลาย Provider ควรพิจารณาข้อนี้ให้ดี
สร้าง Golden Dataset จาก Production Failure
Golden Dataset คือชุดคู่ (Input, Expected Output หรือ Reference Context) ที่ใช้เป็นฐานเปรียบเทียบทุกครั้งที่รัน Eval คุณภาพของ Dataset สำคัญกว่าจำนวน. เอาจริง ๆ 300 เคสที่มาจาก Production จริงชนะ 3,000 เคสที่ให้ ChatGPT แต่งขึ้นเสมอ ตอนเริ่มต้นผมมักแนะนำขนาด 200–500 เคส แบ่งเป็น 3 กลุ่ม:
- Happy Path (~50%) คือคำถามธรรมดาที่ผู้ใช้ถามบ่อยที่สุด สะท้อน Traffic จริง
- Edge Case (~30%) คือคำถามที่ตอบยาก มีความคลุมเครือ, ข้อมูลไม่มีใน Corpus, Multi-hop Question
- Adversarial (~20%) คือ Prompt Injection, Off-topic Question, คำถามที่ควร Refuse
วิธีสร้างจริง: ดึง Log จาก Production 2–4 สัปดาห์แรก, ให้ Domain Expert ติด Label ว่าตอบดี/ไม่ดี, เอาเคสที่ตอบไม่ดีมาเป็นแกน. ต่อจากนั้นทุกครั้งที่มี User รายงานปัญหา ให้เพิ่มเข้า Dataset เป็น Regression Test ทันที Dataset จะเติบโตแบบมีทิศทางเสมอ
เก็บ Dataset ในรูปแบบ JSONL หรือ CSV ในรีโปเดียวกับ Code เพื่อให้ Version Control ได้ ใน DeepEval สามารถโหลดเข้า EvaluationDataset ได้โดยตรง:
from deepeval.dataset import EvaluationDataset
from deepeval.test_case import LLMTestCase
dataset = EvaluationDataset()
dataset.add_test_cases_from_json_file(
file_path="golden/support_qa.jsonl",
input_key_name="question",
actual_output_key_name="answer",
expected_output_key_name="reference",
retrieval_context_key_name="context",
)
print(f"Loaded {len(dataset.test_cases)} test cases")
เขียน Eval แรกด้วย DeepEval: G-Eval และ Faithfulness
เริ่มจากติดตั้ง DeepEval v3 และตั้งค่า Judge Model (แนะนำ Claude Sonnet 4.5 หรือ GPT-4o สำหรับความน่าเชื่อถือของคะแนน):
pip install "deepeval>=3.0" pytest
export OPENAI_API_KEY="sk-..." # หรือ ANTHROPIC_API_KEY สำหรับ Claude
ต่อไปเป็น Test แรกที่ใช้ FaithfulnessMetric (ใช้ตรวจ Hallucination ใน RAG) และ G-Eval ที่ตั้ง Rubric เอง save เป็น tests/test_rag_quality.py:
import pytest
from deepeval import assert_test
from deepeval.metrics import FaithfulnessMetric, GEval
from deepeval.test_case import LLMTestCase, LLMTestCaseParams
# กำหนดเมตริก 2 ตัว: Faithfulness (built-in) และ Tone (G-Eval custom)
faithfulness = FaithfulnessMetric(threshold=0.75, model="gpt-4o")
tone_metric = GEval(
name="ProfessionalTone",
criteria=(
"ตรวจว่าคำตอบใช้ภาษาสุภาพ กระชับ ไม่มีคำพูดล้อเลียนหรือ Slang "
"ถ้าคำตอบใช้ภาษาที่ไม่เหมาะสมกับ Support Channel ให้คะแนนต่ำ"
),
evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT],
threshold=0.8,
model="gpt-4o",
)
@pytest.mark.parametrize("case", [
LLMTestCase(
input="เปลี่ยนอีเมลบัญชียังไง",
actual_output=(
"เข้าไปที่ Settings > Account > Email แล้วกดปุ่ม Change "
"ระบบจะส่งลิงก์ยืนยันไปที่อีเมลใหม่"
),
retrieval_context=[
"ผู้ใช้เปลี่ยนอีเมลได้ที่หน้า Settings > Account > Email "
"ระบบจะส่งอีเมลยืนยันไปที่อีเมลใหม่ก่อนสลับให้เสร็จ"
],
),
])
def test_support_answers(case):
assert_test(case, [faithfulness, tone_metric])
รันด้วย pytest tests/test_rag_quality.py -v. ผลลัพธ์จะบอกทั้งคะแนนและเหตุผลที่ Judge ให้คะแนนนั้น (คุณสมบัติ Self-explaining LLM-Eval ของ DeepEval) จุดสำคัญคือ Test เหล่านี้รันในไปป์ไลน์เดียวกับ Unit Test ปกติ ทำให้ Developer ไม่ต้องเรียนรู้เครื่องมือใหม่
เมตริกสำคัญสำหรับ RAG Pipeline
RAG มี 2 ครึ่งที่ต้องประเมินแยกกันเสมอ ทั้ง Retriever (ค้นข้อมูลถูกไหม) และ Generator (สร้างคำตอบจาก Context ที่ได้ถูกต้องไหม) ถ้าไม่แยก คุณจะซ่อม Prompt ไปเรื่อย ๆ ทั้งที่ปัญหาจริงคือ Embedding Model ดึงเอกสารผิดตัวมา ผมเจอกรณีนี้ในทีมลูกค้าซ้ำ ๆ กันบ่อยมาก
Retriever Metrics
- Contextual Precision คือเอกสารที่ Rank สูงสุดเกี่ยวข้องกับคำถามหรือไม่ ใช้จับปัญหา Re-ranker
- Contextual Recall คือเอกสารที่ควรถูกดึงมา ถูกดึงมาครบไหม ใช้จับปัญหา top_k ต่ำเกินไป
- Contextual Relevancy คือสัดส่วนข้อความใน Context ที่เกี่ยวข้อง เอาไว้จับ Chunk ที่ใหญ่เกินความจำเป็น
Generator Metrics
- Faithfulness คือคำตอบขัดแย้งกับ Context หรือไม่ (Hallucination check)
- Answer Relevancy คือคำตอบตรงกับคำถามหรือไม่ ไม่วน ไม่นอกเรื่อง
- Hallucination คือโมเดลแต่ง Fact ขึ้นเองที่ไม่มีใน Context
วิธีที่ผมใช้เสมอคือรันทั้ง 5 เมตริกเป็นชุดเดียว เรียกว่า RAG Triad+. ถ้า Faithfulness ต่ำแต่ Contextual Recall สูง แปลว่าปัญหาอยู่ที่ Prompt Grounding แต่ถ้า Contextual Recall ต่ำ แปลว่าปัญหาอยู่ที่ Retriever ไม่ใช่ Prompt สำหรับผู้ที่กำลังสร้าง Retrieval System อยู่ ผมแนะนำให้อ่าน คู่มือ Agentic RAG ฉบับสมบูรณ์ ควบคู่กัน เพราะ Metric เหล่านี้จะสมเหตุสมผลก็ต่อเมื่อคุณเข้าใจว่า Retriever ทำงานอย่างไร
LLM-as-a-Judge: หลีกเลี่ยง Position Bias และปัญหาที่เจอบ่อย
เมตริกใน DeepEval เกือบทั้งหมด (ยกเว้น Exact Match) ใช้ LLM อีกตัวเป็นผู้ตัดสิน วิธีนี้ยืดหยุ่นแต่มี Bias ที่ต้องรู้:
- Position Bias คือ Judge มักโหวตให้คำตอบแรกที่เห็น วิธีแก้คือรันเปรียบเทียบ 2 ทิศทาง (สลับตำแหน่ง) ถ้า Judge ให้ผลไม่ตรงกัน แสดงว่าผลไม่น่าเชื่อถือ
- Verbosity Bias คือ Judge มักให้คะแนนคำตอบยาวสูงกว่า ตั้ง Criteria ให้ระบุชัดว่า "กระชับ" คือค่าบวก
- Self-preference Bias คือถ้าใช้ Model ตระกูลเดียวกันเป็นทั้ง Generator และ Judge Judge จะให้คะแนนคำตอบตัวเองสูงเกินจริง แก้โดยใช้ Model จากคนละค่ายเป็น Judge (ผมมักใช้ Claude เป็น Judge ให้ GPT หรือกลับกัน)
สำคัญที่สุดคือ Judge Calibration. ก่อนเชื่อคะแนนจาก Judge ต้องมี Human-annotated Reference Set ประมาณ 50–100 เคส แล้ววัดว่า Judge เห็นด้วยกับมนุษย์กี่เปอร์เซ็นต์ ถ้าต่ำกว่า 85% แปลว่า Rubric ยังไม่ชัดพอ ให้กลับไปเขียน Criteria ใหม่ก่อน อย่ารีบใช้ตัดสิน PR
ตั้ง Threshold อย่างไรไม่ให้ CI พังตลอด
ข้อผิดพลาดที่พบมากที่สุดในทีมที่เพิ่งเริ่มทำ Eval คือ ตั้ง Threshold สูงเกินจริงตอนอยู่ใน Dev (เช่น Faithfulness > 0.95) พอขึ้น Production ก็ Fail ทันทีเพราะ Distribution ของคำถามจริงแตกต่างจากตัวอย่างที่ทดสอบ. โปรเจ็กต์ล่าสุดที่ผมเข้าไปช่วยก็เจอปัญหานี้ตรง ๆ วิธีที่เวิร์คในทางปฏิบัติ:
- รัน Baseline 1–2 สัปดาห์แรกโดยไม่ Block คือเก็บคะแนนไว้ดู Distribution ก่อน
- ตั้ง Threshold ที่ P10 ของ Baseline คือคะแนนต่ำสุดใน 10% ล่าง ไม่ใช่ค่าเฉลี่ย
- แยก Threshold ตาม Segment คำถามเทคนิคกับคำถามทั่วไปควรมี Threshold ไม่เท่ากัน
- Re-baseline ทุก Quarter เมื่อ Model, Prompt, หรือ Corpus เปลี่ยน Threshold เดิมอาจไม่เหมาะแล้ว
งานวิจัยจาก arXiv 2412.12148 เสนอวิธีเลือก Threshold ที่เชื่อมโยงกับ Business Impact โดยตรงแทนที่จะเลือกจากสถิติเฉย ๆ สำคัญมากถ้าเมตริกของคุณไปเชื่อมกับ SLA ที่มีผลกับสัญญากับลูกค้า
Integrate เข้า CI/CD ด้วย GitHub Actions
ทั้งหมดที่กล่าวมาจะไม่มีความหมายถ้าไม่บังคับผ่าน CI. Setup ที่ง่ายที่สุดคือใช้ GitHub Actions รัน pytest ทุกครั้งที่มี PR แตะ Prompt, Model Config, หรือ Retrieval Setting ตัวอย่าง Workflow file:
# .github/workflows/llm-eval.yml
name: LLM Evaluation
on:
pull_request:
paths:
- "prompts/**"
- "src/rag/**"
- "config/model_versions.yaml"
- "golden/**"
jobs:
eval:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements-eval.txt
- name: Run DeepEval quality gate
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
pytest tests/eval/ \
--junitxml=eval-results.xml \
-m "quality_gate"
- name: Upload eval report
if: always()
uses: actions/upload-artifact@v4
with:
name: eval-results
path: eval-results.xml
ข้อสังเกต: ตั้ง paths ให้ตรงเฉพาะไฟล์ที่เปลี่ยนแล้วมีผลกับ LLM เท่านั้น ไม่งั้น PR ที่แค่แก้ CSS ก็จะไป Trigger Eval และเผา Token ฟรี สำหรับทีมที่กังวลเรื่องต้นทุน Judge Call แนะนำอ่าน Prompt Caching ด้วย Claude API เพื่อลดต้นทุน 90% เพราะเทคนิคเดียวกันใช้ Cache Judge Prompt ที่รันซ้ำ ๆ ได้ทั้งหมด
Red-teaming ด้วย Promptfoo: จับ Prompt Injection ก่อน Attacker
DeepEval ครอบคลุมด้านคุณภาพได้ แต่จุดอ่อนคือ Adversarial Testing นี่คือที่ที่ Promptfoo โดดเด่น สั่ง promptfoo redteam init จะสร้าง Config ที่รัน Attack Plugin ครบชุด ไม่ว่าจะเป็น Jailbreak, Prompt Injection, PII Leakage, BOLA (Broken Object Level Authorization), Excessive Agency ฯลฯ ตัวอย่าง Config ขั้นต่ำ:
# promptfooconfig.yaml
description: "Red-team check for support agent"
providers:
- id: anthropic:claude-sonnet-4-5
targets:
- id: http
config:
url: https://staging.myapp.com/api/agent
body: { "message": "{{prompt}}" }
redteam:
purpose: "Customer support agent for a SaaS product"
plugins:
- harmful
- pii
- prompt-injection
- excessive-agency
- bola
strategies:
- jailbreak
- jailbreak:composite
- multilingual
numTests: 20
รันด้วย promptfoo redteam run. ผลลัพธ์จะเป็น Report HTML ที่แสดงว่า Attack แบบไหนทะลุได้บ้าง แนะนำให้รันเป็น Scheduled Job รายสัปดาห์แยกจาก PR Check เพราะใช้เวลานานและ Token เยอะ Result ที่ Fail ควรกลายเป็น Test Case ใหม่ใน Golden Dataset สำหรับ Regression Gate
Production Monitoring และ Feedback Loop
Eval ก่อน Deploy บอกได้แค่ว่า Code ที่กำลังจะปล่อย ไม่ทำให้แย่ลงจาก Baseline แต่ไม่ได้บอกว่า Baseline นั้นตรงกับสิ่งที่ผู้ใช้พึงพอใจหรือไม่ ทีมที่ทำถูกจะเชื่อม 2 ฝั่ง:
- Sample Traffic จริง คือสุ่ม 1–5% ของ Request ใน Production มารัน Metric ชุดเดียวกับที่ใช้ใน CI แล้วส่งเข้า Observability (เช่น Langfuse, Arize Phoenix)
- เก็บ User Feedback ทั้ง Thumbs up/down และสัญญาณ Implicit (ผู้ใช้ Rephrase คำถาม, ปิดหน้าเร็ว)
- เทียบ Score จาก Judge กับ Signal จากผู้ใช้ ถ้า Judge ให้คะแนนสูงแต่ผู้ใช้กด thumbs-down บ่อย แปลว่า Metric ของคุณ Miscalibrate ต้องปรับ Criteria
- ป้อน Failure กลับเข้า Golden Dataset ทุก Regression ที่เจอใน Prod ต้องกลายเป็น Test Case ใหม่ วนซ้ำ
Loop นี้คือหัวใจของสิ่งที่แยกทีมที่ Ship LLM แล้ว "รู้ว่าตัวเองรู้อะไร" ออกจากทีมที่ Ship แล้วภาวนา. ปี 2026 ไม่มีทางลัด ต่อให้ Model ฉลาดขึ้น 10 เท่า ระบบก็ยังต้องมีวิธีวัดตัวเอง
คำถามที่พบบ่อย
DeepEval กับ Promptfoo ต่างกันยังไง ควรเลือกอันไหน?
DeepEval เป็น pytest-native ใน Python เน้น Quality Gate (Faithfulness, RAG Metric, G-Eval) ในขณะที่ Promptfoo เป็น YAML/CLI เน้น Model Comparison และ Red-teaming กว่า 40 Plugin ในทีมที่จริงจังผมแนะนำใช้ทั้งคู่ โดย Promptfoo สำหรับ Security และ DeepEval สำหรับ Regression Gate
Golden Dataset ควรมีกี่เคสถึงจะพอ?
เริ่มที่ 200–500 เคสจาก Production Traffic จริง แบ่งเป็น Happy Path 50%, Edge Case 30%, Adversarial 20% คุณภาพสำคัญกว่าจำนวน เพราะ 300 เคสที่คัดจริงชนะ 3,000 เคสที่ให้ LLM แต่งขึ้น จากนั้นให้ Dataset โตตามที่เก็บ Failure จาก Prod
G-Eval คืออะไร และเมื่อไหร่ควรใช้แทน Metric สำเร็จรูป?
G-Eval คือ Metric ที่ให้เราเขียน Criteria ด้วยภาษาธรรมชาติแล้วให้ LLM Judge ให้คะแนนตามนั้น (Chain-of-thought scoring) เหมาะเมื่อคุณต้องประเมินคุณสมบัติที่ Metric สำเร็จรูปไม่ครอบคลุม เช่น โทนภาษา, การอ้าง Source Format หรือความสอดคล้องกับ Brand Voice
Faithfulness Metric ต่างจาก Hallucination Metric อย่างไร?
Faithfulness วัดว่าคำตอบขัดแย้งกับ Retrieval Context หรือไม่ (เน้นบริบท RAG) ส่วน Hallucination วัดว่าโมเดลแต่ง Fact ขึ้นเองที่ไม่มีใน Ground Truth ทั้งคู่ใช้ LLM-as-a-Judge แต่ Scope ต่างกัน ในระบบ RAG โดยทั่วไปใช้ Faithfulness เพราะสะท้อนปัญหาจริงมากกว่า
รัน Eval ใน CI แล้วเปลืองต้นทุน API มาก ควรทำอย่างไร?
3 วิธีคือ (1) ใช้ Prompt Caching สำหรับ Judge Prompt ที่รันซ้ำ ๆ ลด Cost ได้ถึง 90%; (2) จำกัด Trigger CI เฉพาะไฟล์ที่กระทบ LLM เท่านั้น (Prompt, Retrieval Config, Model Version); (3) แยก Fast Gate (Subset 50 เคส) จาก Full Suite (500 เคส) โดย Fast รันทุก PR ส่วน Full รันก่อน Merge เข้า Main