/
Alekron
/
Unbeatable_Benchmarking
Обзор
Документация
Войти
/
Alekron
/
Unbeatable_Benchmarking
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Backend/mcp_processor.py
196 строк
8 KB
Alekron
Initial Backend
23 ноя 2025, 09:12
23 ноя 2025, 09:12
245fffc
Код
Авторство
О чём код?
import requests import json import logging from typing import Dict, Any from datetime import datetime from models import ExtractionRequest, ProductInfo from model_logger import ModelLogger # Добавляем импорт logger = logging.getLogger(__name__) class MCPProcessor: def __init__(self, ollama_url: str = "http://localhost:11434"): self.ollama_url = ollama_url self.model_logger = ModelLogger() # Инициализируем логгер self._test_connection() def _test_connection(self): """Тестируем подключение к Ollama""" try: response = requests.get(f"{self.ollama_url}/api/tags", timeout=10) if response.status_code == 200: models = response.json().get('models', []) logger.info(f"Ollama подключен. Доступные модели: {[m['name'] for m in models]}") else: logger.error(f"Ollama недоступен. Status: {response.status_code}") except Exception as e: logger.error(f"Не удалось подключиться к Ollama: {e}") async def extract_product_info(self, request: ExtractionRequest) -> ProductInfo: """Извлекает информацию о продукте из контента страницы через Ollama""" prompt = self._create_extraction_prompt(request) model_response = "" extracted_data = {} try: logger.info(f"Отправляем запрос к Ollama для банка {request.bank}...") payload = { "model": "qwen3:8b", # ИСПРАВЛЕНО название модели "prompt": prompt, "stream": False, "options": { "temperature": 0.1, "top_p": 0.9 } } response = requests.post( f"{self.ollama_url}/api/generate", json=payload, timeout=120 ) logger.info(f"Ollama response status: {response.status_code}") if response.status_code == 200: result = response.json() model_response = result["response"].strip() logger.info(f"Ollama вернул ответ длиной {len(model_response)} символов") # Чистим JSON от возможных оберток json_str = self._clean_json_response(model_response) # Парсим JSON try: extracted_data = json.loads(json_str) except json.JSONDecodeError as e: logger.error(f"Ошибка парсинга JSON: {e}") logger.error(f"Сырой ответ модели: {model_response}") extracted_data = self._parse_fallback_response(model_response) # Логируем взаимодействие self.model_logger.log_interaction( bank=request.bank, url=request.page_content[:100] + "..." if len(request.page_content) > 100 else request.page_content, # Для логирования page_content=request.page_content, prompt=prompt, model_response=model_response, extracted_data=extracted_data ) return ProductInfo( bank=request.bank, product_name=extracted_data.get("product_name", "Не определено"), conditions=extracted_data.get("conditions", {}), source_url=request.page_content[:100] + "..." if len( request.page_content) > 100 else request.page_content, extracted_at=datetime.now().isoformat(), confidence=extracted_data.get("confidence", 0.5) ) else: logger.error(f"Ollama error: {response.status_code} - {response.text}") # Логируем ошибку self.model_logger.log_interaction( bank=request.bank, url="", page_content=request.page_content, prompt=prompt, model_response=f"ERROR: {response.status_code} - {response.text}", extracted_data={"error": "Ollama request failed"} ) return self._create_fallback_response(request) except Exception as e: logger.error(f"Error in MCP processing: {e}") # Логируем исключение self.model_logger.log_interaction( bank=request.bank, url="", page_content=request.page_content, prompt=prompt, model_response=f"EXCEPTION: {str(e)}", extracted_data={"error": str(e)} ) return self._create_fallback_response(request) def _parse_fallback_response(self, model_response: str) -> Dict[str, Any]: """Пытается распарсить ответ модели если JSON сломан""" try: # Ищем JSON в тексте start = model_response.find('{') end = model_response.rfind('}') + 1 if start != -1 and end != 0: json_str = model_response[start:end] return json.loads(json_str) # Если JSON не найден, создаем базовую структуру return { "product_name": "Не определено", "conditions": {}, "confidence": 0.1 } except: return { "product_name": "Не определено", "conditions": {}, "confidence": 0.1 } def _clean_json_response(self, json_str: str) -> str: """Очищает JSON ответ от лишних оберток""" # Удаляем markdown обертки json_str = json_str.replace('```json', '').replace('```', '').strip() # Иногда модель возвращает текст перед JSON if '{' in json_str and '}' in json_str: start = json_str.find('{') end = json_str.rfind('}') + 1 json_str = json_str[start:end] return json_str def _create_extraction_prompt(self, request: ExtractionRequest) -> str: return f""" ТЕКСТ С САЙТА БАНКА: {request.page_content[:6000]} ИНСТРУКЦИЯ: Извлеки информацию о банковском продукте из текста выше. БАНК: {request.bank} ТИП ПРОДУКТА: {request.product_description} КРИТЕРИИ ДЛЯ ИЗВЛЕЧЕНИЯ: {', '.join(request.criteria)} Найди в тексте информацию по указанным критериям. Если информация не найдена, используй "не указано". Определи название продукта. Оцени уверенность в извлечении от 0.1 до 1.0. ВЕРНИ ОТВЕТ В ФОРМАТЕ JSON: {{ "product_name": "название продукта", "conditions": {{ "критерий1": "значение1", "критерий2": "значение2" }}, "confidence": 0.8 }} Только JSON, без других текстов. """ def _create_fallback_response(self, request: ExtractionRequest) -> ProductInfo: """Создает заглушку при ошибке обработки""" conditions = {criterion: "не указано" for criterion in request.criteria} return ProductInfo( bank=request.bank, product_name=f"{request.product_description} ({request.bank})", conditions=conditions, source_url="", extracted_at=datetime.now().isoformat(), confidence=0.1 )