Галоўная / Артыкулы / Рэакцыя з нуля: думка, дзеянне, пауза, абазаванне без кантрэлі.

Рэакцыя з нуля: думка, дзеянне, пауза, абазаванне без кантрэлі.

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

1373 слоў

Вступ

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

Калітэт адпаведзяў паднялася, і моделі паказаліся корыстнымі для адгукнення ў адзін момент. Толькі планаванне яшчэ не дае автонаміі. Запытанні, якія визначаюць агентаў, такія: што знаходзіцца за межамі ведомасці моделі, якія дзеяння ўможлівяюць ёй яго атрыманне, як практычна даць рэзультат, як перасмотраць план і калі зупыніцца. Процес «Рашэнь → дзеянне → перакантроль → парадзейнаўкі» — це цыкл, а не праўая лінія. Гэты цыкл ёсць вучою савечных штучных інтэлектуальных агентаў. Модель ReAct (Рашэнь + Дзеянне) задала такі патэрн. Пачатковыя дамастанты не былі прыкладамі аплікацый LangChain — гэта быў LLM, структураваны запыт і чалавек (або пазней скрыпт), які выкананаў дзеянні і вернуў адзысканыя данні.

Практычны прыклад 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() да складанага запитання і старожыце процес Thought → Action → Observation → Answer без ручных крокаў.
  • Вывык

    Агенты начынаюць з ціклу, а не з фрэймворку. Моделі, навучаныя методам Chain-of-thought, разумеюць голасна; ReAct структуруець процес think–act–observe на адпаведнасці з спосабамі работы людзей. Популярныя фрэймворкі агентаў індустрыялізуюць гэю ідею. Стварэнне ціклу вручную назаўсёды зменяе адчуванне ўжо існуючых фрэймворкаў. Адпаведны артыкул знаходзіцца на https://arxiv.org/abs/2210.03629.

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

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

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

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