Практичні нотатки: Оркестрація багатьох агентів: створення та спостереження за багатьма агентами
Покрокове керівництво з практичних нотаток: Оркестрація багатьох агентів: створення та спостереження за багатьма агентами, контракти, перевірки та готові блоки коду для команд, які використовують цю схему.
У цьому посібнику детально описано шлях від сировини до функціональної системи для: Оркестрація багатьох агентів: створення та спостереження за багатоагентними системами за допомогою LangGraph та LangSmith. Основна увага приділяється крокам виконання, чітким перевіркам та коду, який можна просто додати до репозиторію, не здогадуючись про його призначення. На етапі огляду необхідно визначити вхідні дані, виконавця кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не здогадуючись про прихований стан. Конфігурацію слід тримати окремо від коду програми. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, яке оператори можуть перевірити, не читаючи весь граф.
Вступ: Що таке оркестрація агентів LangSmith?
Під час роботи над етапом «Що таке LangSmith?» у розділі Вступ спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допоможе зберегти чесність подальших змін у коді. Одночасно задокументуйте шлях успішного виконання та шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої доробки. Робіть контрольні пункти після дорогих кроків. Система відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор намагається знову виконати пізніший етап.
Архітектура: як працює оркестрація в LangGraph
Під час роботи над етапом «Архітектура оркестрації» спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям перед об’ємними скриптами. Коли якась крок виявляється невдалим, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій. Робіть контрольні пункти після дорогих кроків. Система відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор намагається знову виконати пізнішу операцію.
Як налаштовується оркестрація:
Під час роботи над етапом «Як відбувається оркестрація» спочатку запишіть угоду: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте беззвучного часткового виконання завдань. Робіть контрольні пункти після дорогих кроків. Система відновлення не повинна знову стягувати плату за один і той самий виклик LLM, коли оператор перезапускає пізніший етап. Під час роботи над етапом «Як відбувається оркестрація» спочатку запишіть угоду: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф.
Крок за кроком: приклад, який можна відтворити
Етап «Покроковий приклад, який можна відтворити» найкраще функціонує, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний варіант виконання, один випадок збою та примітку щодо скасування змін перед розширенням обсягу. Документуйте як успішний, так і відновлювальний шляхи роботи разом. Повторні спроби, людські контролі та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.
Попередні вимоги
Етап попередніх умов найкраще функціонує, якщо його розглядати як вимірювану поверхню. Зафіксуйте один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.
Крок 1: Налаштування середовища
Етап налаштування середовища кроку 1 працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв. Етап налаштування середовища кроку 1 працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Зберігайте конфігурацію окремо від коду програми. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф.
uv add langgraph langchain-anthropic langsmith python-dotenv
Крок 2: Код на Python
Для другого кроку – етапу Python – необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Необхідно розділити процес створення клієнта від циклу обробки повідомлень, щоб можна було замінювати постачальників без переписування машини станів розмови.
import os
from typing import TypedDict, Literal
from dotenv import load_dotenv
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, SystemMessage
from langgraph.graph import StateGraph, END
# ==========================================
# 0. Load Environment Variables
# ==========================================
# This loads the API keys and LangSmith configs from the .env file
load_dotenv()
# ==========================================
# 1. Define the Shared State
# ==========================================
class AgentState(TypedDict):
messages: list
next_agent: str
# ==========================================
# 2. Define the Nodes (The Agents)
# ==========================================
# Initialize Claude 3.5 Sonnet
llm = ChatAnthropic(model="claude-sonnet-4-5-20250929", temperature=0)
def router_node(state: AgentState):
"""Acts as the router. Classifies the user query and directs it to the correct department."""
system_prompt = SystemMessage(content=(
"You are a router agent. Look at the user's message and classify it as either "
"'billing' or 'technical'. Reply with ONLY the word 'billing' or 'technical'."
))
response = llm.invoke([system_prompt] + state["messages"])
classification = response.content.strip().lower()
# Update state with the routing decision
return {"next_agent": classification, "messages": [response]}
def billing_node(state: AgentState):
"""Handles billing-related queries."""
system_prompt = SystemMessage(content=(
"You are a billing support agent. Help the user with invoices, refunds, and payments. "
"Be polite and professional."
))
response = llm.invoke([system_prompt] + state["messages"])
return {"messages": [response]}
def tech_support_node(state: AgentState):
"""Handles technical issues."""
system_prompt = SystemMessage(content=(
"You are a technical support agent. Help the user troubleshoot bugs, login issues, "
"and software errors. Be analytical and helpful."
))
response = llm.invoke([system_prompt] + state["messages"])
return {"messages": [response]}
# ==========================================
# 3. Define the Routing Logic
# ==========================================
def route_decision(state: AgentState) -> Literal["billing", "technical"]:
"""Reads the state to decide which node to visit next."""
next_agent = state.get("next_agent", "technical")
# Claude is highly instruction-following, but we use 'in' to safely handle
# any edge cases where it might add conversational filler.
if "billing" in next_agent:
return "billing"
return "technical"
# ==========================================
# 4. Build and Compile the Graph
# ==========================================
workflow = StateGraph(AgentState)
# Add nodes
workflow.add_node("router", router_node)
workflow.add_node("billing", billing_node)
workflow.add_node("technical", tech_support_node)
# Define edges
workflow.set_entry_point("router")
# The magic of orchestration: Conditional routing based on state
workflow.add_conditional_edges(
"router",
route_decision,
{
"billing": "billing",
"technical": "technical",
}
)
# Both specialized agents end the workflow
workflow.add_edge("billing", END)
workflow.add_edge("technical", END)
# Compile the graph
app = workflow.compile()
# ==========================================
# 5. Run the Orchestration
# ==========================================
if __name__ == "__main__":
# Test Case 1: Billing Query
print("--- Running Billing Test ---")
inputs = {"messages": [HumanMessage(content="I was charged twice for my subscription!")]}
result = app.invoke(inputs)
print(result["messages"][-1].content)
print("\n")
# Test Case 2: Tech Support Query
print("--- Running Tech Support Test ---")
inputs = {"messages": [HumanMessage(content="My app keeps crashing when I click the save button.")]}
result = app.invoke(inputs)
print(result["messages"][-1].content)
Крок 3: Запустити скрипт для тестування
Для кроку 3 «Запустити етап» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру процесу. Встановлюйте людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повноти бізнес-функціоналу.
uv run multiagent-orchestration.py
Крок 4: Переглянути оркестрацію в LangSmith
На етапі 4 «Перегляд» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Призначте назви елементів, визначте критерії успіху та не допускайте мовчазного часткового завершення. Забезпечте людське схвалення для операцій, які вимагають витрат грошей чи змінюють дані у продакшені. Підключення під час компіляції не є гарантією повності бізнес-процесу. На етапі 4 «Перегляд» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретних даних та флаги функцій мають знаходитися в одному місці, яке оператори можуть перевіряти, не читаючи весь кодовий граф.
Що ви побачите в LangSmith:
Під час роботи над етапом «Що ви побачите» спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Одночасно задокументуйте шлях успішного виконання та шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Робіть контрольні пункти після дорогих кроків. Система відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор намагається знову виконати пізнішу операцію.