初始化agent 消息 实现说明

2 阅读2分钟

核心实现

文件路径: 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 执行起点

关键约束

字段说明
nodeIdStateGraph.START = "__START__"标识这是起始节点的 checkpoint
nextNodeIdRunnableConfig.AGENT_MODEL_NAME = "_AGENT_MODEL_"ReactAgent 入口节点,不能为 null
checkPointIdnull强制走 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();
        // 控制台输出:你的名字是张三。
    }
}