Головна / Статті

Реакція з нуля: думка, дія, пауза, спостереження без жодної структури

Крихітний цикл ReAct, створений вручну на Groq: думка, дія, пауза, спостереження — спочатку вручну, потім виклики інструментів regex, тому фреймворки агентів LLM мають сенс.

Видавець: REACTAPP.TOP6 хв читання1373 слів

Джерела: The Origins of AI Agents -How ReAct Rewired the Way LLMs Think and Act

Вступ

Запитайте модель мови простою мовою про поточну ціну акцій 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. Надішліть просте повідомлення „Hello“ до налаштованої моделі для підтвердження з’єднання.
  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.

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

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

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

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

    ) # python regular expression to selection action\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E \u003Cpre\u003E\u003Ccode class=\"language-python\"\u003Edef query(question, max_turns=5):\n i = 0\n bot = Agent(prompt)\n next_prompt = question\n while i < max_turns:\n i += 1\n result = bot(next_prompt)\n print(result)\n actions = [\n action_re.match(a)\n for a in result.split('\\n')\n if action_re.match(a)\n ]\n if actions:\n # There is an action to run\n action, action_input = actions[0].groups()\n if action not in known_actions:\n raise Exception("Unknown action: {}: {}".format(action, action_input))\n print(" -- running {} {}".format(action, action_input))\n observation = known_actions[action](action_input)\n print("Observation:", observation)\n next_prompt = "Observation: {}".format(observation)\n else:\n return\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E \u003Cpre\u003E\u003Ccode class=\"language-text\"\u003Equestion = """I have 2 fishes, a Shark and a puffer fish. \\\nWhat is their combined weight"""\nquery(question)\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E \u003Col\u003E \u003Cli\u003EІмпортуйте клієнт Groq, модуль \u003Ccode\u003Ere\u003C\u002Fcode\u003E для зіставлення шаблонів та функцію \u003Ccode\u003Eload_dotenv\u003C\u002Fcode\u003E; завантажте параметри середовища, щоб ключ API не був закодований безпосередньо у коді.\u003C\u002Fli\u003E \u003Cli\u003EСтворіть клієнт Groq, який читає значення \u003Ccode\u003EGROQ_API_KEY\u003C\u002Fcode\u003E з середовища.\u003C\u002Fli\u003E \u003Cli\u003EНадішліть просте повідомлення „Hello“ до налаштованої моделі для підтвердження з’єднання.\u003C\u002Fli\u003E \u003Cli\u003EСконструюйте об’єкт \u003Ccode\u003EAgent\u003C\u002Fcode\u003E, який зберігає системні вказівки та історію повідомлень: при кожному запиті додається наступне повідомлення від користувача, надсилається повна історія, додається відповідь моделі та повертається текст.\u003C\u002Fli\u003E \u003C\u002Fol\u003E\u003Cli\u003EНапишіть системний запит, який забезпечує послідовність „Мислення → Дія → ПАУЗА → Спостереження“.\u003C\u002Fli\u003E \u003Cli\u003EЗареєструйте інструменти, такі як \u003Ccode\u003Ecalculate()\u003C\u002Fcode\u003E для арифметичних обчислень та \u003Ccode\u003Efish_weight()\u003C\u002Fcode\u003E для невеликої таблиці пошуку в \u003Ccode\u003Eknown_actions\u003C\u002Fcode\u003E.\u003C\u002Fli\u003E \u003Cli\u003EСтворіть екземпляр агента з використанням системного запиту ReAct.\u003C\u002Fli\u003E \u003Cli\u003EЗапитайте „Скільки важить акула?“ Очікуйте пропозицію у вигляді \u003Ccode\u003EAction: fish_weight: Shark\u003C\u002Fcode\u003E, а не просто числа.\u003C\u002Fli\u003E \u003Cli\u003EВручну виконайте \u003Ccode\u003Efish_weight("Shark")\u003C\u002Fcode\u003E та збережіть результат (спочатку у ручному режимі).\u003C\u002Fli\u003E \u003Cli\u003EНадайте назад значення у форматі \u003Ccode\u003EObservation: …\u003C\u002Fcode\u003E, щоб агент міг дати відповідь.\u003C\u002Fli\u003E \u003Cli\u003EПерегляньте \u003Ccode\u003Eabot.messages\u003C\u002Fcode\u003E, щоб побачити повний текст, який бачить модель.\u003C\u002Fli\u003E \u003Cli\u003EСтворіть нового агента з комплексним запитанням (вага акули та фугу).\n13–15. Вручну введіть кожне спостереження, включаючи сумарну суму за потреби.\u003C\u002Fli\u003E\u003Cli\u003EДозвольте агенту синтезувати кінцеву відповідь, як тільки з’являться спостереження.\u003C\u002Fli\u003E \u003Cli\u003EДодайте регулярний вираз, який автоматично виявляє \u003Ccode\u003EAction: name: input\u003C\u002Fcode\u003E.\u003C\u002Fli\u003E \u003Cli\u003EОбгорніть цикл у функцію \u003Ccode\u003Equery()\u003C\u002Fcode\u003E: створіть нового агента, надішліть запитання, виведіть результат, розпарсьте дію, перевірьте її проти списку \u003Ccode\u003Eknown_actions\u003C\u002Fcode\u003E, виконайте дію, спостерігайте та повторюйте процес до отримання кінцевої відповіді або до досягнення ліміту кроків.\u003C\u002Fli\u003E \u003Cli\u003EЗапустіть \u003Ccode\u003Equery()\u003C\u002Fcode\u003E на складному запитанні та спостерігайте за послідовністю «Мислення → Дія → Спостереження → Відповідь» без необхідності виконання ручних кроків.\u003C\u002Fli\u003E \u003C\u002Fol\u003E \u003Ch2\u003EВисновок\u003C\u002Fh2\u003E \u003Cp\u003EАгенти починають свою роботу з циклу, а не з певної фреймворк-структури. Моделі, навчені методом ланцюга мислення, вчилися міркувати вголос; підхід ReAct структурує процес мислення–дія–спостереження так, як це роблять люди. Популярні фреймворки для агентів індустріалізують цю ідею. Створення циклу вручну назавжди змінює сприйняття фреймворків. Оригінальна стаття доступна за посиланням https:\u002F\u002Farxiv.org\u002Fabs\u002F2210.03629.\u003C\u002Fp\u003E\u003Cp\u003EПромисловий урок полягає у тому, що фреймворки приховують цикл за елементами типу runners та tool nodes, але контракт залишається незмінним: модель мусить генерувати дію, яку можна проаналізувати, середовище виконання мусить виконувати лише дозволені інструменти, а спостереження мусять знову потрапляти до транскрипту як повноцінні повідомлення. Якщо проігнорувати будь-який з цих елементів, у результаті вийде чат-бот із побічними ефектами, а не агент.\u003C\u002Fp\u003E \u003Cp\u003EПромисловий урок полягає у тому, що фреймворки приховують цикл за елементами типу runners та tool nodes, але контракт залишається незмінним: модель мусить генерувати дію, яку можна проаналізувати, середовище виконання мусить виконувати лише дозволені інструменти, а спостереження мусять знову потрапляти до транскрипту як повноцінні повідомлення. Якщо проігнорувати будь-який з цих елементів, у результаті вийде чат-бот із побічними ефектами, а не агент.\u003C\u002Fp\u003E\u003Cp\u003EПромисловий урок полягає у тому, що фреймворки приховують цикл за елементами типу runners та tool nodes, але контракт залишається незмінним: модель мусить генерувати дію, яку можна проаналізувати, середовище виконання мусить виконувати лише дозволені інструменти, а спостереження мусять знову потрапляти до транскрипту як повноцінні повідомлення. Якщо проігнорувати будь-який з цих елементів, у вас вийде чат-бот із побічними ефектами, а не агент.\u003C\u002Fp\u003E \u003Cp\u003EПромисловий урок полягає у тому, що фреймворки приховують цикл за елементами типу runners та tool nodes, але контракт залишається незмінним: модель мусить генерувати дію, яку можна проаналізувати, середовище виконання мусить виконувати лише дозволені інструменти, а спостереження мусять знову потрапляти до транскрипту як повноцінні повідомлення. Якщо проігнорувати будь-який з цих елементів, у вас вийде чат-бот із побічними ефектами, а не агент.\u003C\u002Fp\u003E ","showLangNote":false,"schemaTitle":"Реакція з нуля: думка, дія, пауза, спостереження без жодної структури","schemaDescription":"Крихітний цикл ReAct, створений вручну на Groq: думка, дія, пауза, спостереження — спочатку вручну, потім виклики інструментів regex, тому фреймворки агентів LLM мають сенс.","contentLanguage":"uk","sources":[{"title":"The Origins of AI Agents -How ReAct Rewired the Way LLMs Think and Act","url":"https:\u002F\u002Fmedium.com\u002F@khanejasamridh\u002Fthe-origins-of-ai-agents-how-react-rewired-the-way-llms-think-and-act-731895739b4f"}],"related":[{"slug":"introduction-to-mcp-from-fragmentation-to-shared-tool-servers","title":"Вступ до MCP: від фрагментації до спільних серверів інструментів"},{"slug":"mcp-from-scratch-part-3-connect-a-client-to-your-server","title":"MCP З нуля, частина 3: Підключення клієнта до вашого сервера"},{"slug":"ai-agents-for-future-engineers-memory-tools-and-control-loops","title":"Штучні інтелектуальні агенти для майбутніх інженерів: пам’ять, інструменти та контрольні цикли"}]}},"actionData":null,"errors":null} ) # 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. Надішліть просте повідомлення „Hello“ до налаштованої моделі для підтвердження з’єднання.
    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.

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

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

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

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

    ← Усі статтіНадіслати бриф