视频版:space.bilibili.com/2012289471 源码:github.com/yinqqm/spri...
基础调用
通过call方法实现基础调用,返回的是AssistantMessage,也就是模型输出的消息。就是普通的调用和接收响应信息。
获取完整状态
基本使用:
java
public void test8() throws GraphRunnerException {
// ★ 关键:通过 Map 传入自定义状态(messages/input 之外的都是自定义状态)
Map<String, Object> inputs = new HashMap<>();
inputs.put("input", "帮我写一首诗"); // 预留关键字:用户输入
inputs.put("messages", Messageutils.convertToMessages("帮我写一首诗"));
inputs.put("custom_key", "这是我在test8中设置的值"); // ★ 自定义状态
inputs.put("user_role", "admin"); // ★ 自定义状态
inputs.put("session_id", "sess_001"); // ★ 自定义状态
inputs.put("request_count", 0); // ★ 自定义状态
ChatModel chatModel = CreateChatClient.createDashScopeChatModel();
ReactAgent getAllState = ReactAgent.builder()
.model(chatModel)
.name("get_all_state")
// .instruction("你是一个助手,请根据用户输入完成任务。")
.build();
Optional<OverAllState> result = getAllState.invoke(inputs);
if (result.isPresent()) {
OverAllState overAllState = result.get();
Optional<Object> messages = overAllState.value("messages");
// 访问自定义状态
Optional<Object> customData = overAllState.value("custom_key");
if (customData.isPresent()) {
System.out.println("获取设置的custom_key:" + customData);
}
System.out.println("完整状态:" + overAllState);
}
}
它的作用:
OverAllState 作为基于图的流水线处理管道中跨节点共享数据的中央容器 。 它支持键值对数据存储,并为每个 key 配置自定义更新策略 (KeyStrategy), 允许灵活的合并或替换逻辑。此类是可序列化的,适用于持久化、检查点或跨节点通信。
⚠️ 警告: 这个类不是线程安全的,在并发场景需要同步。
核心字段:
java
public final class OverAllState implements Serializable {
/** ★ 核心数据存储:所有状态值的实际存放位置 */
private final Map<String, Object> data;
/** ★ 键策略映射:每个 key 对应一个更新策略 */
private final Map<String, KeyStrategy> keyStrategies;
/** ★ 长期记忆存储:用于跨执行周期的持久化 */
private Store store;
/** ★ 默认输入键名 */
public static final String DEFAULT_INPUT_KEY = "input";
/** ★ 删除标记常量 */
public static final Object MARK_FOR_REMOVAL = new Object();
}
实际使用讲解:
实际使用场景:智能客服 + 用户画像注入
场景描述
假设你有一个智能客服 Agent,需要:
- 传入用户基本信息(VIP等级、偏好语言等)
- 传入当前订单号(用于工具查询)
- 在工具中读取这些自定义状态
- 最终从 OverAllState 中验证所有状态
最终回复:
diff
🤖 Agent 最终回复:
尊敬的VIP客户张三您好!
关于您的订单 ORD-20260525-8899 的查询结果如下:
📦 订单状态:已发货
🚚 物流公司:顺丰速运 SF1234567890
📅 预计送达:2026-05-27
商品明细:
- MacBook Pro 16寸 x 1台 = ¥19,999
使用配置
基本使用:
它是每次agent调用call方法执行时传入的。表示它是在agent真正执行的时候给定的参数。
ini
AssistantMessage response = agent.call("你的问题", runnableConfig);
通过RunnableConfig来实现的,它的主要作用是:
会话记忆, 为不同的用户区分上下文,通过threadId来实现的。
java
/**
* 使用配置,测试threadId实现不同用户之间使用不同的上下文信息
*/
@Test
public void test9() throws GraphRunnerException {
ChatModel chatModel = CreateChatClient.createDashScopeChatModel();
ReactAgent agent = ReactAgent.builder()
.name("runnable_config")
.model(chatModel)
.saver(new MemorySaver())
.build();
//通过RunnableConfig 传递运行时配置
String threadId123 = "thread_123";
String threadId456 = "thread_456";
RunnableConfig runnableConfig123 = RunnableConfig.builder()
.threadId(threadId123)
.build();
RunnableConfig runnableConfig456 = RunnableConfig.builder()
.threadId(threadId456)
.build();
agent.call("我叫123",runnableConfig123);
agent.call("我叫456",runnableConfig456);
AssistantMessage response123 = agent.call("我叫什么?",runnableConfig123);
System.out.println("runnableConfig123:"+response123.getText());
AssistantMessage response456 = agent.call("我叫什么?",runnableConfig456);
System.out.println("runnableConfig456:"+response456.getText());
}
存放不可变数据
通过metadata 来是实现。它是在运行期是不可变的。可以为Interceptor和tool提供参数。
在interceptor中获取:
java
public void test10() throws GraphRunnerException {
ChatModel chatModel = CreateChatClient.createDashScopeChatModel();
ReactAgent agent = ReactAgent.builder()
.name("model_interceptor_agent")
.model(chatModel)
.interceptors(new DynamicPromptInterceptor()).build();
//通过runnableConfig指定配置信息
RunnableConfig runnableConfig = RunnableConfig.builder().addMetadata("user_role", "expert").build();
AssistantMessage assistantMessage = agent.call("介绍一下Spring boot的自动注入原理。", runnableConfig);
System.out.println(assistantMessage.getText());
}
python
public class DynamicPromptInterceptor extends ModelInterceptor {
@Override
public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
// 基于上下文构建动态 system prompt
String userRole = (String) request.getContext().getOrDefault("user_role", "default");
String dynamicPrompt = switch (userRole) {
case "expert" -> """
你正在与技术专家对话。
- 使用专业术语
- 深入技术细节""";
case "beginner" -> """
你正在与初学者对话。
- 使用简单语言
- 解释基础概念""";
default -> "你是一个专业的助手,保持友好和专业。";
};
SystemMessage enhancedSystemMessage;
if (request.getSystemMessage() == null) {
enhancedSystemMessage = new SystemMessage(dynamicPrompt);
} else {
enhancedSystemMessage = new SystemMessage(request.getSystemMessage().getText() + dynamicPrompt);
}
ModelRequest modified = ModelRequest.builder(request)
.systemMessage(enhancedSystemMessage)
.build();
return handler.call(modified);
}
在Tool中获取
java
public void test11() throws GraphRunnerException {
//创建tool 类
ToolCallback searchTool = FunctionToolCallback.
builder("search", new SearchTool()).description("通过给定的参数查询线上新闻并返回结果") //定义工具描述,提供给模型的使用指南
.inputType(SearchToolInput.class).build();
ChatModel chatModel = CreateChatClient.createDashScopeChatModel();
ReactAgent metaDataTool = ReactAgent.builder()
.name("meta_data_tool")
.model(chatModel)
.tools(searchTool)
.build();
//创建RunnableConfig
RunnableConfig runnableConfig = RunnableConfig.builder()
.addMetadata("call_tool", "yes")
.build();
AssistantMessage assistantMessage = metaDataTool.call("今天发生了什么新闻", runnableConfig);
System.out.println(assistantMessage.getText());
}
SearchTool的定义
java
public class SearchTool implements BiFunction<SearchToolInput, ToolContext, String> {
@Override
public String apply(SearchToolInput query, ToolContext toolContext) {
String callTool = (String) toolContext.getContext().getOrDefault("call_tool", "N");
System.out.println("get data from mateData:"+callTool);
//mock 搜索web
return """
习近平就推动哲学社会科学高质量发展作出重要指示
习近平同美国总统特朗普在中南海小范围会晤
【微镜头·中美元首在北京举行会谈】"让2026年成为中美关系继往开来的历史性、标志性年份"
武汉票价最贵火车发车:20999元起
""";
}
}
总结
| OverAllState | RunnableConfig | |
|---|---|---|
| 定义 | 全局状态,业务数据 | 运行时配置,系统的环境配置信息 |
| 包含的数据 | input:用户的提问message:模型响应的结果其他自定义的key:用户自定义保存的信息 | threadId:区分会话metadata:在运行时不可变,可以用来配置系统变量和环境参数等。 |
| 获取和使用 | 在Hooks中获取,后面会讲解Hooks的使用。 | 在interceptor中,通过ModelRequest.getContext().get()或getOrDefault()获取。在Tool中,通过ToolContext.getContext().get()或getOrDefault()获取。 |
| 可变性 | 可以变的,每个节点都可以读取它,并且它的key有对应的update strategy 更新策略。 | 通常只读,不可变。 |