Що таке graph engineering: вузли, ребра й LangGraph на практиці
Чи це щось нове? Розбираємо prompt, context, harness і loop engineering, що робить систему графом, і приклад агента на LangGraph з 11 вузлів.
Що кілька місяців у ШІ-розробці з'являється новий ярлик із "-engineering", і останній із них — graph engineering. Термін широко розійшовся після допису Пітера Штайнбергера, творця відкритого автономного агента OpenClaw, який тепер працює в OpenAI, тож він потрапив у дуже багато стрічок. Розумна реакція — запитати, чи називає він щось нове, чи це просто чергове модне слово.
Коротка відповідь: здебільшого це нова назва того, що творці агентів уже роблять. А тепер докладніше.
Коротка історія ярликів
Галузь уже пережила кілька хвиль назв.
Першим був prompt engineering. Це вміння писати промпти настільки точно й ясно, щоб модель зрозуміла, що вам потрібно.
Потім з'явився context engineering, коли самого промпту стало замало, щоб передати все, що потрібно агентові перед початком задачі. Ви додаєте більше матеріалів, наприклад скриншоти, markdown-файли й код, щоб агент зрозумів і ваш намір, і ваше оточення.
Harness engineering, мабуть, став найбільшим кроком. Замість того щоб просто передавати агентові інформацію, ви будуєте довкола нього середовище: деталі проєкту, доступні йому інструменти, пам'ять про попередню роботу, а також правила й обмежувачі, які тримають його в межах.
Далі йде loop engineering. Ви кажете агентові продовжувати ітерації над задачею, доки він не досягне критерію успіху. Типовий приклад — сумаризатор, якого просять написати звіт на певну тему, а потім раз у раз доопрацьовувати власну чернетку до планки якості, яку він перевіряє сам, замість віддавати її окремому кроку ревю.
Сенс у тому, щоб скоротити власну участь, передавши агентові етап перевірки. Ви більше не переглядаєте кожну ітерацію й не даєте зворотного зв'язку. Агент сам оцінює поточний результат за критеріями й коригує свій підхід. Є й зворотний бік: агент, залишений без нагляду, може піти врозтіч і спалити ваші токени. Та все ж, якщо ідеал — система без людини в циклі, це крок до нього.
Більшість розробників агентів робили так задовго до появи ярлика. Нова лексика, а не принцип. Graph engineering іде тією самою схемою.
Що робить систему графом
Граф визначають три складники.
- Вузли. Вузол — це окрема одиниця обчислень з однією зрозумілою задачею: функція, яка приймає поточний стан і повертає оновлення. У LangGraph вузол реєструється через
graph.add_node(name, fn). - Ребра. Ребро передає керування від одного вузла до іншого. У LangGraph
graph.add_edge(a, b)означає, що вузол b може запуститися, щойно завершиться вузол a. Напрямок важливий: a веде до b, а не навпаки. Бувають і умовні ребра. Зgraph.add_conditional_edge(a, func)функція виконується після вузла a й обирає, який вузол піде наступним. - Форма. Вузли й ребра разом утворюють візерунок, і ця структура проявляється, лише коли дивишся на пов'язане ціле.
Graph engineering на практиці
Щоб відчути підхід, візьмімо агента на LangGraph, який обирає місце зустрічі для компанії з двох–п'яти людей, що живуть далеко одне від одного й сперечаються, який район підходить. Він зважає на реальні чинники, як-от час у дорозі й кількість пересадок на автобусах для кожного, а не просто бере середину мапи.
Граф складається з одинадцяти вузлів, і кожен робить щось одне. Вони звужують зону пошуку, перевіряють доступність для кожної людини, відбирають райони, шукають заклади, оцінюють їх і наприкінці перевіряють результат. Умовних ребер немає, тож на папері структура лінійна: кожен крок веде до наступного, без розгалужень.
Код на LangGraph, який її задає, простий:
# Initialize the graph with our shared state schema.
graph = StateGraph(MeetingState)
graph.add_node(”node0_search_area”, search_area_node)
# list the nodes
# register one reachability node per possible party member
for index, node in enumerate(REACHABILITY_NODES):
graph.add_node(f”reach_person{index + 1}”, node)
graph.add_node(”nodeC_shortlist”, shortlist_node)
graph.add_node(”nodeD_spots”, spot_finder_node)
graph.add_node(”nodeE_score”, scorer_node)
graph.add_node(”nodeF_review”, reviewer_node)
# list the edges
graph.add_edge(START, “node0_search_area”)
for index in range(len(REACHABILITY_NODES)):
graph.add_edge(”node0_search_area”, f”reach_person{index + 1}”)
graph.add_edge(f”reach_person{index + 1}”, “nodeC_shortlist”)
graph.add_edge(”nodeC_shortlist”, “nodeD_spots”)
graph.add_edge(”nodeD_spots”, “nodeE_score”)
graph.add_edge(”nodeE_score”, “nodeF_review”)
graph.add_edge(”nodeF_review”, END)
# Compile into a runnable graph.
compiled = graph.compile() Engineering w/ a Project
Це й є весь граф, і читається він як звичайний пайплайн.
Що дає така структура
Навіть лінійний граф має практичну користь. Кожен вузол читає й пише в один спільний об'єкт стану, тож видно, що саме змінив кожен крок. Крок, що впав, легко ізолювати й перезапустити, бо це невелика функція з визначеним входом. Можна замінити чи покращити один вузол, скажімо оцінювання закладів, не чіпаючи решти. А коли знадобиться розгалуження чи цикл повторних спроб, додати умовне ребро — невелика зміна, а не переписування.
Нічого екзотичного в цьому немає. Це звичайне проєктування ПЗ, застосоване до робочих процесів агентів, тому ярлик звучить драматичніше за саму ідею.
То що ж це?
Graph engineering — не нова концепція. Це свіжа назва підходу, який став звичним у міру того, як дедалі більше людей будують агентів. Якщо ви його ще не пробували, спробуйте на LangGraph чи на будь-якому фреймворку, який вам уже до вподоби. Ярлик важить менше, ніж звичка: розбивайте роботу на невеликі кроки з однією задачею, робіть зв'язки між ними явними й тримайте спільний стан зрозумілим.