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

Спеціалізовані агенти, маршрутизатор за ключовими словами та функція interrupt(): Коуч LangGraph

Розробити асистента LangGraph з двома спеціалістами, який має детерміністичний маршрутизатор, спільний стан, що зберігається під час передачі обов’язків, та людський пункт перевірки, створений на основі переривань та команд.

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

Джерела: Building Multi-Agent AI Systems with LangGraph: A Fitness & Nutrition Coach Example

Один запит до асистента, який має охоплювати дві непов’язані сфери, зазвичай погано справляється з обома. У цій статті замість цього створюється невелика система LangGraph у вигляді тренера з фітнесу та дієти: два спеціалізовані агента, детерміністичний маршрутизатор, який вирішує, хто має відповісти, один спільний стан, що передає обмеження під час передачі завдань, та людський контрольний пункт, який зупиняє роботу системи після кожної відповіді. До кінця ви дізнаєтесь, як працює кожен елемент, чому система має саме таку структуру та як можна використовувати цю структуру для будь-якої пари спеціалістів.

Сценарій та тестове спілкування

Цільовим продуктом є асистент у додатку для фітнесу, який допомагає як з тренуваннями, так і з дієтою. Дизайн перевіряється за допомогою трьох етапів спілкування: запит на план тренувань, подальше запитання щодо їжі та обмеження в харчуванні, яке змушує змінити поради щодо прийому їжі:

Scenario: A fitness app wants an assistant that helps with workouts and nutrition.

Test conversation flow (3 turns):
"Build muscle, lose fat — suggest a workout plan"
"What should I eat to support this workout plan?"
"I'm vegetarian, adjust the meal suggestion"

Кожне з рішень щодо дизайну призначене для того, щоб ці три етапи взаємодії працювали правильно та передбачувано.

Чому загальний чат-бот не підходить

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

  • Розділення сфер. Планування тренувань та харчування — це різні галузі знань, і поради в одній сфері не повинні впливати на іншу чи суперечити їй.
  • Збереження контексту між етапами. Обмеження, вказане раніше, наприклад травма коліна, згадана три повідомлення тому, все ще має впливати на план їжі чи тренувань, створений пізніше.
  • Підтримка контролю з боку людини. Система не повинна працювати повністю автономно; їй потрібна чітка точка, де людина може схвалити результати, додати деталі чи змінити напрямок перед тим, як буде створено щось інше.
  • Замість того, щоб просити один запит виконати три завдання, архітектура надає кожній вимозі власний механізм: спеціалістів для розділення областей, спільний стан для контексту та людський елемент для нагляду.

    П’ять основних елементів

    Система складається з п’яти частин, кожна з яких має одну конкретну функцію:

    • Консультант з тренувань: агент, відповідальний за розробку програм тренувань, їх поділ на сесії та корекцію у разі травм.
    • Консультант з харчування: агент, відповідальний за плани їжі, співвідношення макронутриєнтів та дієтичні обмеження, такі як вегетаріанська, веганська дієта чи дієта через алергію.
    • Роутер: простий етап прийняття рішень, який аналізує останнє повідомлення користувача та вибирає, який консультант має відповісти наступним.
    • Людський вузол: навмисна пауза, під час якої граф очікує на людину замість того, щоб генерувати додатковий вихідний матеріал.
    • Спільний стан: єдиний авторитетний запис діалогу до цього моменту, а також інформація про те, який консультант відповів останнім.

    Це ієрархічний або оркестраційний патерн. Координатор делегує завдання спеціалізованим підагентам замість того, щоб один агент намагався виконувати всі ролі одночасно.

    Правило, яке забезпечує прогнозованість передачі обов’язків

    Одне структурне правило робить усю систему дизайну надійною: консультант ніколи не передає керування безпосередньо іншому консультанту. Кожен вихідний сигнал агента проходить через людський вузол, а потім через маршрутизатор. Тому два агенти ніколи не можуть тихо передавати керування один одному, а кожна зміна є видимою та може бути перервана.

    Як повідомлення переміщується по графі

    Слідкуйте за одним повідомленням користувача. Спочатку воно потрапляє на людський вузол, а потім передається маршрутизатору, який читає текст повідомлення; якщо сам текст є недостатнім для прийняття рішення, маршрутизатор звертається до поля last_active_agent у спільному стані, щоб вибрати між консультантом з тренувань та консультантом з харчування. Обраний консультант може викликати свій інструмент, після чого повертає керування людському вузлу, який знову припиняє роботу та чекає на наступне повідомлення. Таким чином цикл завжди складається з людського вузла, маршрутизатора, консультанта, необов’язкового інструменту та людського вузла.

    Модель: LLM з відкритими вагами на Groq

    Обидва консультанти використовують модель з відкритими вагами openai/gpt-oss-120b, яку надає Groq, налаштовану з параметром temperature=0:

    • Чому саме ця модель: у неї є надійний вбудований механізм виклику інструментів, від якого залежить робота за схемою, що ґрунтується на інструментах.
  • Чому температура 0: це робить результати максимально відтворюваними, тож графік дотримується запланованих правил, а не змінюється з кожної нової обробки. Це важливо, коли хтось неодноразово тестує або оцінює одну й ту саму розмову. На практиці навіть температура 0 не гарантує суворого детермінізму у хостованих моделях, тому тести повинні перевіряти маршрутизацію та структуру, а не точність формулювань.
  • Чому Groq: його апаратне забезпечення для інференсу LPU підтримує низьку затримку, що має значення, коли один крок користувача викликає кілька етапів обробки (людський вузол, маршрутизатор, консультант, інструмент, людський вузол) перед тим, як з’явиться відповідь.
  • У графі немає нічого, що б безпосередньо залежало від Groq. Будь-яка модель чату з можливістю виклику інструментів підходить, наприклад Google Gemini або Anthropic Claude, за умови надання відповідного ключа API (такого як GEMINI_API_KEY або ANTHROPIC_API_KEY) та налаштування обгортки моделі на цього постачальника. Доступність моделей на хостованих платформах змінюється з часом, тому перевірте ідентифікатор моделі у поточному каталозі постачальника.

    Спільний стан: де фактично знаходиться пам’ять

    Кожен вузол читає та записує дані у один об’єкт типового стану MultiAgentState, який має два поля:

    • messages: повна хід розмови, успадкована від MessagesState LangGraph; цей об’єкт також забезпечує функцію додавання нових повідомлень замість їх перезапису.
  • last_active_agent: встановлюється на "workout_advisor" або "nutrition_advisor"; маршрутизатор використовує його тоді, коли не може визначити, до якого домену належить подальше запитання, наприклад "tell me more".
  • Саме через цей стан обмеження, вказані на певному кроці, залишаються чинними на наступних кроках, навіть якщо відповідь генерується іншим вузлом. Моделі нічого не підсумовується та не пояснюється заново. Кожен консультант просто отримує той самий накопичений список messages щоразу, коли він запускається, а механізм checkpointer зберігає стан між кроками однієї розмови.

    Ключовим елементом проектування є те, що пам’ять є централізованою, а не прив’язаною до кожного агента. Якби кожен консультант зберігав власну приватну історію, консультант з питань харчування ніколи б не дізнався про цілі, які користувач визначив для консультанта з фізичних вправ.

    Один вузькоспеціалізований інструмент на консультанта

    Кожен агент може використовувати лише один інструмент, який належить до його сфери діяльності. У цій версії інструменти базуються на правилах та знаходять відповідність ключовим словам, що робить їхню поведінку прогнозованою та легко пояснюваною. У продакшені їх можна замінити на справжні API для оцінки фізичної форми чи харчування, або дозволити моделі повністю генерувати відповідь. Оскільки решта графу залежить лише від інтерфейсу інструменту, заміна реалізації не вимагає інших змін.

    Збереження кожного консультанта у своїй сфері

    Одного спеціалізованого інструменту недостатньо, щоб агент залишався у темі; модель також має знати, на що не слід відповідати. Тому кожен системний запит встановлює чіткі обмеження:

    • Консультанту з тренувань наказано не давати порад щодо їжі чи харчування, оскільки граф направлятиме такі запити до Консультанта з харчування.
  • Консультанту з харчування наказано не розробляти програми тренувань, оскільки це належить до обов’язків Консультанта з тренувань.
  • Принцип полягає у тому, що модель не самостійно вибирає шлях. Завдяки чітким вказівкам кожен агент змушений дотримуватися своєї спеціалізації, а не імпровізувати поза нею, причому кожне рішення щодо маршрутизації прямо визначається графом, а не ШІ.

    Маршрутизатор, побудований на ключових словах, а не на ще одному виклику ШІ

    Оскільки консультанти мають обмеження, маршрутизатору достатньо лише вибрати наступного спікера, і він робить це за допомогою простого зіставлення ключових слів:

    • Слова на кшталт спортзал, повтори, кардіо чи м’язи направляються до Консультанта з тренувань.
    • Слова на кшталт дієта, їжа, їсти, білок чи вегетаріанство направляються до Консультанта з харчування.
    • Усе, що не відповідає жодному з списків, надсилається до того консультанта, який зберігається у last_active_agent.

    Детерміністичне маршрутизування є передбачуваним та легко піддається тестуванню: одне й те саме повідомлення завжди призводить до одного й того самого кроку, і ви можете протестувати маршрутизатор окремо, не викликаючи модель. Недоліком є крихкість цього підходу. Списки ключових слів не враховують синоніми, тож повідомлення, яке стосується обох тем (наприклад, запитання про їжу перед тренуванням), потребує чіткого правила вирішення суперечок. Коли формулювання стає занадто різноманітним для ключових слів, доцільно використати просту класифікацію, але це позбавляє систему часткової передбачуваності.

    Участь людини з можливістю переривання та командування

    Найважливішим рішенням у дизайні є не маршрутизатор, а людський вузол. Після кожного кроку радника граф навмисно зупиняється. Він не вгадує наступне запитання користувача та не продовжує генерувати інформацію. Для реалізації паузи використовуються два примітиви LangGraph:

    • interrupt(...) зупиняє виконання саме там, де він був викликаний, та повертає керування до того, хто його викликав, разом із будь-якою значенням, яке ви йому передали.
    • Command(resume=...) продовжує роботу графа після паузи, передаючи вхідні дані людини як значення повернення від виклику interrupt у тому самому вузлі.

    Інтерупції ґрунтуються на створенні контрольних точок: граф має зберігати свій стан під час паузи, щоб зможти продовжити роботу пізніше, тому скомпільований граф потребує контрольної точки та ідентифікатора потоку.

    Для асистента з фітнесу та харчування ця пауза — це не просто зручність інтерфейсу. Це момент, коли користувач може додати обмеження, пов’язані з безпекою, такі як травма, алергія чи дієтичні обмеження, ще до того, як розпочнеться діалог, а не після того, як система вже надала пораду, яка їх ігнорує.

    Поетапний огляд трьох етапів

    Етап 1: початок розмови

    Користувач визначає дві мети — наростити м’язову масу та позбутися частини жиру — і просить рекомендації щодо тренувань. Кожна нова розмова завжди починається з сервісу Workout Advisor. Він бере до уваги поняття „м’язи“ та „жир“, запускає відповідний інструмент та повертає структурований план тренувань, наприклад чотириденну схему тренувань верхньої та нижньої частин тіла з 8–12 повторень у 3–4 підходи, поєднуючи три дні на силові тренування з двома сеансами кардіо.

    Етап 2: передача завдання з харчуванням

    Далі користувач хоче дізнатися, які продукти можуть сприяти цьому плану. Вузол людини передає повідомлення, маршрутизатор знаходить опцію „їсти“, і виконання передається до Консультанта з харчування. Він повертає щоденний план приблизно на 2 500 ккал, орієнтований на нежирний білок та складні вуглеводи, із прийомами їжі кожні три–чотири години.

    Етап 3: застосування обмеження зі спільного стану

    Нарешті користувач зазначає, що є вегетаріанцем, та просить скоригувати рекомендації щодо їжі, додавши перекус. Повідомлення знову надсилається до консультанта з харчування, і тут діють два фактори: сам термін „вегетаріанець“ є ключовим словом у сфері харчування, а last_active_agent все ще вказує на цю категорію, тож навіть більш нечітке запитання на кшталт „скоригуйте це та додайте перекус“ потрапляє в той самий розділ. Консультант знову читає всю історію запитів, продовжує дотримуватися цілі, встановленої на першому кроці, додає обмеження щодо вегетаріанства та розробляє план без м’яса для нарощування м’язів, заснований на тофу, панірі та сочевиці, з перекусами у вигляді едамаме та чашок з хумусом. Нікому не довелося повторювати ціль, оскільки вона вже була задана у системному стані.

    Цей крок є результатом дії централізованої системи керування. Відповідь генерується іншим вузлом, ніж той, який отримав початкову мету, проте він має повний контекст.

    Складання машини станів

    За структурою результатом є невелика графічна модель станів із трьома вузлами (два консультанти та людський вузол) плюс маршрутизатор, який забезпечує умовну логіку між ними, та одна фіксована точка входу:

    • Кожен новий потік починається з консультанта з тренувань. Цей стандарт відображає особливості продукту: користувачі, які відкривають додаток для фітнесу, зазвичай спочатку запитують про тренування, а вже потім — про їжу.
    • Після цієї першої відповіді людський вузол та маршрутизатор вирішують про кожен наступний крок.
  • Граф складається з використанням контрольного показника MemorySaver, і кожна розмова ідентифікується за допомогою thread_id. Кожного разу, коли користувач знову використовує той самий thread_id, граф автоматично відновлює повідомлення та значення last_active_agent; користувач ніколи не передає історію повернення.
  • MemorySaver зберігає контрольні точки у пам’яті процесу, що ідеально підходить для нотбуків, але втрачає все під час перезапуску. Версія, яка використовується в продакшені, повинна застосовувати постійний контрольний показник, підтримуваний базою даних.

    Повна функціональна реалізація доступна у вигляді супровідного ноутбука. Щодо інших способів структурування кроків маршрутизації та схвалення, дивіться п’ять шаблонів LangGraph, які охоплюють маршрутизацію, розповсюдження інформації, критику та схвалення.

    Повторне використання шаблону поза сферою фітнесу

    Ніщо тут не є специфічним для тренувань чи прийому їжі. Ті самі три елементи – спеціалізовані агенти, маршрутизатор, керований спільним станом, та людський контрольний пункт з можливістю переривання та продовження роботи – підходять для будь-якої системи, яка має:

    • більше ніж одну окрему сферу експертизи, яка може знадобитися під час розмови,
  • потреба зупинятися на півдорозі, щоб хтось вніс свої дані чи підтвердив дії, чи то з метою дотримання правил, безпеки чи індивідуалізації,
  • ситуація, визначена у одній частині розмови, яка має правильно вплинути на іншого спеціаліста пізніше.
  • Якщо замінити двох консультантів на іншу пару експертів у цій галузі, форма графа залишиться незмінною. Змінюються лише інструменти, запити та ключові слова маршрутизатора.

    Принципи проектування

    • Оркестрація замість монолітів. Використовуйте граф для створення окремих каналів експертизи замість того, щоб покладатися на одного агента для виконання всіх завдань.
    • Централізація стану. Один MultiAgentState, який зберігається за допомогою чекпоїнта, забезпечує плавний перехід обов’язків та постійну дію обмежень.
  • Контроль є критично важливим. Уникайте повністю автономних циклів у продакшені; використовуйте interrupt(), щоб забезпечити участь людини у важливих моментах.
  • Ключові висновки

    • Розділіть роботу за доменами та делегуйте прийняття рішень щодо маршрутизації детерміністичній логіці графа, а не моделі.
    • Зберігайте одну спільну історію messages разом із last_active_agent, щоб обмеження, встановлені раніше, впливали на відповіді, які формуються пізніше іншим агентом.
    • Розглядайте interrupt() та Command(resume=...) як механізми, що дозволяють людині додавати правила безпеки перед наступною відповіддю, та пам’ятайте, що для цього потрібні контрольний показник та ідентифікатор потоку.
    • Зберігайте інструменти у вузьких межах за допомогою стабільного інтерфейсу, щоб логіка, заснована на правилах, згодом могла стати справжнім API або повноцінним модельним міркуванням без змін графа.
    • Найшвидший спосіб засвоїти цю схему — це обрати двох добре визначених фахівців серед своїх працівників та створити повну функціональну схему; топологія залишається незмінною, тоді як запити та інструменти — це ті елементи, які ви адаптуєте під свої потреби.