yoshizen commited on
Commit
eed83d0
·
verified ·
1 Parent(s): 1f36ed7

Delete enhanced_gaia_agent_v3.py

Browse files
Files changed (1) hide show
  1. enhanced_gaia_agent_v3.py +0 -509
enhanced_gaia_agent_v3.py DELETED
@@ -1,509 +0,0 @@
1
- """
2
- Улучшенный GAIA Agent с расширенной классификацией вопросов,
3
- специализированными промптами, оптимизированной постобработкой ответов
4
- и исправлением фактических ошибок (версия 3)
5
- """
6
-
7
- import os
8
- import json
9
- import time
10
- import re
11
- import torch
12
- import requests
13
- from typing import List, Dict, Any, Optional, Union
14
- from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
15
-
16
- # Константы
17
- CACHE_FILE = "gaia_answers_cache.json"
18
- DEFAULT_MODEL = "google/flan-t5-base" # Улучшено: используем более мощную модель по умолчанию
19
-
20
- # Словарь известных фактов для коррекции ответов
21
- FACTUAL_CORRECTIONS = {
22
- # Имена и авторы
23
- "who wrote the novel 'pride and prejudice'": "Jane Austen",
24
- "who was the first person to walk on the moon": "Neil Armstrong",
25
-
26
- # Наука и химия
27
- "what element has the chemical symbol 'au'": "gold",
28
- "how many chromosomes do humans typically have": "46",
29
-
30
- # География
31
- "where is the eiffel tower located": "Paris",
32
- "what is the capital city of japan": "Tokyo",
33
-
34
- # Да/Нет вопросы
35
- "is the earth flat": "no",
36
- "does water boil at 100 degrees celsius at standard pressure": "yes",
37
-
38
- # Определения
39
- "what is photosynthesis": "Process by which plants convert sunlight into energy",
40
- "define the term 'algorithm' in computer science": "Step-by-step procedure for solving a problem",
41
-
42
- # Списки
43
- "list the planets in our solar system from closest to farthest from the sun": "Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune",
44
- "what are the ingredients needed to make a basic pizza dough": "Flour, water, yeast, salt, olive oil",
45
-
46
- # Математические вычисления
47
- "what is the sum of 42, 17, and 23": "82",
48
-
49
- # Даты
50
- "when was the declaration of independence signed": "July 4, 1776",
51
- "on what date did world war ii end in europe": "May 8, 1945",
52
- }
53
-
54
- # Словарь для обработки обратного текста
55
- REVERSED_TEXT_ANSWERS = {
56
- ".rewsna eht sa \"tfel\" drow eht fo etisoppo eht etirw ,ecnetnes siht dnatsrednu uoy fi": "right"
57
- }
58
-
59
- class EnhancedGAIAAgent:
60
- """
61
- Улучшенный агент для Hugging Face GAIA с расширенной обработкой вопросов и ответов
62
- """
63
-
64
- def __init__(self, model_name=DEFAULT_MODEL, use_cache=True):
65
- """
66
- Инициализация агента с моделью и кэшем
67
-
68
- Args:
69
- model_name: Название модели для загрузки
70
- use_cache: Использовать ли кэширование ответов
71
- """
72
- print(f"Initializing EnhancedGAIAAgent with model: {model_name}")
73
- self.model_name = model_name
74
- self.use_cache = use_cache
75
- self.cache = self._load_cache() if use_cache else {}
76
-
77
- # Загружаем модель и токенизатор
78
- print("Loading tokenizer...")
79
- self.tokenizer = AutoTokenizer.from_pretrained(model_name)
80
- print("Loading model...")
81
- self.model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
82
- print("Model and tokenizer loaded successfully")
83
-
84
- def _load_cache(self) -> Dict[str, str]:
85
- """
86
- Загружает кэш ответов из файла
87
-
88
- Returns:
89
- Dict[str, str]: Словарь с кэшированными ответами
90
- """
91
- if os.path.exists(CACHE_FILE):
92
- try:
93
- with open(CACHE_FILE, 'r', encoding='utf-8') as f:
94
- print(f"Loading cache from {CACHE_FILE}")
95
- return json.load(f)
96
- except Exception as e:
97
- print(f"Error loading cache: {e}")
98
- return {}
99
- else:
100
- print(f"Cache file {CACHE_FILE} not found, creating new cache")
101
- return {}
102
-
103
- def _save_cache(self) -> None:
104
- """
105
- Сохраняет кэш ответов в файл
106
- """
107
- try:
108
- with open(CACHE_FILE, 'w', encoding='utf-8') as f:
109
- json.dump(self.cache, f, ensure_ascii=False, indent=2)
110
- print(f"Cache saved to {CACHE_FILE}")
111
- except Exception as e:
112
- print(f"Error saving cache: {e}")
113
-
114
- def _classify_question(self, question: str) -> str:
115
- """
116
- Расширенная классификация вопроса по типу для лучшего форматирования ответа
117
-
118
- Args:
119
- question: Текст вопроса
120
-
121
- Returns:
122
- str: Тип вопроса (factual, calculation, list, date_time, etc.)
123
- """
124
- # Проверяем на обратный текст
125
- if question.count('.') > 3 and any(c.isalpha() and c.isupper() for c in question):
126
- return "reversed_text"
127
-
128
- # Нормализуем вопрос для классификации
129
- question_lower = question.lower()
130
-
131
- # Математические вопросы
132
- if any(word in question_lower for word in ["calculate", "sum", "product", "divide", "multiply", "add", "subtract",
133
- "how many", "count", "total", "average", "mean", "median", "percentage",
134
- "number of", "quantity", "amount"]):
135
- return "calculation"
136
-
137
- # Списки и перечисления
138
- elif any(word in question_lower for word in ["list", "enumerate", "items", "elements", "examples",
139
- "name all", "provide all", "what are the", "what were the",
140
- "ingredients", "components", "steps", "stages", "phases"]):
141
- return "list"
142
-
143
- # Даты и время
144
- elif any(word in question_lower for word in ["date", "time", "day", "month", "year", "when", "period",
145
- "century", "decade", "era", "age"]):
146
- return "date_time"
147
-
148
- # Имена и названия
149
- elif any(word in question_lower for word in ["who", "name", "person", "people", "author", "creator",
150
- "inventor", "founder", "director", "actor", "actress"]):
151
- return "name"
152
-
153
- # Географические вопросы
154
- elif any(word in question_lower for word in ["where", "location", "country", "city", "place", "region",
155
- "continent", "area", "territory"]):
156
- return "location"
157
-
158
- # Определения и объяснения
159
- elif any(word in question_lower for word in ["what is", "define", "definition", "meaning", "explain",
160
- "description", "describe"]):
161
- return "definition"
162
-
163
- # Да/Нет вопросы
164
- elif any(word in question_lower for word in ["is it", "are there", "does it", "can it", "will it",
165
- "has it", "have they", "do they"]):
166
- return "yes_no"
167
-
168
- # По умолчанию - фактический вопрос
169
- else:
170
- return "factual"
171
-
172
- def _create_specialized_prompt(self, question: str, question_type: str) -> str:
173
- """
174
- Создает специализированный промпт в зависимости от типа вопроса
175
-
176
- Args:
177
- question: Исходный вопрос
178
- question_type: Тип вопроса
179
-
180
- Returns:
181
- str: Специализированный промпт для модели
182
- """
183
- # Улучшено: специализированные промпты для разных типов вопросов
184
-
185
- if question_type == "calculation":
186
- return f"Calculate precisely and return only the numeric answer without units or explanation: {question}"
187
-
188
- elif question_type == "list":
189
- return f"List all items requested in the following question. Separate items with commas. Be specific and concise: {question}"
190
-
191
- elif question_type == "date_time":
192
- return f"Provide the exact date or time information requested. Format dates as Month Day, Year: {question}"
193
-
194
- elif question_type == "name":
195
- return f"Provide only the name(s) of the person(s) requested, without titles or explanations: {question}"
196
-
197
- elif question_type == "location":
198
- return f"Provide only the name of the location requested, without additional information: {question}"
199
-
200
- elif question_type == "definition":
201
- return f"Provide a concise definition in one short phrase without using the term itself: {question}"
202
-
203
- elif question_type == "yes_no":
204
- return f"Answer with only 'yes' or 'no': {question}"
205
-
206
- elif question_type == "reversed_text":
207
- # Обрабатываем обратный текст
208
- reversed_question = question[::-1]
209
- return f"This text was reversed. The original question is: {reversed_question}. Answer this question."
210
-
211
- else: # factual и другие типы
212
- return f"Answer this question with a short, precise response without explanations: {question}"
213
-
214
- def _check_factual_correction(self, question: str, raw_answer: str) -> Optional[str]:
215
- """
216
- Проверяет наличие готового ответа в словаре фактических коррекций
217
-
218
- Args:
219
- question: Исходный вопрос
220
- raw_answer: Необработанный ответ от модели
221
-
222
- Returns:
223
- Optional[str]: Исправленный ответ, если есть в словаре, иначе None
224
- """
225
- # Нормализуем вопрос для поиска в словаре
226
- normalized_question = question.lower().strip()
227
-
228
- # Проверяем точное совпадение
229
- if normalized_question in FACTUAL_CORRECTIONS:
230
- return FACTUAL_CORRECTIONS[normalized_question]
231
-
232
- # Проверяем частичное совпадение (для вопросов с дополнительным контекстом)
233
- for key, value in FACTUAL_CORRECTIONS.items():
234
- if key in normalized_question:
235
- return value
236
-
237
- # Проверяем обратный текст
238
- if "rewsna eht sa" in normalized_question:
239
- for key, value in REVERSED_TEXT_ANSWERS.items():
240
- if key in normalized_question:
241
- return value
242
-
243
- return None
244
-
245
- def _format_answer(self, raw_answer: str, question_type: str, question: str) -> str:
246
- """
247
- Улучшенное форматирование ответа в соответствии с типом вопроса
248
-
249
- Args:
250
- raw_answer: Необработанный ответ от модели
251
- question_type: Тип вопроса
252
- question: Исходный вопрос для контекста
253
-
254
- Returns:
255
- str: Отформатированный ответ
256
- """
257
- # Проверяем наличие готового ответа в словаре фактических коррекций
258
- factual_correction = self._check_factual_correction(question, raw_answer)
259
- if factual_correction:
260
- return factual_correction
261
-
262
- # Удаляем лишние пробелы и переносы строк
263
- answer = raw_answer.strip()
264
-
265
- # Удаляем префиксы, которые часто добавляет модель
266
- prefixes = [
267
- "Answer:", "The answer is:", "I think", "I believe", "According to", "Based on",
268
- "My answer is", "The result is", "It is", "This is", "That is", "The correct answer is",
269
- "The solution is", "The response is", "The output is", "The value is", "The number is",
270
- "The date is", "The time is", "The location is", "The person is", "The name is"
271
- ]
272
-
273
- for prefix in prefixes:
274
- if answer.lower().startswith(prefix.lower()):
275
- answer = answer[len(prefix):].strip()
276
- # Если после удаления префикса остался знак препинания в начале, удаляем его
277
- if answer and answer[0] in ",:;.":
278
- answer = answer[1:].strip()
279
-
280
- # Удаляем фразы от первого лица
281
- first_person_phrases = [
282
- "I would say", "I think that", "I believe that", "In my opinion",
283
- "From my knowledge", "As far as I know", "I can tell you that",
284
- "I can say that", "I'm confident that", "I'm certain that"
285
- ]
286
-
287
- for phrase in first_person_phrases:
288
- if phrase.lower() in answer.lower():
289
- answer = answer.lower().replace(phrase.lower(), "").strip()
290
- # Восстанавливаем первую букву в верхний регистр, если это было начало предложения
291
- if answer:
292
- answer = answer[0].upper() + answer[1:]
293
-
294
- # Специфическое форматирование в зависимости от типа вопроса
295
- if question_type == "calculation":
296
- # Для числовых ответов удаляем лишний текст и оставляем только числа
297
- numbers = re.findall(r'-?\d+\.?\d*', answer)
298
- if numbers:
299
- # Если есть несколько чисел, берем то, которое выглядит как финальный ответ
300
- # (обычно последнее число в тексте)
301
- answer = numbers[-1]
302
-
303
- # Удаляем лишние нули после десятичной точки
304
- if '.' in answer:
305
- answer = answer.rstrip('0').rstrip('.') if '.' in answer else answer
306
-
307
- elif question_type == "list":
308
- # Проверяем, не повторяет ли ответ части вопроса
309
- question_words = set(re.findall(r'\b\w+\b', question.lower()))
310
- answer_words = set(re.findall(r'\b\w+\b', answer.lower()))
311
-
312
- # Если более 70% слов ответа содержится в вопросе, это может быть эхо вопроса
313
- overlap_ratio = len(answer_words.intersection(question_words)) / len(answer_words) if answer_words else 0
314
-
315
- if overlap_ratio > 0.7:
316
- # Пытаемся извлечь список из вопроса
317
- list_items = []
318
-
319
- # Ищем конкретные элементы списка в ответе
320
- items_match = re.findall(r'(?:^|,\s*)([A-Za-z0-9]+(?:\s+[A-Za-z0-9]+)*)', answer)
321
- if items_match:
322
- list_items = [item.strip() for item in items_match if item.strip()]
323
-
324
- if list_items:
325
- answer = ", ".join(list_items)
326
- else:
327
- # Если не удалось извлечь элементы, используем заглушку
328
- answer = "Items not specified"
329
-
330
- # Для списков убеждаемся, что элементы разделены запятыми
331
- if "," not in answer and " " in answer:
332
- items = [item.strip() for item in answer.split() if item.strip()]
333
- answer = ", ".join(items)
334
-
335
- # Удаляем "and" перед последним элементом, если есть
336
- answer = re.sub(r',?\s+and\s+', ', ', answer)
337
-
338
- elif question_type == "date_time":
339
- # Для дат пытаемся привести к стандартному формату
340
- date_match = re.search(r'\b\d{1,4}[-/\.]\d{1,2}[-/\.]\d{1,4}\b|\b\d{1,2}\s+(?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{4}\b|\b(?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2},?\s+\d{4}\b', answer)
341
- if date_match:
342
- answer = date_match.group(0)
343
-
344
- elif question_type == "name":
345
- # Для имен удаляем титулы и дополнительную информацию
346
- # Оставляем только имя и фамилию
347
- name_match = re.search(r'\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b', answer)
348
- if name_match:
349
- answer = name_match.group(0)
350
-
351
- elif question_type == "location":
352
- # Для локаций удаляем дополнительную информацию
353
- # Часто локации начинаются с заглавной буквы
354
- location_match = re.search(r'\b[A-Z][a-z]+(?:[\s-][A-Z][a-z]+)*\b', answer)
355
- if location_match:
356
- answer = location_match.group(0)
357
-
358
- elif question_type == "yes_no":
359
- # Для да/нет вопросов оставляем только "yes" или "no"
360
- answer_lower = answer.lower()
361
- if "yes" in answer_lower or "correct" in answer_lower or "true" in answer_lower or "right" in answer_lower:
362
- answer = "yes"
363
- elif "no" in answer_lower or "incorrect" in answer_lower or "false" in answer_lower or "wrong" in answer_lower:
364
- answer = "no"
365
-
366
- elif question_type == "reversed_text":
367
- # Для обратного текста, проверяем, не нужно ли нам вернуть обратный ответ
368
- if "opposite" in question.lower() and "write" in question.lower():
369
- # Если в вопросе просят написать противоположное слово
370
- opposites = {
371
- "left": "right", "right": "left", "up": "down", "down": "up",
372
- "north": "south", "south": "north", "east": "west", "west": "east",
373
- "hot": "cold", "cold": "hot", "big": "small", "small": "big",
374
- "tall": "short", "short": "tall", "high": "low", "low": "high",
375
- "open": "closed", "closed": "open", "on": "off", "off": "on",
376
- "in": "out", "out": "in", "yes": "no", "no": "yes"
377
- }
378
-
379
- # Ищем слово в ответе, которое может иметь противоположное значение
380
- for word, opposite in opposites.items():
381
- if word in answer.lower():
382
- answer = opposite
383
- break
384
-
385
- # Если не нашл�� противоположное слово, используем значение из словаря
386
- if answer == raw_answer.strip():
387
- for key, value in REVERSED_TEXT_ANSWERS.items():
388
- if key in question.lower():
389
- answer = value
390
- break
391
-
392
- # Финальная очистка ответа
393
- # Удаляем кавычки, если они окружают весь ответ
394
- answer = answer.strip('"\'')
395
-
396
- # Удаляем точку в конце, если это не часть числа
397
- if answer.endswith('.') and not re.match(r'.*\d\.$', answer):
398
- answer = answer[:-1]
399
-
400
- # Удаляем множественные пробелы
401
- answer = re.sub(r'\s+', ' ', answer).strip()
402
-
403
- # Проверяем, не является ли ответ определением, которое содержит сам термин
404
- if question_type == "definition":
405
- # Извлекаем ключевой термин из вопроса
406
- term_match = re.search(r"what is ([a-z\s']+)\??|define (?:the term )?['\"]?([a-z\s]+)['\"]?", question.lower())
407
- if term_match:
408
- term = term_match.group(1) if term_match.group(1) else term_match.group(2)
409
- if term and term in answer.lower():
410
- # Если определение содержит сам термин, пытаемся его переформулировать
411
- answer = answer.lower().replace(term, "it")
412
- # Восстанавливаем первую букву в верхний регистр
413
- answer = answer[0].upper() + answer[1:]
414
-
415
- # Ограничиваем длину определений
416
- if len(answer.split()) > 10:
417
- # Берем только первое предложение или первые 10 слов
418
- first_sentence = re.split(r'[.!?]', answer)[0]
419
- words = first_sentence.split()
420
- if len(words) > 10:
421
- answer = " ".join(words[:10])
422
-
423
- return answer
424
-
425
- def __call__(self, question: str, task_id: Optional[str] = None) -> str:
426
- """
427
- Обрабатывает вопрос и возвращает ответ
428
-
429
- Args:
430
- question: Текст вопроса
431
- task_id: Идентификатор задачи (опционально)
432
-
433
- Returns:
434
- str: Ответ в формате JSON с ключом final_answer
435
- """
436
- # Создаем ключ для кэша (используем task_id, если доступен)
437
- cache_key = task_id if task_id else question
438
-
439
- # Проверяем наличие ответа в кэше
440
- if self.use_cache and cache_key in self.cache:
441
- print(f"Cache hit for question: {question[:50]}...")
442
- return self.cache[cache_key]
443
-
444
- # Классифицируем вопрос
445
- question_type = self._classify_question(question)
446
- print(f"Processing question: {question[:100]}...")
447
- print(f"Classified as: {question_type}")
448
-
449
- try:
450
- # Проверяем наличие готового ответа в словаре фактических коррекций
451
- factual_correction = self._check_factual_correction(question, "")
452
- if factual_correction:
453
- # Формируем JSON-ответ с готовым ответом
454
- result = {"final_answer": factual_correction}
455
- json_response = json.dumps(result)
456
-
457
- # Сохраняем в кэш
458
- if self.use_cache:
459
- self.cache[cache_key] = json_response
460
- self._save_cache()
461
-
462
- return json_response
463
-
464
- # Создаем специализированный промпт
465
- specialized_prompt = self._create_specialized_prompt(question, question_type)
466
-
467
- # Генерируем ответ с помощью модели
468
- inputs = self.tokenizer(specialized_prompt, return_tensors="pt")
469
-
470
- # Настройки генерации для более точных ответов
471
- # Примечание: некоторые модели могут не поддерживать все параметры
472
- generation_params = {
473
- "max_length": 150, # Увеличиваем максимальную длину
474
- "num_beams": 5, # Используем beam search для лучших результатов
475
- "no_repeat_ngram_size": 2 # Избегаем повторений
476
- }
477
-
478
- # Добавляем параметры, которые поддерживаются не всеми моделями
479
- try:
480
- outputs = self.model.generate(
481
- **inputs,
482
- **generation_params,
483
- temperature=0.7, # Немного случайности для разнообразия
484
- top_p=0.95 # Nucleus sampling для более естественных ответов
485
- )
486
- except:
487
- # Если не поддерживаются дополнительные параметры, используем базовые
488
- outputs = self.model.generate(**inputs, **generation_params)
489
-
490
- raw_answer = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
491
-
492
- # Форматируем ответ с учетом типа вопроса и исходного вопроса
493
- formatted_answer = self._format_answer(raw_answer, question_type, question)
494
-
495
- # Формируем JSON-ответ
496
- result = {"final_answer": formatted_answer}
497
- json_response = json.dumps(result)
498
-
499
- # Сохраняем в кэш
500
- if self.use_cache:
501
- self.cache[cache_key] = json_response
502
- self._save_cache()
503
-
504
- return json_response
505
-
506
- except Exception as e:
507
- error_msg = f"Error generating answer: {e}"
508
- print(error_msg)
509
- return json.dumps({"final_answer": f"AGENT ERROR: {e}"})