Чаму доўгім агентам патрэбна машына станаў: ролі лідара, выканаўцы і правершчыка
Чатыры спосабы, якімі адзін цыкл агента падводзіць на доўгіх задачах, і як каманда з лідара, выканаўцы і правершчыка пад дэтэрмінаваным рухавіком гэта выпраўляе — і калі яна не патрэбна.
Крыніцы: MiniMax Agent Team Best Practice: An AI Agent Is a While Loop. A Reliable One Is a State Machine.
Пераход з чат-бота на агента адзейнае за дапамою ціклу while, а не за рахунак наўкладных адкрыццяў. Гэта правда, і самэ гэтае месца, дзе большасць адказаў завершаецца, а пачынаюцца большасць проблем у практычнай експлуатацыі. Болей корыстным ўпыткам яе, чаму гэты цікл тыха парадуецца пад час выканання задач, якія трываюць дольш чым дзесяць хвілін, і якая структура яго можа вярнуць у робочы стан. MiniMax выпусціла два адкрытыя матэрыялы на гэту тэму: інструкцыю Mini-Agent з адкрытым кодам, якая прадстаўляе базовы цікл, і тэхнічны апіс ўзроўню ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы ўзбудовы
Калі спроставіць агента, застанемся чатырам элементам: модэлю, спісу паведамленняў, рэестру інструментаў і цыклу, які іх обрабоцуе. Рэферэнс Mini-Agent працуе так: спачатку пераканаліваецца, чы не наблізілася довжына історыі да максімальнага ліміту, і як толькі так, яна скорачваецца. Пасля чаго паведамленняі і апісанні інструментаў адправляюцца модэлі. Калі модэль не запрашае жадных інструментаў, цыкл завершваецца. У іншым случае запускаюцца запрасітыя інструменты, ўхідныя даны з іх працы дадаюцца да паведамленняў, і працэс павтараецца. Ёсць полная версія для будзь-каго канцэнтрабіну, сумеснага з OpenAI, з двума рэальнымі інструментамі і функцыяй стварэння апісання:
# naive_loop.py -- a single-agent tool loop,
# Mini-Agent style: summarize if needed ->
# llm.generate(messages, tools) -> execute
# tool_calls -> append results -> repeat.
import json
import subprocess
from openai import OpenAI
client = OpenAI()
MODEL = "gpt-4o-mini"
MAX_HISTORY = 40 # crude context budget
def read_file(path: str) -> str:
with open(path) as f:
return f.read()[:4000]
def run_cmd(cmd: str) -> str:
p = subprocess.run(
cmd, shell=True, timeout=30,
capture_output=True, text=True)
out = p.stdout + p.stderr
return f"exit={p.returncode}\n{out[-2000:]}"
TOOLS = {"read_file": read_file,
"run_cmd": run_cmd}
def schema(name, desc, arg):
return {
"type": "function",
"function": {
"name": name,
"description": desc,
"parameters": {
"type": "object",
"properties": {
arg: {"type": "string"},
},
"required": [arg],
},
},
}
SCHEMAS = [
schema("read_file",
"Read a local text file.", "path"),
schema("run_cmd",
"Run a shell command.", "cmd"),
]
def summarize(msgs):
# Mini-Agent compresses old turns with an
# LLM summary call when the context nears
# its limit; hard truncation is the v0.
if len(msgs) <= MAX_HISTORY:
return msgs
return [msgs[0]] + msgs[-MAX_HISTORY:]
def run(task: str, max_steps: int = 20):
msgs = [{"role": "user", "content": task}]
for _ in range(max_steps):
msgs = summarize(msgs)
r = client.chat.completions.create(
model=MODEL,
messages=msgs,
tools=SCHEMAS,
)
msg = r.choices[0].message
msgs.append(msg)
if not msg.tool_calls:
return msg.content # model says done
for call in msg.tool_calls:
fn = TOOLS[call.function.name]
args = json.loads(
call.function.arguments)
try:
out = fn(**args)
except Exception as e:
out = f"tool error: {e}"
msgs.append({
"role": "tool",
"tool_call_id": call.id,
"content": out,
})
return "hit max_steps -- gave up"
if __name__ == "__main__":
print(run("Count the .py files here, then "
"summarize naive_loop.py."))
У цім цыкле ёсць два спосабы выходу, і кожны з іх мае значэнне. Успех — цэ характэрыстыка самага модэля, якая выражаецца ў тым, што не трэба яшчэ вызываць інструменты. Безпека — цэ просты лічыльнік, які фіксуе максымальную колькасць крокаў. Гэтая асаматычнасць, калі толькі модэль вяршыць рашэнне пра тое, калі праца завершана, якраз і ёсць тым, што першым даўляе.
Калі адны цыкл не справляецца з дзейнасцямі дліжынай у гадзіны
Калі запусціць той самы цыкл для працы, якая трывае два гадзіны, а не две хвіліны, пачынаюцься аберанцыі. Команда MiniMax апісала тые проблемы, з якімі яны сталкваліся. Жадна з іх не ўскладнення. Цэлкам нормальныя якосці адныя кантэкстаў, таму ні большы тымчасовы прыметак, ні крашчыі запіт не можаць іх усунуць.
Спачатку выклікаецца трывога, вызваная кантэкстам: агент зупіняецца, калі ніхто яго пра гэта не прасіў. З семі падзаданняў ён завершае тры і кажа, што адмахнуўся падзаданняў 1, 2 і 3, і запытае, чы хочаць продаць. Мадэлі маюць нечыткія уявленні пра тое, калі можа завершыцца дуга задача, а прымусовая налагодка гэтых уявленняў ёсць дужа дорогай як з пункту грошовых вітатаў, так і з пункту алгорытмічных зусіль. Простая петля перакладае рашэнне пра зупінку на яе найменш надзеяны частку.
Далей выклікаецца падупад карыстнасці: агент стае менш розумным па меры зростання кантэксту. Чэрэз пяць хвілін ён працюе адмахнуто, а чэрэз шастаццаць пяць хвілін ведае ся як розсеяны стажыст. Одна неправільна дзеянне, напрыклад, калі задача дакладнага адзінства ператвараецца на маркетынгавы тэкст, парадужае ўсё, што выйшла пасля, таму што кожна наступная генерацыя формуецца на адной занятый історыі.
Трэцім, ніхто не перакантролюе працу, адзінэ ўжо суддзя ёсць і учаснік. Адзін агент, які стварае заканчоўны рэзультат, самацэ сабе оцэнку ў тым самым кантэксце, дзе і з’явіліся ягоіяі памылкі. Гэта не нечыстасць. Гэта проста аналіз сцэны, якую сама модэль ўжо стварыла.
Чэтыртым ёсць мовчанне. Інтэрфейсы у стылі чату прывучаюць корыстувачаў спадзявацца на адпаведзь за калькі секунд, нават для задання, якое трэба выконваць гадзіну, але цыкл, які знаходзіцца ўнутры вызову модэлі, не можа паведаміць, што ён прымеў запіт і даўае адпаведзь. За данымі MiniMax, запыт „Чаму мой агент не адпавядае?“ прынёс больш адгукоў корыстувачаў, чым будзь-што іншае.
Рашэнне: тры ролі і дэтэрміністычны двыжак
MiniMax адказвае з структурам. Тры відэныя ролі коордынуецца за дапамою звычайнага дэтэрміністычнага коду, а не за дапамою хітрых запытанняў, і гэты код называецца Team Engine. Адзін цыкл выканання задання — адзін Сэшан, і гэты механізм прыводзіць кожны Сэшан праз тры станы: стан стварэння, стан пераканання і стан завершэння, пры чым знову запускае працэс стварэння, калі перакананне не выйшла.
- Лідэр разбівае запыт корыстніка на структураваны план, выбірае, калія часткі будуць выкананы адночасова, адказвае корыстніку за калькі секунд, пакуль рэштка задання выканана поза ўвагай, і нарэшце з’едынае рэзультаты.
- Рабоўцы беруць на сябе падзаданні, кожна з яых выканана ў аднаковым кантэксце, які абзяц сваімі інструментамі, памяцю і неабходным фарматам выходных даных. Гэта раздзеленне мае значэнне, таму што рабоўцы бачыць толькі адна падзаданне, а не два гады накупленых пахылень.
Ядро двіжка ўстаткаваеся настолькі, што яго можна запісаць. Штая версіі не патрабуе ключа API, адколі ролі-стабы заменяюць вызовы LLM і можу быць змененыя без вплыву на машыну стаўаў:
# team_engine.py -- the loop promoted to a
# state machine. One task lifecycle is one
# Session; deterministic code owns every
# transition: producing -> verifying -> done.
from dataclasses import dataclass, field
PRODUCING = "producing"
VERIFYING = "verifying"
DONE = "done"
FAILED = "failed"
@dataclass
class Session:
goal: str
state: str = PRODUCING
attempts: int = 0
artifact: str = ""
reviews: list = field(default_factory=list)
def run_session(s, worker, verifier,
max_retries: int = 3):
while s.state not in (DONE, FAILED):
if s.state == PRODUCING:
s.attempts += 1
s.artifact = worker(
s.goal, s.reviews)
s.state = VERIFYING
elif s.state == VERIFYING:
ok, report = verifier(
s.goal, s.artifact)
s.reviews.append(report)
if ok:
s.state = DONE
elif s.attempts >= max_retries:
s.state = FAILED
else:
# Reject: wake the worker with
# the review log in context.
s.state = PRODUCING
return s
def run_team(goals, worker, verifier):
# Leader's job: split the goal, run the
# sessions (a thread pool in real use),
# then merge N artifacts into 1 result.
done, failed = [], []
for g in goals:
s = run_session(Session(g),
worker, verifier)
if s.state == DONE:
done.append(s.artifact)
else:
failed.append(s.goal)
return done, failed
if __name__ == "__main__":
# Stub roles so the engine runs anywhere;
# swap in LLM calls for the real thing.
def worker(goal, reviews):
v = len(reviews) + 1
return f"draft v{v}: {goal}"
def verifier(goal, artifact):
ok = "v2" in artifact
return ok, f"review {artifact!r}: {ok}"
done, failed = run_team(
["intro", "benchmarks", "faq"],
worker, verifier)
print("delivered:", done)
print("failed:", failed)
Яго запускаюць, і першая версія кожнай сесіі адхоўваная; працоўнік працюе з журналам перагляду ў своем кантэксце, а другая версія прыймаецца. Калі яго размясціць паляў з простым цыклам, будуць відны калькі разлікі.
- Зупненне: адзін цыкл заканчываецца, калі мадэль перастае выкананяць запыты інструментаў (fuzzy) або досягае максымальнага колькісці крокаў, тады як двіжак выкарыстоўвае явныя статусы «завершана» і «неудача» з лімітам павторных спроб, заданым у кодзе.
- Перакананне: у цыкле той самы кантэкст, які стварыў працу, яе пераканвае, тады як команда выкарыстоўвае адварсарскі пераканавач у аддзельным кантэксце, базаваным на командах.
- Паралелізм: цыкл — гэта адзін последовны кантэкст, а команда запускае працоўнікі ў паралельных сесіях, пры чым лідэр з’еднае ўсе ў адну.
- Стойкасць: цыкл зберагае спіс паведамленняў пакуль не будзе адбытае стыканне, а команда зберагае стан сесіі, запісы перагляду, артыфакты і памяць кожнага агента.
Лідэр таксама не абмежваецца толькі одноразовымі запытамі, якія ствараюць падзею і вяртаюць тэкст. У працэўнасупрацоўцы MiniMax ён чытае актуальны статус двухэлементнага механізма і можа дадаць дапаможныя інструкцыі ў сесію, якая ў тэчасці выконвання або пераканання, таму існуе як актыўнае выкананне запытання, так і запытання за патрэбай. Гэта таксама рашае проблему мовчання: Лідэр падтверджвае рэакцію за калькі секунд, а паўтарыўшыся корыстувач атрымвае апошнія звесткі на паказчыках статусу, такіх як «пачатана», «заблокавана», «трэба які-небудзь рашэнні» і «завершана».
Фіксацыя статусу пераканвальніка
Проблема сама-ацэнкі знікае толькі тады, калі вердыкт выходзіць не з якога-небудзь конкрэтнага модэля. Для коду алгорытм MiniMax абгаруе яго выконанне спецыяльным механізамам. Работа ведзецца на галузях, выконваецца ў «пісчаныях коробках», змены фіксуюцца у відміннасцях, тэсты можна павтараць, перагляды залішаюць запісы, а нявяскласці можна практыкаваць знову. Найважлівейшы момент: умова завершэння павінна быць прыязваная да чаго-небудзь дэтэрміністычнага і відчутнага з зовнішніх джэрэл, такіх як тэсты, інструменты перагляду коду і працэсы кампілявання, а ніколі не да власнага відчутка модэля пра тое, што ён завершыў работу.
# grounded_verifier.py -- evidence over vibes.
# The verdict comes from exit codes and logs,
# never from the model's self-assessment.
import subprocess
import sys
PY = sys.executable
# pytest exit codes: 0 = all passed,
# 1 = failures, 5 = no tests collected
# (acceptable for a docs-only change).
PYTEST_OK = (0, 5)
CHECKS = [
("diff", ["git", "diff", "--stat"], (0,)),
("build", [PY, "-m", "compileall",
"-q", "."], (0,)),
("tests", [PY, "-m", "pytest", "-q",
"--maxfail", "1"], PYTEST_OK),
]
def run_check(name, cmd, ok_codes, cwd):
p = subprocess.run(
cmd, cwd=cwd,
capture_output=True, text=True)
tail = (p.stdout + p.stderr)[-2000:]
return {
"check": name,
"cmd": " ".join(cmd),
"exit_code": p.returncode,
"ok": p.returncode in ok_codes,
"output_tail": tail,
}
def verify(repo: str):
evidence = []
for name, cmd, ok_codes in CHECKS:
e = run_check(name, cmd, ok_codes, repo)
evidence.append(e)
if not e["ok"]:
# Reject with logs attached: the
# worker retries against real
# errors, not "please improve".
return False, evidence
return True, evidence
if __name__ == "__main__":
ok, ev = verify(".")
for e in ev:
print(f"{e['check']:>6} exit "
f"{e['exit_code']} ok={e['ok']}")
print("verdict:", "pass" if ok else "fail")
Спісак доказаў таксама выступае як запіс перагляду, якая ўходзіць у склад відтворуемага, працэздатнага следу, які розлічвае тое, што можна выдаты, ад таго, што ўсьмо толькі правдападобнае. MiniMax далей дзеліць працэс перагляду. Тэстэр стварае зовнішнія доказы, запускаючы тэсты, стыскаючы логі пра аберанціях і дадаючы мінімальнае відтворэння сітуаціі. Пераглядач ставіць іншыя запітанні: не пра тое, чы праца працавала, а пра тое, чы код павінен змяніцца такім чынам, аналізуючы межы абстракцыі, новыя залежнасці, расшырэныя правыя на доступ і секрэты, якія прасачуюцца ў логах. Пераглядачы можуць працаваць паралельна з спецыялістамі у такіх галузях, як загальная аналіза, безпека і конкрэтныя домены.
Скількі коштае каманда і калі ўзяць ўрахованне толькі іншых вароў
Каманда — гэта не безкоштовны паралелізм. Тры вітраты з’яўляюцца як толькі агенты пачынаюць саўместную працу, і шырэй вікна кантэксту нічога з іх не усувае.
Косц адраджавання: тыя ж самыя мяркі зноў трэба арганізаваць межы агентамі, таму сторанкі з даследаванням стаюць кіраванням для пісьменніка, якое пасля таго служыць вхідным дадзенням для пераконтроўкі фармата. Раўяннем яўнае перадача файлаў, тыя можна прачытаць, праз паводзі, з короткімі апісаннямі, заместо працоўных копій кантэксту.
Косц аднароджэння: кожны глобальна аднароджаны токен плацяць усі працавікі ў кожны раунд. Працэсабельны план дазволяе кожнаму агенту зберагаць мэмарыі для сябе, агентам можна даўаць сігналы адзін другаму праз перэрывы праз CLI, а таксама існуе адна спяльная дошка, яю агенты чытаюць толькі тады, калі гэта неабходна.
Трэцім яўнае аграгаванне. Збіраць дзесяць частковых праектаў — гэта проста. Але з’едыніць іх у адны файл з адпаведнымі фактамі, цітатамі і тонам — гэта найважэлівейшая задача для лідэра, і для яе трэба етап з’едынення, а не дадатковыя працавікі.
Кансэнсус таксавацца і ценай. Вольная дыялогія між агентамі можа спрацавіць колькісна больш токенав, чым патрэбна для самастойнай корэкціі, і тады такса не павышыць точнасці. Ценнае — гэта структура: обмежаныя сесіі, чыста структура дадзеных і можлівасць для пераканальніка адмовіць.
Адкладзіце саўместную працу, калі задача мала, можа быць выканана локальна і ў яе перакананне некалькі коштаў, таму што адна петля коштае менш. Ожывайце саўместную працу, калі праца можа быць роздзелена чыста, памылкі маюць вялікія наследкі і незалежныя перакананні можа даць доказы таго, што мадэль не можа сама сабе падтрымаць. Хорашы тэст: дадзіце ролю толькі тады, калі яна пакрывае спосаб абякання, якога не пакрывае іншая роль; інакша додатковыі трыбут саўместнай працы нічога не дае.
Што стварыць перша
Пачніце з самай маленькай машыны станоў, якой можаце даваць доверы. Кожная сесія мае цель, артыфакт, журнал перагляду і ліміт павторных спроб, а кожны перэход чытальна опісаны. Размясціце верыфікатор за прыемнікам, чыяя адпаведзі з’яўляюцца з тэстаў, перагляду коду, складання чы ў іншых падобных механічных пераканальстваў. Паралельныя працоўнікі будуць дадаткова: паралелізм — гэта оптымізацыя, тады як межы правільнасці — гэта архітэктура. Залічыце крок з’еднання ад лідэра відным, заставіце артыфакты перадачы відкрытымі для перагляду і запісвайце, чаму заканчылася кожная сесія.
Тое, што выходзіць, — гэта не автонамны разум. Гэта кантролюемы працэс: мадэлі прыказуюць, інструменты выкананыя працу, а звычны код вяршыць рашэнне, чы праца завершана. Перш чым адпрацоўваць фрэймворк, спачатку ручна створыце просты цыкл. Чакайце, што дугія задачы можу разбіць яго структуральна праз трывогу, адхіленьне, самаоцэнку і мовчанне, і ніякія падказкі гэта не вылечыць. Спрыяйце мульты-агентам як сістэме з планам кантролю стану, запускаючых падзеяў, праваў, можлівасцяў абзору і восстанавлімых элементаў, а не як ролевай грэ на падказкі. І запланаваце перадачу, аднароджэнне і аграгацыю дахоўкі ўжо перад стварэннем каманды, і ніколі не ствараюце каманду праз адлік.
Супаўзвязаныя матэрыялы
- Практычныя прытамулы: Claude Code Agent Teams: Практычныя вядомасці па стварэнню каманды — Дэталізаваныя інструкцыі па прытамулах: Claude Code Agent Teams: Практычныя вядомасці па стварэнню каманды: контракты, перакананні і месцы для коду для каманд, якія выкарыстоўваюць гэты патэрн.
- Чаму логі агента не будуць адпаведныя статтэйцы 12: Запісы рашэнняў, каузальнасць і паходжанне — Следы не ўсунуць адпаведнасці. Як запісваць рашэнні агента як стойкія об’екты з каузальнымі зв’язкамі, хеш-зв’язаным паходжаннем і дэтэрміністычнымі правіламі, выкарыстоўваючы Semantica.