Редактировать react.py

This commit is contained in:
Markov Andrey
2026-06-30 21:01:28 +00:00
parent 1ad6decc01
commit 6353a9421e

View File

@@ -47,12 +47,10 @@ async def react_loop(
Исключения: Исключения:
RuntimeError: если превышен лимит итераций или возникла критическая ошибка. RuntimeError: если превышен лимит итераций или возникла критическая ошибка.
""" """
# Формируем системный промпт с описанием инструментов
system_prompt_template = prompts.get("react_system") system_prompt_template = prompts.get("react_system")
if not system_prompt_template: if not system_prompt_template:
raise ValueError("Промпт react_system не найден") raise ValueError("Промпт react_system не найден")
# Описание инструментов для модели
tools_desc = [] tools_desc = []
for tool in tools: for tool in tools:
tools_desc.append(f"- {tool['name']}: {tool['description']} (параметры: {tool.get('parameters', {})})") tools_desc.append(f"- {tool['name']}: {tool['description']} (параметры: {tool.get('parameters', {})})")
@@ -60,10 +58,7 @@ async def react_loop(
system_prompt = system_prompt_template.format(tools_description=tools_description) system_prompt = system_prompt_template.format(tools_description=tools_description)
# Создаём внутреннюю историю (копируем переданную, но не модифицируем)
internal_history = history.copy() if history else [] internal_history = history.copy() if history else []
# Добавляем текущий запрос как сообщение пользователя (если его нет в конце)
if not internal_history or internal_history[-1].get("role") != "user" or internal_history[-1].get("content") != query: if not internal_history or internal_history[-1].get("role") != "user" or internal_history[-1].get("content") != query:
internal_history.append({"role": "user", "content": query}) internal_history.append({"role": "user", "content": query})
@@ -72,52 +67,39 @@ async def react_loop(
iteration += 1 iteration += 1
logger.debug(f"ReAct итерация {iteration}") logger.debug(f"ReAct итерация {iteration}")
# Формируем промпт (можно использовать стандартный билдер, но здесь упрощённо)
messages = [{"role": "system", "content": system_prompt}] + internal_history messages = [{"role": "system", "content": system_prompt}] + internal_history
# Добавляем указание формата вывода
messages.append({"role": "user", "content": "Выведи JSON с действием или финальным ответом."}) messages.append({"role": "user", "content": "Выведи JSON с действием или финальным ответом."})
# Вызываем GigaChat
response = await giga.chat( response = await giga.chat(
history=messages, # history передаётся как список сообщений history=messages,
query="", # не нужно, так как мы уже включили в history query="",
system_prompt=None, system_prompt=None,
file_id=None, file_id=None,
temperature=getattr(config, 'react_temperature', 0.3), temperature=getattr(config, 'react_temperature', 0.3),
) )
# Парсим JSON
try: try:
data = json.loads(response.strip()) data = json.loads(response.strip())
except json.JSONDecodeError: except json.JSONDecodeError:
logger.error(f"Невалидный JSON от модели: {response[:200]}") logger.error(f"Невалидный JSON от модели: {response[:200]}")
# В качестве fallback пробуем извлечь final_answer или action вручную
# Если не удаётся, считаем, что это финальный ответ
if "final_answer" in response.lower(): if "final_answer" in response.lower():
# грубое извлечение
import re import re
match = re.search(r'"final_answer"\s*:\s*"([^"]+)"', response) match = re.search(r'"final_answer"\s*:\s*"([^"]+)"', response)
if match: if match:
return match.group(1) return match.group(1)
# Если ничего не вышло, выбрасываем ошибку
raise RuntimeError(f"Модель вернула невалидный JSON на итерации {iteration}: {response[:200]}") raise RuntimeError(f"Модель вернула невалидный JSON на итерации {iteration}: {response[:200]}")
# Проверяем наличие финального ответа
if "final_answer" in data: if "final_answer" in data:
return data["final_answer"] return data["final_answer"]
# Проверяем наличие действия
action = data.get("action") action = data.get("action")
action_input = data.get("action_input", {}) action_input = data.get("action_input", {})
if not action: if not action:
# Если нет ни final_answer, ни action, считаем, что модель ошиблась
# Добавляем сообщение об ошибке в историю и продолжаем
internal_history.append({"role": "assistant", "content": response}) internal_history.append({"role": "assistant", "content": response})
internal_history.append({"role": "user", "content": "Не удалось распознать действие или финальный ответ. Пожалуйста, выведи JSON с 'action' или 'final_answer'."}) internal_history.append({"role": "user", "content": "Не удалось распознать действие или финальный ответ. Пожалуйста, выведи JSON с 'action' или 'final_answer'."})
continue continue
# Находим обработчик инструмента
handler = None handler = None
for tool in tools: for tool in tools:
if tool["name"] == action: if tool["name"] == action:
@@ -131,16 +113,13 @@ async def react_loop(
internal_history.append({"role": "user", "content": f"Ошибка: {error_msg}. Попробуй другой инструмент или дай финальный ответ."}) internal_history.append({"role": "user", "content": f"Ошибка: {error_msg}. Попробуй другой инструмент или дай финальный ответ."})
continue continue
# Вызываем обработчик
try: try:
result = await handler(action_input) result = await handler(action_input)
except Exception as e: except Exception as e:
result = f"Ошибка при вызове инструмента '{action}': {str(e)}" result = f"Ошибка при вызове инструмента '{action}': {str(e)}"
logger.exception(result) logger.exception(result)
# Добавляем результат в историю
internal_history.append({"role": "assistant", "content": response}) internal_history.append({"role": "assistant", "content": response})
internal_history.append({"role": "tool", "content": result}) internal_history.append({"role": "tool", "content": result})
# Если цикл завершился без ответа, генерируем исключение
raise RuntimeError(f"Превышено максимальное число итераций ({max_iterations}) без получения финального ответа.") raise RuntimeError(f"Превышено максимальное число итераций ({max_iterations}) без получения финального ответа.")