核心实现
文件路径: src/main/java/com/jc/spring/ai/react/brd/util/ThreadInitializer.java
package com.jc.spring.ai.react.brd.util;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.checkpoint.Checkpoint;
import com.jc.spring.ai.react.alibaba.saver.PlainJsonMysqlSaver;
import java.util.Map;
public class ThreadInitializer {
private final PlainJsonMysqlSaver saver;
public ThreadInitializer(PlainJsonMysqlSaver saver) {
this.saver = saver;
}
/**
* 为指定 thread 预置初始状态数据。必须在 stream()/invoke() 之前调用。
*
* @param config 含 threadId 的运行时配置(不含 checkpointId)
* @param seedData 要预置的初始状态数据
* @throws Exception checkpoint 写入失败时抛出
*/
public void initThreadState(RunnableConfig config, Map<String, Object> seedData) throws Exception {
Checkpoint checkpoint = Checkpoint.builder()
.nodeId(StateGraph.START)
.nextNodeId(RunnableConfig.AGENT_MODEL_NAME)
.state(seedData)
.build();
saver.put(RunnableConfig.builder(config).checkPointId(null).build(), checkpoint);
}
}
原理
initThreadState(config, seedData)
│
├─ Checkpoint.builder()
│ .nodeId("__START__")
│ .nextNodeId("_AGENT_MODEL_") ← ReactAgent 入口节点
│ .state(seedData)
│ .build()
│
└─ saver.put(config_no_checkpointId, checkpoint)
│
├─ UPSERT GRAPH_THREAD (thread_name = threadId)
└─ INSERT GRAPH_CHECKPOINT (node_id=__START__, state_data=JSON)
↓
reactAgent.stream(message, config)
└─ getInitialState(message, config)
└─ saver.list(config) → 发现 seed checkpoint
→ 合并 seedData + 新 message → 作为 Agent 执行起点
关键约束
| 字段 | 值 | 说明 |
|---|---|---|
nodeId | StateGraph.START = "__START__" | 标识这是起始节点的 checkpoint |
nextNodeId | RunnableConfig.AGENT_MODEL_NAME = "_AGENT_MODEL_" | ReactAgent 入口节点,不能为 null |
checkPointId | null | 强制走 insert 路径而非 update |
Spring Bean 注册
// BrdToolAgentConfig.java
@Bean
public ThreadInitializer brdToolThreadInitializer(PlainJsonMysqlSaver brdToolMysqlSaver) {
return new ThreadInitializer(brdToolMysqlSaver);
}
使用示例
场景 1:预置业务上下文(brdText)
RunnableConfig config = RunnableConfig.builder()
.threadId("brdtool-supervisor-" + sessionId)
.build();
threadInitializer.initThreadState(config, Map.of(
"brdText", "用户需要一个电商平台...",
"sessionId", sessionId
));
// Agent 执行时从 state 中读取 brdText,无需每次在 message 里拼接
supervisorAgent.stream("请开始分析", config);
场景 2:预置对话历史(messages)
messages key 是 ReactAgent 用于构建 LLM 上下文的标准字段,
预置后模型可直接感知历史对话内容。
threadInitializer.initThreadState(config, Map.of(
"messages", List.of(
new UserMessage("我的名字是张三,语言偏好是中文。"),
new AssistantMessage("好的")
)
));
// 模型在后续对话中可感知"张三"这个名字
reactAgent.stream("你好,请问你知道我的名字吗?", config);
// → 输出:你的名字是张三。
集成测试
文件路径: src/test/java/com/jc/spring/ai/react/brd/util/ThreadInitializerIT.java
@SpringBootTest
class ThreadInitializerIT {
@Autowired
@Qualifier("brdToolThreadInitializer")
ThreadInitializer threadInitializer;
@Autowired
@Qualifier("brdToolMysqlSaver")
PlainJsonMysqlSaver saver;
@Autowired
ChatModel chatModel;
@BeforeEach
void setUp() throws Exception {
simpleAgent = ReactAgent.builder()
.name("simple-qa-agent")
.systemPrompt("你是一个助手,根据用户的问题直接给出简洁回答。")
.model(chatModel)
.saver(saver)
.build();
}
@Test
void initThreadState_agentCanReadSeededState() throws Exception {
RunnableConfig config = RunnableConfig.builder().threadId(currentThreadId).build();
threadInitializer.initThreadState(config, Map.of(
"messages", List.of(
new UserMessage("我的名字是张三,语言偏好是中文。"),
new AssistantMessage("好的")
)
));
// Agent 真实执行,验证能读取预置消息历史
simpleAgent.stream("你好,请问你知道我的名字吗?", config).blockLast();
// 控制台输出:你的名字是张三。
}
}