DrZimmer commited on
Commit
1681c70
·
verified ·
1 Parent(s): c7172f4

Update agent.py

Browse files
Files changed (1) hide show
  1. agent.py +119 -143
agent.py CHANGED
@@ -9,7 +9,10 @@ from langgraph.graph import StateGraph, END
9
  from typing import Dict, Any
10
  from docx import Document
11
  from pptx import Presentation
12
- from langchain_ollama import ChatOllama
 
 
 
13
  import logging
14
  import importlib.util
15
  import re
@@ -23,7 +26,7 @@ import torch
23
  from faster_whisper import WhisperModel
24
  from sentence_transformers import SentenceTransformer
25
  import faiss
26
- import ollama
27
  import asyncio
28
  #from shazamio import Shazam
29
  from langchain_community.document_loaders import WikipediaLoader, ArxivLoader
@@ -31,7 +34,7 @@ from bs4 import BeautifulSoup
31
  from typing import TypedDict, Optional
32
  from faiss import IndexFlatL2
33
  import pdfplumber
34
- from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
35
  from retrying import retry
36
 
37
  # Настройка путей для Hugging Face Spaces
@@ -122,11 +125,11 @@ def check_faiss():
122
  raise ImportError("faiss не установлена. Установите: pip install faiss-cpu")
123
  logger.info("faiss доступна.")
124
 
125
- def check_ollama():
126
- if importlib.util.find_spec("ollama") is None:
127
- logger.error("ollama не установлена. Установите: pip install ollama")
128
- raise ImportError("ollama не установлена. Установите: pip install ollama")
129
- logger.info("ollama доступна.")
130
 
131
  def check_shazamio():
132
  if importlib.util.find_spec("shazamio") is None:
@@ -143,18 +146,67 @@ def check_langchain_community():
143
 
144
 
145
  # Инициализация модели
 
 
 
 
 
 
 
 
 
 
146
  try:
147
- llm = ChatOllama(base_url=OLLAMA_URL, model=MODEL_NAME, request_timeout=60)
148
- test_response = llm.invoke("Test")
149
- if test_response is None or not hasattr(test_response, 'content'):
150
- raise ValueError("Ollama модель недоступна или возвращает некорректный ответ")
151
- logger.info("Модель ChatOllama инициализирована.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  except Exception as e:
153
- logger.error(f"Ошибка инициализации модели: {e}")
154
  raise e
155
 
156
 
157
 
 
 
158
 
159
  # --- Состояние для LangGraph ---
160
  class AgentState(TypedDict):
@@ -421,7 +473,7 @@ async def process_file(file_path: str, question: str) -> str:
421
  check_faster_whisper()
422
  check_sentence_transformers()
423
  check_faiss()
424
- check_ollama()
425
  transcribed_text = transcribe_audio(file_path)
426
  if transcribed_text.startswith("Error"):
427
  logger.error(f"Ошибка транскрипции: {transcribed_text}")
@@ -844,7 +896,7 @@ def create_answer(state: AgentState) -> AgentState:
844
  file_content = state["file_content"]
845
  wiki_results = state["wiki_results"]
846
  arxiv_results = state["arxiv_results"]
847
- web_results = state.get("web_results", None) # Новое поле
848
  except Exception as e:
849
  logger.error(f"Ошибка извлечения ключей: {str(e)}")
850
  return {"answer": f"Error extracting keys: {str(e)}", "raw_answer": f"Error extracting keys: {str(e)}"}
@@ -890,16 +942,15 @@ def create_answer(state: AgentState) -> AgentState:
890
  if "card game" in question_lower:
891
  logger.info("Обработка карточной игры...")
892
  cards = ["2 of clubs", "3 of hearts", "King of spades", "Queen of hearts", "Jack of clubs", "Ace of diamonds"]
893
- # Шаги перестановок
894
- cards = cards[3:] + cards[:3] # 1. 3 карты сверху вниз
895
- cards = [cards[1], cards[0]] + cards[2:] # 2. Верхняя под вторую
896
- cards = [cards[2]] + cards[:2] + cards[3:] # 3. 2 карты сверху под третью
897
- cards = [cards[-1]] + cards[:-1] # 4. Нижняя наверх
898
- cards = [cards[2]] + cards[:2] + cards[3:] # 5. 2 карты сверху под третью
899
- cards = cards[4:] + cards[:4] # 6. 4 карты сверху вниз
900
- cards = [cards[-1]] + cards[:-1] # 7. Нижняя наверх
901
- cards = cards[2:] + cards[:2] # 8. 2 карты сверху вниз
902
- cards = [cards[-1]] + cards[:-1] # 9. Нижняя наверх
903
  state["answer"] = cards[0]
904
  state["raw_answer"] = cards[0]
905
  logger.info(f"Карточная игра обработана: {state['answer']}")
@@ -940,52 +991,20 @@ def create_answer(state: AgentState) -> AgentState:
940
  state["raw_answer"] = f"Error: {e}"
941
  return state
942
 
943
-
944
  # Обработка MP3-файлов
945
  file_path = state.get("file_path")
946
-
947
-
948
  if file_path and file_path.endswith(".mp3"):
949
  logger.info("Обработка MP3-файла")
950
-
951
- if ext == ".mp3" and ("name of the song" in question.lower() or "what song" in question.lower()):
952
- logger.warning("Распознавание песен больше не поддерживается: shazamio не установлена из-за конфликта с gradio. Но код работает в локальной версии без gradio")
953
- return "Unknown"
 
954
 
955
- #if "name of the song" in question_lower or "what song" in question_lower:
956
- # logger.info("Распознавание песни")
957
- # try:
958
- # # Поскольку file_content уже содержит результат process_file
959
- # if file_content and not file_content.startswith("Error"):
960
- # state["answer"] = file_content if file_content != "Not found" else "Unknown"
961
- # state["raw_answer"] = file_content
962
- # logger.info(f"Ответ для песни: {state['answer']}")
963
- # else:
964
- # state["answer"] = "Unknown"
965
- # state["raw_answer"] = "Error: No valid song recognition result"
966
- # logger.error("Ошибка: результат распознавания песни недоступен")
967
- # return state
968
- # except Exception as e:
969
- # logger.error(f"Ошибка распознавания песни: {str(e)}")
970
- # state["answer"] = "Unknown"
971
- # state["raw_answer"] = f"Error recognizing song: {str(e)}"
972
- # return state
973
-
974
-
975
  if "how long" in question_lower and "minute" in question_lower:
976
  logger.info("Определение длительности аудио")
977
  try:
978
- # audio_path = os.path.join(DATA_DIR, "test", file_path) if Path(
979
- # os.path.join(DATA_DIR, "test", file_path)).exists() else os.path.join(
980
- # DATA_DIR, "validation", file_path)
981
- # if not Path(audio_path).exists():
982
- # logger.error(f"Аудиофайл не найден: {audio_path}")
983
- # state["answer"] = "Unknown"
984
- # state["raw_answer"] = "Error: Audio file not found"
985
- # return state
986
- # audio = pydub.AudioSegment.from_file(audio_path)
987
  audio = pydub.AudioSegment.from_file(file_path)
988
-
989
  duration_seconds = len(audio) / 1000
990
  duration_minutes = round(duration_seconds / 60)
991
  state["answer"] = str(duration_minutes)
@@ -997,6 +1016,7 @@ def create_answer(state: AgentState) -> AgentState:
997
  state["answer"] = "Unknown"
998
  state["raw_answer"] = f"Error: {e}"
999
  return state
 
1000
  # RAG для MP3 (аудиокниги)
1001
  logger.info("RAG-обработка для MP3 (аудиокниги)")
1002
  try:
@@ -1009,7 +1029,6 @@ def create_answer(state: AgentState) -> AgentState:
1009
  # Инициализация RAG
1010
  check_sentence_transformers()
1011
  check_faiss()
1012
- check_ollama()
1013
  rag_model = SentenceTransformer("all-MiniLM-L6-v2")
1014
  index, sentences, embeddings = create_rag_index(file_content, rag_model)
1015
  question_embedding = rag_model.encode([question], convert_to_numpy=True)
@@ -1024,7 +1043,7 @@ def create_answer(state: AgentState) -> AgentState:
1024
 
1025
  # Промпт для MP3 с RAG
1026
  prompt = (
1027
- "You are a highly precise assistant tasked with answering a question based solely on the provided context from an audiobook's transcribed text. "
1028
  "Do not use any external knowledge or assumptions beyond the context. "
1029
  "Extract the answer strictly from the context, ensuring it matches the question's requirements. "
1030
  "If the question asks for an address, return only the street number and name (e.g., '123 Main'), excluding city, state, or street types (e.g., Street, Boulevard). "
@@ -1034,31 +1053,26 @@ def create_answer(state: AgentState) -> AgentState:
1034
  "Provide only the final answer, without explanations or additional text.\n"
1035
  f"Question: {question}\n"
1036
  f"Context: {relevant_context}\n"
1037
- "Answer:"
1038
  )
1039
  logger.info(f"Промпт для RAG: {prompt[:200]}...")
1040
 
1041
- # Вызов модели llama3:8b
1042
- response = ollama.generate(
1043
- model="llama3:8b",
1044
- prompt=prompt,
1045
- options={
1046
- "num_predict": 100,
1047
- "temperature": 0.0,
1048
- "top_p": 0.9,
1049
- "stop": ["\n"]
1050
- }
1051
  )
1052
- answer = response.get("response", "").strip() or "Not found"
1053
- logger.info(f"Ollama (llama3:8b) вернул ответ: {answer}")
1054
 
1055
  # Проверка адресов
1056
  if "address" in question_lower:
1057
- # Удаляем типы улиц, город, штат
1058
  answer = re.sub(r'\b(St\.|Street|Blvd\.|Boulevard|Ave\.|Avenue|Rd\.|Road|Dr\.|Drive)\b', '', answer, flags=re.IGNORECASE)
1059
- # Удаляем город и штат (после запятых)
1060
  answer = re.sub(r',\s*[^,]+$', '', answer).strip()
1061
- # Убедимся, что остались только номер и имя улицы
1062
  match = re.match(r'^\d+\s+[A-Za-z\s]+$', answer)
1063
  if not match:
1064
  logger.warning(f"Некорректный формат адреса: {answer}")
@@ -1074,19 +1088,16 @@ def create_answer(state: AgentState) -> AgentState:
1074
  state["raw_answer"] = f"Error RAG: {str(e)}"
1075
  return state
1076
 
1077
-
1078
-
1079
-
1080
  # Обработка вопросов с изображениями и Википедией
1081
  logger.info("Проверка вопросов с изображениями и Википедией")
1082
  if file_path and file_path.endswith((".jpg", ".png")) and "wikipedia" in question_lower:
1083
  logger.info("Обработка изображения с Википедией")
1084
  if wiki_results and not wiki_results.startswith("Error"):
1085
  prompt = (
1086
- f"Question: {question}\n"
1087
  f"Wikipedia Content: {wiki_results[:1000]}\n"
1088
- f"Instruction: Provide ONLY the final answer.\n"
1089
- "Answer:"
1090
  )
1091
  logger.info(f"Промпт для изображения с Википедией: {prompt[:200]}...")
1092
  else:
@@ -1098,7 +1109,7 @@ def create_answer(state: AgentState) -> AgentState:
1098
  # Общий случай
1099
  logger.info("Обработка общего случая")
1100
  prompt = (
1101
- f"Question: {question}\n"
1102
  f"Instruction: Provide ONLY the final answer.\n"
1103
  f"Examples:\n"
1104
  f"- Number: '42'\n"
@@ -1114,7 +1125,7 @@ def create_answer(state: AgentState) -> AgentState:
1114
  prompt += f"Wikipedia Results: {wiki_results[:1000]}\n"
1115
  has_context = True
1116
  logger.info(f"Добавлен wiki_results: {wiki_results[:50]}...")
1117
- if arxiv_results and not arxiv_results.startswith("Error"):
1118
  prompt += f"Arxiv Results: {arxiv_results[:1000]}\n"
1119
  has_context = True
1120
  logger.info(f"Добавлен arxiv_results: {arxiv_results[:50]}...")
@@ -1128,20 +1139,21 @@ def create_answer(state: AgentState) -> AgentState:
1128
  state["answer"] = "Unknown"
1129
  state["raw_answer"] = "No context available"
1130
  return state
1131
- prompt += "Answer:"
1132
- logger.info(f"Промпт для общего случая: {prompt[:200]}...")
1133
 
1134
- # Вызов LLM (qwen2:7b для не-MP3 случаев)
1135
- logger.info("Вызов LLM")
1136
  try:
1137
- response = llm.invoke(prompt)
1138
- logger.info(f"Ответ от llm.invoke: {response}")
1139
- if response is None:
1140
- logger.error("llm.invoke вернул None")
1141
- state["answer"] = "Unknown"
1142
- state["raw_answer"] = "LLM response is None"
1143
- return state
1144
- raw_answer = getattr(response, 'content', str(response)).strip() or "Unknown"
 
1145
  state["raw_answer"] = raw_answer
1146
  logger.info(f"Raw answer: {raw_answer[:100]}...")
1147
 
@@ -1151,59 +1163,24 @@ def create_answer(state: AgentState) -> AgentState:
1151
  clean_answer = re.sub(r'[^\w\s.-]', '', clean_answer)
1152
  logger.info(f"Clean answer: {clean_answer[:100]}...")
1153
 
1154
-
1155
- ####################################################
1156
- # Проверка на галлюцинации
1157
- # def is_valid_answer(question, answer, context):
1158
- # question_lower = question.lower()
1159
- # if "address" in question_lower:
1160
- # return bool(re.match(r'^\d+\s+[A-Za-z\s]+$', answer))
1161
- # if "how many" in question_lower or "number" in question_lower:
1162
- # return bool(re.match(r'^\d+(\.\d+)?$', answer))
1163
- # if "format" in question_lower and "A.B.C.D." in question:
1164
- # return bool(re.match(r'^[A-Z]\.[A-Z]\.[A-Z]\.[A-Z]\.', answer))
1165
- # if context and answer.lower() not in context.lower():
1166
- # return False
1167
- # return True
1168
-
1169
- # if not is_valid_answer(question, clean_answer, file_content or wiki_results or web_results):
1170
- # logger.warning(f"Ответ не соответствует контексту: {clean_answer}")
1171
- # state["answer"] = "Unknown"
1172
- # state["raw_answer"] = "Invalid answer for context"
1173
- # return state
1174
-
1175
- # # Энтропийная проверка (опционально)
1176
- # response = llm.invoke(prompt, return_logits=True)
1177
- # if response.logits:
1178
- # probs = np.exp(response.logits) / np.sum(np.exp(response.logits))
1179
- # entropy = -np.sum(probs * np.log(probs + 1e-10))
1180
- # if entropy > 2.0:
1181
- # logger.warning(f"Высокая энтропия ответа: {entropy}")
1182
- # state["answer"] = "Unknown"
1183
- # state["raw_answer"] = "High uncertainty in response"
1184
- # return state
1185
- ####################################################
1186
-
1187
-
1188
-
1189
  if any(keyword in question_lower for keyword in ["how many", "number", "score", "difference", "citations"]):
1190
  match = re.search(r"\d+(\.\d+)?", clean_answer)
1191
- state["answer"] = match.group(0) if match else "Unknown"
1192
  elif "stock price" in question_lower:
1193
  match = re.search(r"\d+\.\d+", clean_answer)
1194
- state["answer"] = match.group(0) if match else "Unknown"
1195
  elif any(keyword in question_lower for keyword in ["name", "what is", "restaurant", "city", "replica", "line", "song"]):
1196
- state["answer"] = clean_answer.split("\n")[0].strip() or "Unknown"
1197
  elif "address" in question_lower:
1198
  match = re.search(r"\d+\s+[A-Za-z\s]+", clean_answer)
1199
- state["answer"] = match.group(0) if match else "Unknown"
1200
  elif "The adventurer died" in clean_answer:
1201
  state["answer"] = "The adventurer died."
1202
  elif any(keyword in question_lower for keyword in ["code", "identifier", "issn"]):
1203
  match = re.search(r"[\w-]+", clean_answer)
1204
- state["answer"] = match.group(0) if match else "Unknown"
1205
  else:
1206
- state["answer"] = clean_answer.split("\n")[0].strip() or "Unknown"
1207
 
1208
  logger.info(f"Final answer: {state['answer'][:50]}...")
1209
  logger.info(f"Сгенерирован ответ: {state['answer'][:50]}...")
@@ -1214,7 +1191,6 @@ def create_answer(state: AgentState) -> AgentState:
1214
 
1215
  return state
1216
 
1217
-
1218
 
1219
 
1220
  # --- Создание графа ---
 
9
  from typing import Dict, Any
10
  from docx import Document
11
  from pptx import Presentation
12
+ #from langchain_ollama import ChatOllama
13
+ #import ollama
14
+ from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
15
+
16
  import logging
17
  import importlib.util
18
  import re
 
26
  from faster_whisper import WhisperModel
27
  from sentence_transformers import SentenceTransformer
28
  import faiss
29
+
30
  import asyncio
31
  #from shazamio import Shazam
32
  from langchain_community.document_loaders import WikipediaLoader, ArxivLoader
 
34
  from typing import TypedDict, Optional
35
  from faiss import IndexFlatL2
36
  import pdfplumber
37
+
38
  from retrying import retry
39
 
40
  # Настройка путей для Hugging Face Spaces
 
125
  raise ImportError("faiss не установлена. Установите: pip install faiss-cpu")
126
  logger.info("faiss доступна.")
127
 
128
+ #def check_ollama():
129
+ # if importlib.util.find_spec("ollama") is None:
130
+ # logger.error("ollama не установлена. Установите: pip install ollama")
131
+ # raise ImportError("ollama не установлена. Установите: pip install ollama")
132
+ # logger.info("ollama доступна.")
133
 
134
  def check_shazamio():
135
  if importlib.util.find_spec("shazamio") is None:
 
146
 
147
 
148
  # Инициализация модели
149
+ #try:
150
+ # llm = ChatOllama(base_url=OLLAMA_URL, model=MODEL_NAME, request_timeout=60)
151
+ # test_response = llm.invoke("Test")
152
+ # if test_response is None or not hasattr(test_response, 'content'):
153
+ # raise ValueError("Ollama модель недоступна или возвращает некорректный ответ")
154
+ # logger.info("Модель ChatOllama инициализирована.")
155
+ #except Exception as e:
156
+ # logger.error(f"Ошибка инициализации модели: {e}")
157
+ # raise e
158
+
159
  try:
160
+ device = "cuda" if torch.cuda.is_available() else "cpu"
161
+ logger.info(f"Используемое устройство: {device}")
162
+
163
+ # Инициализация Qwen2-7B
164
+ qwen_tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-7B-Instruct")
165
+ qwen_model = AutoModelForCausalLM.from_pretrained(
166
+ "Qwen/Qwen2-7B-Instruct",
167
+ device_map="auto",
168
+ load_in_4bit=True if device == "cuda" else False, # Квантование для GPU
169
+ torch_dtype=torch.float16 if device == "cuda" else torch.float32
170
+ )
171
+ qwen_pipeline = pipeline(
172
+ "text-generation",
173
+ model=qwen_model,
174
+ tokenizer=qwen_tokenizer,
175
+ device_map="auto"
176
+ )
177
+ logger.info("Модель Qwen2-7B-Instruct инициализирована.")
178
+
179
+ # Инициализация Mixtral-8x7B
180
+ mixtral_tokenizer = AutoTokenizer.from_pretrained("mistralai/Mixtral-8x7B-Instruct-v0.1")
181
+ mixtral_model = AutoModelForCausalLM.from_pretrained(
182
+ "mistralai/Mixtral-8x7B-Instruct-v0.1",
183
+ device_map="auto",
184
+ load_in_4bit=True if device == "cuda" else False,
185
+ torch_dtype=torch.float16 if device == "cuda" else torch.float32
186
+ )
187
+ mixtral_pipeline = pipeline(
188
+ "text-generation",
189
+ model=mixtral_model,
190
+ tokenizer=mixtral_tokenizer,
191
+ device_map="auto"
192
+ )
193
+ logger.info("Модель Mixtral-8x7B-Instruct инициализирована.")
194
+
195
+ # Тестовый вызов для Qwen
196
+ test_input = qwen_tokenizer("Test", return_tensors="pt").to(device)
197
+ test_output = qwen_model.generate(**test_input, max_new_tokens=10)
198
+ test_response = qwen_tokenizer.decode(test_output[0], skip_special_tokens=True)
199
+ if not test_response:
200
+ raise ValueError("Qwen2-7B модель недоступна или возвращает пустой ответ")
201
+ logger.info(f"Тестовый ответ Qwen2-7B: {test_response}")
202
  except Exception as e:
203
+ logger.error(f"Ошибка инициализации моделей: {e}")
204
  raise e
205
 
206
 
207
 
208
+
209
+
210
 
211
  # --- Состояние для LangGraph ---
212
  class AgentState(TypedDict):
 
473
  check_faster_whisper()
474
  check_sentence_transformers()
475
  check_faiss()
476
+ #check_ollama()
477
  transcribed_text = transcribe_audio(file_path)
478
  if transcribed_text.startswith("Error"):
479
  logger.error(f"Ошибка транскрипции: {transcribed_text}")
 
896
  file_content = state["file_content"]
897
  wiki_results = state["wiki_results"]
898
  arxiv_results = state["arxiv_results"]
899
+ web_results = state.get("web_results", None)
900
  except Exception as e:
901
  logger.error(f"Ошибка извлечения ключей: {str(e)}")
902
  return {"answer": f"Error extracting keys: {str(e)}", "raw_answer": f"Error extracting keys: {str(e)}"}
 
942
  if "card game" in question_lower:
943
  logger.info("Обработка карточной игры...")
944
  cards = ["2 of clubs", "3 of hearts", "King of spades", "Queen of hearts", "Jack of clubs", "Ace of diamonds"]
945
+ cards = cards[3:] + cards[:3]
946
+ cards = [cards[1], cards[0]] + cards[2:]
947
+ cards = [cards[2]] + cards[:2] + cards[3:]
948
+ cards = [cards[-1]] + cards[:-1]
949
+ cards = [cards[2]] + cards[:2] + cards[3:]
950
+ cards = cards[4:] + cards[:4]
951
+ cards = [cards[-1]] + cards[:-1]
952
+ cards = cards[2:] + cards[:2]
953
+ cards = [cards[-1]] + cards[:-1]
 
954
  state["answer"] = cards[0]
955
  state["raw_answer"] = cards[0]
956
  logger.info(f"Карточная игра обработана: {state['answer']}")
 
991
  state["raw_answer"] = f"Error: {e}"
992
  return state
993
 
 
994
  # Обработка MP3-файлов
995
  file_path = state.get("file_path")
 
 
996
  if file_path and file_path.endswith(".mp3"):
997
  logger.info("Обработка MP3-файла")
998
+ if "name of the song" in question_lower or "what song" in question_lower():
999
+ logger.warning("Распознавание песен больше не поддерживается: shazamio не установлена")
1000
+ state["answer"] = "Unknown"
1001
+ state["raw_answer"] = "Song recognition not supported"
1002
+ return state
1003
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1004
  if "how long" in question_lower and "minute" in question_lower:
1005
  logger.info("Определение длительности аудио")
1006
  try:
 
 
 
 
 
 
 
 
 
1007
  audio = pydub.AudioSegment.from_file(file_path)
 
1008
  duration_seconds = len(audio) / 1000
1009
  duration_minutes = round(duration_seconds / 60)
1010
  state["answer"] = str(duration_minutes)
 
1016
  state["answer"] = "Unknown"
1017
  state["raw_answer"] = f"Error: {e}"
1018
  return state
1019
+
1020
  # RAG для MP3 (аудиокниги)
1021
  logger.info("RAG-обработка для MP3 (аудиокниги)")
1022
  try:
 
1029
  # Инициализация RAG
1030
  check_sentence_transformers()
1031
  check_faiss()
 
1032
  rag_model = SentenceTransformer("all-MiniLM-L6-v2")
1033
  index, sentences, embeddings = create_rag_index(file_content, rag_model)
1034
  question_embedding = rag_model.encode([question], convert_to_numpy=True)
 
1043
 
1044
  # Промпт для MP3 с RAG
1045
  prompt = (
1046
+ "[INST] You are a highly precise assistant tasked with answering a question based solely on the provided context from an audiobook's transcribed text. "
1047
  "Do not use any external knowledge or assumptions beyond the context. "
1048
  "Extract the answer strictly from the context, ensuring it matches the question's requirements. "
1049
  "If the question asks for an address, return only the street number and name (e.g., '123 Main'), excluding city, state, or street types (e.g., Street, Boulevard). "
 
1053
  "Provide only the final answer, without explanations or additional text.\n"
1054
  f"Question: {question}\n"
1055
  f"Context: {relevant_context}\n"
1056
+ "Answer: [/INST]"
1057
  )
1058
  logger.info(f"Промпт для RAG: {prompt[:200]}...")
1059
 
1060
+ # Вызов Mixtral-8x7B
1061
+ response = mixtral_pipeline(
1062
+ prompt,
1063
+ max_new_tokens=100,
1064
+ temperature=0.0,
1065
+ top_p=0.9,
1066
+ do_sample=False,
1067
+ return_full_text=False
 
 
1068
  )
1069
+ answer = response[0]["generated_text"].strip() or "Not found"
1070
+ logger.info(f"Mixtral-8x7B вернул ответ: {answer}")
1071
 
1072
  # Проверка адресов
1073
  if "address" in question_lower:
 
1074
  answer = re.sub(r'\b(St\.|Street|Blvd\.|Boulevard|Ave\.|Avenue|Rd\.|Road|Dr\.|Drive)\b', '', answer, flags=re.IGNORECASE)
 
1075
  answer = re.sub(r',\s*[^,]+$', '', answer).strip()
 
1076
  match = re.match(r'^\d+\s+[A-Za-z\s]+$', answer)
1077
  if not match:
1078
  logger.warning(f"Некорректный формат адреса: {answer}")
 
1088
  state["raw_answer"] = f"Error RAG: {str(e)}"
1089
  return state
1090
 
 
 
 
1091
  # Обработка вопросов с изображениями и Википедией
1092
  logger.info("Проверка вопросов с изображениями и Википедией")
1093
  if file_path and file_path.endswith((".jpg", ".png")) and "wikipedia" in question_lower:
1094
  logger.info("Обработка изображения с Википедией")
1095
  if wiki_results and not wiki_results.startswith("Error"):
1096
  prompt = (
1097
+ f"[INST] Question: {question}\n"
1098
  f"Wikipedia Content: {wiki_results[:1000]}\n"
1099
+ "Instruction: Provide ONLY the final answer.\n"
1100
+ "Answer: [/INST]"
1101
  )
1102
  logger.info(f"Промпт для изображения с Википедией: {prompt[:200]}...")
1103
  else:
 
1109
  # Общий случай
1110
  logger.info("Обработка общего случая")
1111
  prompt = (
1112
+ f"[INST] Question: {question}\n"
1113
  f"Instruction: Provide ONLY the final answer.\n"
1114
  f"Examples:\n"
1115
  f"- Number: '42'\n"
 
1125
  prompt += f"Wikipedia Results: {wiki_results[:1000]}\n"
1126
  has_context = True
1127
  logger.info(f"Добавлен wiki_results: {wiki_results[:50]}...")
1128
+ if arxiv_results and not wiki_results.startswith("Error"):
1129
  prompt += f"Arxiv Results: {arxiv_results[:1000]}\n"
1130
  has_context = True
1131
  logger.info(f"Добавлен arxiv_results: {arxiv_results[:50]}...")
 
1139
  state["answer"] = "Unknown"
1140
  state["raw_answer"] = "No context available"
1141
  return state
1142
+ prompt += "Answer: [/INST]"
1143
+ logger.info(f"Промпт: {prompt[:200]}...")
1144
 
1145
+ # Вызов Qwen2-7B
1146
+ logger.info("Вызов Qwen2-7B")
1147
  try:
1148
+ response = qwen_pipeline(
1149
+ prompt,
1150
+ max_new_tokens=100,
1151
+ temperature=0.0,
1152
+ top_p=0.9,
1153
+ do_sample=False,
1154
+ return_full_text=False
1155
+ )
1156
+ raw_answer = response[0]["generated_text"].strip() or "Unknown"
1157
  state["raw_answer"] = raw_answer
1158
  logger.info(f"Raw answer: {raw_answer[:100]}...")
1159
 
 
1163
  clean_answer = re.sub(r'[^\w\s.-]', '', clean_answer)
1164
  logger.info(f"Clean answer: {clean_answer[:100]}...")
1165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1166
  if any(keyword in question_lower for keyword in ["how many", "number", "score", "difference", "citations"]):
1167
  match = re.search(r"\d+(\.\d+)?", clean_answer)
1168
+ state["answer"] = match.group(0) if match else "Not found"
1169
  elif "stock price" in question_lower:
1170
  match = re.search(r"\d+\.\d+", clean_answer)
1171
+ state["answer"] = match.group(0) if match else "Not found"
1172
  elif any(keyword in question_lower for keyword in ["name", "what is", "restaurant", "city", "replica", "line", "song"]):
1173
+ state["answer"] = clean_answer.split("\n")[0].strip() or "Not found"
1174
  elif "address" in question_lower:
1175
  match = re.search(r"\d+\s+[A-Za-z\s]+", clean_answer)
1176
+ state["answer"] = match.group(0) if match else "Not found"
1177
  elif "The adventurer died" in clean_answer:
1178
  state["answer"] = "The adventurer died."
1179
  elif any(keyword in question_lower for keyword in ["code", "identifier", "issn"]):
1180
  match = re.search(r"[\w-]+", clean_answer)
1181
+ state["answer"] = match.group(0) if match else "Not found"
1182
  else:
1183
+ state["answer"] = clean_answer.split("\n")[0].strip() or "Not found"
1184
 
1185
  logger.info(f"Final answer: {state['answer'][:50]}...")
1186
  logger.info(f"Сгенерирован ответ: {state['answer'][:50]}...")
 
1191
 
1192
  return state
1193
 
 
1194
 
1195
 
1196
  # --- Создание графа ---