File size: 9,692 Bytes
6a2aeb0
 
 
 
2697e31
737fe0e
 
 
ecb4e3d
2697e31
 
737fe0e
 
a3faa74
737fe0e
6a2aeb0
 
 
2697e31
737fe0e
2697e31
6a2aeb0
2697e31
 
 
 
 
 
 
 
 
 
 
 
 
 
737fe0e
2697e31
 
 
 
dd0280b
2697e31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737fe0e
2697e31
 
 
ecb4e3d
2697e31
 
0d4c845
2697e31
 
 
 
 
 
 
ecb4e3d
737fe0e
2697e31
 
 
0d4c845
2697e31
 
 
 
 
 
 
 
 
 
 
 
737fe0e
2697e31
6a2aeb0
2697e31
6a2aeb0
 
 
 
737fe0e
2697e31
 
 
5a25e2d
2697e31
 
 
 
5a25e2d
2697e31
 
 
 
 
5a25e2d
 
 
 
 
2697e31
 
 
5a25e2d
2697e31
 
 
5a25e2d
 
 
2697e31
5a25e2d
 
2697e31
5a25e2d
 
 
 
 
2697e31
 
af37df4
2697e31
 
 
 
 
 
 
 
 
 
 
 
 
af37df4
2697e31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737fe0e
2697e31
737fe0e
2697e31
 
 
737fe0e
2697e31
 
 
7c56a86
2697e31
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import re
import requests
import pandas as pd
import torch
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
import json
import logging
import time
import sys
import os
from functools import lru_cache

# Настройка логирования
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger("GAIA-Mastermind")

# Конфигурация
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
MODEL_NAME = "google/flan-t5-small"  # Используем меньшую модель для быстрой загрузки
API_RETRIES = 3
API_TIMEOUT = 30

# Настройка кэширования моделей
os.environ["TRANSFORMERS_CACHE"] = "/tmp/transformers_cache"
os.environ["HF_HOME"] = "/tmp/hf_home"

class GAIAExpert:
    _instance = None
    _is_initialized = False
    
    def __new__(cls):
        # Паттерн Singleton для предотвращения повторной загрузки модели
        if cls._instance is None:
            cls._instance = super(GAIAExpert, cls).__new__(cls)
        return cls._instance
    
    def __init__(self):
        # Инициализируем только один раз
        if not GAIAExpert._is_initialized:
            self.device = "cuda" if torch.cuda.is_available() else "cpu"
            logger.info(f"Инициализация модели на {self.device.upper()}")
            
            # Отложенная инициализация - токенизатор загружаем сразу, модель - по требованию
            self.tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
            self.model = None
            GAIAExpert._is_initialized = True
    
    def _ensure_model_loaded(self):
        """Ленивая загрузка модели только при необходимости"""
        if self.model is None:
            try:
                logger.info("Загрузка модели...")
                # Оптимизированная загрузка модели
                self.model = AutoModelForSeq2SeqLM.from_pretrained(
                    MODEL_NAME,
                    torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
                    low_cpu_mem_usage=True,
                    device_map="auto"  # Автоматическое распределение на доступные устройства
                ).eval()
                logger.info("Модель успешно загружена")
            except Exception as e:
                logger.exception("Ошибка загрузки модели")
                raise RuntimeError(f"Ошибка инициализации: {str(e)}")

    @lru_cache(maxsize=100)  # Кэширование ответов для повторяющихся вопросов
    def process_question(self, question: str) -> str:
        """Обработка вопроса с оптимизацией и кэшированием"""
        try:
            # Загружаем модель только при первом вызове
            self._ensure_model_loaded()
            
            # Оптимизированная обработка токенов
            inputs = self.tokenizer(
                f"Вопрос: {question}\nОтвет:",
                return_tensors="pt",
                max_length=256,
                truncation=True,
                padding="max_length"
            )
            
            # Перемещаем тензоры на нужное устройство
            if self.device == "cuda":
                inputs = {k: v.to(self.device) for k, v in inputs.items()}
            
            # Оптимизированная генерация
            with torch.no_grad():  # Отключаем вычисление градиентов для экономии памяти
                outputs = self.model.generate(
                    **inputs,
                    max_new_tokens=50,
                    num_beams=1,  # Ускорение генерации
                    early_stopping=True,
                    do_sample=False  # Детерминированная генерация для скорости
                )
            
            answer = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
            return json.dumps({"final_answer": answer.strip()})
        except Exception as e:
            return json.dumps({"final_answer": f"ERROR: {str(e)}"})

class GAIAEvaluator:
    def __init__(self, api_url: str = DEFAULT_API_URL):
        self.api_url = api_url
        self.questions_url = f"{api_url}/questions"
        self.submit_url = f"{api_url}/submit"
        self.session = requests.Session()
        self.session.headers.update({"Content-Type": "application/json"})
        # Настройка повторных попыток и таймаутов
        self.session.mount('https://', requests.adapters.HTTPAdapter(max_retries=API_RETRIES))

    def run_evaluation(self, username: str, agent_code: str):
        """Консольный процесс оценки без интерфейса"""
        # Создаем агента только при необходимости
        agent = GAIAExpert()
        
        # Получение вопросов с повторными попытками
        questions = self._fetch_questions_with_retry()
        if not isinstance(questions, list):
            logger.error(f"Ошибка получения вопросов: {questions}")
            return 0, 0
        
        # Обработка вопросов
        answers = []
        
        for i, q in enumerate(questions):
            task_id = q.get("task_id", f"task_{i}")
            logger.info(f"Обработка задачи {i+1}/{len(questions)}: {q['question'][:50]}...")
            
            try:
                json_response = agent.process_question(q["question"])
                response_obj = json.loads(json_response)
                answer = response_obj.get("final_answer", "")
                
                answers.append({
                    "task_id": task_id,
                    "answer": str(answer)[:300]
                })
            except Exception as e:
                logger.error(f"Ошибка обработки: {str(e)}")
                answers.append({
                    "task_id": task_id,
                    "answer": f"ERROR: {str(e)}"
                })
        
        # Отправка ответов с повторными попытками
        return self._submit_answers_with_retry(username, agent_code, answers)

    def _fetch_questions_with_retry(self, max_retries=3):
        """Получение вопросов с API с повторными попытками"""
        for attempt in range(max_retries):
            try:
                response = self.session.get(self.questions_url, timeout=API_TIMEOUT)
                if response.status_code == 200:
                    return response.json()
                logger.warning(f"HTTP error {response.status_code}, попытка {attempt+1}/{max_retries}")
                time.sleep(2 ** attempt)  # Экспоненциальная задержка между попытками
            except Exception as e:
                logger.warning(f"Connection error: {str(e)}, попытка {attempt+1}/{max_retries}")
                time.sleep(2 ** attempt)
        return f"Failed after {max_retries} attempts"

    def _submit_answers_with_retry(self, username: str, agent_code: str, answers: list, max_retries=3):
        """Отправка ответов на сервер с повторными попытками"""
        for attempt in range(max_retries):
            try:
                payload = {
                    "username": username.strip(),
                    "agent_code": agent_code.strip(),
                    "answers": answers
                }
                
                response = self.session.post(
                    self.submit_url,
                    json=payload,
                    timeout=API_TIMEOUT * 2
                )
                
                if response.status_code == 200:
                    result = response.json()
                    score = result.get("score", 0)
                    return score, len(answers)
                
                logger.warning(f"HTTP error {response.status_code}, попытка {attempt+1}/{max_retries}")
                time.sleep(2 ** attempt)
            except Exception as e:
                logger.error(f"Ошибка отправки: {str(e)}, попытка {attempt+1}/{max_retries}")
                time.sleep(2 ** attempt)
        return 0, len(answers)

if __name__ == "__main__":
    # Параметры запуска
    USERNAME = "yoshizen"
    AGENT_CODE = "https://huggingface.co/spaces/yoshizen/FinalTest"
    
    logger.info(f"Запуск оценки для {USERNAME}")
    
    start_time = time.time()
    evaluator = GAIAEvaluator()
    score, total = evaluator.run_evaluation(USERNAME, AGENT_CODE)
    
    elapsed = time.time() - start_time
    logger.info(f"Оценка завершена за {elapsed:.1f} сек")
    logger.info(f"Результат: {score}/{total} правильных ответов")
    
    if total > 0:
        logger.info(f"Точность: {score/total*100:.1f}%")
    else:
        logger.error("Не удалось обработать ни одного вопроса")