LangGraph4j 落地(一)— 图编排骨架与线性管道等价迁移
系列第 17 篇我们刻意不用 LangChain4j
AiServices做主编排。本篇开始 LangGraph4j 演进:先在 Java 侧引入 状态图,但不破坏现有 trace 与 SPI 边界。
写在前面
当客服场景从「单轮问答」走向「查订单 → 判断 → 建工单 → 人工审核」时,线性七步管道 的表达力不够。LangGraph4j 提供了第三条路:图编排 + 显式节点 + checkpoint/interrupt,且与 Spring、LangChain4j 同栈。
你将学到什么
- 为什么选 LangGraph4j 而不是 Python LangGraph sidecar
ai-graph模块结构与ChatGraphState设计CustomerServiceGraph如何把 SPI 包成节点OrchestrationProperties.engine双引擎开关GraphOrchestrationEquivalenceTest怎么做回归
1. 背景:七步管道遇到了什么天花板
原编排入口 AiChatService是固定顺序:
load memory → route → rag? → tool? → build prompt → LLM → save memory
优点:trace 清晰、每步可测、与前端 Agent 面板一一对应。
局限:
| 局限 | 业务影响 |
|---|---|
| 单次工具 | 无法「查订单再建工单」 |
| 路由与主 LLM 分离 | 固定 2 次模型调用 |
| 无条件分支 | 售前/售后/投诉无法走不同子流程 |
| 无 interrupt | 敏感操作无法等人点头 |
LangGraph4j 补的是 编排表达力;LangChain4j 仍只负责 Model Layer(LlmClient / StreamingLlmClient)。
2. 为什么用 LangGraph4j(Java 版)
| 方案 | 优点 | 缺点 |
|---|---|---|
| 继续手写 if-else 管道 | 简单 | 复杂流程不可维护 |
| LangChain4j AiServices | 开发快 | trace 黑盒 |
| Python LangGraph 微服务 | 官方生态全 | 双运行时、契约维护成本高 |
| LangGraph4j | 与 Spring 同栈、支持 interrupt/checkpoint | 需学习 StateGraph API |
本项目为 Java 17 + Spring Boot 3.3 多模块骨架,选用 LangGraph4j 1.8.x(BOM 锁定于 ai-parent)。
3. Phase 1 架构:只换编排层
不变的部分:
- HTTP:
ChatReactiveController(8081) - 门面:
CustomerChatFacade(限流) - SPI:
ChatMemory、KnowledgeRetriever、ToolExecutor、PromptComposer、LlmClient - 对外契约:
ChatTurnTraceResult→ChatTraceResponse→ 前端 Agent 面板
新增模块:ai-graph(图定义、状态、节点、配置)
4. ChatGraphState:图状态与 trace 对齐
ChatGraphState 继承 LangGraph4j AgentState,用 Channel 描述字段合并策略:
- 普通字段:
Channels.base((old, neu) -> neu)覆盖写 executedNodes:Channels.appender追加(用于图轨迹)
核心字段与 trace 映射:
| 状态键 | trace 字段 |
|---|---|
routerDecision |
agentDecision |
ragUsed / ragContext |
RAG 面板 |
toolResult / toolCalls |
Tool 面板 |
prompt |
Prompt 面板 |
answer |
助手回复 |
executedNodes |
Graph 轨迹(Phase 4 前端展示) |
节点原则:薄包装——只读写 state,调用 SPI Bean,不写业务 if-else 泥潭。逻辑集中在 GraphNodes。
5. CustomerServiceGraph:Phase 1 等价图
CustomerServiceGraph 在 Phase 1 等价于线性管道(react-enabled: false 时走 tool_execute_linear):
构建方式(节选):
var graph = new StateGraph<>(ChatGraphState.SCHEMA, ChatGraphState::new)
.addNode("load_memory", node_async(nodes::loadMemory))
.addNode("route", node_async(nodes::route))
// ...
.addEdge(START, "load_memory")
.addEdge("save_memory", END);
CompiledGraph<ChatGraphState> compiled = graph.compile(compileConfig());
invoke(sessionId, message, context) 返回最终 ChatGraphState,由 AiChatService 映射为 ChatTurnTraceResult。
6. 双引擎开关:linear 与 graph 并存
aics:
orchestration:
engine: graph # linear | graph
LinearChatPipeline 保留原七步实现,用于:
- 回滚:生产事故时一行配置切回
linear - 对比测试:同一 fixture 下断言 graph 与 linear 等价
AiChatService 委托逻辑:
if (orchestrationProperties.getEngine() == OrchestrationEngine.GRAPH) {
return fromGraphState(customerServiceGraph.invoke(...));
}
return linearChatPipeline.chatWithTrace(...);
7. 动手验证
7.1 编译与等价性测试
cd ai-customer-service
mvn -pl ai-service -am test -DskipTests=false -Dmaven.test.skip=false
关注:
GraphOrchestrationEquivalenceTest ... OK
AiServiceEvolutionTest ............ OK
GraphOrchestrationEquivalenceTest 断言:answer、ragUsed、toolsUsed、toolResult、prompt 在 linear/graph 下一致。
7.2 运行时切换引擎
ai-reactive-chat/src/main/resources/application.yml:
aics:
orchestration:
engine: graph # 改为 linear 可即时回退
启动聊天服务:
mvn -pl ai-reactive-chat spring-boot:run
curl -s -X POST http://localhost:8081/api/chat \
-H "Content-Type: application/json" \
-d '{"sessionId":"lg-phase1","message":"我的订单123为什么还没发货?"}' | jq .
开发环境 expose-prompt-trace: true 时,响应仍含 agentDecision、ragContext、toolResult、prompt——前端四个 Agent 面板无需改动。
更多推荐




所有评论(0)