74 lines
3.1 KiB
Python
74 lines
3.1 KiB
Python
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"} |