Главная / Статьи / Реакция с нуля: мысль, действие, пауза, наблюдение без опорной структуры

Реакция с нуля: мысль, действие, пауза, наблюдение без опорной структуры

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

1373 слов

Введение

Можно спросить у модели простого языка текущую цену акций Nvidia и количество акций, которые можно купить за 100 000 долларов. Без специальных инструментов она выдумывает устаревшие данные из материалов обучения, а затем правильно выполняет расчёты при неверных входных данных. У неё нет встроенной функции, означающей «Я не знаю — нужно поискать», поскольку поиск не является отдельным этапом обработки данных.

По мере улучшения качества ответов модели оказались полезными для однократного рассуждения. Одних лишь планов недостаточно для обеспечения автономии. Вопросы, определяющие агентов, заключаются в следующем: что находится за пределами знаний модели, какое действие позволяет их получить, как достичь результата, как скорректировать план и когда прекратить действия. Последовательность «решение → действие → проверка → перепланирование» представляет собой цикл, а не прямую линию. Именно этот цикл стал основой современных искусственных интеллектуальных агентов. Подход ReAct (Рассуждение + Действие) задал этот образец. Первоначальные демонстрации не были приложениями LangChain — это был большой язык модели, структурированный запрос и человек (или позже скрипт), выполняющий действия и возвращающий полученные данные.

Представляем ReAct: Рассуждение + Действие

Пример работы по шагам:

from groq import Groq
import re
from dotenv import load_dotenv

_ = load_dotenv()
client = Groq()
message = client.chat.completions.create(
    model="openai/gpt-oss-120b",  # free, fast open-weight model hosted on Groq
    max_tokens=1000,
    messages=[
        {"role": "user", "content": "Hello, GPT!"}
    ],
)
print(message.choices[0].message.content) # Check the client
class Agent:
    def __init__(self, system=""):
        self.system = system
        self.messages = []
        if self.system:
            self.messages.append({"role": "system", "content": system})

    def __call__(self, message):
        self.messages.append({"role": "user", "content": message})
        result = self.execute()
        self.messages.append({"role": "assistant", "content": result})
        return result

    def execute(self):
        response = client.chat.completions.create(
            model="openai/gpt-oss-120b",
            max_tokens=1000,
            messages=self.messages,
        ).choices[0].message.content
        return response
prompt = """
You run in a loop of Thought, Action, PAUSE, Observation.
At the end of the loop you output an Answer
Use Thought to describe your thoughts about the question you have been asked.
Use Action to run one of the actions available to you - then return PAUSE.
Observation will be the result of running those actions.

Your available actions are:

calculate:
e.g. calculate: 4 * 7 / 3
Runs a calculation and returns the number - uses Python so be sure to use floating point syntax if necessary

fish_weight:
e.g. fish_weight: Shark
returns weight of a fish when given the breed

Example session:

Question: How much does a shark weigh?
Thought: I should look the fish weight using fish_weight
Action: fish_weight: Shark
PAUSE

You will be called again with this:

Observation: A Great white shark weights 41000 lbs

You then output:

Answer: A Great white shark weights 41000 lbs
""".strip()
def calculate(expression):
    return eval(expression)

def fish_weight(name):
    if "Shark" in name:
        return("Great white shark weighs 41000 lbs")
    elif "Puffer" in name:
        return("A puffer fish weighs 20 lbs")
    else:
        return("A fish can weight upto 47000 lbs")

known_actions = {
    "calculate": calculate,
    "fish_weight": fish_weight
}
abot = Agent(prompt)
result = abot("How much does a Shark weigh?")
print(result)
result = fish_weight("Shark")
result
next_prompt = "Observation: {}".format(result)
abot(next_prompt)
abot.messages # View the list of messages containing the conversation
# Present a compund query
abot = Agent(prompt)
# New query
question = """I have 2 fishes, a Shark and a puffer fish. \
What is their combined weight"""
abot(question)
next_prompt = "Observation: {}".format(fish_weight("Shark"))
print(next_prompt)
abot(next_prompt)
next_prompt = "Observation: {}".format(fish_weight("Puffer fish"))
print(next_prompt)
abot(next_prompt)
next_prompt = "Observation: {}".format(eval("41000 + 20"))
print(next_prompt)
abot(next_prompt)
## Add loop (automate the reasoning + act process)
action_re = re.compile('^Action: (\w+): (.*)
) # python regular expression to selection action
def query(question, max_turns=5):
    i = 0
    bot = Agent(prompt)
    next_prompt = question
    while i < max_turns:
        i += 1
        result = bot(next_prompt)
        print(result)
        actions = [
            action_re.match(a)
            for a in result.split('\n')
            if action_re.match(a)
        ]
        if actions:
            # There is an action to run
            action, action_input = actions[0].groups()
            if action not in known_actions:
                raise Exception("Unknown action: {}: {}".format(action, action_input))
            print(" -- running {} {}".format(action, action_input))
            observation = known_actions[action](action_input)
            print("Observation:", observation)
            next_prompt = "Observation: {}".format(observation)
        else:
            return
question = """I have 2 fishes, a Shark and a puffer fish. \
What is their combined weight"""
query(question)
  1. Импортируйте клиент Groq, модуль re для поиска шаблонов и функцию load_dotenv; загрузите переменные окружения, чтобы ключ API не был закодирован в коде.
  2. Создайте клиент Groq, который будет читать значение GROQ_API_KEY из переменных окружения.
  3. Отправьте простое сообщение «Привет» на настроенную модель для подтверждения соединения.
  4. Создайте объект Agent, который будет хранить системное промпт и постоянно пополнять историю сообщений: при каждом вызове добавляется следующий ответ пользователя, отправляется полная история, добавляется ответ модели, после чего возвращается текст.
  • Запишите системный промпт, обеспечивающий последовательность мышление → действие → пауза → наблюдение.
  • Зарегистрируйте инструменты, такие как calculate() для арифметических вычислений и fish_weight() для небольшой таблицы поиска в known_actions.
  • Создайте экземпляр агента с использованием системного промпта ReAct.
  • Задайте вопрос «Сколько весит акула?» Ожидайте предложение действия Action: fish_weight: Shark, а не просто числа.
  • Вручную выполните вызов fish_weight("Shark") и сохраните результат (сначала в режиме ручного управления).
  • Предоставьте обратно данные вида Observation: …, чтобы агент мог ответить.
  • Просмотрите содержимое abot.messages, чтобы увидеть полный текст, который видит модель.
  • Запустите нового агента с комплексным вопросом (о весе акулы и рыбы-пуффера). 13–15. Вручную вводите каждое наблюдение, включая суммарное значение при необходимости.
  • Пусть агент синтезирует итоговый ответ, как только появятся наблюдения.
  • Добавьте регулярное выражение, которое автоматически обнаруживает формат Action: name: input.
  • Заключите цикл в функцию query(): создайте нового агента, отправьте вопрос, выведите результат, проанализируйте действие, проверьте его на соответствие списку known_actions, выполните действие, сделайте наблюдение и повторите процесс до получения окончательного ответа или достижения лимита попыток.
  • Запустите функцию query() для составленного вопроса и наблюдайте за последовательностью «Мысль → Действие → Наблюдение → Ответ» без необходимости вручную выполнять каждый шаг.
  • Заключение

    Агенты начинают свою работу с цикла, а не с какой-либо структуры. Модели, обученные методом цепочки мыслей, учились рассуждать вслух; подход ReAct организует процесс мышления, действия и наблюдения так, как это делают люди. Популярные фреймворки для агентов индустриализируют эту идею. Ручная реализация цикла однажды навсегда меняет восприятие работы с фреймворками. Оригинальная статья доступна по адресу https://arxiv.org/abs/2210.03629.

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

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

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

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