Files
wiki_crawler/tests/rag_benchmark/evaluator.py
2026-01-27 01:41:45 +08:00

74 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import logging
from backend.services.llm_service import llm_service
logger = logging.getLogger(__name__)
class RAGEvaluator:
def __init__(self):
self.llm = llm_service
def calculate_retrieval_metrics(self, retrieved_docs, dataset_item):
"""
计算检索阶段指标: Keyword Recall (关键词覆盖率)
检查 dataset 中的 keywords 有多少出现在了 retrieved_docs 的 content 中
"""
required_keywords = dataset_item.get("keywords", [])
if not required_keywords:
return {"keyword_recall": 1.0, "hit": True} # 没有关键词要求,默认算对
# 将所有检索到的文本拼接并转小写
full_context = " ".join([doc['content'] for doc in retrieved_docs]).lower()
found_count = 0
for kw in required_keywords:
if kw.lower() in full_context:
found_count += 1
recall = found_count / len(required_keywords)
return {
"keyword_recall": recall,
# 只要召回率大于 0 就认为 Hit 了一部分;
# 严格一点可以要求 recall > 0.5,这里我们设定只要沾边就算 Hit
"hit": recall > 0
}
def evaluate_generation_quality(self, question, generated_answer, ground_truth_answer, q_type):
"""
使用 LLM 作为裁判,评估生成质量 (1-5分)
"""
prompt = f"""
你是一名RAG系统的自动化测试裁判。请根据以下信息对“系统回答”进行评分1-5分
【测试类型】: {q_type}
【用户问题】: {question}
【标准答案 (Ground Truth)】: {ground_truth_answer}
【系统回答】: {generated_answer}
评分标准:
- 5分: 含义与标准答案完全一致,逻辑正确,无幻觉。
- 4分: 核心意思正确,但缺少部分细节或废话较多。
- 3分: 回答了一部分正确信息,但有遗漏或轻微错误。
- 2分: 包含大量错误信息或严重答非所问。
- 1分: 完全错误,或产生了严重幻觉(例如在负向测试中编造了不存在的功能)。
注意:对于"negative_test"(负向测试),如果标准答案是“不支持/文档未提及”,而系统回答诚实地说“未找到相关信息”或“不支持”,应给满分。
请仅返回JSON格式: {{"score": 5, "reason": "理由..."}}
"""
try:
# 使用 system_prompt 强制约束格式
result_str = self.llm.chat(prompt, system_prompt="你是一个只输出JSON的评测机器人。")
# 清洗 Markdown 格式 (```json ... ```)
if "```" in result_str:
result_str = result_str.split("```json")[-1].split("```")[0].strip()
eval_result = json.loads(result_str)
return eval_result
except Exception as e:
logger.error(f"Eval LLM failed: {e}")
# 降级处理
return {"score": 0, "reason": "Evaluation Script Error"}