File size: 13,758 Bytes
f8ef382
b763a9b
f8ef382
 
ca297a7
b763a9b
ca297a7
b763a9b
 
 
 
 
f8ef382
b763a9b
 
 
 
 
 
 
f8ef382
b763a9b
f8ef382
 
b763a9b
 
 
aade89a
b763a9b
 
 
 
aade89a
b763a9b
 
aade89a
b763a9b
 
aade89a
b763a9b
 
 
f8ef382
b763a9b
 
ca297a7
aade89a
b763a9b
 
aade89a
 
b763a9b
 
 
 
aade89a
 
 
 
b763a9b
aade89a
 
b763a9b
 
aade89a
b763a9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aade89a
 
 
b763a9b
 
aade89a
 
b763a9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca297a7
 
 
b763a9b
ca297a7
 
b763a9b
 
ca297a7
 
 
 
 
b763a9b
ca297a7
b763a9b
 
ca297a7
b763a9b
ca297a7
 
 
 
b763a9b
ca297a7
b763a9b
 
ca297a7
 
b763a9b
 
ca297a7
 
 
 
 
b763a9b
 
ca297a7
 
b763a9b
ca297a7
 
b763a9b
ca297a7
b763a9b
ca297a7
 
b763a9b
ca297a7
 
 
 
 
 
b763a9b
 
 
ca297a7
b763a9b
ca297a7
 
b763a9b
 
 
 
 
ca297a7
 
b763a9b
ca297a7
 
b763a9b
 
aade89a
b763a9b
 
985047d
b763a9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
985047d
b763a9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca297a7
b763a9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca297a7
b763a9b
aade89a
b763a9b
 
aade89a
b763a9b
 
 
aade89a
 
b763a9b
 
 
 
ca297a7
 
aade89a
b763a9b
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
"""
Улучшенный агент GAIA с интеграцией LLM для курса Hugging Face
"""

import os
import gradio as gr
import requests
import pandas as pd
import json
import time
from typing import List, Dict, Any, Optional, Callable, Union
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer

# --- Константы ---
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
DEFAULT_MODEL = "google/flan-t5-small"  # Меньшая модель для быстрой загрузки
MAX_RETRIES = 3  # Максимальное количество попыток отправки
RETRY_DELAY = 5  # Задержка между попытками в секундах

class LLMGAIAAgent:
    """
    Улучшенный агент GAIA, использующий языковую модель для генерации ответов.
    """
    
    def __init__(self, model_name=DEFAULT_MODEL):
        """Инициализация агента с языковой моделью."""
        print(f"Инициализация LLMGAIAAgent с моделью: {model_name}")
        try:
            self.tokenizer = AutoTokenizer.from_pretrained(model_name)
            self.model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
            self.model_name = model_name
            print(f"Успешно загружена модель: {model_name}")
        except Exception as e:
            print(f"Ошибка загрузки модели: {e}")
            print("Переход к шаблонным ответам")
            self.model = None
            self.tokenizer = None
            self.model_name = None
    
    def __call__(self, question: str) -> str:
        """Обработка вопроса и возврат ответа с использованием языковой модели."""
        print(f"Обработка вопроса: {question}")
        
        if self.model is None or self.tokenizer is None:
            return self._fallback_response(question)
        
        try:
            prompt = self._prepare_prompt(question)
            inputs = self.tokenizer(prompt, return_tensors="pt", max_length=512, truncation=True)
            outputs = self.model.generate(
                inputs["input_ids"],
                max_length=150,
                min_length=20,
                temperature=0.7,
                top_p=0.9,
                do_sample=True,
                num_return_sequences=1
            )
            response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
            response = self._clean_response(response)
            return response
        except Exception as e:
            print(f"Ошибка генерации ответа: {e}")
            return self._fallback_response(question)
    
    def _prepare_prompt(self, question: str) -> str:
        """Подготовка подходящего запроса на основе типа вопроса."""
        question_lower = question.lower()
        if any(keyword in question_lower for keyword in [
            "calculate", "compute", "sum", "difference", 
            "product", "divide", "plus", "minus", "times"
        ]):
            return f"Решите эту математическую задачу шаг за шагом: {question}"
        elif any(keyword in question_lower for keyword in [
            "image", "picture", "photo", "graph", "chart", "diagram"
        ]):
            return f"Опишите, что может быть изображено на картинке, связанной с этим вопросом: {question}"
        elif any(keyword in question_lower for keyword in [
            "who", "what", "where", "when", "why", "how"
        ]):
            return f"Дайте краткий и точный ответ на этот фактический вопрос: {question}"
        else:
            return f"Дайте краткий, информативный ответ на этот вопрос: {question}"
    
    def _clean_response(self, response: str) -> str:
        """Очистка ответа модели для получения чистого текста."""
        prefixes = [
            "Answer:", "Response:", "A:", "The answer is:", 
            "It is:", "I think it is:", "The result is:", 
            "Based on the image:", "In the image:", 
            "The image shows:", "From the image:"
        ]
        for prefix in prefixes:
            if response.lower().startswith(prefix.lower()):
                response = response[len(prefix):].strip()
        if len(response) < 10:
            return self._fallback_response("general")
        return response.strip()
    
    def _fallback_response(self, question: str) -> str:
        """Резервный ответ, если модель не сработала."""
        question_lower = question.lower() if isinstance(question, str) else ""
        if "who" in question_lower:
            return "Известная личность в этой области."
        elif "when" in question_lower:
            return "Это произошло в значительный исторический период."
        elif "where" in question_lower:
            return "Место известно своей культурной значимостью."
        elif "what" in question_lower:
            return "Это важное понятие или объект."
        elif "why" in question_lower:
            return "Это произошло из-за ряда факторов."
        elif "how" in question_lower:
            return "Процесс включает несколько ключевых шагов."
        return "Ответ включает несколько важных факторов."

class EvaluationRunner:
    """
    Управление процессом оценки: получение вопросов, запуск агента и отправка ответов.
    """
    
    def __init__(self, api_url: str = DEFAULT_API_URL):
        """Инициализация с конечными точками API."""
        self.api_url = api_url
        self.questions_url = f"{api_url}/questions"
        self.submit_url = f"{api_url}/submit"
    
    def run_evaluation(self, 
                      agent: Callable[[str], str], 
                      username: str, 
                      agent_code_url: str) -> tuple[str, pd.DataFrame]:
        """Запуск полного процесса оценки."""
        questions_data = self._fetch_questions()
        if isinstance(questions_data, str):
            return questions_data, None
        
        results_log, answers_payload = self._run_agent_on_questions(agent, questions_data)
        if not answers_payload:
            return "Агент не дал ответов для отправки.", pd.DataFrame(results_log)
        
        submission_result = self._submit_answers_with_retry(username, agent_code_url, answers_payload)
        return submission_result, pd.DataFrame(results_log)
    
    def _fetch_questions(self) -> Union[List[Dict[str, Any]], str]:
        """Получение вопросов с сервера оценки."""
        print(f"Получение вопросов с: {self.questions_url}")
        try:
            response = requests.get(self.questions_url, timeout=15)
            response.raise_for_status()
            questions_data = response.json()
            if not questions_data:
                return "Список вопросов пуст или некорректен."
            print(f"Успешно получено {len(questions_data)} вопросов.")
            return questions_data
        except Exception as e:
            return f"Ошибка получения вопросов: {e}"
    
    def _run_agent_on_questions(self, 
                               agent: Callable[[str], str], 
                               questions_data: List[Dict[str, Any]]) -> tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
        """Запуск агента на всех вопросах."""
        results_log = []
        answers_payload = []
        print(f"Запуск агента на {len(questions_data)} вопросах...")
        for item in questions_data:
            task_id = item.get("task_id")
            question_text = item.get("question")
            if not task_id or question_text is None:
                continue
            try:
                submitted_answer = agent(question_text)
                answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
                results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
            except Exception as e:
                results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"ОШИБКА: {e}"})
        return results_log, answers_payload
    
    def _submit_answers_with_retry(self, 
                                  username: str, 
                                  agent_code_url: str, 
                                  answers_payload: List[Dict[str, Any]]) -> str:
        """Отправка ответов с логикой повтора."""
        submission_data = {
            "username": username.strip(),
            "agent_code_url": agent_code_url,  # Исправленный ключ
            "answers": answers_payload
        }
        print(f"Отправка {len(answers_payload)} ответов для пользователя '{username}'...")
        for attempt in range(1, MAX_RETRIES + 1):
            try:
                print(f"Попытка {attempt} из {MAX_RETRIES}...")
                response = requests.post(self.submit_url, json=submission_data, timeout=60)
                response.raise_for_status()
                result_data = response.json()
                final_status = (
                    f"Отправка успешна!\n"
                    f"Пользователь: {result_data.get('username')}\n"
                    f"Общий балл: {result_data.get('overall_score', 'N/A')}\n"
                    f"Правильные ответы: {result_data.get('correct_answers', 'N/A')}\n"
                    f"Всего вопросов: {result_data.get('total_questions', 'N/A')}\n"
                )
                if all(result_data.get(key, "N/A") == "N/A" for key in ["overall_score", "correct_answers", "total_questions"]):
                    final_status += (
                        "\nПримечание: Результаты показывают 'N/A'. Возможные причины:\n"
                        "- Ограничения активности аккаунта\n"
                        "- Задержка обработки\n"
                        "- Проблема с API\n"
                        f"Проверьте статус: {DEFAULT_API_URL}/results?username={username}"
                    )
                print(final_status)
                return final_status
            except Exception as e:
                if attempt < MAX_RETRIES:
                    time.sleep(RETRY_DELAY)
                else:
                    return f"Ошибка отправки после {MAX_RETRIES} попыток: {e}"

def run_and_submit_all(profile: gr.OAuthProfile | None, *args):
    """Основная функция для запуска через Gradio."""
    if not profile:
        return "Пожалуйста, войдите в Hugging Face.", None
    username = profile.username
    space_id = os.getenv("SPACE_ID")
    agent_code_url = f"https://huggingface.co/spaces/{space_id}/tree/main"
    print(f"URL кода агента: {agent_code_url}")
    try:
        agent = LLMGAIAAgent()
        runner = EvaluationRunner()
        return runner.run_evaluation(agent, username, agent_code_url)
    except Exception as e:
        return f"Ошибка инициализации: {e}", None

# --- Интерфейс Gradio ---
with gr.Blocks() as demo:
    gr.Markdown("# Оценка агента GAIA (с улучшенным LLM)")
    gr.Markdown("## Инструкции:")
    gr.Markdown("1. Войдите в аккаунт Hugging Face.")
    gr.Markdown("2. Нажмите 'Запустить оценку и отправить все ответы'.")
    gr.Markdown("3. Посмотрите результаты в разделе вывода.")
    with gr.Row():
        login_button = gr.LoginButton(value="Войти через Hugging Face")
    with gr.Row():
        submit_button = gr.Button("Запустить оценку и отправить все ответы")
    with gr.Row():
        output_status = gr.Textbox(label="Результат отправки", lines=10)
        output_results = gr.Dataframe(label="Вопросы и ответы агента")
    submit_button.click(run_and_submit_all, inputs=[login_button], outputs=[output_status, output_results])

# --- Локальная тестовая функция ---
def test_agent():
    """Тестирование агента с примерами вопросов."""
    agent = LLMGAIAAgent()
    test_questions = [
        "What is 2 + 2?",
        "Who is the first president of the USA?",
        "What is the capital of France?"
    ]
    for question in test_questions:
        answer = agent(question)
        print(f"Вопрос: {question}")
        print(f"Ответ: {answer}")
        print("---")

if __name__ == "__main__":
    test_agent()
    # demo.launch()