Initial commit: LangChain evolution tutorial deck (132 slides)
- Cover, TOC, 5 dividers, 3 recap slides - 5 sections (chains, langgraph, deepagents, openswe, ecosystem) - design-system.js with theme tokens + 9 helper functions - research/: timeline + sources + per-tech notes - final-compile.js + merge.js for rebuild pipeline - output/: langchain-evolution.pptx (2.3 MB) + langchain-evolution.pdf (1.1 MB) + 7 sample previews
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
# LangGraph (≥ 1.0)
|
||||
|
||||
## Что это в одном абзаце
|
||||
|
||||
LangGraph — это низкоуровневый оркестрационный фреймворк LangChain Inc. для построения долгоживущих stateful-агентов. В отличие от LangChain (high-level `create_agent`), LangGraph даёт явный контроль над формой графа: узлы (`add_node`), рёбра (`add_edge`), условные переходы (`add_conditional_edges`), checkpointing, human-in-the-loop через `interrupt`, stream-режимы. С версии 1.0 (релиз 22 октября 2025) LangGraph — это production-ready durable runtime: состояние графа персистится автоматически, при падении сервера посреди long-running workflow он восстанавливается ровно с точки остановки. Вдохновлён Pregel и Apache Beam, public interface похож на NetworkX.
|
||||
|
||||
**Метаданные на дату snapshot 2026-06-22:**
|
||||
- GitHub stars: ~35.4k
|
||||
- Latest stable (Python): `langgraph==1.2.6` (от 18.06.2026)
|
||||
- License: MIT
|
||||
- JS-аналог: `@langchain/langgraph` (npm)
|
||||
|
||||
**Источники:**
|
||||
- README `github.com/langchain-ai/langgraph`
|
||||
- https://changelog.langchain.com/announcements/langgraph-1-0-is-now-generally-available
|
||||
- https://blog.langchain.com/langchain-langgraph-1dot0
|
||||
- https://blog.langchain.com/fault-tolerance-in-langgraph
|
||||
|
||||
---
|
||||
|
||||
## Ключевые API (≥ 1.0)
|
||||
|
||||
### Импорты верхнего уровня
|
||||
|
||||
```python
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.checkpoint.sqlite import SqliteSaver
|
||||
from langgraph.checkpoint.postgres import PostgresSaver
|
||||
from langgraph.types import Command, interrupt, Send
|
||||
```
|
||||
|
||||
### Базовый граф с состоянием
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from typing_extensions import TypedDict
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
from langgraph.graph.message import add_messages
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
|
||||
class State(TypedDict):
|
||||
messages: Annotated[list, add_messages]
|
||||
|
||||
def node_a(state: State):
|
||||
return {"messages": [{"role": "assistant", "content": "hi"}]}
|
||||
|
||||
builder = StateGraph(State)
|
||||
builder.add_node("a", node_a)
|
||||
builder.add_edge(START, "a")
|
||||
builder.add_edge("a", END)
|
||||
|
||||
checkpointer = InMemorySaver()
|
||||
graph = builder.compile(checkpointer=checkpointer)
|
||||
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
result = graph.invoke({"messages": []}, config=config)
|
||||
```
|
||||
|
||||
### Условные рёбра
|
||||
|
||||
```python
|
||||
def route(state: State) -> str:
|
||||
return "tool_node" if state.get("needs_tool") else END
|
||||
|
||||
builder.add_conditional_edges("agent", route, {
|
||||
"tool_node": "tool_node",
|
||||
END: END,
|
||||
})
|
||||
```
|
||||
|
||||
### Human-in-the-loop через interrupt
|
||||
|
||||
```python
|
||||
from langgraph.types import interrupt
|
||||
|
||||
def approval_node(state: State):
|
||||
decision = interrupt({"question": "Approve?", "data": state["messages"]})
|
||||
return {"approved": decision == "yes"}
|
||||
```
|
||||
|
||||
### Subgraphs
|
||||
|
||||
```python
|
||||
sub_builder = StateGraph(SubState)
|
||||
sub_builder.add_node("x", x_node)
|
||||
sub_graph = sub_builder.compile()
|
||||
|
||||
# В родительском графе
|
||||
parent_builder.add_node("sub", sub_graph)
|
||||
```
|
||||
|
||||
### Store (долгосрочная память)
|
||||
|
||||
```python
|
||||
from langgraph.store.memory import InMemoryStore
|
||||
|
||||
store = InMemoryStore()
|
||||
graph = builder.compile(checkpointer=checkpointer, store=store)
|
||||
|
||||
# Внутри ноды
|
||||
store.put(("user_123", "prefs"), "key", {"value": "dark"})
|
||||
```
|
||||
|
||||
### Streaming
|
||||
|
||||
```python
|
||||
for mode, chunk in graph.stream({"messages": []}, config, stream_mode=["values", "updates"]):
|
||||
print(mode, chunk)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Что нового в 1.0
|
||||
|
||||
1. **Durable execution (стабилизировано)** — автоматическая персистенция state, восстановление ровно с точки падения. Без своего DB-кода.
|
||||
2. **Built-in persistence как стабильное API** — `checkpointer` теперь контракт, а не фича; Postgres / SQLite / memory — все first-class.
|
||||
3. **Human-in-the-loop first-class** — `interrupt()` стал стабильным API, поддерживает multi-day approval workflows.
|
||||
4. **Graph-based execution как production pattern** — смесь детерминированных узлов и агентных.
|
||||
5. **Deprecation:** `langgraph.prebuilt.create_react_agent` → перенесён в `langchain.agents.create_agent` (LangChain 1.0).
|
||||
6. **API stability promise** — без breaking changes до 2.0.
|
||||
7. **Middleware hooks (в 1.2)** — fault tolerance: retries / timeouts / error handlers.
|
||||
|
||||
---
|
||||
|
||||
## Что нужно раскрыть в презентации
|
||||
|
||||
- **State, Channels, Reducers** — что такое `Annotated[list, add_messages]` и зачем нужен reducer.
|
||||
- **Checkpointing** — `InMemorySaver` для dev, `PostgresSaver` для prod. Что хранится в `StateSnapshot`.
|
||||
- **Threads** — `configurable.thread_id` как ключ сессии.
|
||||
- **Human-in-the-loop через `interrupt`** — не через callback, а через настоящий graph pause.
|
||||
- **Subgraphs** — композитность графов, parent может заходить в subgraph целиком.
|
||||
- **Send / Map-reduce** — параллельные ветки графа.
|
||||
- **Streaming modes** — `values` / `updates` / `events` / `messages` / `custom`.
|
||||
- **Store vs Checkpointer** — checkpoint для сессии, store для cross-session долгосрочной памяти.
|
||||
- **Pregel / Beam inspiration** — почему именно «graph», а не «chain».
|
||||
|
||||
---
|
||||
|
||||
## 8 рабочих примеров кода Python (≥ 1.0)
|
||||
|
||||
### 1. StateGraph с message reducer
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from typing_extensions import TypedDict
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
from langgraph.graph.message import add_messages
|
||||
|
||||
class State(TypedDict):
|
||||
messages: Annotated[list, add_messages]
|
||||
|
||||
def echo(state: State):
|
||||
last = state["messages"][-1]
|
||||
return {"messages": [{"role": "assistant", "content": f"echo: {last.content}"}]}
|
||||
|
||||
g = StateGraph(State)
|
||||
g.add_node("echo", echo)
|
||||
g.add_edge(START, "echo")
|
||||
g.add_edge("echo", END)
|
||||
app = g.compile()
|
||||
print(app.invoke({"messages": [{"role": "user", "content": "hi"}]}))
|
||||
```
|
||||
|
||||
### 2. Checkpointing + thread_id
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
|
||||
checkpointer = InMemorySaver()
|
||||
app = g.compile(checkpointer=checkpointer)
|
||||
|
||||
cfg = {"configurable": {"thread_id": "user-1"}}
|
||||
app.invoke({"messages": [{"role": "user", "content": "hi"}]}, cfg)
|
||||
app.invoke({"messages": [{"role": "user", "content": "again"}]}, cfg)
|
||||
# state["messages"] содержит оба сообщения — thread persistence работает
|
||||
```
|
||||
|
||||
### 3. Conditional edges (роутинг по содержимому)
|
||||
|
||||
```python
|
||||
def route(state: State) -> str:
|
||||
if "tool" in state["messages"][-1].content:
|
||||
return "tool_node"
|
||||
return END
|
||||
|
||||
builder.add_conditional_edges("agent", route, {"tool_node": "tool_node", END: END})
|
||||
```
|
||||
|
||||
### 4. Human-in-the-loop через interrupt
|
||||
|
||||
```python
|
||||
from langgraph.types import interrupt
|
||||
|
||||
def approval(state: State):
|
||||
answer = interrupt({"prompt": "Approve?", "context": state})
|
||||
return {"approved": answer}
|
||||
|
||||
builder.add_node("approval", approval)
|
||||
builder.add_edge(START, "approval")
|
||||
app = builder.compile(checkpointer=InMemorySaver())
|
||||
|
||||
cfg = {"configurable": {"thread_id": "t1"}}
|
||||
# Первый вызов упадёт в interrupt
|
||||
try:
|
||||
app.invoke({}, cfg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Возобновляем с ответом пользователя
|
||||
from langgraph.types import Command
|
||||
result = app.invoke(Command(resume="yes"), cfg)
|
||||
```
|
||||
|
||||
### 5. Send / Map-reduce (параллельные ветки)
|
||||
|
||||
```python
|
||||
from langgraph.types import Send
|
||||
|
||||
def fanout(state: State):
|
||||
return [Send("process", {"item": i}) for i in state["items"]]
|
||||
|
||||
def process(state: dict):
|
||||
return {"results": [state["item"] * 2]}
|
||||
|
||||
builder.add_conditional_edges("start", fanout)
|
||||
builder.add_node("process", process)
|
||||
```
|
||||
|
||||
### 6. Subgraphs
|
||||
|
||||
```python
|
||||
sub = StateGraph(SubState)
|
||||
sub.add_node("inner", inner_fn)
|
||||
sub.add_edge(START, "inner")
|
||||
sub_compiled = sub.compile()
|
||||
|
||||
parent = StateGraph(ParentState)
|
||||
parent.add_node("sub_block", sub_compiled)
|
||||
parent.add_edge(START, "sub_block")
|
||||
```
|
||||
|
||||
### 7. Store для долгосрочной памяти
|
||||
|
||||
```python
|
||||
from langgraph.store.memory import InMemoryStore
|
||||
|
||||
store = InMemoryStore()
|
||||
app = builder.compile(checkpointer=InMemorySaver(), store=store)
|
||||
|
||||
def remember(state: State):
|
||||
store.put(("user-1", "facts"), "name", {"value": "Alice"})
|
||||
return {}
|
||||
|
||||
# В другом turn:
|
||||
def recall(state: State):
|
||||
fact = store.get(("user-1", "facts"), "name")
|
||||
return {"user_name": fact.value["value"]}
|
||||
```
|
||||
|
||||
### 8. Streaming
|
||||
|
||||
```python
|
||||
for event in app.stream({"messages": [{"role": "user", "content": "hi"}]}, stream_mode="values"):
|
||||
print(event)
|
||||
|
||||
# Кастомный streaming через writer
|
||||
def node(state: State):
|
||||
writer = get_stream_writer()
|
||||
writer({"progress": "50%"})
|
||||
return {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TypeScript-аналог
|
||||
|
||||
Все примеры имеют аналог в `@langchain/langgraph`:
|
||||
|
||||
```typescript
|
||||
import { StateGraph, START, END } from "@langchain/langgraph";
|
||||
import { MemorySaver } from "@langchain/langgraph-checkpoint";
|
||||
import { Annotation, messagesStateReducer } from "@langchain/langgraph";
|
||||
|
||||
const State = Annotation.Root({
|
||||
messages: Annotation({ reducer: messagesStateReducer, default: () => [] }),
|
||||
});
|
||||
|
||||
const g = new StateGraph(State)
|
||||
.addNode("echo", (s) => ({ messages: [{ role: "assistant", content: "hi" }] }))
|
||||
.addEdge(START, "echo")
|
||||
.addEdge("echo", END);
|
||||
|
||||
const app = g.compile({ checkpointer: new MemorySaver() });
|
||||
const cfg = { configurable: { thread_id: "t1" } };
|
||||
const result = await app.invoke({ messages: [{ role: "user", content: "hi" }] }, cfg);
|
||||
```
|
||||
|
||||
**Где аналог есть:** весь базовый API (StateGraph, conditional edges, checkpoint, interrupt).
|
||||
**Где нет / отличается:** некоторые специфичные savers (PostgresSaver в JS требует отдельного пакета), `Send` API полностью паритетно.
|
||||
|
||||
---
|
||||
|
||||
## Плюсы и минусы текущей версии (1.x)
|
||||
|
||||
### Плюсы
|
||||
- **Durable execution из коробки** — killer-фича для long-running агентов.
|
||||
- **HITL first-class API** — `interrupt()` вместо костылей с callback-ами.
|
||||
- **Гибкость** — можно построить любую топологию графа (циклы, ветки, параллелизм).
|
||||
- **Прозрачность** — graph inspection в LangGraph Studio.
|
||||
- **Семантическая стабильность** — semver до 2.0.
|
||||
|
||||
### Минусы
|
||||
- **Кривая обучения** — concepts (channels, reducers, send/receive) требуют времени.
|
||||
- **Boilerplate** — базовый граф требует много кода по сравнению с `create_agent`.
|
||||
- **Checkpointing требует инфраструктуры** — для prod нужен Postgres, настройка schema.
|
||||
- **Stream API многослойный** — `stream_mode` (`values` / `updates` / `events` / `messages` / `debug`) сбивает с толку.
|
||||
- **Debugging сложных графов** — без LangSmith Studio тяжело.
|
||||
|
||||
---
|
||||
|
||||
## Заметки для презентации
|
||||
|
||||
- Подчеркнуть: **LangGraph — runtime, не agent-harness**. `create_agent` (LangChain) и `create_deep_agent` (Deep Agents) работают *поверх* LangGraph.
|
||||
- Если есть HITL-сценарий — показать `interrupt()` как killer-фичу 1.0.
|
||||
- Использовать аналогию: **LangGraph = база данных для состояния агента**, LangChain = ORM поверх.
|
||||
- Упомянуть, что LangGraph вдохновлён Pregel (Google) и Apache Beam — это не новость из AI, это паттерн из распределённых систем.
|
||||
- В 1.2 — fault tolerance (retries / timeouts / error handlers) — отдельная тема.
|
||||
Reference in New Issue
Block a user