Add new file
This commit is contained in:
117
rag/functions/soma_evaluate.py
Normal file
117
rag/functions/soma_evaluate.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# rag/functions/soma_evaluate.py
|
||||
"""
|
||||
SOMA-анализ (LLM-as-judge) для оценки качества ответа.
|
||||
Оценивает ответ по критериям: релевантность, полнота, отсутствие галлюцинаций,
|
||||
стиль, полезность. Возвращает оценки и вердикт.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from rag.services.giga_client import GigaClient
|
||||
from rag.config_models import AppConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def soma_evaluate(
|
||||
query: str,
|
||||
context: str,
|
||||
answer: str,
|
||||
config: AppConfig,
|
||||
giga: GigaClient,
|
||||
prompts: Dict[str, str],
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Оценивает ответ LLM с помощью другого вызова GigaChat.
|
||||
|
||||
Аргументы:
|
||||
query: исходный запрос пользователя.
|
||||
context: контекст из базы знаний (может быть пустым).
|
||||
answer: сгенерированный ответ.
|
||||
config: объект конфигурации.
|
||||
giga: клиент GigaChat для вызова.
|
||||
prompts: словарь с содержимым промптов (ключ 'soma_evaluate').
|
||||
|
||||
Возвращает:
|
||||
dict с полями:
|
||||
scores: dict с оценками по критериям (числа от 1 до 5).
|
||||
overall_score: средняя оценка (float).
|
||||
verdict: 'pass' или 'fail' (в зависимости от порога).
|
||||
feedback: текстовое обоснование.
|
||||
error: строка ошибки (если есть).
|
||||
"""
|
||||
default_result = {
|
||||
"scores": {
|
||||
"relevance": 3.0,
|
||||
"completeness": 3.0,
|
||||
"no_hallucination": 3.0,
|
||||
"style": 3.0,
|
||||
"usefulness": 3.0,
|
||||
},
|
||||
"overall_score": 3.0,
|
||||
"verdict": "fail",
|
||||
"feedback": "Оценка не удалась, возвращены значения по умолчанию.",
|
||||
"error": None,
|
||||
}
|
||||
|
||||
prompt_template = prompts.get("soma_evaluate")
|
||||
if not prompt_template:
|
||||
logger.error("Промпт для SOMA-оценки не найден")
|
||||
default_result["error"] = "Отсутствует промпт soma_evaluate"
|
||||
return default_result
|
||||
|
||||
# Формируем полный промпт
|
||||
full_prompt = prompt_template.format(
|
||||
query=query,
|
||||
context=context[:3000] if context else "Нет контекста",
|
||||
answer=answer,
|
||||
)
|
||||
|
||||
# Параметры из конфига (можно сделать отдельную секцию)
|
||||
temperature = getattr(config, 'soma_temperature', 0.1) # низкая для детерминизма
|
||||
timeout = getattr(config, 'soma_timeout', 30)
|
||||
|
||||
try:
|
||||
response = await giga.chat(
|
||||
history=[],
|
||||
query=full_prompt,
|
||||
system_prompt=None,
|
||||
file_id=None,
|
||||
temperature=temperature,
|
||||
timeout=timeout, # нужно добавить timeout в GigaClient.chat (если нет, игнорировать)
|
||||
)
|
||||
# Ожидаем JSON
|
||||
data = json.loads(response.strip())
|
||||
scores = data.get("scores", {})
|
||||
feedback = data.get("feedback", "")
|
||||
|
||||
# Приводим оценки к числам
|
||||
for key in scores:
|
||||
try:
|
||||
scores[key] = float(scores[key])
|
||||
except (ValueError, TypeError):
|
||||
scores[key] = 3.0
|
||||
overall = sum(scores.values()) / len(scores) if scores else 3.0
|
||||
|
||||
# Порог можно задать в конфиге
|
||||
threshold = getattr(config, 'soma_threshold', 3.5)
|
||||
verdict = "pass" if overall >= threshold else "fail"
|
||||
|
||||
return {
|
||||
"scores": scores,
|
||||
"overall_score": overall,
|
||||
"verdict": verdict,
|
||||
"feedback": feedback,
|
||||
"error": None,
|
||||
}
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Ошибка парсинга JSON от GigaChat: {e}, ответ: {response[:200]}")
|
||||
default_result["error"] = f"Ошибка парсинга JSON: {e}"
|
||||
return default_result
|
||||
except Exception as e:
|
||||
logger.exception(f"Ошибка при вызове SOMA-оценки: {e}")
|
||||
default_result["error"] = str(e)
|
||||
return default_result
|
||||
Reference in New Issue
Block a user