0Pricing
AI Engineering Academy · 课时

为什么 RAG 中的评估很重要

了解 RAG 系统中的两种独立失败模式:检索失败和生成失败,并学习为什么需要使用不同指标分别诊断它们。

为什么 RAG 中的评估很重要 是 CoddyKit 上的免费 AI Engineering Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Engineering Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Engineering Academy 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

You Cannot Improve What You Do Not Measure

A RAG system can feel like it is working because it returns fluent, plausible-sounding answers. But without measurement, you have no idea whether it is actually retrieving the right chunks or generating faithful answers. Teams that skip evaluation often spend months tweaking chunking strategies and prompt formats based on gut feeling, only to discover they made things worse. Rigorous evaluation is what turns RAG development from guessing into engineering.

Two Independent Failure Modes

RAG has two distinct stages that can fail independently: retrieval and generation. Retrieval fails when the relevant chunks are not ranked in the top-K results — the LLM cannot generate a good answer if the right information was never retrieved. Generation fails when the correct chunks were retrieved but the LLM ignored them, misread them, or added hallucinated information. You need separate metrics for each stage to pinpoint which component is causing the problem.

The Danger of End-to-End Evaluation Only

Measuring only the final answer quality hides where failures come from. Suppose your system gives wrong answers 30% of the time. Is that because retrieval is missing the right chunks, or because the LLM is ignoring good chunks? If you only know the final error rate, you cannot know which component to fix. Instrument both stages separately: measure retrieval quality with golden datasets and generation quality with faithfulness scores.

# Diagnosis example: which stage is failing?

# Test 1: Is retrieval finding the right chunks?
retrieval_hit_rate = evaluate_retrieval(questions, ground_truth_chunks)
print(f'Retrieval hit rate@5: {retrieval_hit_rate:.1%}')
# If this is low (< 80%), fix chunking and embedding first

# Test 2: Given perfect context, does LLM generate correct answers?
generation_faithfulness = evaluate_generation(questions, perfect_context)
print(f'Generation faithfulness: {generation_faithfulness:.1%}')
# If retrieval is fine but this is low, fix your prompts

Building a Golden Dataset

Evaluation requires a golden dataset: a set of question-answer pairs where you know the correct answer and, ideally, which document and chunk the answer comes from. For a minimum viable evaluation set, collect 50-100 questions representative of real user queries. Answer them by hand or by reading the source documents. Include diverse question types: factual lookups, comparisons, multi-hop reasoning, and out-of-domain questions the system should refuse to answer.

# Golden dataset format
golden_dataset = [
    {
        'question': 'How many vacation days do employees receive in their first year?',
        'answer': '15 days',
        'relevant_chunks': ['employee_handbook_p24', 'benefits_summary_p3'],
        'source_doc': 'employee_handbook_2025.pdf'
    },
    {
        'question': 'What is the parental leave duration for primary caregivers?',
        'answer': '16 weeks fully paid',
        'relevant_chunks': ['parental_leave_policy_p1'],
        'source_doc': 'parental_leave_policy.pdf'
    }
]

Generating Golden Datasets with LLMs

Creating 100 questions manually is tedious. Accelerate this with a data generation LLM: feed each document chunk to GPT-4o and ask it to generate 3-5 diverse questions whose answers can be found in that chunk, plus the expected answer text. Review a sample manually to catch quality issues. This approach scales to thousands of questions quickly, though it may miss edge cases that only real users would ask.

def generate_qa_pairs_for_chunk(chunk_text, llm_client):
    prompt = (
        'Given the following document excerpt, generate 3 diverse questions '
        'that can be answered using ONLY this text. '
        'For each question, provide the exact answer from the text.\n\n'
        f'Text:\n{chunk_text}\n\n'
        'Format each as JSON: {"question": ..., "answer": ...}'
    )
    response = llm_client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': prompt}]
    )
    return response.choices[0].message.content

Retrieval Evaluation: Hit Rate

Hit rate@K is the fraction of questions for which at least one relevant chunk appears in the top-K retrieval results. It is the simplest and most intuitive retrieval metric. A hit rate@5 of 85% means that for 85 out of 100 questions, the relevant chunk was among the top 5 results. Track hit rate separately for different document types, query lengths, and topic categories to find where your retriever struggles most.

def compute_hit_rate(golden_dataset, retriever, top_k=5):
    hits = 0
    for item in golden_dataset:
        results = retriever.retrieve(item['question'], top_k=top_k)
        retrieved_ids = {r['id'] for r in results}
        relevant_ids = set(item['relevant_chunks'])
        if retrieved_ids & relevant_ids:  # intersection not empty
            hits += 1
    hit_rate = hits / len(golden_dataset)
    print(f'Hit rate@{top_k}: {hit_rate:.1%} ({hits}/{len(golden_dataset)})')
    return hit_rate

Generation Evaluation: Faithfulness

Faithfulness measures whether the generated answer contains only information that can be verified in the retrieved context. An unfaithful answer adds facts the context does not support — that is hallucination. Score faithfulness by having a judge LLM (or human evaluator) check each sentence in the answer against the context and flag any claims not supported by the retrieved chunks. A faithfulness score of 95%+ is the target for production systems.

def evaluate_faithfulness(answer, context, llm_client):
    prompt = (
        'Given this context and answer, evaluate faithfulness.\n\n'
        f'Context: {context}\n\n'
        f'Answer: {answer}\n\n'
        'For each sentence in the answer, determine if it is '
        'supported by the context (FAITHFUL) or not (HALLUCINATED). '
        'Return a JSON: {"score": 0.0-1.0, "issues": ["sentence..."]}.'
    )
    response = llm_client.chat.completions.create(
        model='gpt-4o',
        messages=[{'role': 'user', 'content': prompt}]
    )
    return response.choices[0].message.content

Generation Evaluation: Answer Relevance

Answer relevance measures whether the generated answer actually addresses the user's question. A highly faithful answer might still miss the point by answering a related but different question. Measure relevance separately from faithfulness. Use a judge LLM to rate whether the answer directly addresses what was asked, using a 1-5 scale. Low answer relevance often indicates a problem with the prompt structure or the retrieved context not actually containing the answer.

def evaluate_answer_relevance(question, answer, llm_client):
    prompt = (
        f'Question: {question}\n\n'
        f'Answer: {answer}\n\n'
        'Rate how well this answer addresses the question on a 1-5 scale:\n'
        '5 = fully answers the question\n'
        '3 = partially answers but misses key aspects\n'
        '1 = does not address the question at all\n\n'
        'Return JSON: {"score": 1-5, "reason": "brief explanation"}'
    )
    response = llm_client.chat.completions.create(
        model='gpt-4o',
        messages=[{'role': 'user', 'content': prompt}]
    )
    return response.choices[0].message.content

Tracking Metrics Over Time

Evaluation is most valuable when you track metrics over time as you make changes. Store evaluation results in a database or spreadsheet with timestamps and version labels (e.g., chunk_size=500, embed=3-small, k=5). When you try a new chunking strategy or embedding model, run the same evaluation and compare. This prevents regression — you might improve faithfulness but accidentally reduce hit rate. Always run the full evaluation before merging changes to production.

import json
from datetime import datetime

def save_evaluation_results(metrics, config, output_file='eval_history.jsonl'):
    record = {
        'timestamp': datetime.utcnow().isoformat(),
        'config': config,
        'metrics': metrics
    }
    with open(output_file, 'a') as f:
        f.write(json.dumps(record) + '\n')
    print(f'Saved eval result: hit_rate={metrics["hit_rate"]:.1%}, '
          f'faithfulness={metrics["faithfulness"]:.1%}')

The Evaluation Mindset

Beyond specific metrics, evaluation requires a mindset shift: treat RAG as a machine learning system with measurable performance, not a chatbot you subjectively assess by chatting with it. Define success criteria upfront (e.g., hit rate@5 > 85%, faithfulness > 95%). Establish a test set that stays frozen and is never used for development decisions. Reserve a separate dev set for iteration. This discipline separates teams that ship reliable RAG from teams that ship impressive demos that fail in production.

Test Set vs Development Set

A critical discipline in machine learning that also applies to RAG evaluation is the train-dev-test split. Your test set should be completely frozen — never used to make development decisions. Use a separate dev set for experimenting with chunking strategies, prompt changes, and embedding models. Only run the test set when you believe a change is production-ready. This separation prevents overfitting your RAG pipeline to the test set and ensures your final reported metrics reflect genuine generalization.

import json
from sklearn.model_selection import train_test_split

def split_golden_dataset(all_questions, test_ratio=0.3, seed=42):
    dev_set, test_set = train_test_split(
        all_questions,
        test_size=test_ratio,
        random_state=seed
    )
    print(f'Dev set: {len(dev_set)} questions')
    print(f'Test set: {len(test_set)} questions (FROZEN)')
    with open('eval/dev_set.json', 'w') as f:
        json.dump(dev_set, f, indent=2)
    with open('eval/test_set.json', 'w') as f:
        json.dump(test_set, f, indent=2)
    return dev_set, test_set

Quick Check

Test your understanding of AI Engineering concepts from this lesson.

Lesson Recap

In this lesson you learned: the two independent failure modes of retrieval and generation that require separate metrics, how to build a golden dataset of question-answer-chunk triples for objective evaluation, hit rate as the core retrieval metric and faithfulness and answer relevance as the core generation metrics, and the importance of tracking metrics over time to prevent regressions. Next up we implement specific retrieval metrics including MRR and NDCG.

常见问题解答

「为什么 RAG 中的评估很重要」课时是免费的吗?

是的 — 「为什么 RAG 中的评估很重要」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Engineering Academy 课程的其余内容,请升级到 CoddyKit PRO。 AI Engineering Academy 课程共包含 4 节课。

「为什么 RAG 中的评估很重要」这节课中我会学到什么?

了解 RAG 系统中的两种独立失败模式:检索失败和生成失败,并学习为什么需要使用不同指标分别诊断它们。 你通过在浏览器中直接运行的动手代码来练习 AI Engineering Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Engineering Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Engineering Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「为什么 RAG 中的评估很重要」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Engineering Academy 课中编写并运行代码吗?

能。每节 AI Engineering Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 为什么 RAG 中的评估很重要
  2. 检索指标:命中率、MRR 与 NDCG
  3. 生成指标:忠实度与答案相关性
  4. 构建自动化评估工具
← 返回 AI Engineering Academy