Агенты · Agents · урок 2 · 13 мин

ReAct: как агент думает и действует

ReAct: reasoning and acting

ТемыАгенты

Зачем

После урока ты разберёшь любого агента на шаги Thought → Action → Observation и покажешь, в каком месте программа вокруг модели проверяет вызов. На собеседовании это отвечает на «how does an agent actually work?», а на работе — на вопрос, где ставить лимиты и проверки.

Откуда ReAct

ReAct (Reasoning + Acting, рассуждение плюс действие) — схема из статьи Yao и соавторов 2022 года «ReAct: Synergizing Reasoning and Acting in Language Models». До неё было два отдельных подхода. Chain-of-thought (цепочка рассуждений): модель рассуждает вслух, но опирается только на свою память, поэтому легко выдумывает факты. Act-only (только действия): модель вызывает инструменты, но не объясняет себе, зачем, и теряет план. ReAct чередует их: рассуждение выбирает следующее действие, результат действия исправляет рассуждение. В аннотации статьи ReAct обошёл методы imitation learning (обучение на примерах) и reinforcement learning (обучение с подкреплением) на 34 процентных пункта доли успеха в ALFWorld (текстовая игра с бытовыми задачами) и на 10 пунктов в WebShop (симулятор интернет-магазина).

Три шага одного круга

Один круг ReAct выглядит так:

Пример для банковского помощника (данные выдуманы), вопрос клиента: «Почему с меня сняли 2 000 ₸ комиссии 3 октября?»

Thought: Нужно найти операцию от 3 октября.
Action: {"tool": "txn_search", "args": {"date": "2026-10-03", "type": "fee"}}
Observation: {"amount_kzt": 2000, "code": "FX_FEE", "card": "*4417"}
Thought: Код FX_FEE. Нужен тариф на конвертацию для этой карты.
Action: {"tool": "tariff_lookup", "args": {"code": "FX_FEE"}}
Observation: {"rule": "1.5% от суммы в валюте, минимум 2000 ₸"}
Final Answer: Это комиссия за конвертацию: 1,5% от суммы, но не меньше 2 000 ₸.

Здесь модель вызвана три раза: после каждого Observation harness (обвязка, программа вокруг модели) вызывает модель заново с выросшим context, и на третий раз она пишет Final Answer (итоговый ответ).

Observation пишет программа, а не модель

Это самый важный пункт урока. Модель — машина продолжения текста. Если её не остановить после строки Action, она продолжит и напишет правдоподобное Observation сама: выдуманную сумму, выдуманный тариф. В курсе агентов Hugging Face это видно на живом примере: без остановки модель «узнала» погоду в Лондоне, не вызвав никакой функции.

Классическое решение — stop sequence (стоп-последовательность): строка Observation:, на которой API (программный интерфейс модели) обрывает генерацию. Программа берёт текст до этой строки, разбирает JSON действия, сама выполняет функцию и сама дописывает настоящее Observation. Только потом модель вызывается снова.

Современные API делают то же самое встроенно, через native tool calling (вызов инструментов на уровне API). Модель возвращает не свободный текст, а отдельный структурированный блок вызова, и ответ помечен причиной остановки (у Claude это stop_reason: "tool_use"). Твой код выполняет tool и отправляет результат отдельным блоком tool_result. Формат другой, принцип тот же: Observation приходит от кода, и модель не может его подделать.

Где программа проверяет вызов

Между Action и Observation стоит код, и именно здесь живут гарантии. Перед выполнением harness проверяет:

  1. Есть ли такой tool и разрешён ли он этому агенту.
  2. Совпадают ли аргументы со schema (описанием параметров с типами).
  3. Имеет ли право этот клиент на это действие; личность берётся из сессии, а не из аргумента.
  4. Нужно ли подтверждение человека (блокировка карты, спор по операции).

Если проверка не прошла, harness не падает, а возвращает ошибку как Observation: «аргумент date должен быть в формате YYYY-MM-DD». Модель читает её и исправляет вызов. Так ошибка становится шагом цикла, а не аварией.

Каждый круг — Thought, Action, проверка, Observation — пишется в trace (журнал шагов). Когда агент ответил неверно, ты открываешь trace и видишь, на каком шаге он свернул: выбрал не тот tool, передал неверный аргумент или верно получил данные, но неверно их пересказал. Это три разные поломки с тремя разными исправлениями. Одна оговорка: текст Thought полезен для отладки, но не считай его точной записью того, как модель на самом деле пришла к решению. Это ещё один сгенерированный текст, и судить агента надо по действиям и результатам.

Лимиты: почему цикл обязан кончаться

Без лимита шагов агент может ходить по кругу: вызывать один и тот же поиск с почти одинаковыми аргументами, пока не кончатся деньги. Минимальный набор лимитов для production (рабочей среды):

Лимит Пример значения Что делает при срабатывании
шаги 10 вызовов модели честный частичный ответ или передача человеку
время 60 с на запрос то же, клиент не ждёт вечно
деньги $0,05 на запрос остановка и запись в журнал
один tool 3 одинаковых вызова подряд признак петли, остановка

Значения в таблице — пример для разговора, а не норма: их выбирают по замерам своих traces (журналов шагов). Сам цикл в псевдокоде:

for step in range(MAX_STEPS):
    reply = model(context)
    if reply.is_final: return reply.text
    if not allowed(reply.tool, session): obs = "error: not allowed"
    else: obs = run(reply.tool, reply.args)
    context.append(reply, obs)
return handoff_to_human()

Память против context одного вызова

В конструкторах агентов есть узел memory (память). Это не свойство модели: модель между вызовами ничего не помнит. Memory — хранилище, из которого harness перед каждым вызовом достаёт прошлые сообщения (последние N или найденные поиском) и кладёт их в context. Значит, у памяти те же беды, что у любого поиска: старые факты, лишние tokens (куски слов, единицы длины текста), чужие данные, если хранилище не разделено по клиентам.

Схема

Термины · Key terms

ReAct
схема «мысль → действие → наблюдение», чередование рассуждения и вызовов
Thought
шаг, где модель пишет план следующего действия
Action
вызов tool в строгом формате: имя и аргументы
Observation
результат вызова; пишет его программа, а не модель
stop sequence
строка, на которой API обрывает генерацию модели
native tool calling
вызов tool отдельным структурированным блоком на уровне API
chain-of-thought
рассуждение вслух без действий во внешнем мире
schema
описание параметров tool с типами и обязательностью
memory
хранилище прошлых сообщений, которое harness снова кладёт в context

Сделай сам

Запиши в заметках псевдокод цикла в 10 строк: пока шагов меньше MAX — вызвать модель; если она просит tool — проверить, что tool существует, аргументы совпадают со schema и есть право; выполнить; добавить результат в context; иначе вернуть ответ. Добавь вторую остановку по деньгам.

Обсуди с тьютором: вставь свой псевдокод и спроси, что произойдёт, если tool упадёт с исключением, и куда в твоём цикле поставить обработку.

Проверь себя · ответ под вопросом

1Что случится без лимита шагов, и какие ещё лимиты нужны в банке?
Агент может ходить по кругу и тратить деньги и время клиента без конца. Кроме шагов нужны лимит времени на запрос, лимит денег и лимит повторов одного tool. При срабатывании — честный частичный ответ или передача человеку.
2Почему Observation должна писать программа, а не модель?
Модель продолжает текст и без остановки напишет правдоподобный, но выдуманный результат. Настоящий результат есть только у кода, который выполнил функцию. Поэтому генерацию обрывают на Observation: или используют native tool calling.
3Чем memory агента отличается от context одного вызова?
Context — то, что модель видит в этом вызове. Memory — внешнее хранилище, из которого harness перед вызовом снова кладёт прошлое в context. Сама модель между вызовами ничего не помнит.

Interview questions · US / UK

1Explain ReAct. Why interleave reasoning and acting instead of just doing one?
Strong answer: Thought → Action → Observation loop; reasoning alone hallucinates facts, acting alone loses the plan; observations ground the next thought; Yao et al. 2022 showed +34 points on ALFWorld and +10 on WebShop over imitation/RL baselines.
2Who produces the Observation, and what goes wrong if the model does?
Strong answer: the harness, after executing the real tool; an unstopped model fabricates plausible results; fix with a stop sequence or native tool calling (tool_use block, stop_reason); validation errors are returned as observations so the model can self-correct.
3Follow-up: your agent loops, calling the same search tool with slightly different queries. What do you add?
Strong answer: step, time and cost budgets; detect repeated near-identical calls; graceful exit with partial answer or human handoff; read the traces to fix the root cause (vague tool description, empty results without guidance).

Ключевые фразы · Say it aloud

Материалы