Files
fckbot/rag/functions/soma_evaluate.py
2026-06-30 20:49:14 +00:00

116 lines
4.4 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.
"""
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