Архитектура ReAct и планирование действий в Gemini 3
Архитектура ReAct и планирование действий в Gemini 3
Добро пожаловать на один из самых захватывающих уроков нашего курса. Сегодня мы переходим от использования простых запросов к построению настоящих автономных агентов. Мы разберем архитектуру ReAct (Reasoning + Acting) — фундаментальный паттерн, который превращает LLM из «болтливого собеседника» в «интеллектуального исполнителя».
Почему стандартных промптов недостаточно?
Представьте, что вы просите модель: «Узнай цену акций Apple, сравни её с ценой Microsoft и скажи, во сколько раз одна больше другой».
Обычная языковая модель (даже мощная, как Gemini) без доступа к инструментам начнет галлюцинировать, придумывая цены. Если же мы дадим ей инструменты (поиск), она может просто выполнить поиск, но забыть сравнить числа. Ей не хватает внутреннего монолога и возможности корректировать свои действия.
Именно здесь на сцену выходит ReAct. Эта методология объединяет два процесса:
- Reasoning (Рассуждение): Модель планирует, что делать, анализирует результаты и корректирует стратегию.
- Acting (Действие): Модель вызывает внешние инструменты (API, поиск, калькулятор).
Анатомия ReAct-цикла
В контексте Gemini 3, ReAct — это не просто промпт, это бесконечный цикл (Loop), который работает до достижения цели. Классический цикл выглядит так:
- Thought (Мысль): Агент анализирует текущую задачу и решает, какой шаг сделать следующим. «Мне нужно узнать цену акций Apple».
- Action (Действие): Агент выбирает инструмент и параметры для него. «Вызвать инструмент search_stock('AAPL')».
- Observation (Наблюдение): Среда (ваш код) выполняет действие и возвращает результат агенту. «Цена: 150$».
- Repeat (Повтор): Агент получает наблюдение и снова переходит к Мысли. «Теперь мне нужно узнать цену Microsoft...»
Этот подход позволяет Gemini 3 справляться с задачами, требующими многоступенчатого планирования, где результат одного шага влияет на следующий.
# ПРИМЕР: Базовый промпт для ReAct агента
REACT_PROMPT_TEMPLATE = """
Ты — умный исследовательский агент. Твоя задача — отвечать на вопросы, используя доступные инструменты.
У тебя есть доступ к следующим инструментам:
{tool_descriptions}
Используй следующий формат строго:
Question: вопрос, на который нужно ответить
Thought: ты всегда должен думать о том, что делать дальше
Action: название инструмента, который нужно использовать (одно из [{tool_names}])
Action Input: входные данные для действия
Observation: результат действия (это предоставит система, не пиши это сам)
... (этот цикл Thought/Action/Observation может повторяться N раз)
Thought: я теперь знаю окончательный ответ
Final Answer: окончательный ответ на исходный вопрос
Начинай!
Question: {question}
"""
Реализация цикла в Gemini 3
Gemini 3 обладает значительно увеличенным контекстным окном и нативной поддержкой Function Calling. Это упрощает создание ReAct агентов, так как нам реже приходится парсить текст вручную. Однако, для полного контроля над «мыслительным процессом» (Chain of Thought), мы часто комбинируем нативные функции с явным выводом мыслей.
В классическом ReAct агент генерирует текст до момента вызова инструмента. Наша программа должна:
- Остановить генерацию, когда модель напишет ключевое слово (например,
Observation:или конецAction Input). - Выполнить функцию на Python.
- Добавить результат в историю диалога.
- Снова вызвать модель.
Давайте посмотрим, как реализовать этот цикл программно.
import re
import google.generativeai as genai
# Имитация инструментов
def get_weather(city):
return f"Погода в {city}: +20C, солнечно."
def calculate(expression):
try:
return str(eval(expression))
except:
return "Ошибка вычисления"
tools_map = {
"get_weather": get_weather,
"calculate": calculate
}
class ReActAgent:
def __init__(self, model, tools):
self.model = model
self.tools = tools
self.history = []
def step(self, prompt):
# Добавляем входные данные в историю
self.history.append(prompt)
full_context = "\n".join(self.history)
# Генерация ответа (Thought + Action)
# В реальном коде Gemini 3 используйте stop_sequences=['Observation:']
response = self.model.generate_content(
full_context,
generation_config={"stop_sequences": ["Observation:"]}
)
text_response = response.text.strip()
self.history.append(text_response)
print(f"\n[AGENT]: {text_response}")
# Парсинг действия
if "Final Answer:" in text_response:
return text_response.split("Final Answer:")[1].strip()
action_match = re.search(r"Action: (\w+)\nAction Input: (.*)", text_response)
if action_match:
tool_name = action_match.group(1)
tool_input = action_match.group(2)
# Выполнение действия (Acting)
if tool_name in self.tools:
observation = self.tools[tool_name](tool_input)
obs_str = f"Observation: {observation}"
self.history.append(obs_str)
print(f"[SYSTEM]: {obs_str}")
# Рекурсивный вызов для следующего шага
return self.step("") # Пустой промпт, так как история уже обновлена
else:
self.history.append("Observation: Tool not found")
return self.step("")
return "Не удалось определить действие."
Особенности и подводные камни
При построении агентов на базе Gemini 3 вы столкнетесь с несколькими вызовами:
1. Зацикливание (Infinite Loops)
Агент может попасть в ловушку, повторяя одно и то же действие: «Поиск погоды -> Ошибка -> Поиск погоды».
Решение: Всегда ограничивайте максимальное количество шагов (например, `max_steps=10`) и добавляйте системное сообщение, если инструмент вернул ошибку, чтобы агент попробовал другую стратегию.
2. Галлюцинации инструментов
Иногда модель может придумать инструмент, которого нет, например send_email, хотя вы его не описывали.
Решение: Четкое описание доступных инструментов в системном промпте и использование нативного Function Calling в Gemini API, который принуждает модель выбирать только из заданного JSON-схемой списка.
3. Проблема контекста
Длинные цепочки рассуждений (Reasoning traces) могут засорять контекстное окно.
Решение: В Gemini 3 контекстное окно огромно (1M+ токенов), но для оптимизации скорости и цены можно использовать технику Summarization старых шагов, оставляя только ключевые факты.
Создайте текстовый промпт для ReAct агента, который должен помочь пользователю выбрать ноутбук. Агент имеет доступ к двум инструментам: `search_laptops(query)` и `compare_specs(laptop1, laptop2)`. <br><br>Ваша задача: Написать только раздел 'Инструкция' (System Prompt), который объяснит модели, как комбинировать эти инструменты. Модель должна сначала искать варианты, а потом сравнивать их перед тем, как дать ответ.
Продвинутое планирование: Tree of Thoughts
ReAct линеен. Агент делает шаг, смотрит результат и идет дальше. Но что, если первый шаг был ошибочным? В сложных задачах мы используем подход Tree of Thoughts (Дерево мыслей).
Вместо генерации одной мысли, мы просим Gemini сгенерировать три варианта следующего шага, оцениваем их (можно использовать саму же модель для оценки) и выбираем лучший путь. Это похоже на поиск пути в шахматах.
В Gemini 3 это реализуется через параметр `candidate_count` при генерации или через отдельные запросы, где мы просим модель: «Предложи 3 стратегии решения этой задачи, оцени плюсы и минусы каждой, и выбери лучшую для первого шага».
Что произойдет, если в архитектуре ReAct агент сгенерирует токен 'Observation:' самостоятельно, вместо того чтобы ждать ответа от системы?
Заключение
Архитектура ReAct — это мост между знаниями LLM и реальным миром. Используя способность Gemini 3 к рассуждению, вы можете создавать агентов, которые не просто отвечают на вопросы, но и выполняют работу за вас: бронируют билеты, анализируют данные, пишут код и тестируют его.
В следующем уроке мы углубимся в нативные инструменты Gemini (Function Calling) и узнаем, как сделать этот процесс еще более надежным и типизированным.