在图执行期间,状态需要被序列化(主要用于克隆目的),同时也为跨不同执行持久化状态提供能力。Spring AI Alibaba 目前提供了 Jackson、JDK 两种序列化策略实现。默认使用 Jackson 实现。
在序列化过程中,重点关注如下内容:
- 不依赖不安全的标准序列化框架
- 允许为第三方(非可序列化)类实现序列化
- 尽可能避免类加载问题
- 在序列化过程中管理可空值
自定义序列化器
默认情况下,Graph 使用 Jackson 作为序列化器,并且对几个主流模型厂商的 Message 类型做了兼容。但是对于用户自定义的消息类型,可能无法做到完全兼容,这里有三种常见做法:
1. 为自定义数据类增加 Jackson 注解
为自定义数据类添加 Jackson 注解,提升序列化兼容性。这是最简单直接的方式:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
// 使用 @JsonIgnoreProperties 忽略未知属性
@JsonIgnoreProperties(ignoreUnknown = true)
public class CustomMessage {
private String content;
private String type;
// 使用 @JsonCreator 和 @JsonProperty 指定构造函数参数映射
@JsonCreator
public CustomMessage(
@JsonProperty("content") String content,
@JsonProperty("type") String type) {
this.content = content;
this.type = type;
}
// Getter 和 Setter 方法
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
2. 定制 StateGraph 中的默认 Serializer
通过 StateGraph.getStateSerializer() 获取序列化器,转换为 JacksonSerializer 类型,然后获取 ObjectMapper 进行定制:
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.serializer.StateSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
// 创建 StateGraph(使用默认的 JacksonSerializer)
StateGraph graph = new StateGraph(keyStrategyFactory);
// 获取序列化器并转换为 JacksonSerializer
StateSerializer stateSerializer = graph.getStateSerializer();
if (stateSerializer instanceof StateGraph.JacksonSerializer) {
StateGraph.JacksonSerializer jacksonSerializer =
(StateGraph.JacksonSerializer) stateSerializer;
// 获取 ObjectMapper
ObjectMapper objectMapper = jacksonSerializer.getObjectMapper();
// 定制 ObjectMapper,例如:
// 1. 配置序列化选项
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 2. 注册自定义模块
var module = new SimpleModule();
// module.addSerializer(CustomMessage.class, new CustomMessageSerializer());
// module.addDeserializer(CustomMessage.class, new CustomMessageDeserializer());
objectMapper.registerModule(module);
}
说明:
- StateGraph 默认使用 JacksonSerializer(继承自 SpringAIJacksonStateSerializer)
- 通过 getStateSerializer() 获取序列化器后,可以转换为 JacksonSerializer 类型
- 使用 getObjectMapper() 方法获取 ObjectMapper 进行定制
- 这种方式适合在创建图之后需要动态定制序列化器的场景
3. 为 Graph 提供自定义序列化器(推荐)
可以为 StateGraph 指定任意序列化器,如框架已经提供的 JDK 原生类型 ObjectStateSerializer 或自定义的 Gson 序列化器实现等。
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.serializer.plain_text.jackson.SpringAIJacksonStateSerializer;
import com.alibaba.cloud.ai.graph.state.AgentStateFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.alibaba.cloud.ai.graph.StateGraph;
import java.io.IOException;
// 自定义消息类型
public class CustomMessage {
private String content;
private String type;
// 构造函数、Getter、Setter...
public CustomMessage(String content, String type) {
this.content = content;
this.type = type;
}
public String getContent() { return content; }
public String getType() { return type; }
}
// 自定义序列化器
public class CustomMessageSerializer extends JsonSerializer<CustomMessage> {
@Override
public void serialize(CustomMessage value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
gen.writeStartObject();
gen.writeStringField("content", value.getContent());
gen.writeStringField("type", value.getType());
gen.writeEndObject();
}
}
// 自定义反序列化器
public class CustomMessageDeserializer extends JsonDeserializer<CustomMessage> {
@Override
public CustomMessage deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
String content = null;
String type = null;
p.nextToken(); // 跳过 START_OBJECT
while (p.nextToken() != com.fasterxml.jackson.core.JsonToken.END_OBJECT) {
String fieldName = p.getCurrentName();
p.nextToken();
if ("content".equals(fieldName)) {
content = p.getText();
} else if ("type".equals(fieldName)) {
type = p.getText();
}
}
return new CustomMessage(content, type);
}
}
// 创建自定义序列化器类
class CustomizedSerializer extends SpringAIJacksonStateSerializer {
public CustomizedSerializer() {
super(OverAllState::new);
// 创建 SimpleModule 并注册自定义序列化器/反序列化器
var module = new SimpleModule();
module.addSerializer(CustomMessage.class, new CustomMessageSerializer());
module.addDeserializer(CustomMessage.class, new CustomMessageDeserializer());
// 注册模块到 ObjectMapper
objectMapper.registerModule(module);
// 可以继续定制 ObjectMapper,例如:
// objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
}
}
// 使用自定义序列化器创建 StateGraph
KeyStrategyFactory keyStrategyFactory = () -> new HashMap<>();
StateGraph graph = new StateGraph("demo", keyStrategyFactory, new CustomizedSerializer());
说明:
- SpringAIJacksonStateSerializer 在构造函数中已经注册了 Spring AI 相关的消息类型序列化器
- 继承后可以在构造函数中继续注册自定义类型的序列化器/反序列化器
- 可以通过 objectMapper 字段(继承自父类)访问和定制 ObjectMapper
- 这种方式可以完全控制序列化行为,适合复杂的自定义类型
在 Spring AI Alibaba 中,节点通常是
- 一个函数式接口 ([AsyncNodeAction]),其入参是 OverAllState,您可以使用 [addNode] 方法将这些节点添加到图中。
- 一个函数式接口 ([AsyncNodeActionWithConfig]),其入参是 OverAllState 和 RunnableConfig,您可以使用 [addNode] 方法将这些节点添加到图中。
由于 [AsyncNodeAction] 设计用于与 [CompletableFuture] 一起工作,您可以使用 node_async 静态方法将其适配为更简单的同步场景。
START 节点
// add a normal edge
START 节点是一个特殊节点,表示将用户输入发送到图的节点。引用此节点的主要目的是确定首先应该调用哪些节点。
import static com.alibaba.cloud.ai.graph.StateGraph.START;
END 节点
END 节点是一个特殊节点,表示终端节点。当您想要表示哪些边在完成后没有任何操作时,会引用此节点。
边定义了逻辑如何路由以及图如何决定停止。这是智能体工作方式和不同节点之间如何通信的重要部分。有几种关键类型的边:
- 普通边(Normal Edges):
直接从一个节点到下一个节点。
- 条件边(Conditional Edges):
调用函数来确定接下来要去哪个节点。
- 入口点(Entry Point):
当用户输入到达时首先调用哪个节点。
- 条件入口点(Conditional Entry Point):
调用函数来确定当用户输入到达时首先调用哪个节点。
普通边
如果您总是想从节点 A 到节点 B,可以直接使用 [addEdge] 方法。
import static com.alibaba.cloud.ai.graph.StateGraph.START;
import static com.alibaba.cloud.ai.graph.StateGraph.END;
import static com.alibaba.cloud.ai.graph.action.AsyncEdgeAction.edge_async;
// 添加条件边
graph.addConditionalEdges("nodeA", edge_async(state -> "nodeB"),
Map.of("nodeB", "nodeB", "nodeC", "nodeC"));
您必须提供一个对象,将 routingFunction 的输出映射到下一个节点的名称。
Conditional Edges
若需可选地路由至一个或多个 Node 节点,可使用 addConditionalEdges 方法。该方法接收节点名称及一个函数式接口 AsyncEdgeAction 或 AsyncEdgeActionWithConfig,该接口将作为“路由函数”在节点执行后调用,用来决策下一个节点应该走向哪里。
为同一个节点设置出边
一个节点可以拥有多个出边,这样就形成了节点间的并行关系。如果一个节点有多个出边,那么所有这些目标节点将作为下一个并行执行。关于并行节点,可参考示例目录中的详细文档描述。
本文整理自 Spring AI Alibaba 官方文档《核心库概念指南》:https://java2ai.com/docs/frameworks/graph-core/core/core-library

AI项目博客






