Appearance
与 LangGraph4j 对比
如果你是从 LangGraph4j(Java 版)过来学 LangGraph(Python 版),本篇就是为你准备的迁移指南。两套库出自同一思想(图、节点、边、状态、检查点),但语言范式差异让 API 长得很不一样。本篇用对照表 + 代码对照片段,帮你快速建立映射。
一、总览:同源不同貌
LangGraph(Python)是官方「第一公民」,新特性先在这里落地;LangGraph4j 是社区移植的 Java 版,概念对齐但 API 风格 Java 化(强类型、Builder、注解)。
| 维度 | LangGraph (Python) | LangGraph4j (Java) |
|---|---|---|
| 语言 | Python 3.10+(动态类型) | Java 17+(强类型) |
| 范式 | 函数式为主,函数一等公民 | OOP 为主,类 + 注解 |
| 状态 | TypedDict + Annotated reducer | StateRecord / AgentState + record |
| 节点 | 普通函数 | Channel / Lambda / 方法引用 |
| 工具 | @tool 装饰器 | @Tool 注解 |
| 异步 | async/await 原生 | CompletableFuture / 响应式流 |
| 构建工具 | pip / uv | Maven / Gradle |
| 生态成熟度 | 高(官方) | 中(社区跟进) |
心态
把 LangGraph4j 当成「带 Java 语法糖的 LangGraph 镜像」,核心概念 1:1,差异主要在「怎么定义」而非「是什么」。
二、核心概念对照表
1. 状态定义
| Python | Java | |
|---|---|---|
| 定义方式 | TypedDict + Annotated | record 实现 AgentState |
| reducer | Annotated[list, add_messages] | 在 StateFactory 注册 reducer |
| 消息字段 | MessagesState 预置 | MessagesState 同名类 |
python
# Python
from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list, add_messages]
count: intjava
// Java (LangGraph4j)
public record State(Map<String, Object> data) implements AgentState {
public List<Message> messages() { return this.value("messages", new ArrayList<>()); }
public int count() { return this.<Integer>value("count"); }
}
// reducer 在 channel 定义时指定2. 节点
| Python | Java | |
|---|---|---|
| 节点定义 | 函数 def node(state): | Channel 或 Lambda |
| 注册 | builder.add_node("name", fn) | builder.addNode("name", channel) |
| 返回 | dict(state 增量) | Map<String, Object> |
python
# Python
def chatbot(state: State) -> dict:
return {"messages": [llm.invoke(state["messages"])]}
builder.add_node("chatbot", chatbot)java
// Java
var chatbot = Channel.<State>of()
.key("messages")
.value(state -> Map.of("messages", llm.invoke(state.messages())))
.build();
builder.addNode("chatbot", chatbot);3. 边与条件边
| Python | Java | |
|---|---|---|
| 普通边 | add_edge("a", "b") | addEdge("a", "b") |
| 条件边 | add_conditional_edges("a", fn, map) | addConditionalEdges("a", fn, map) |
| 起止 | START / END | START / END |
python
# Python
from langgraph.graph import START, END
builder.add_edge(START, "chatbot")
builder.add_conditional_edges("chatbot", tools_condition, "tools")
builder.add_edge("tools", "chatbot")
builder.add_edge("chatbot", END)java
// Java
builder.addEdge(START, "chatbot");
builder.addConditionalEdges("chatbot", toolsCondition, Map.of("tools", "tools"));
builder.addEdge("tools", "chatbot");
builder.addEdge("chatbot", END);4. compile 与执行
| Python | Java | |
|---|---|---|
| 编译 | builder.compile(checkpointer=saver) | builder.compile(checkpointer) |
| 同步执行 | graph.invoke(input, config) | graph.invoke(input, config) |
| 异步执行 | await graph.ainvoke(...) | graph.invoke(...) 内部 CompletableFuture |
| 流式 | graph.stream(..., stream_mode=) | graph.stream(...) |
python
# Python
graph = builder.compile(checkpointer=MemorySaver())
result = await graph.ainvoke(
{"messages": [HumanMessage("hi")]},
config={"configurable": {"thread_id": "t1"}},
)java
// Java
var graph = builder.compile(new MemorySaver());
var result = graph.invoke(
Map.of("messages", List.of(new HumanMessage("hi"))),
ThreadRunnableConfig.builder().threadId("t1").build()
).get();5. Checkpointer
| Python | Java | |
|---|---|---|
| 内存 | MemorySaver | MemorySaver |
| 文件 | SqliteSaver | FileSystemSaver |
| 数据库 | AsyncPostgresSaver | 需自实现 BaseCheckpointSaver |
Java 侧差异
LangGraph4j 的持久化 saver 选择比 Python 少,Postgres 等常需自己实现 BaseCheckpointSaver 接口。Python 这边官方已提供 Postgres/SQLite。
6. 工具定义
| Python | Java | |
|---|---|---|
| 装饰器 | @tool | @Tool |
| 参数 schema | Pydantic BaseModel | record / Bean |
| 描述来源 | docstring | @Tool(description=) 或注解 |
python
# Python
from langchain_core.tools import tool
@tool
def add(a: float, b: float) -> float:
"""两数相加"""
return a + bjava
// Java
@Tool("两数相加")
public double add(double a, double b) {
return a + b;
}7. 预置 ReAct 智能体
| Python | Java | |
|---|---|---|
| API | create_react_agent(llm, tools, ...) | createReactAgent(llm, tools, ...) |
| ToolNode | ToolNode(tools) | 内置工具节点 |
| tools_condition | tools_condition | ToolsCondition |
python
# Python 一行
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(llm, tools, checkpointer=saver)java
// Java
var agent = createReactAgent(llm, tools, saver);8. 消息类型
| Python | Java |
|---|---|
HumanMessage | HumanMessage |
AIMessage | AiMessage |
SystemMessage | SystemMessage |
ToolMessage | ToolExecutionResultMessage |
AIMessageChunk | (流式用 Token 消费) |
9. HITL(人机交互)
| Python | Java | |
|---|---|---|
| 中断 | interrupt(value) | interrupt(value) |
| 恢复 | Command(resume=...) | Command(resume(...)) |
| 更新 state | update_state(config, values) | updateState(config, values) |
10. Studio / 可视化
| Python | Java | |
|---|---|---|
| 官方 Studio | ✅ 原生支持 | ❌ 无,需自建前端 |
| 调试可视化 | LangGraph Studio 桌面/网页 | 社区方案(Mermaid 导出) |
Java 同学的福音
学完 Python 版,你能在 Studio 里可视化调试图——这是 LangGraph4j 没有的体验,强烈建议试试 LangGraph 平台与 Studio。
三、Java 背景者迁移注意点
1. 动态类型
Python 不声明变量类型(运行时推导),Java 同学初期会慌。靠 TypedDict 和 Pydantic 补回「类型契约」:
python
def chatbot(state: State) -> dict: # 参数注解只是提示,运行时不强校验习惯用 mypy 或 IDE(PyCharm/VS Code Pylance)做静态检查,相当于 Java 编译器。
2. 函数是一等公民
Python 节点就是函数,能直接传、能闭包捕获;Java 要包成 Lambda 或 Channel。迁移时把「写一个类」简化成「写一个函数」:
python
# Python:直接函数,无需类
def my_node(state):
return {"count": state["count"] + 1}3. 装饰器
@tool 装饰器本质是「函数包装函数」,等价于 Java 的注解 + 处理器,但更灵活:
python
@tool # 等价 Java @Tool,但运行时即生效,无需注解处理器
def search(query: str) -> str: ...4. async/await
Python 协程比 Java 线程轻,但和 Java 响应式(Mono/Flux)思路相通:
python
# Python 协程
async def node(state):
resp = await llm.ainvoke(state["messages"]) # 异步调
return {"messages": [resp]}相当于 Java 的 Mono.fromCallable + flatMap,但语法更直接。
5. 字典即状态
Java 习惯用强类型 record 封装 state;Python 直接用 TypedDict(本质是带类型提示的 dict)。state["messages"] 等价 Java state.messages(),但前者是字典取值。
四、可直接迁移 vs 需重新理解
✅ 可直接迁移(概念 1:1)
- 图/节点/边/条件边的拓扑思维
- 状态 + reducer 的合并机制
- checkpointer 持久化 + thread_id 会话隔离
- ReAct 智能体的「LLM-工具循环」
- HITL 中断/恢复语义
- stream_mode 的几种模式
🔄 需重新理解(实现不同)
| 概念 | 重新理解点 |
|---|---|
| 异步 | Python async/await 全链路,比 CompletableFuture 更轻;别在 async 里调同步阻塞 |
| 类型 | 动态类型,靠注解 + mypy + IDE 提示补回安全 |
| 工具 | @tool 看 docstring,Java 看注解 description |
| 持久化 | Python 官方 PostgresSaver 开箱即用,Java 常需自实现 |
| 多线程 | Python 受 GIL 影响,并发靠多进程/协程而非多线程 |
| Studio | Python 有官方可视化调试,Java 没有 |
五、同语义 ReAct 代码对照片段
Python 版
python
from typing import Annotated, TypedDict
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
@tool
def add(a: float, b: float) -> float:
"""两数相加"""
return a + b
tools = [add]
llm = ChatOpenAI(model="gpt-4o-mini").bind_tools(tools)
class State(TypedDict):
messages: Annotated[list, add_messages]
def chatbot(state: State):
return {"messages": [llm.invoke(state["messages"])]}
builder = StateGraph(State)
builder.add_node("chatbot", chatbot)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "chatbot")
builder.add_conditional_edges("chatbot", tools_condition, "tools")
builder.add_edge("tools", "chatbot")
builder.add_edge("chatbot", END)
graph = builder.compile(checkpointer=MemorySaver())
import asyncio
result = asyncio.run(graph.ainvoke(
{"messages": [HumanMessage("3 加 5 等于几")]},
config={"configurable": {"thread_id": "t1"}},
))
print(result["messages"][-1].content) # 8Java 版(LangGraph4j)
java
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.data.message.HumanMessage;
import org.bsc.langgraph4j.StateGraph;
import org.bsc.langgraph4j.state.AgentState;
import org.bsc.langgraph4j.state.MemorySaver;
import static org.bsc.langgraph4j.action.AsyncNodeAction.node_async;
import static org.bsc.langgraph4j.action.AsyncEdgeAction.edge_async;
public class State implements AgentState {
public List<Message> messages() { return this.value("messages", new ArrayList<>()); }
}
public class AddService {
@Tool("两数相加")
public double add(double a, double b) { return a + b; }
}
var tools = ToolSpecifications.toolSpecificationsFrom(new AddService());
var llm = OpenAiChatModel.builder().apiKey(key).build();
// 节点
var chatbot = node_async(state -> {
var resp = llm.generate(state.messages(), tools);
return Map.of("messages", resp.content());
});
var toolNode = /* ToolNode 等价物 */;
// 图
var builder = new StateGraph<>(State.class);
builder.addNode("chatbot", chatbot);
builder.addNode("tools", toolNode);
builder.addEdge(START, "chatbot");
builder.addConditionalEdges("chatbot", toolsCondition, Map.of("tools","tools"));
builder.addEdge("tools", "chatbot");
builder.addEdge("chatbot", END);
var graph = builder.compile(new MemorySaver());
var result = graph.invoke(
Map.of("messages", List.of(new HumanMessage("3 加 5 等于几"))),
ThreadRunnableConfig.builder().threadId("t1").build()
).get();
System.out.println(result.value("messages").get(result.value("messages").size()-1));对照可见:拓扑结构完全一致,差异在「节点用函数 vs Channel」「工具用 docstring vs 注解」「异步用 await vs CompletableFuture」。
六、迁移建议
- 先按本教程过一遍 Python 基础(快速入门 → 核心概念),建立 Python 心智模型
- 拿一个你用 LangGraph4j 写过的图,用 Python 重写一遍,对照本篇映射表
- 重点体验 Studio——这是 Java 版没有的红利,调试效率成倍提升
- 生产部署参考服务化部署,FastAPI 对应你的 Spring Boot
七、小结
- 概念层 1:1:图、节点、边、状态、检查点、ReAct、HITL——你已会的都能迁移
- 实现层有差异:动态类型、函数一等公民、装饰器、async——这些是 Python 范式
- Python 版生态更全(官方 Studio、PostgresSaver、LangSmith 深度集成),值得学
- 拿一个旧图重写一遍,是最快的迁移方式
下一篇给出后续学习资源与进阶路线。