Создание агента ReAct в LangChain: Executorы, Scratchpads и их ограничения
Создайте агента на основе подхода ReAct, использующего инструменты, посмотрите, как агент и AgentExecutor делят работу, и узнайте, почему LangGraph лучше справляется с управлением потоком выполнения, циклами и состоянием.
Что такое агент
ИИ-агент — это система, которая берёт у человека высокоуровневую цель, затем самостоятельно планирует, принимает решения и действует. Она выполняет последовательность шагов с использованием внешних инструментов, API или источников знаний, сохраняет контекст на протяжении всего процесса, рассуждает между несколькими шагами, корректирует свои действия при поступлении новой информации и стремится к желаемому результату.
В упрощённом виде агент — это LLM плюс некоторые инструменты. LLM отвечает за рассуждения и принятие решений, а инструменты — за выполнение действий.
Характеристики агента
- Ориентирован на цель. Вы указываете, чего хотите, а не как этого добиться.
- Самостоятельно планирует. Агент разделяет задачу на части и порядок действий без помощи.
- Использует инструменты. Он обращается к API, калькуляторам, поисковым системам и подобным ресурсам. LLM знает, какие инструменты существуют и когда каждый из них применим.
Агент, работающий в коде
В приведённом ниже скрипте создаётся небольшой агент с двумя инструментами: поиск в Интернете через DuckDuckGo и проверка погоды. Ему задаётся вопрос из двух частей. Для запроса погоды требуется собственный API-ключ от поставщика погодных данных; здесь он показан в качестве заменителя.
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
import requests
from langchain_community.tools import DuckDuckGoSearchRun
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub # It is a place from where we can get many type of prompts, so if we want any predefined prompt, we can get it from here
# Step 1: Creating tool definitions
search_tool = DuckDuckGoSearchRun()
@tool
def get_weather_data(city: str) -> str:
"""
This function fetches the current weather data for a given city
"""
url = f'https://api.weatherstack.com/current?access_key=4d1d8ae207a8c845a52df8a67bf3623e&query={city}'
response = requests.get(url)
return response.json()
llm = ChatOpenAI()
# Step 2: Pull the ReAct prompt from LangChain Hub
prompt = hub.pull("hwchase17/react") # pulls the standard ReAct agent prompt
# The agent which we are gonna make is a ReAct agent (reasoning + action = ReAct)
# ReAct is a design pattern
# Step 3: Create the ReAct agent manually with the pulled prompt
agent = create_react_agent(
llm=llm,
tools=[search_tool, get_weather_data],
prompt=prompt
)
# Step 4: Wrap it with AgentExecutor
agent_executor = AgentExecutor(
agent=agent,
tools=[search_tool, get_weather_data],
verbose=True # whatever the agent thinks, it will be visible to us
)
# Step 5: Invoke
response = agent_executor.invoke({"input": "Find the capital of Madhya Pradesh, then find it's current weather condition"})
print(response)
'''
Entering new AgentExecutor chain...
I should first find out the capital of Madhya Pradesh and then check the current weather condition for that city.
Action: duckduckgo_search
Action Input: "capital of Madhya Pradesh" Madhya Pradesh, state of India that is situated in the heart of the country. It has no coastline and no international frontier. Its physiography is characterized by low hills, extensive plateaus, and river valleys. The capital is Bhopal, in the west-central part of the state. Bhopal, city, capital of Madhya Pradesh state, central India. Situated in the fertile plain of the Malwa Plateau, the city lies just north of the Vindhya Range, along the slopes of a sandstone ridge. It is a major rail junction and has an airport. Pop. (2001) 1,437,354; (2011) 1,798,218. Bhopal was Indore (/ ɪ n ˈ d ɔːr / ⓘ; ISO: Indaura, Hindi: [ɪn̪d̪ɔːr]) is the largest and most populous city in the Indian state of Madhya Pradesh. [15] It is the commercial hub of Madhya Pradesh. It is consistently ranked as the cleanest city in India. [16] It serves as the headquarters of both the Indore District and the Indore Division.It is also considered the state education hub and ... In 1956, Bhopal became part of the state of Madhya Pradesh. Bhopal district was carved out on October 2, 1972, and is one of the 45 districts in the state. 4. Why is Bhopal famous? Bhopal is the capital city of the Indian state of Madhya Pradesh. It is known as the City of Lakes due to the presence of various natural and artificial lakes. In 1948, Madhya Bharat was created, with Indore designated as its summer capital and Gwalior as its winter capital. This arrangement highlights Indore's significance even then. However, Madhya Bharat was a temporary entity, and the map of central India was soon to be redrawn. The Creation of Madhya PradeshNow that I know the capital of Madhya Pradesh is Bhopal, I can use the get_weather_data function to check its current weather condition.
Action: get_weather_data
Action Input: Bhopal {'request': {'type': 'City', 'query': 'Bhopal, India', 'language': 'en', 'unit': 'm'}, 'location': {'name': 'Bhopal', 'country': 'India', 'region': 'Madhya Pradesh', 'lat': '23.267', 'lon': '77.400', 'timezone_id': 'Asia/Kolkata', 'localtime': '2025-05-01 17:52', 'localtime_epoch': 1746121920, 'utc_offset': '5.50'}, 'current': {'observation_time': '12:22 PM', 'temperature': 40, 'weather_code': 116, 'weather_icons': ['https://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0002_sunny_intervals.png'], 'weather_descriptions': ['Partly Cloudy '], 'astro': {'sunrise': '05:47 AM', 'sunset': '06:48 PM', 'moonrise': '08:38 AM', 'moonset': '11:01 PM', 'moon_phase': 'Waxing Crescent', 'moon_illumination': 15}, 'air_quality': {'co': '510.6', 'no2': '1.665', 'o3': '180', 'so2': '10.175', 'pm2_5': '29.045', 'pm10': '68.635', 'us-epa-index': '2', 'gb-defra-index': '2'}, 'wind_speed': 12, 'wind_degree': 302, 'wind_dir': 'WNW', 'pressure': 1005, 'precip': 0, 'humidity': 7, 'cloudcover': 25, 'feelslike': 40, 'uv_index': 1, 'visibility': 6, 'is_day': 'yes'}}The current weather condition in Bhopal, Madhya Pradesh is partly cloudy with a temperature of 40°C.
Final Answer: The capital of Madhya Pradesh is Bhopal, and the current weather condition in Bhopal is partly cloudy with a temperature of 40°C.
> Finished chain.
{'input': "Find the capital of Madhya Pradesh, then find it's current weather condition", 'output': 'The capital of Madhya Pradesh is Bhopal, and the current weather condition in Bhopal is partly cloudy with a temperature of 40°C.'}
'''
response['output']
# The capital of Madhya Pradesh is Bhopal, and the current weather condition in Bhopal is partly cloudy with a temperature of 40°C.
Пять указанных шагов в скрипте охватывают определение инструментов, загрузку стандартного промпта ReAct из хаба LangChain, создание агента, его обертку в исполнитель и вызов. Выведенная в конце трассировка показывает, что агент сначала решает найти столицу, а затем проверить погоду.
ReAct
LangChain поддерживает несколько подходов к проектированию агентов. Рассматриваемый здесь — это ReAct, сокращение от Reasoning plus Acting. Он позволяет модели чередовать внутренние рассуждения, то есть процесс мышления, и внешние действия, такие как вызов инструментов, в структурированном цикле, включающем несколько шагов.
Модель не формирует ответ сразу. Она думает пошагово, решает, что делать дальше, и может использовать такие инструменты, как API, калькуляторы или поиск в интернете, для получения помощи.
Эта схема подходит для:
- задач, требующих нескольких шагов;
- задач, для выполнения которых необходимы инструменты;
- Ситуации, когда необходимо, чтобы логика решения была видна и подлежала проверке.
В качестве примера попросите модель указать численность населения столицы Франции. Сначала она рассуждает о необходимости определения столицы, находит эту информацию, видит, что ответ — Париж, затем решает, что ей нужна численность населения Парижа, снова выполняет поиск и наконец отвечает.
ReAct состоит из пяти компонентов:
- Мышление. Личные рассуждения агента, в ходе которых он оценивает текущее состояние и планирует следующий шаг. Он объясняет, почему был выбран тот или иной инструмент или что ещё остаётся неизвестным.
- Действие. Решение использовать один из доступных инструментов, указанное через функцию или API для вызова.
- Входные данные для действия. Аргументы, передаваемые этому инструменту. Если действие —
search, входными данными является точный текст запроса, например, «погода в Дели». - Наблюдение. Чистые данные, которые возвращает инструмент после его выполнения. Агент читает их, чтобы определить, завершена ли работа или требуются дополнительные шаги.
- Окончательный ответ. Заключительный ответ, сгенерированный тогда, когда агент приходит к выводу, что информации достаточно; в нем все наблюдения излагаются простым языком.
Шаблон запроса, управляющий этим циклом, выглядит следующим образом:
Answer the following questions as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action …
(this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {input}
Thought:{agent_scratchpad} —
В этом шаблоне результат наблюдения — это просто то, что возвращается после выполнения инструмента.
Рабочая тетрадь агента
Рабочая тетрадь служит краткосрочной памятью или рабочим блокнотом агента. Она записывает историю текущей задачи: предыдущие мысли, выполненные действия, такие как вызовы инструментов, и результаты наблюдений.
Читая эту тетрадь, агент вспоминает, что уже выяснил столицу Франции, и теперь ему нужно найти численность населения Парижа. Это временное хранилище, которое обновляется каждый раз, когда исполнитель возвращает новую информацию. Как только окончательный ответ отправляется пользователю, рабочая тетрадь обычно очищается или архивируется, поскольку её задача по данной задаче завершена.
Это название становится понятным, если вспомнить, что LLM не имеет состояния. Он не может естественным образом запоминать то, что произошло несколько секунд назад в цикле кода. Поэтому LangChain берет всю историю текущего цикла и снова включает её в запрос каждый раз, когда задаёт агенту вопрос о дальнейших действиях.
Agent versus AgentExecutor
У этих двух компонентов разные задачи.
Агент — это мыслитель, немного похожий на менеджера проектов. Он читает запрос, разделяет его на более мелкие шаги и определяет, какие инструменты необходимы. В его входных данных содержатся как вопрос пользователя, так и всё, что было сделано до этого момента.
AgentExecutor — это исполнитель, более похожий на помощника. Он слушает указания агента, фактически запускает код для каждого инструмента и возвращает результаты.
Другой способ посмотреть на это: агент — это архитектор, который разрабатывает проект, а исполнитель — это строительная бригада, которая возводит здание. Без агента не будет плана, а без исполнителя план так и останется на бумаге.
Исполнитель координирует весь цикл:
- Он отправляет входные данные и предыдущие сообщения агенту, чтобы тот знал вопрос и то, что уже произошло.
- Он получает от агента следующее действие.
- Он запускает соответствующий инструмент с предоставленными данными.
- Он добавляет результаты работы инструмента в историю.
- Он отправляет обновленную историю вместе с вопросом пользователя обратно агенту, и этот процесс повторяется до тех пор, пока агент не сообщит «Окончательный ответ».
Возьмем запрос о температуре в Дели и умножим его на 10. Агент решает, что нужен инструмент для получения погодных данных, исполнитель вызывает его, приходит результат наблюдения, агент определяет, что требуется умножение, исполнитель выполняет его, и в конце концов агент дает ответ. Исполнитель выполняет всё, что предусмотрено агентом.
Ограничения этого подхода
Заранее созданный исполнитель подходит для простых случаев, но у него серьезные недостатки, когда агенты становятся более сложными. Ниже приведено сравнение с LangGraph — графовой библиотекой LangChain. Также обратите внимание, что новые версии LangChain рекомендуют использовать LangGraph для агентов, поэтому перед работой с более старыми классами исполнителей проверяйте актуальную документацию.
1. Черный ящик
Стандартный AgentExecutor уже готов к использованию. Ему достаточно предоставить инструменты и инструкции, после чего скрытый цикл while продолжает работу до завершения задачи. Вмешаться в его работу сложно. Если вы хотите, чтобы агент всегда спрашивал человека перед тем, как тратить деньги, или соблюдал определённый порядок действий — сначала шаг А, затем шаг Б, — executor сопротивляется, поскольку он разработан для полной автономности.
Рассмотрим агента, имеющего инструменты search_flights и book_flight. Внутри executor LangChain ЯЗЫК ИИ сам принимает решения о дальнейших действиях в скрытом цикле. При запросе на бронирование рейса он может сразу же провести поиск и совершить бронирование без дополнительных вопросов, возможно, выбрав тариф стоимостью десять тысяч долларов, в то время как пользователь хотел сначала одобрить покупку. Промежуточные шаги, такие как обязательное одобрение, невозможно строго обеспечить, поскольку поток управления является неявным и контролируется самим моделью.
В LangGraph рабочий процесс определяется как набор явных шагов или узлов, таких как поиск рейсов, запрос на одобрение и бронирование. Они связаны между собой, поэтому система должна сначала получить одобрение, прежде чем осуществить бронирование. Это обеспечивает полный контроль и упрощает соблюдение правил, добавление этапов проверки со стороны человека, возможность повторных попыток или использование условных веток.
2. Циклы и повторы
Настоящие рабочие процессы редко бывают линейными. Они включают в себя возврат назад, исправление ошибок и повторную обработку наборов документов.
- Цепочки LangChain в основном разработаны как ориентированные ациклические графы, поэтому данные передаются только в одном направлении.
- LangGraph предназначен для работы с циклами. Он моделирует поток данных как карту, в которой узлы представляют собой функции, а ребра — пути между ними.
Для задачи ответов на вопросы в документах пайплайн LangChain может загрузить документы, написать ответ и вернуть его. Если ответ неверен или неполон, система сама по себе не возвращается для повторной загрузки документов или уточнения запроса без дополнительной ручной логики. Как только процесс продолжается, ему сложно вернуться назад для улучшения результата.
LangGraph позволяет явно создавать циклы: загрузка данных, ответ, проверка качества, и если результат плох, уточнение запроса и повторная загрузка, пока результат не станет хорошим. Система может переходить между узлами до тех пор, пока не будет выполнено определенное условие, что подходит для задач, требующих итераций, корректировок и обратной связи.
3. Состояние и сохранение данных
Это имеет наибольшее значение для масштабируемости.
Память LangChain в основном связана с контекстом разговора, например, с сохранением предыдущих сообщений, чтобы модель могла давать более качественные ответы. Она не тесно связана с рабочим процессом и не отслеживает прогресс выполнения задачи. Если данные не сохраняются явно, они обычно теряются при перезагрузке системы, поэтому задачи с длительным временем выполнения не являются надежными.
LangGraph рассматривает память как полное состояние рабочего процесса, а не только историю чатов. На каждом этапе он сохраняет сделанное, полученные результаты и следующие действия. Если процесс останавливается на полпути, его можно возобновить именно с того места, где он прервался. Кроме того, он позволяет приостанавливать, проверять и редактировать состояние при участии человека, а также возвращаться к более раннему этапу, что делает сложные многоэтапные задачи гораздо более надежными.
Представьте себе длительную задачу, например обработку десяти документов по одному, которая сбрасывается на 7-м шаге. При отсутствии встроенной функции сохранения всё теряется, и приходится начинать с нуля, без какой-либо записи о прогрессе. Если же состояние сохраняется после каждого шага, можно возобновить работу с 7-го шага. Также можно приостановить процесс, позволить человеку просмотреть или изменить состояние, затем продолжить, или вернуться к более раннему шагу — это своего рода путешествие во времени. Длительные рабочие процессы становятся надежнее, позволяют возобновлять работу и упрощают отладку или аудит.
Главный вывод заключается в том, что базовый исполнитель затрудняет создание масштабируемых агентов промышленного уровня, и именно поэтому существуют инструменты на основе графов для решения этой задачи.
Связанная литература
- Снимки памяти агента, навыки, затухание данных и ограничение действия памяти по веткам — Четыре дополнения к встроенному слою памяти векторной базы данных для программных агентов: переносимые снимки с возможностью слияния, активация навыков, затухание данных с проверкой конфликтов и память, ограниченная по веткам.
- Ваш первый агент LangGraph: помощник по футболу с инструментами и маршрутизацией — Пошаговый учебник LangGraph: общее состояние, инструменты, узел планировщика, условные ребра и краткое резюме в формате Markdown, созданное на примере виртуального помощника по статистике футбола.