ReAct — Reasoning + Acting

[object Object]

ReAct (Reasoning + Acting, рассуждение + действие) — техника, при которой модель чередует шаги рассуждения (Thought) с действиями (Action) и наблюдениями (Observation). Вместо того чтобы просто рассуждать «в вакууме», модель может обращаться к внешним инструментам — искать информацию, выполнять вычисления, вызывать API — и учитывать результат в дальнейших рассуждениях.

В 2026 году ReAct уже полезнее воспринимать не как prompt format с буквальными метками Thought/Action/Observation, а как базовый control loop для агентов. Современные tool-use API, Responses API, Agents SDK, LangGraph и похожие рантаймы часто скрывают этот формат, но логика цикла остаётся той же.

Представьте, что вы отвечаете на вопрос «Какой фильм получил Оскар за лучший фильм в прошлом году?». Вы думаете: «Я не уверен, нужно проверить». Заходите в поисковик (действие), видите результат (наблюдение), и теперь уверенно отвечаете. ReAct учит модель делать то же самое — не гадать, а проверять.

Суть в двух словах

ReAct — это цикл: модель думает (Thought), делает что-то (Action) — например, ищет в интернете или считает на калькуляторе, — видит результат (Observation), и снова думает. Цикл повторяется, пока задача не решена.

Это фундамент для всех AI-агентов: именно ReAct позволяет модели не просто рассуждать, а действовать в реальном мире.

ПромптChatGPT
Ответь на вопрос, используя цикл Thought-Action-Observation.

Вопрос: Какой город является столицей страны, где родился создатель Linux?

Доступные инструменты:
- search(запрос) — поиск в интернете
- calculator(выражение) — калькулятор
Ответ модели

Thought: Мне нужно узнать, кто создал Linux, потом — где он родился, потом — столицу этой страны.

Action: search("создатель Linux") Observation: Linux был создан Линусом Торвальдсом (Linus Torvalds).

Thought: Теперь нужно узнать, где родился Линус Торвальдс.

Action: search("Линус Торвальдс место рождения") Observation: Линус Торвальдс родился в Хельсинки, Финляндия.

Thought: Линус Торвальдс родился в Финляндии. Столица Финляндии — Хельсинки. Это я знаю точно.

Ответ: Хельсинки — столица Финляндии, страны, где родился создатель Linux.

Подходит для: задач, требующих актуальной информации, многошаговых рассуждений с проверкой фактов, любых задач с инструментами.

Что такое ReAct

Техника ReAct была предложена Yao et al. в 2022 году. Авторы обнаружили, что чистое рассуждение (Chain of Thought) страдает от галлюцинаций — модель может уверенно «рассуждать» о ложных фактах. С другой стороны, чистое действие (Action-only) не позволяет модели планировать и корректировать курс. ReAct объединяет оба подхода.

Как это работает

Зачем нужен ReAct

Без ReAct модель:

  • Может выдумать факт вместо того, чтобы его проверить
  • Не может работать с актуальными данными (цены, курсы, новости)
  • Не может выполнять вычисления точно (большие числа, формулы)
  • Не может взаимодействовать с внешними системами

С ReAct модель становится агентом — она может планировать, действовать и корректировать свои действия на основе реальных данных.

Примеры использования

Фактический вопрос с поиском

ПромптClaude Sonnet 4
Ответь на вопрос, используя цикл Thought-Action-Observation.

Вопрос: Во сколько раз население Токио больше населения Москвы?

Доступные инструменты:
- search(запрос) — поиск
- calculator(выражение) — калькулятор
Ответ модели

Thought: Мне нужны актуальные данные о населении Токио и Москвы. Затем — разделить одно на другое.

Action: search("население Токио 2025") Observation: Население Большого Токио — около 37.4 млн человек (агломерация), сам город — около 14 млн.

Thought: Нужно уточнить — сравнивать город или агломерацию? Буду сравнивать города для корректности.

Action: search("население Москвы 2025") Observation: Население Москвы — около 13.1 млн человек.

Thought: Теперь могу посчитать соотношение: 14 / 13.1.

Action: calculator(14 / 13.1) Observation: 1.069

Thought: Токио (город) и Москва почти одинаковы по населению — разница всего в 1.07 раза. Если сравнивать агломерации, разница будет больше.

Ответ: Население города Токио (14 млн) примерно в 1.07 раза больше населения Москвы (13.1 млн). Если сравнивать агломерации (Большой Токио — 37.4 млн), то Токио больше примерно в 2.9 раза.

Многошаговая задача

ПромптChatGPT
Используя цикл ReAct, определи: что дешевле — долететь из Москвы в Казань или доехать на поезде? Учти текущие цены.

Инструменты: search(запрос)
Ответ модели

Thought: Нужно найти текущие цены на авиабилеты и ж/д билеты Москва-Казань.

Action: search("цена авиабилета Москва Казань 2026") Observation: Средняя цена авиабилета Москва-Казань — от 3500 до 8000 руб. в эконом-классе.

Thought: Теперь нужно узнать цену поезда.

Action: search("цена жд билета Москва Казань 2026") Observation: Плацкарт — от 1800 руб., купе — от 3500 руб., фирменный поезд — от 4500 руб.

Thought: Сравниваю:

  • Самолёт: от 3500 руб., время — ~1.5 часа + трансфер
  • Поезд (плацкарт): от 1800 руб., время — ~12 часов
  • Поезд (купе): от 3500 руб., время — ~12 часов

Ответ: Поезд в плацкарте дешевле (от 1800 руб. vs от 3500 руб. за самолёт). Купе стоит примерно столько же, сколько дешёвый авиабилет. Самолёт выигрывает по времени (1.5 часа vs 12 часов).

Сравнение: CoT vs ReAct

Chain of Thought (только рассуждения)
Модель рассуждает на основе своих знаний. Если знания устарели или неточны — ответ будет с ошибками. Нет способа проверить себя.
ReAct (рассуждения + действия)
Модель рассуждает И действует: ищет информацию, считает, проверяет факты. Если что-то не знает — не гадает, а ищет. Каждый шаг основан на реальных данных.
ReAct — это основа для AI-агентов. Если вы хотите, чтобы модель не просто отвечала на вопросы, а решала задачи (бронировала билеты, искала информацию, анализировала данные) — ReAct является отправной точкой.

Как понимать ReAct в 2026

Сегодня ReAct — это скорее минимальная операционная схема агента:

  • подумай, чего не хватает;
  • вызови нужный инструмент;
  • прочитай observation;
  • скорректируй следующий шаг.

Поэтому ReAct стоит рядом не только с prompt engineering, но и с tool routing, workflow orchestration, tracing и stop conditions. Сильная сторона техники в том, что она не требует сложной архитектуры, чтобы получить agent-like поведение. Слабая — в том, что она очень легко превращается в бесконечный цикл думай -> ищи -> думай.

Где ReAct реально полезен

Подход хорошо окупается там, где:

  • ответ зависит от внешней информации или инструментов;
  • задачу нужно решать поэтапно, а не одним вызовом;
  • промежуточные observations реально меняют дальнейший маршрут.

Типичные сценарии: web research, data lookup, support-agents, internal copilots, browser workflows и простые task agents.

Где literal ReAct уже не нужен

Если у вас есть современный tool-calling runtime, не обязательно заставлять модель печатать Thought: и Action: явно. Во многих продакшн-системах это уже избыточно: reasoning может быть скрытым или кратким, action оформляется как structured tool call, а orchestration живёт снаружи модели. Поэтому сегодня лучше переносить из ReAct не формат, а принцип: не отвечать до тех пор, пока не получены нужные внешние данные.

Когда использовать

Плюсы

  • Модель может работать с актуальной информацией через поиск
  • Точные вычисления через калькулятор/код
  • Прозрачный процесс — видно каждый шаг рассуждения и каждое действие
  • Значительно снижает галлюцинации за счёт проверки фактов
  • Фундамент для создания AI-агентов

Минусы

  • Требует настроенных инструментов (search, calculator и т.д.)
  • Увеличивает задержку — каждое действие это отдельный вызов
  • Модель может зациклиться в повторяющихся действиях
  • Стоимость зависит от количества шагов
  • Нужна продуманная остановка цикла

Компоненты ReAct

КомпонентРольПример
ThoughtРассуждение и планирование«Мне нужно узнать население обоих городов»
ActionВызов инструментаsearch("население Москвы")
ObservationРезультат действия«13.1 млн человек»
AnswerФинальный ответ«Москва — 13.1 млн»

Техническая реализация

Базовый ReAct через промпт (OpenAI)

from openai import OpenAI
import json
import re

client = OpenAI()

def search(query: str) -> str:
    """Заглушка для поиска. В продакшене — Tavily, Serper и т.д."""
    # В реальности: вызов поискового API
    return f"Результат поиска по запросу '{query}': [данные]"

def calculator(expression: str) -> str:
    """Безопасный калькулятор."""
    try:
        # В продакшене используйте ast.literal_eval или sympy
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"Ошибка: {e}"

TOOLS = {"search": search, "calculator": calculator}

def react_loop(
    question: str, max_steps: int = 5
) -> dict:
    """
    ReAct цикл: Thought → Action → Observation → ...
    """
    messages = [
        {
            "role": "system",
            "content": (
                "Ты — AI-ассистент с доступом к инструментам.\n"
                "На каждом шаге:\n"
                "1. Thought: рассуждай, что нужно сделать\n"
                "2. Action: вызови инструмент в формате "
                "action_name(argument)\n"
                "3. Дождись Observation\n"
                "4. Повторяй, пока не получишь ответ\n"
                "5. В конце напиши: ФИНАЛЬНЫЙ ОТВЕТ: ...\n\n"
                "Доступные инструменты:\n"
                "- search(запрос) — поиск информации\n"
                "- calculator(выражение) — калькулятор"
            ),
        },
        {"role": "user", "content": question},
    ]

    for step in range(max_steps):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            temperature=0,
        )

        text = response.choices[0].message.content
        messages.append({"role": "assistant", "content": text})

        # Проверяем, есть ли финальный ответ
        if "ФИНАЛЬНЫЙ ОТВЕТ:" in text:
            answer = text.split("ФИНАЛЬНЫЙ ОТВЕТ:")[-1].strip()
            return {
                "answer": answer,
                "steps": step + 1,
                "trace": messages,
            }

        # Ищем вызов инструмента
        action_match = re.search(
            r"Action:\s*(\w+)\((.+?)\)", text
        )
        if action_match:
            tool_name = action_match.group(1)
            tool_arg = action_match.group(2).strip("\"'")

            if tool_name in TOOLS:
                observation = TOOLS[tool_name](tool_arg)
            else:
                observation = f"Инструмент '{tool_name}' не найден"

            messages.append({
                "role": "user",
                "content": f"Observation: {observation}",
            })

    return {"answer": "Превышен лимит шагов", "steps": max_steps}


result = react_loop(
    "Какова площадь круга с диаметром равным "
    "высоте Эйфелевой башни в метрах?"
)
print(f"Ответ: {result['answer']}")
print(f"Шагов: {result['steps']}")

ReAct через Function Calling (OpenAI)

from openai import OpenAI
import json

client = OpenAI()

tools = [
    {
        "type": "function",
        "function": {
            "name": "search",
            "description": "Поиск информации в интернете",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "Поисковый запрос",
                    }
                },
                "required": ["query"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "calculator",
            "description": "Математические вычисления",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "Математическое выражение",
                    }
                },
                "required": ["expression"],
            },
        },
    },
]


def react_function_calling(question: str, max_steps: int = 5):
    """ReAct через нативный Function Calling."""
    messages = [
        {
            "role": "system",
            "content": (
                "Рассуждай пошагово. Используй инструменты "
                "для получения данных и вычислений."
            ),
        },
        {"role": "user", "content": question},
    ]

    for step in range(max_steps):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
            tool_choice="auto",
        )

        msg = response.choices[0].message
        messages.append(msg)

        # Если нет вызова инструмента — это финальный ответ
        if not msg.tool_calls:
            return {
                "answer": msg.content,
                "steps": step + 1,
            }

        # Обрабатываем вызовы инструментов
        for tool_call in msg.tool_calls:
            name = tool_call.function.name
            args = json.loads(tool_call.function.arguments)

            if name == "search":
                result = search(args["query"])
            elif name == "calculator":
                result = calculator(args["expression"])
            else:
                result = "Неизвестный инструмент"

            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result,
            })

    return {"answer": "Лимит шагов", "steps": max_steps}

ReAct через Anthropic Tool Use

import anthropic
import json

client = anthropic.Anthropic()

claude_tools = [
    {
        "name": "search",
        "description": "Поиск информации в интернете",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Поисковый запрос",
                }
            },
            "required": ["query"],
        },
    },
    {
        "name": "calculator",
        "description": "Математические вычисления",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "Математическое выражение",
                }
            },
            "required": ["expression"],
        },
    },
]


def react_claude(question: str, max_steps: int = 5):
    """ReAct через Anthropic Tool Use."""
    messages = [{"role": "user", "content": question}]

    for step in range(max_steps):
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            system=(
                "Рассуждай пошагово. Используй инструменты "
                "для проверки фактов и вычислений."
            ),
            tools=claude_tools,
            messages=messages,
        )

        # Проверяем, закончил ли Claude
        if response.stop_reason == "end_turn":
            text_blocks = [
                b.text for b in response.content
                if b.type == "text"
            ]
            return {
                "answer": "\n".join(text_blocks),
                "steps": step + 1,
            }

        # Обрабатываем tool use
        messages.append({
            "role": "assistant",
            "content": response.content,
        })

        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                if block.name == "search":
                    result = search(block.input["query"])
                elif block.name == "calculator":
                    result = calculator(
                        block.input["expression"]
                    )
                else:
                    result = "Неизвестный инструмент"

                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                })

        messages.append({
            "role": "user",
            "content": tool_results,
        })

    return {"answer": "Лимит шагов", "steps": max_steps}
Всегда устанавливайте максимальное количество шагов (max_steps). Без этого ограничения модель может зациклиться: например, повторять один и тот же поисковый запрос или вызывать инструменты, не приближаясь к ответу. 5-7 шагов достаточно для большинства задач.

Оценка эффективности

Точность ответов: ReAct vs другие методы (HotpotQA, многошаговые вопросы)
Стандартный промпт29%
Chain of Thought34%
Action-only (без рассуждений)25%
ReAct (Thought + Action)40%

На бенчмарке HotpotQA (многошаговые вопросы, требующие нескольких фактов) ReAct значительно превосходит как чистое рассуждение, так и чистое действие. Данные из оригинальной статьи Yao et al.

Проверьте себя

Проверьте себя

1. Из каких трёх компонентов состоит цикл ReAct?

2. Чем ReAct лучше обычного Chain of Thought?

3. Для чего нужно ограничение max_steps в реализации ReAct?