Створыце маршрутаўчык з калькольнікаю агентаў за дапамогою LangGraph і агента-надзірніка.
Як разліцаваць аднаго перанасытанага агента на спецыялістаў з камандавальнікам у LangGraph, выкарыстоўваючы готавыя дапаможнікі і ручны StateGraph, а таксама прыметы дзеяння і падазры ў цікле.
Крыніцы: Build a Multi-Agent Router with LangGraph in 30 Minutes
Як толькі агента разам з рэальным продуктом, ён быстра пачынае выканаць занадта много задач. Калі прабаваць паказваць аднаго агента для адказвання на запыткі клёянтаў, назірання за замовленнямі і выплатаў грошаў, ён будзе сярэднім у всіх трох аспектах. Яго інструкцыі прыводзяць да 2000-словага клубка правілаў на кшталт „калі корыстувач запытае пра X, трэба зрабіць Y, але якшо таксама і пра Z, тады...“. Рашэнням ня ў тым, каб стварыць хутчэйшага агента; трэба больш, меньшых агентоў плюс аднаго керавальніка, які распрацоўвае запыткі между ўсімі. Гэта, па-вялікаму, найбольш распространены ў настоўчы час патэрн выкарыстання агентных AI, і працоўная версія такога рашэння помешчаецца ў короткай програме LangGraph.
Проблемы з адным велікім агентам
Спадзяйце, што вы ствараеце асістэнта, які адпавядае на загальныя запытанні, выконвае арыметычныя расчытанні і шукае актуальную інфармацыю ў Інтэрнэце. Пахаманы падход — адна ейніца, адны вялікі запыт і всі засобы прыўязаны да яго. Калі колькісць засобаў і інструкцый зростае, мадэль становіцца непакойная ў выборы засобу, а яе логіка становіцца менш чыстай. Адлагоджэнне ператвараецца на кошмар, таму што немагчыма з’ясавіць, чаму ён выбраў неправільны засоб, калі усё знаходзіцца ў аднам запытанні.
Рашэнне — раздзеліць абавясанні: адна ейніца для матэматыкі, адная для даследжэнняў, і керавальнік, які ёсць проста маршрутарам і вяршыць выбіранне таго спецыяліста, які будзе займацца кожной часткаю запытання.
Шаблон керавальніка
Уявіць собі центр обслуговувань кліентыв. Ві тэлефонуеце з пітанням, але неспрамовуюцца безпосерэдно до експерта. Спочатку слухаецьфоністка, вірышуе, чы потрэбуеце ві бюрокофтынг, тэхнічноі підтрымкі чы продажыв, і перэдае вас. Сам фонд ніколі не відповідае на запітання. Яго едыне задачы — обраты, хто мае відповісты.
Візуально гэта віглядае так:
┌─────────────┐
│ User │
│ Request │
└──────┬──────┘
│
▼
┌───────────────┐
│ Supervisor │
│ (routes task)│
└───────┬───────┘
┌──────┴──────┐
▼ ▼
┌────────────┐ ┌────────────┐
│ Research │ │ Math │
│ Agent │ │ Agent │
└──────┬─────┘ └──────┬─────┘
│ │
└──────┬───────┘
▼
┌───────────────┐
│ Supervisor │
│ (final answer)│
└───────────────┘
Надзірнік берэ задачы, перадае яго правам спецыялісту, апроцэсуе рэзультат і тады вырашае, чы роўна ўжо раз праадрасаваць задачы чы даць корыстніку фінальную адпаведзь.
Рэкамендаваны спосаб стварэння, па кроках
Тое, што варта ведаць пры напісанні коду: каманда LangChain зараз радзіць ствараць патэрн надзірніка безпосерэдна з выклікання інструментаў, а не толькі на базе старэй бібліятэкі-памагчыцы langgraph-supervisor, таму што выклікання інструментаў дае большую контроль над канструюванням кантэкста. Ёсць швыдкая ужо створаная версія, а пасля — ручная, ўбачлівае, што відбываецца пад час експлуатацыі.
Крок 1: інсталюйце пакеты
pip install langgraph langchain-openai langgraph-supervisor
Шаг 2: стварэнне спецыялістаў
Пачніце з імпорту:
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
Першы спецыяліст займаецца матэматыкай, а яе інструментамі ўсьмо два маленькія функцыі. Спачатку выконуецца аднаўленне:
# Specialist 1: handles math
def add(a: float, b: float) -> float:
"""Add two numbers together."""
return a + b
Потым умножэнне:
def multiply(a: float, b: float) -> float:
"""Multiply two numbers together."""
return a * b
Загульціюйце іх у агента ReAct з іменем і заповедзю, якая обмежвае яго ролю:
math_agent = create_react_agent(
model="openai:gpt-4o",
tools=[add, multiply],
name="math_expert",
prompt="You are a math specialist. Solve numeric problems precisely."
)
Другі спецыялісты здзейснююць даследаванні. Аднам з іхніх інструментаў ёсць функцыя пошуку, якая паказана тут:
# Specialist 2: handles research
def web_search(query: str) -> str:
"""Search the web for current information."""
# In production, replace this with a real search API call
return f"Search results for '{query}': [your real search integration here]"
Таксама загуліце гэта ў агента:
research_agent = create_react_agent(
model="openai:gpt-4o",
tools=[web_search],
name="research_expert",
prompt="You are a research specialist. Find accurate, current information."
)
Кожны агент мае роўна адну задачу і толькі тыя засобы, якія яе выкананню неабходны, таму немае плутання і некальканая інструкцыя. У прыкладах выкарыстоўваныя мадэлі OpenAI, хоць такі безкоштовны альтэрнатыўны варыянт, як Groq або OpenRouter, таксама падойдзе.
Шаг 3: стварэнне супервізора
Імпортуйце дапаможнік:
from langgraph_supervisor import create_supervisor
Tады дадзець дваякіх спецаў, модэль і падказваць як канфыяклівыя, калі вжываць кожны з яўных:
workflow = create_supervisor(
[research_agent, math_agent],
model=ChatOpenAI(model="gpt-4o"),
prompt=(
"You are a team supervisor managing a research expert and a math expert. "
"For questions requiring current information or web lookups, use research_expert. "
"For anything involving calculation, use math_expert. "
"Route each request to exactly the right specialist."
)
)
Скомпілюйце рабочы працэс у запускаемыя прыкладніцу:
app = workflow.compile()
Шаг 4: запуск
Адрасуйце задачу з матэматыкі:
result = app.invoke({
"messages": [
{"role": "user", "content": "What's 1,847 multiplied by 293?"}
]
})
Івадзецькі словы:
print(result["messages"][-1].content)
Гэта тое, што адзначаецца ўнутршняя частка прылады:
- Надзорнік чытае запіт.
- Ён бачыць вырахунак, а не пытанне для даследжэння.
- Ён перадае задачу
math_expert. math_expertвырахоўвае адпаведны адказ за дапамогою свайго інструментаmultiply.- Надзорнік знову берае кантроль і вяртае адказ.
Для пытанняў даследжэння, такіх як ныякія паказчыкі колькасці працавальнікаў у вялікіх тэхналогічных компаніях, той самы надзорнік перадае ўпрабавку research_expert. Код застаецца ідэнтычным, а зменшыцца толькі рашэнне пра маршрутазаванне, і LLM сама прыме гэта рашэння.
Што на самай працы ўпрабавкі
Што стосуецца гэтай часткі, яна усуняе адчуванне магіі. Супервайзар не працюе праз які-небудзь суперашчытны механізм LangGraph; ён віконвае свае задачы за дапамою вызоваў інструментаў, так сама як і будзь-яй звычайны агент, які вікорыстоўвае інструменты. У тле функцыя create_supervisor стварае інструменты для перадачы задач кожнаму спеціялісту, з такімі назвамі, як transfer_to_research_expert і transfer_to_math_expert. Мадэль супервайзара спрыяе гэтыя інструменты як можлівасці для вызова, не адрознюючы іх ад, напрыклад, запітаў пра пагоду; таму, калі ён вырашывае, што патрэбны дальнейшыя даследжэння, ён проста вызывае функцыю перадачы. Рэзультат ўтымчас цікавы: супервайзар ужо не ўяўляе сабой новую концэпцыю. Гэта проста звычайны цыкл агента, у якім дэякія з інструментаў ёсць іншымі агентамі, а не вызывамі API.
Стварэнне яго вручную
Калі вы хочаце цалковы кантроль над маршрутызацыяй, што рэкамендуецца празь час пераходу да прымэтнага выкарыстоўвання, то гэта ўсноўная версія, створаная на базе неапранутага StateGraph. Пачніце з імпортаў:
from typing import Annotated, Sequence, TypedDict
import operator
from langchain_core.messages import BaseMessage
from langgraph.graph import StateGraph, END
З’явіце спадзейны стан, які храніць спіс паведамленняў і назву наступнага вузла:
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
next: str
Блок керування дывіцца на найновішэ повідомлення:
def supervisor_node(state: AgentState):
"""Decide which specialist should act next."""
last_message = state["messages"][-1].content
У рэальных умовах рашэнне будзе прынятыя на адказе LLM. Чырвоны ў выпадку тут скарацаны да простых пераглядоў ключоўых слоў, тады логіка направлення застаецца відна:
# In production, this decision comes from an LLM call —
# simplified here to show the routing logic clearly
if any(word in last_message.lower() for word in ["calculate", "multiply", "add", "sum"]):
return {"next": "math_agent"}
else:
return {"next": "research_agent"}
Кожны спецыяліст атрымляе вузел, які запускае яго агента, а пасля вяртае кантроль назад супервізору. Спачатку матэматыка:
def math_node(state: AgentState):
result = math_agent.invoke(state)
return {"messages": [result], "next": "supervisor"}
Потым — даследжэнне:
def research_node(state: AgentState):
result = research_agent.invoke(state)
return {"messages": [result], "next": "supervisor"}
Далей з’ѐднайце граф, зарэгіструаваўшы вузлы:
# Wire the graph together
workflow = StateGraph(AgentState)
workflow.add_node("supervisor", supervisor_node)
workflow.add_node("math_agent", math_node)
workflow.add_node("research_agent", research_node)
Установіце точку запуску:
workflow.set_entry_point("supervisor")
Дадзіце умовныя рэшткі, якія вылічваюцца на адказ адпраўніка:
workflow.add_conditional_edges(
"supervisor",
lambda state: state["next"],
{"math_agent": "math_agent", "research_agent": "research_agent", "end": END}
)
Пасля гэтага адразу падаць кожнага спецыяліста да надзірніка:
workflow.add_edge("math_agent", "supervisor")
workflow.add_edge("research_agent", "supervisor")
І складаць:
app = workflow.compile()
Гэта більш дэталізована версія, але нічого не прыховуецца. Ві можэце бачыты, як працюе сістэма, як прыймаюцца кожні рышэння шчодо маршрутызаціі, а таксама дэ можна додаты власну логіку – напрыклад, функціі журналування, захісні механізмі чы умові завершэння працы.
Вібір структуры даных, які найбільшэ ускладнюе працу з часом
Поперэджэння з практыкі вікорыстання: схема даных ёсць найскладнішою частыною, яку пізнішэ важко змініты. Існуе спокуса ужо на початку помістыты всі можліві поля у одын плоскій словнік, які бі охоплював повідомлення, кантэкст корыстувача і проміжні рэзультаты. Гэта підходыць для сістэм з двома агентамі. Однак пры вікорыстанні восьмі чы дэсяты вузлів, які всі разом чытаюць і запісуюць однаковій спільній стан, гэта стае проблематычнім, оскількі перэвірка тыпів у LangGraph ёсць суворою, а рэдагування перэвантажэного стану вімагае змін у кожному вузлі. Тож необхідно з самого початку рэтэльно сплануваты схему даных, навіць для сістэмі з двома агентамі. Візначце, што саме кожэн агент дыйсно потрэбуе чытаты і запісуваты, а все іншэ залішце поза спільнім станом.
Такая проблема, з якой вы станеце сустрэчыцца
Сістэмы-надзірнікі, якія выходзяць у працю, часта практыкуюць такі патэрн. Уявіце, што надзірнік дава задачэ спецыялісту. Спецыяліст яго выкананае, але надзірнік судзіць, што рэзультат недастатковы, і вяртае яго назад. Спецыяліст вносіць правкі, і новая версія насправды стае ўжо гorsай, чым першая. Надзірнік заўсёды незадоволены і зноў вяртае яго, і калі вы не захавацеся ад гэтага, гэта можа продовжвацца несканчатна.
Рэшэнняя — гэта ліміт колькасці разоў, калі надзірнік вяртае заданне, што можа быць простым лічыльнікам у стане, а таксама чыткай, дэтэрміністычной умовай завершэння. Мненне LLM пра тое, чы рэзультат дастаткова хорашы, не ўсегда являецца надзійным едынственным крытэрыям для завершэння.
Калі не варта яго выкарыстоўваць
Будзьце чыстыя ў пытанні падходжання. Калі задача складаецца з аднаго запрашэння інфармацыі па ўзнакамі, а пасля — з аднаго стварэння адпаведнай адпаведзі, як у звычным фармаце RAG для запытань і адпаведзей, тады наявнасць керуючага агента ёсць зайвай. Дадатковая праця з кераваннем станамі і маршрутызацыяй не прыносіць нічога, калі на самай працэ не трэба прымкнуць жадных рашэнняў па маршрутызацыі. Вжывайце патэрн керуючага агента тады, калі у вас ёсць справжнія, адзінаковыя спецыялісты і рэальны выбор маршруту, а не прыменяйце яго як стандарт.
Падсумак
- Адзін перанасытаны агент перастае эфектывна, калі складнасць зрастае.
- Керуючы агент — гэта агент-маршрутызатор, які дэлегуе задачы спецыялістам.
create_supervisor— гэта шырока, ужо створаная маршрутная схема.- Пад час работы перадачы інформацыі адбываюцца як звычныя вызовы інструментаў.
- Ручны
StateGraphдае повны контроль над маршрутызацыяй. - Два урокі з практыкі: з самага пачатку разрабатывайце схему станоў і обмежывайце цыклы перазначэння.