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"}