Адаптазія чатбота з адамовым вузлом у агента, які падтрымваецца MCP, у LangGraph
Створыце прыкладніка LangGraph па штырь слоёў: стан і редуктары, канцы, ціклы з адзінамі інструментамі, ниткі з кэшаванням стану, тры режымы стрімінгу і інструменты, якія выдаюцца через MCP.
Крыніцы: Building AI Applications with LangGraph: From a Basic Chatbot to Tools, Memory, Streaming and MCP
Большасць пратэтыпаў LLM-аў пачынаюцца з адной простай дзеяння: надсылаецца запит, выдруковываецца адпаведная адпаведзь. Такі формат руйнуецца як толькі програме патрабуецца веб-шук, адліквідацыя пра тое, який крок трэба выконаць далей, памяць пра пакульніе крокі чы ўжыванне рэальнага прыгледу праходку ў інтэрфейсе. LangGraph рашае гэтыя проблемы, ператвараючы прабег кантролю на чыстае графічнае представленне спакульнага стану. У гэтым карце мы ператвораем просты чат-бот з аднаго вузла ў агента ReAct, які выкарыстоўвае інструменты, мае памяць у вигляде чэкпоінтаў, падтрымка стрімінгу і інструменты, якія задае адзіны сервер MCP; таксама паказаны деталі, якія часта ствараюць труднасці ў процесе.
Ментальная модель: стан праходзіць через вузлы па рэхбрах
Звычная інтеграцыя LLM адбываецца па простай лініі: з запиту да модэлю і да адказу. LangGraph жа прасіць працаваць у цыкле: об’ект state прайходзіць у граф, nodes яго чытаюць і вяртаюць змены, edges выбіраюць, який вузел будзе працаваць далей, а пасля — апдэйтаваны стан выходзіць на выходзе. Такім чынам, тры основныя складовыя ўсё гэта:
- State: даны, якія перавозяцца между крокамі, напрыклад, історыя паведамленняў.
- Nodes: звычныя функціі, якія выкаанаюць адзін карысны крок і вяртаюць частковы апдэйт.
- Edges: правілы, якія з’ѐднуюць вузелі, уключаючы пачатак і канец работы.
Самы просты граф складаецца з аднаго вузла между точкай входу і выходу:
┌─────────────┐
│ State │
└──────┬──────┘
│
▼
┌─────────────┐
│ Node │
│ chatbot │
└──────┬──────┘
│
▼
┌─────────────┐
│ END │
└─────────────┘
Раздзелэнне „якія даныя існуюць“ ад „што выконваецца ў якім порядку“ ёсць ключовым выборам у дзяржабе. Можна зменіць прабег задачы без вплыву на спосаб зберагчыка даных, а таксама можна пераканаліць стан у будзь-ям моменты без неабяжнай аналізы ланцуга вызоваў функцый.
Чаму граф у змену ланцуга вызоваў
Мінімальны чат-бот – цэлае канале з вядароў на модель і назад:
User
↓
LLM
↓
Response
Тепер дадам рэальныя трэбаванні: пошук у інтэрнете, калькі засобаў, логіка розгалужэння, памяць, этап апраўлення чалавеком, больш за аднаго агента, стрімованы выхід і разныя формы поведзення залежна ад таго, што вяртае модель. Кожны з гэтых элементаў дадае розгалужэнне або цыкл, а вкладзеныя оператары if навакол вызоваў API быстра становяцца складнымі для разумення. Нават самы распашчэты прыклад – „вызвац засоб, якшо модель гэта просіць“ – вже мае цыкл:
┌─────────────┐
│ LLM │
└──────┬──────┘
│
Need a tool?
/ \
Yes No
↓ ↓
Tool Response
│
↓
LLM
Калі логіка працы ператвараецца ў граф, галузі і цыклы стаюць виднымі об’ектамі, якія можна назваць, перапрацаваць і адрасаваць, замест таго каб воні залишаліся ў складзе дапаможных функцый, якія керуюць прайомамі.
Шаг 1: чат-бот з адним вузлом за дапамою StateGraph
Першая версія ўмыслова простая — у яй є адзін вузел, з’ўязаны между пазначкамі пачатку і канца:
START
↓
Chatbot Node
↓
END
Апішчыце стан і яго редюсер
Для чат-бота стан зазвычай патрабуе історыю дыялога. Першая спроба — это TypedDict зі спискам messages:
from typing import TypedDict
class State(TypedDict):
messages: list
Канцэптуальна, гэты список сакрэвае адна за іншайю чалавечыя та ШІ-варыанты розмовы:
State
└── messages
├── HumanMessage
├── AIMessage
├── HumanMessage
└── AIMessage
Хутчэйшы дзеянне тут мае вельмі важлівыя наследкі. У LangGraph кожны ключ у стане можа мяць reducer, які вяршыць, як значэнне, яке вяртаеся з вузла, павінна быць адналожана да існуючага значэння. Простая анотацыя list не мае reducer, таму вузел, який вяртае {"messages": [reply]}, заменяе цэлы список усюдзе, а не дадае да яго. Для історыі чату вы практычна завжды хочаце викорыстоўваць reducer add_messages, які адзначаецца як Annotated[list, add_messages] (альбо спадчынае ад ужо створанага MessagesState). З яго дапамогай вярнутыя прывітанні дадаюцца да списку, а прывітанні з адпаведным ID адначасова апдэйтуюцца. Вышэўшы фрагмент падходзіць як уявны прыклад, але ў рэальнам кодзе трэба викорыстоўваць анотаваную версію.
Стварэнне вузла чат-бота
Узел — это простая функцыя, якая прыме текущы стан і вяртае ключы, якія ёй трэба зменіць. Узел чат-бота перадае історыю дзеянням модэлю і вяртае адпаведную адказ:
def chatbot(state: State):
return {
"messages": [
llm.invoke(state["messages"])
]
}
Чытайце яго знізу даго. Функцыя прабывае ўсі стан:
state
Ён выявляе ўсю паўтарэлую розмову:
state["messages"]
Ён перадае гэтую історыю дзеянням модэлю:
llm.invoke(state["messages"])
І ён укладае адказ модэлю ў слоўнік, у яком ключамі выступаюць поля, якія ён апдэйтуе. Заўважыце, што узел не мутуе стана безпасэчна; ён вяртае частковы апдэйт і дазваляе LangGraph яго з’еднаныць:
{
"messages": [
llm.invoke(state["messages"])
]
}
У відобразэнні потоку дадзенаў узел знаходзіцца межы двумя версіямі таго ж стана:
State
│
│ messages
▼
┌─────────────┐
│ Chatbot │
│ Node │
└──────┬──────┘
│
│ LLM Response
▼
State
Рэўістрацыя узла
Функцыя стае часткай графа, калі яе рэўіструюць у інструменте для стварэння пад адным іменам:
graph_builder.add_node(
"llmchatbot",
chatbot
)
Першым аргументам являецца ідэнтыфікатор вузла, на які будуць сягацца канектамі та логамі:
"llmchatbot"
Другым аргументам являецца функцыя, якая выконваецца кожны раз, калі выкананне доходзіць да гэтага вузла:
chatbot
Шаг 2: падключэнне канектамі та компіляцыя
Вузел сам по сабе ніколі не выконваецца; канекты паведамляюць LangGraph, як туда дася і куды пайсці далей. Для гэтага графа патрэбны два канекты. Першы з’яўляецца між спецыяльным маркерам START та чат-ботам:
graph_builder.add_edge(
START,
"llmchatbot"
)
У форме дыяграмы выкананне заходзіць у граф і ідzie працоўна да вузла:
START
↓
llmchatbot
Другі канект перадае керуванне з чат-бота на END, дзе завершаецца выкананне:
graph_builder.add_edge(
"llmchatbot",
END
)
Гэта адпавядае:
llmchatbot
↓
END
Разам яны утвараюць цэлы, хоць і мінімальны, граф:
START
│
▼
┌────────────────┐
│ llmchatbot │
│ Node │
└────────┬───────┘
│
▼
END
Компіляцыя та запуск
Кампайлінг пераканальвае структуру (на прыклад, што кожны рэг чырвяцца на вядомы вузел) і стварае об’ект, які можна запусціць:
graph = graph_builder.compile()
Яго запускаюць, паслаўшы пачатковы стан. Прыймаюцца звычныя словнікі ролей/контэнту, якія ператвараюцца на об’екты паведамлення LangChain:
result = graph.invoke({
"messages": [
{
"role": "user",
"content": "What is LangGraph?"
}
]
})
Унутрана пачатковы стан прыйходзіць у стане START, вузел чат-бота вызывае модель, адпаведная адказ прыўлекаецца да стану, і запуск завершваецца:
Input State
↓
START
↓
llmchatbot
↓
LLM
↓
Updated State
↓
END
Значэнне, якое вяртае invoke, — это заканчальны стан, таму адказ модэлі ёсць последным элементам у result["messages"].
Шаг 3: атрыманне графа
Кампайльваны граф можа сам сабе атрымліць. У Jupyter notebook-е IPython-авы прыборы для адображэння паказваюць PNG, ствараны з апісу графа на Mermaid:
from IPython.display import Image, display
display(
Image(
graph.get_graph().draw_mermaid_png()
)
)
Для гэтага графа зяўротак ёсць корачы:
┌───────┐
│ START │
└───┬───┘
│
▼
┌──────────────┐
│ llmchatbot │
└──────┬───────┘
│
▼
┌───────┐
│ END │
└───────┘
Значэнне ня ўсё такі зрозумела з трохі ящыкаў, але ён быстра расте. Калі у вас з’яўляюцца умовныя рэшткі, вузлы інструментаў і ціклы, атрыбутаваная дыяграма — гэта найшырэйшы спосаб упэўніцца, што граф, які вы створылі, ёсць тым графам, які вы хацелі створыць. Згадайце, што draw_mermaid_png() за замовчаннем атрыбутаваець рэндарыванне через зовнішню службу Mermaid, таму ёму патрэбны доступы да сеті; якщо гэта ня ўмога, draw_mermaid() вяртае тэкст Mermaid, які можна атрыбутаваць рэндарыванне ў іншым месца.
Шаг 4: стварэнне інструмента пошуку для моделі
Чат-бот можа адпавядаць толькі на тое, што модэль выучыла пад час трэніравання. Запит пра апошнія развіткі у сферы агентных AI вымагае свежых дазвял, таму наступны шаг — ададзець інструмент веб-пошуку. У прыкладзе выкарыстоўваецца Tavily Search, а план дзейнасці выглядае так:
User
↓
LLM
↓
Search required?
↓
Tavily Search
↓
Search Results
↓
LLM
↓
Final Answer
Прыўязка інструментаў не тое ж самае, што ўжыццё іх
Першая частка задачы — прыяжо інструментаў: паведамленне модэлю, якія інструменты існуюць і якія аргументы яны прыймаюць.
llm_with_tools = llm.bind_tools(
[tavily_search]
)
Пасля прыяжа модэль можа адпаведзець структураваным вызвам інструмента („Выклікай tavily_search з гэтым запитам“) у замене на просты текст. Яна сама не можа выконваць гэты вызов. Калісьцо ў вашай прыемлі должна зазначыць вызванне інструмента, запустіць інструмент, дадаць рэзультат у вигляде ToolMessage і адправіць усё назад да модэля. Наданне модэлю доступу да інструмента не робіць яе агентам; гэта робіць асоўны поток выконання. Гэты поток — гэта самае, што задае граф.
Шаг 5: ператворэнне ў цыкл ReAct
ReAct спаўнае разумаванне і дзеяння: модель размышляе пра заданне, вырашае, чырэй трэба дзейсці, выканае дзеянне за дапамою інструмента, абліквіруе рэзультат і продаўжывае. У термінах графа, пряма лінія з ранейшага варыянта:
START
↓
LLM
↓
END
стае графам з тачкай прыняцтва рашэння і шляхам, який ведае назад да модэлі:
START
│
▼
Agent
│
▼
┌─────────┐
│ LLM │
└────┬────┘
│
Need a tool?
/ \
Yes No
│ │
▼ ▼
Tool END
│
▼
Agent
│
▼
LLM
У коде LangGraph гэта рашынка ёсць умовным канектам. Заздалега створаны ToolNode выконвае тое, што міститься ў последнай спрамове ШІ, якая вызываецца інструментам, а tools_condition направляе трафік да гэтага вузла, калі існуюць вызывы інструментам, або да END, калі іх няма. Канект з вузла інструмента назад да моделі заканчоўвае цыкл. Заздалега створаны помачнік create_react_agent складае для вас тую ж структуру; пераканайцеся ў актуальных дасведчэннях LangGraph па яго точным распадзе, адколі помачнікі для стварэння агентаў з часам перайшлі з адных пакетаў у іншыя.
Аналіз адной праходзкі через цыкл
Падазроўваючы, што корыстнік запитае пра найновейшыя новіны пра LangGraph. Адна праходзка цыклу стварае такую последовальнасць спрамоў і крокаў:
HumanMessage
↓
Agent
↓
LLM
↓
"I need current information."
↓
Tool Call
↓
Tavily Search
↓
Search Results
↓
ToolMessage
↓
Agent
↓
LLM
↓
Final Answer
Важным структурным разлікам ёсць цыкл. Першы чат-бот быў простай лініяй:
START → LLM → END
Граф ReAct можа зноў і зноў запускати модель столькі разоў, сколькі це неабходна:
START
↓
Agent
↓
LLM
↓
Tool?
┌───────┐
│ │
Yes No
│ │
↓ ↓
Tool END
│
↓
Agent
Паколькі ціклы, у принцыпе, можу працаваць вечна, LangGraph накладае ліміт на рекурсію практычна ў кожны запуск (яго можна налаштаваць за дапамогою параметра recursion_limit). Якщо модель продовжае запрашваць інструменты, вы побачыце памятку пра адміністрацыйныя проблемы, а не безканецны рахунак.
Шаг 6: запам’ятовуванне размоваў за дапомогою MemorySaver
Дасягнуўшы гэтага моменту, кожны вызов invoke пачынаецца з нуля. Разглянем два крокі: спачатку корыстувальнік представляецца:
User:
My name is Himanshi.
Потым, у окремай запросе, задае дапаможнае пытанне:
User:
What is my name?
Другій запит можа быць адпаведзены толькі тады, калі граф все ўсё мае першы запит. LangGraph рашае гэтыя проблемы за дапамою checkpointer: пасля кожнага крока ён зберагае кадру стану, і наступны запуск у той жа розмове продовжваецца з гэтай кадры. MemorySaver — гэта простейшая реалізацыя:
┌─────────────┐
│ Agent │
└──────┬──────┘
│
▼
┌───────────┐
│ State │
└─────┬─────┘
│
▼
┌─────────────┐
│ MemorySaver │
└─────────────┘
Яго прыўязваюць у час компілявання за дапамою graph_builder.compile(checkpointer=memory). У змену таму, што кожны вызов быў аіслараваным выконаннем, тепер кожны запуск належыць да розмовы, а яго стан зберагаецца між запускамі.
Ниткі раздзеляюць розмовы
Ключ, які з’ѐднае запускі, — цэх ID, який пасылаецца ў настройкі як {"configurable": {"thread_id": "..."}}. Кожная нітка мае свою історыю і свой текущы стан:
Thread 1
├── Message 1
├── Message 2
├── Message 3
└── Current State
Thread 2
├── Message 1
├── Message 2
└── Current State
Такім чынам два корыстувачы, які працуюць у двух потах, ніколі не бачаюць паведамленняў ад іншага:
User A → Thread A → State A
User B → Thread B → State B
Існуюць два важлівыя застережэння. MemorySaver зберагае всё у памяці процэса, таму вся історыя зникае пасля перзапуску і не дзеліцца межа сервернымі экземплярамі; у прымэтных рэалізацыйях викорыстоўваецца стойкі чэкпয়েн্টер, такі як у Postgres чыста SQLite. У няўзрачных выданнях клас таксама экспортуецца як InMemorySaver. Чырпакаваючы, што на самай працэ ўтримвае чэкпয়েн্টер, адзірніце як чэкпয়েн্টеры, запісы і блобы работаюць разам у InMemorySaver.
Шаг 7: стрімаванне стану пад час выконання графа
Усё, што было вышэй, выкарыстоўвала блакуючую вызов:
graph.invoke(...)
Ён чакае, пакуль не завершыцца весь граф, пры тым як вяртаеся. Для агентаў з калькама пасёлкаў гэта можа значыць калькі секунд без нічога на экране, што для корыстнікаў выглядае нешта некоректна. Компільваны граф таксама адкрывае stream, який выдае рэзультаты, калі выконанне ўсё яшчэ трывае.
stream_mode="values": павёлідны стан пасля кожнага пасёлка
Першы режым, які трэба знать:
stream_mode="values"
Калі яго викорыстоўваць у цыкле, ён выдае кадру всего стану пасля кожнага пасёлка:
for chunk in graph.stream(
input_state,
stream_mode="values"
):
print(chunk)
Кожны фрагмент паказвае, як выглядае весь стан у тым моменте, так што можна бачыць, як ён расте праз запуск вузлаў і інструментаў:
START
↓
State Update #1
↓
Node Execution
↓
State Update #2
↓
Tool Execution
↓
State Update #3
↓
END
Гэта зручна, калі стан маўкі і вы проста хочаце адобразыць "пасляпэўнюю версію" ў кожны раз.
stream_mode="updates": толькі тое, што змяніў кожны вузел
Другі режым ёсць:
stream_mode="updates"
Разлік заключаецца у тым, што містіць кожны фрагмент. У аднам режыме вы отрымаеце цэлы стан:
values
У іншам режыме вы отрымаеце толькі дэльту, пазначаную назвай вузла, які ўтворыў яе:
updates
Штормае спосаб прыгадаць аб разліку:
- values адпавядаюць на запытанне "Як выглядае стан зараз?"
- updates адпавядаюць на запытанне "Што самэўсількі зменіў гэты конкретны вузел?"
updates зазвычай є кращым выборам для дэбаггінгу і логавання, таму што ён паказвае, які вузел быў адпавядальны за кожную змену, і дапамагае трываліць фрагментам маленькімі, калі стан вялікі. Жадны з режымоў не передае адзінкавыя токены з модэлю; для выходу токен за токенам існуе адзіны режым потоку messages, і вы можете перадаць список режымоў, каб отрымаць кілька ў адночас.
Шаг 8: дзеяльна прозрачнасць за дапамою astream_events
Знімкі стану і разліки вузлаў паведамляюць, што змянілася, але не все, што сталося. astream_events праходзіць на адны ўровень глыбей і выдае запіс пра кожны момент жыцёвага циклу кожнага компонента пад час запуску:
Graph Execution
│
├── Graph Started
│
├── Node Started
│
├── LLM Started
│
├── LLM Streaming
│
├── LLM Completed
│
├── Tool Started
│
├── Tool Completed
│
├── Node Completed
│
└── Graph Completed
Вы выкарыстоўваете яго асінхронна. Заўважыце, што вызов print у прыведзенам нижэй фрагменте павінен быць увідлеглены ў тэле цікла; як паказана, Python адхіліць гэта з памылкай увідлеглення:
async for event in graph.astream_events(
input_state,
version="v2"
):
print(event)
Кожны запіс несе тип (напрыклад on_chat_model_stream або on_tool_start), назву компонента і яго даны. Гэта робіць струмень запіса корыстным для:
- дэбаггіну і структураванага логавання
- загальнай стежыць за роботай агента
- стэпінгу кожнага вызову LLM і кожнага вызову інструмента
Інжынеры з досвядам у роботе з бэкендам разберуць гэты патэрн: гэта больш супаноўваецца з аналізам распрашчаных запытоў, чым з чытаннем значэння, якое вяртае адна функцыя. Пытанне пераходзіць з "Чы робіў агент сваю работу?" на "Што самэўсёлькі зрабіў агент, у якой спраўе і сколькі часу пацягнула кожная частка?"
Як складаюцца элементы
Якща адмаўляцца назад, кожны крок дадаў адну можлівасць да таго ж карэкту. Усё пачалося з аднаго вызову модэлю:
START
↓
LLM
↓
END
Інструменты дадалі можлівасць двухкрокавага вяселля з зовнішнім светам:
START
↓
LLM
↓
Tool
↓
LLM
↓
END
Функцыя контролю стану дазволіла цыклу запам’ятаваць:
START
↓
Agent
↓
LLM ↔ Tools
↓
Memory / Checkpoint
↓
END
Стрімаванне і запускі дадалі можлівасць паказваць роботу ў інтерфейсе та для монітарынгу:
Graph
↓
Events
↓
UI / Logs / Observability
Ніякі з эых крокаў не вымагалі перапісвання пакульшых; кожны з іх — это новы вузол, рэглі, опцыя кампілявання або вызов методу ў той самы граф.
Крок 9: перанесенне інструментаў за сервер MCP
Дасягнуўшы гэтага моменту, інструменты знаходзіліся ўнутрь прыемкі. Model Context Protocol (MCP) выкарыстоўвае іншы падход: окремы сервер выкладзе інструменты (і рэсурсы), а будзь-які MCP кліент можа іх знайсці і вызваць. Прыемка больш не патрабуе мясца для кожнай рэалізацыі інструмента:
┌──────────────┐
│ MCP Client │
└──────┬───────┘
│
│
▼
┌──────────────┐
│ MCP Server │
└──────┬───────┘
│
┌──────────┼──────────┐
↓ ↓ ↓
add() multiply() weather()
Мінімальны сервер з трыма інструментамі
Дастатнек адноўскагу сервера для навучэння, каб пазнаць архітектуру. Ён выкладзе інструмент для адыску:
add(a, b)
інструмент для мультыплікацыі:
multiply(a, b)
і інструмент для паветра, які прыймае назву горада:
weather(city)
Інструмент для перагляду погоды не выкалывае рэальную службу пагоды. Ён вяртае фіксаваны строк, што прыводзіць увагу да протакола, а не да ключоў API:
"It's sunny in Mumbai."
Галоўная мета — це перадача даных туды-назад, а не прогноз пагоды:
Client
↓
MCP Server
↓
Tool
↓
Result
↓
Client
Інструменты больш не павінны знаходзіцца ў самай прыёмнай аплікацыі
Большая ідея заключаецца у тым, што інструменты можна розмішваць незалежна ад аплікацыяў, якія іх выкарыстоўваюць. Аплікацыя ведае розмову з кліентам MCP, кліент перадае даны протаколам на сервер, а сервер адпавядае за ўсі рэалізацыі:
Application
│
▼
MCP Client
│
MCP Protocol
│
▼
MCP Server
/ | \
/ | \
↓ ↓ ↓
Add Multiply Weather
Гэта дазволяе ствараць серверы інструментоў, якія можна перадаць у павторны выкарыстоўванне. Адна команда можа запускать сервер, який абяўляе цэлую категорію можлівасцей:
Database Tools
File Tools
Git Tools
Search Tools
Weather Tools
Internal APIs
І калькі разных агентаў, кліентоў чату або інтеграцый IDE можна выкарыстоўваць той самы сервер без дублювання коду.
Шаг 10: адналічэнне LangGraph і MCP
Этыя два тэхналагіі рашваюць разныя часткі проблемы. LangGraph караце працэйны план: стан, маршрутызацыю, ціклы, памяць і стрімінг. Стандарт MCP стандартызуе спосаб выклікання інструментаў і рэсурсаў. У сумаваным дизайне вузел інструмента агента супрацоўвае з кліентам MCP замест таго, каб вызываць локальныя функцыі:
User
│
▼
LangGraph Agent
│
▼
LLM
│
Need a tool?
│
▼
MCP Client
│
▼
MCP Server
/ | \
↓ ↓ ↓
Math Search Weather
Агенту не патрэбна інфармацыя пра тое, як реалізавана прадмет. Яму проста трэба ведаць, якія можлівасці з’яўляюцца і як іх вызваць, а сервер MCP займаецца рэштой. У Python пакет langchain-mcp-adapters ўжо давно выступае як стандартны спосаб загрузкі прадметаў MCP як прадметаў LangChain, каб іх можна было прыяўняць да модэлю та викорыстоўваць у ToolNode. Існуючая статыя на стандартызацыі інтэграцыі прадметаў у LangGraph з MCP дэтальней раскрывае гэту інтэграцыю.
Полны ментальны модэль
Якшто з’еднаць усё разам, агент LangGraph выглядае так: вхідны даны корыстніка стаюць станам, вузлы працуюць з імі, модэль выявляе, чырпатак прадмет, рэзультаты прадмета вяртаюцца да модэля, а выконанне завершаецца адпаведным адказам:
USER
│
▼
┌────────────┐
│ LangGraph │
│ Agent │
└──────┬─────┘
│
▼
STATE
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
NODE NODE NODE
│ │ │
└─────────────┼─────────────┘
│
▼
LLM
│
Need a tool?
/ \
Yes No
│ │
▼ ▼
TOOL RESPONSE
│
▼
RESULT
│
└──────► LLM
│
▼
END
Акола гэтаго основнага цыклу знаходзяцца функцыі, якія вы актываўце за патрэбай:
┌──────────────────────────────┐
│ Capabilities │
│ │
│ Memory / Checkpointing │
│ Streaming │
│ Event Tracking │
│ MCP │
│ Tools │
└──────────────────────────────┘
Дзе заканчываецца LangChain і пачынаецца LangGraph
Паколькі LangGraph зазвычай выучваецца пасля LangChain, корыстна раздзеліць іх ролі. LangChain сапраўджае элементы для застосоўкаў на аснове LLM: кэрыі модэляў, паведамленні, інструменты, запросы та структураваны выхід. LangGraph організуе гэтыя элементы ў чыткія, з абстацыйнай станамі рабочыя пралэты. Оони ёсць взаімадопамагальнымі слоямі, а не конкуруючымі варыянтамі, і большасць коду LangGraph выкарыстоўвае компоненты LangChain у своіх вузлах.
Урокі, якія прымаюць значэнне для большых графаў
Калькі прынцыпаў з гэтага маленькага прыклада можна без змян застосавіць да значна большых систем.
Абстацыйны стан — галоўная частка
У замяну перадачы дадзейна через аргументы функцыйяў, кожны вузел чытае і запісвае дадзеныя з аднаго спакульнаго стану, а редукеры вялікія, як чынама з’еднаюцца апдэйты:
State
↓
Node
↓
Updated State
↓
Next Node
Вузелі — гэта елементы роботы
Кожны вузел павінен выканаць адну чытальную задачу, што дапамагае легчэй расклацать і працаваць з складнымі прабегамі роботы:
chatbot node
search node
tool node
validation node
human approval node
Рэшткі коду — гэта адпрацоўка прабега роботы
Рэштка адпавядае на адна запытанне: калі гэты вузел завершыў роботу, куды далей будзе продавацца выкананне? Простыя рэшткі ствараюць последовасці; умовныя рэшткі ствараюць галузі і цыклы.
Дыяграмы кращыя за чытанне коду
Для графа з многамі вузеламі і умовнымі шляхамі, адрасаваны дыяграма перадае інфармацію пра архітэктуру быстрей, чым чытанне коду-будавальніка:
Node → Decision → Tool → Agent → END
Чэкпоінты — гэта не проста історыя чату
Checkpointer зберагае цэлы стан графа, а не толькі паведамленні. Самэўсёле гэта дазволяе ведаць кантакты з участнікамі працы ў кальце, а таксама являецца адной з базаў для агентаў, якія працуюць дзёльна, а таксама для перерываў у роботе з участю чалавека і ўзврату да роботы пасля адказоў.
Стрімінг паслужыць як для корыстувачаў, так і для разработчыкаў
Стрімінг павышае вражанне прыемлівасці, а той жа выхід дапамагае у налагоджэнні і абсерваванні роботы.
Работа агента — це множыцтва операцый
За дапамогою astream_events стае ясна, што адна вызовка агента — цэлы ланцоўкі внутраскладзеных операцый з графа, вузлаў, модэляў і інструментаў:
Graph
↓
Node
↓
LLM
↓
Tool
↓
LLM
↓
Node
↓
Graph
Чым сложнейшая ўстаўка, тым больш вам патрэбна такая прозрачнасць.
MCP ад’еюўае інструменты ад устаўкаў
Інструменты, якія ўпрымкнуты через MCP, можна аддаляць між устаўкамі і развиваць незалежна ад агентаў, якія іх вызываюць.
Куды працавац далей
Гэта асоцыяція адкрывае кальколька напрамоў, якія варта дакласці: умовныя рэшткі і паралельныя галузі, перарывы з участю чалавека, дагоўнае запамяцанне і стойкія пункты перагляду, генераванне з падтрымкай выкарыстоўвання інформацыі з графа, системы з кальколька агентаў, глэбэйшая інтеграцыя MCP, стварэнне можлівасцей для спаглядання та ацэнкі агентаў, развёртанне ў прымэнных умовах, а таксама толерантнасць да бядаў з можлівасцю павтарэння спроб.
Адгукуючыся да гэтага, прыклад АІ-практыкі — гэта не проста модель, якая генеруе тэкст. Це система, створаная навакол моделі, якая може прыміць запит, выдаваць рашэнне, дзеяць, спаглядаць рынак, запамятаваць яго і продаваць далей:
Understand
↓
Decide
↓
Act
↓
Observe
↓
Remember
↓
Continue
Граф — гэта натуральны спосаб выражэння такога цыклу.
Заключэнне
Усё пачалося з самага маленькага можлівага графа:
START → Chatbot → END
і паступова, шар за шарам, ператворылася на систему з інструментамі, цыкламі агентаў, запамяцаннем, стрімінгам і MCP:
┌─────────────┐
│ LLM │
└──────┬──────┘
│
▼
┌─────────────┐
│ Tools │
└──────┬──────┘
│
▼
┌─────────────┐
│ Agent │
└──────┬──────┘
│
┌──────────┼──────────┐
↓ ↓ ↓
Memory Streaming MCP
│ │ │
└──────────┼──────────┘
↓
AI Application
Калі ўсе гэта розглядаць разам, элементы перестаюць выглядзець як спіс і пачынаюць выглядзець як адна архітектура:
- Стан храніць даныя, а таксама, через пункты контролю, память; для списоў паведамленняў викорыстоўваецца функцыя
add_messages. - Вузлы виканаюць роботу і вяртаюць частковыя апдэты.
- Грані визначаюць прайом потоку, а умовныя грані ператвараюць яго на цыкл.
- Модэль забезпечвае логіку та мову; інструменты дазволяюць яй дзейваць у зовнішнім свеце.
- Стрімінг і запускі робяць кожны запуск працэздатным для спостерэння, ад апдэтаў інтэрфейса да лог-даных.
- MCP стандартызуе спосаб прыступу да інструментаў, тады яны можу існаваць паza агентам, які іх выкарыстоўвае.