От чат-бота к агенту: Циклы рассуждений (Reasoning Loops)
Введение: Смена парадигмы
Добро пожаловать в Модуль 4. Здесь мы пересекаем невидимую, но важную черту. До этого момента мы создавали системы, которые работают по принципу «Вопрос — Ответ». Вы отправляли запрос в Gemini, модель обрабатывала его и возвращала результат. Это парадигма умного чат-бота.
Но что, если задача не решается одним ответом? Что, если для ответа нужно сначала проверить календарь, затем написать письмо, и только потом подтвердить выполнение? Чат-бот здесь бессилен. Здесь нужен Агент.
Главное отличие Агента от Чат-бота — это способность к рассуждению во времени (Reasoning) и наличие автономности. Агент не просто предсказывает следующее слово; он строит план, выполняет действия, наблюдает за результатами и корректирует свой план.
В этом уроке мы разберем сердце любого агента — Цикл Рассуждений (Reasoning Loop). Мы научимся превращать Gemini 3 из пассивного собеседника в активного сотрудника.
Анатомия Цикла Рассуждений (Think-Act-Observe)
Чтобы понять, как работает агент, давайте вспомним, как люди решают задачи. Представьте, что я прошу вас: «Узнай, какая погода в Лондоне, и если там дождь, найди мне уютное кафе с камином поблизости».
Ваш мыслительный процесс (внутренний монолог) будет выглядеть примерно так:
- Мысль (Think): Мне нужно узнать погоду. Сама я этого не знаю, мне нужно воспользоваться Google.
- Действие (Act): Открываю Google, ввожу «погода Лондон сейчас».
- Наблюдение (Observe): Вижу результат: «+12, Дождь».
- Мысль (Think): Ага, идет дождь. Вторая часть задания — найти кафе с камином.
- Действие (Act): Ищу в Google Maps «London cafes with fireplace».
- Наблюдение (Observe): Вижу список: «The Holly Bush», «Scarfes Bar»...
- Мысль (Think): У меня есть информация. Могу дать финальный ответ.
- Финал (Answer): «В Лондоне дождь, рекомендую заглянуть в The Holly Bush...»
Этот цикл Think → Act → Observe и есть основа агентной архитектуры, часто называемая паттерном ReAct (Reasoning + Acting). В контексте Gemini 3 это означает, что модель не пытается галлюцинировать ответ сразу, а вызывает инструменты (Tools) циклично, пока не соберет достаточно данных.
# Концептуальная схема Reasoning Loop (псевдокод)
messages = [
{"role": "user", "content": "Узнай погоду в Лондоне и подбери одежду."}
]
MAX_ITERATIONS = 5
iteration = 0
while iteration < MAX_ITERATIONS:
# 1. THINK: Спрашиваем модель, что делать дальше
response = model.generate(messages)
# Если модель решила, что у неё есть ответ - выходим из цикла
if response.is_final_answer():
print("Ответ агента:", response.text)
break
# 2. ACT: Если модель хочет вызвать инструмент
if response.has_tool_call():
tool_name = response.tool_name
tool_args = response.tool_args
print(f"Агент вызывает инструмент: {tool_name} с аргументами {tool_args}")
# Выполняем реальный код (например, API запрос к погоде)
tool_result = execute_tool(tool_name, tool_args)
# 3. OBSERVE: Добавляем результат обратно в историю сообщений
# Это критический момент: модель должна "видеть" результат своих действий
messages.append({
"role": "tool_output",
"content": tool_result
})
iteration += 1
Реализация с Gemini 3 API
Gemini 3 значительно упрощает этот процесс благодаря нативной поддержке Function Calling. Модель обучена понимать, когда ей не хватает информации, и вместо текста возвращать специальную структуру вызова функции.
Однако, важно помнить: Gemini сама не выполняет функции и не запускает циклы. API по своей сути stateless (без сохранения состояния выполнения). Вся магия цикла `while` происходит на стороне вашего бэкенда (на Python, Node.js и т.д.).
Ключевые этапы реализации:
- Определение инструментов: Мы передаем модели список доступных функций с описаниями.
- Первичный вызов: Отправляем промпт пользователя.
- Проверка ответа: Смотрим, есть ли в объекте `response` поле `function_call`.
- Исполнение: Если вызов есть, мы запускаем функцию в нашем коде.
- Возврат контекста: Результат функции мы упаковываем в сообщение и отправляем обратно в модель (в тот же объект чата).
Давайте посмотрим на полноценный пример на Python.
import google.generativeai as genai
from google.generativeai.types import FunctionDeclaration, Tool
# 1. Настройка инструментов (Tools)
# Допустим, у нас есть простая функция для получения курса валют
def get_exchange_rate(currency_code: str):
"""Возвращает текущий курс валюты к USD."""
# В реальности здесь был бы запрос к API банка
rates = {"EUR": 1.1, "GBP": 1.3, "JPY": 0.007}
return rates.get(currency_code.upper(), "Unknown currency")
# Оборачиваем функцию для Gemini
tools_list = [get_exchange_rate]
# 2. Инициализация модели с инструментами
model = genai.GenerativeModel(
model_name='gemini-1.5-pro', # Или gemini-3, если доступен
tools=tools_list
)
# Запуск чат-сессии (автоматически управляет историей сообщений)
chat = model.start_chat(enable_automatic_function_calling=True)
# ВНИМАНИЕ: В SDK Google AI есть параметр enable_automatic_function_calling.
# Если он включен, библиотека САМА делает цикл рассуждений за вас.
# Но для понимания темы урока, давайте посмотрим, как это выглядит "под капотом",
# или если нам нужен сложный контроль над циклом.
# --- РУЧНОЙ ЦИКЛ (Manual Reasoning Loop) ---
chat_manual = model.start_chat() # Без авто-вызова
user_prompt = "Сколько стоит 100 евро в долларах?"
response = chat_manual.send_message(user_prompt)
# Проверяем, хочет ли модель вызвать функцию
if response.parts[0].function_call:
fc = response.parts[0].function_call
fname = fc.name
fargs = fc.args
print(f"Thinking: Модель хочет вызвать {fname} с {fargs}")
# Выполняем функцию
if fname == 'get_exchange_rate':
api_result = get_exchange_rate(fargs['currency_code'])
print(f"Observation: Результат {api_result}")
# Отправляем результат обратно модели
# Модель использует это наблюдение, чтобы сформулировать финальный ответ
final_response = chat_manual.send_message(
genai.protos.Content(
parts=[genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=fname,
response={'result': api_result}
)
)]
)
)
print(f"Final Answer: {final_response.text}")
else:
print(f"Answer: {response.text}")
Тонкости управления циклом
Приведенный выше код демонстрирует один шаг рассуждения. Но сложные агенты могут требовать 5, 10 или более шагов. При построении production-ready агентов, вы столкнетесь с несколькими вызовами:
1. Зацикливание (Infinite Loops):
Иногда модель может застрять, вызывая одну и ту же функцию с ошибкой снова и снова.
Решение: Всегда ставьте жесткий лимит итераций (например, `MAX_STEPS = 10`). Если лимит исчерпан, принудительно завершайте работу и возвращайте пользователю сообщение об ошибке.
2. Контекстное окно:
Каждый шаг «Мысль → Действие → Наблюдение» расходует токены. В длинных цепочках рассуждений контекст может переполниться.
Решение: В Gemini 3 контекстное окно огромно, но не бесконечно. Для очень долгих задач используйте технику «Summarization» — периодически просите модель сжать историю предыдущих шагов.
3. Внутренний монолог (Chain of Thought):
Чтобы агент работал лучше, в системном промпте можно явно попросить его рассуждать вслух перед вызовом функции.
Пример System Prompt: «Ты — полезный агент. Прежде чем вызвать инструмент, опиши свой план действий в формате:
Это помогает модели «заземлить» свои решения и снижает вероятность логических ошибок.
Создайте простой цикл рассуждений (Reasoning Loop) на Python (или псевдокоде), который имитирует работу 'Математического Агента'. У агента есть доступ к функции `calculator(expression)`. Задача: Пользователь спрашивает 'Сколько будет (2 + 2) * 5?'. <br><br>Требования:<br>1. Инициировать список сообщений.<br>2. Реализовать цикл `while` (макс 3 итерации).<br>3. В цикле имитировать ответ модели (первый раз - запрос функции, второй раз - финальный ответ).<br>4. Вывести лог 'Think', 'Act', 'Observe' в консоль.
Почему этап 'Observe' (Наблюдение) критически важен в цикле рассуждений агента?