Spring AI Alibaba 1.x 系列【8】Agent 执行结果:NodeOutput
文章目录1. 概述2. NodeOutput2.1 核心属性2.1.1 node2.1.2 agent2.1.3 state2.1.4 tokenUsage2.1.5 subGraph2.2 核心方法3. StreamingOutput4. StateSnapshot5. InterruptionMetadata6. 演示案例6.1 监控完整执行过程6.2 只提取 AI 消息6.3 统计 Token 消耗信息1. 概述NodeOutput从字面上就很好理解图中每个节点执行后的输出包装器。核心职责节点标识标记输出来自哪个节点状态携带传递图的执行状态流式支持支持流式和非流式两种模式子图标识区分父图和子图的输出对象结构FluxNodeOutput└──NodeOutput(节点输出基类)├── node:String节点标识(START/END/agent_model/agent_tool/hook_xxx)├── agent:StringAgent名称 ├── state:OverAllState节点状态 ├── tokenUsage:UsageToken使用量 └── subGraph:boolean是否子图输出类继承体系StreamingOutputT流式输出用于Agent执行过程中的消息流StateSnapshot状态快照用于checkpoint恢复InterruptionMetadata中断元数据用于human-in-the-loop场景简单示例OptionalNodeOutputnodeOutputagent.invokeAndGetOutput(我叫张三,config);返回结果如下2. NodeOutput2.1 核心属性publicclassNodeOutput{protectedfinalStringnode;// 节点标识符protectedStringagent;// Agent 名称protectedfinalOverAllStatestate;// 图执行状态protectedUsagetokenUsage;// Token 使用量protectedbooleansubGraphfalse;// 子图输出标志}2.1.1 node节点标识符表示当前输出来自哪个节点。在com.alibaba.cloud.ai.graph.StateGraph中定义了开始和结束节点的常量/** * 图的起始点 */publicstaticfinalStringEND__END__;/** * 图的结束点 */publicstaticfinalStringSTART__START__;2.1.2 agentAgent名称标识输出所属的Agent。2.1.3 state图执行状态。内部结构OptionalOverAllState└──OverAllState(图执行状态)├── data:MapString,Object状态数据 │ ├──messages:ListMessage消息历史 │ ├──input:String 用户输入 │ └── outputKey:Message输出消息(如果设置了 outputKey)└── keyStrategies:MapString,KeyStrategy状态键策略}访问状态OptionalObjectmessagesstate.value(messages);OptionalStringinputstate.value(input,String.class);2.1.4 tokenUsagetoken使用量统计。使用的是Spring AI声明的UsageJsonPropertyOrder({promptTokens,completionTokens,totalTokens,nativeUsage})publicclassDefaultUsageimplementsUsage{privatefinalIntegerpromptTokens;privatefinalIntegercompletionTokens;privatefinalinttotalTokens;privatefinalObjectnativeUsage;}2.1.5 subGraph是否为子图输出标识。在多Agent编排中使用if(output.isSubGraph()){// 输出来自子 Agent (SequentialAgent 中的某个 ReactAgent)// 可能需要特殊处理如消息过滤}else{// 输出来自父图的直接节点}2.2 核心方法方法返回值说明isSTART()boolean判断是否为起始节点isEND()boolean判断是否为结束节点关键方法isSubGraph()boolean判断是否为子图输出setSubGraph(boolean)NodeOutput设置子图标志链式调用node()String获取节点标识符agent()String获取 Agent 名称state()OverAllState获取图执行状态tokenUsage()Usage获取 Token 使用量3. StreamingOutput用于Agent流式执行过程中的消息输出携带实时消息和原始数据。publicclassStreamingOutputTextendsNodeOutput{DeprecatedprivatefinalStringchunk;privatefinalMessagemessage;// Spring AI MessageprivatefinalToriginData;// 原始数据privateOutputTypeoutputType;// 输出类型枚举// .............}属性类型说明chunkString(Deprecated) 文本片段messageMessageSpring AI 消息对象originDataT原始数据 (ChatResponse 等)outputTypeOutputType输出类型枚举OutputType枚举publicenumOutputType{AGENT_MODEL_STREAMING,// LLM 流式输出中AGENT_MODEL_FINISHED,// LLM 输出完成AGENT_TOOL_STREAMING,// 工具执行中AGENT_TOOL_FINISHED,// 工具执行完成AGENT_HOOK_STREAMING,// Hook 执行中AGENT_HOOK_FINISHED,// Hook 执行完成GRAPH_NODE_STREAMING,// 普通节点执行中GRAPH_NODE_FINISHED// 普通节点执行完成}使用示例FluxNodeOutputfluxagent.stream(你好);flux.filter(o-oinstanceofStreamingOutput?).map(o-(StreamingOutput?)o).subscribe(so-{// 获取输出类型OutputTypetypeso.getOutputType();// 获取消息Messagemsgso.message();if(msginstanceofAssistantMessageam){System.out.println(AI: am.getText());}// 判断是流式还是完成if(typeOutputType.AGENT_MODEL_FINISHED){System.out.println(LLM 输出完成);}});4. StateSnapshot用于checkpoint恢复保存图执行的状态快照。publicfinalclassStateSnapshotextendsNodeOutput{privatefinalRunnableConfigconfig;// 运行时配置// .............}关键方法方法说明next()获取下一个要执行的节点 IDconfig()获取 RunnableConfigof(…)从 Checkpoint 创建 StateSnapshot使用场景// 获取当前状态快照StateSnapshotsnapshotcompiledGraph.getState(config);// 查看下一个要执行的节点StringnextNodesnapshot.next();if(nextNode!null){System.out.println(下一个节点: nextNode);}// 获取当前状态OverAllStatestatesnapshot.state();// 恢复执行compiledGraph.invoke(state,snapshot.config());5. InterruptionMetadata用于human-in-the-loop场景记录中断时的元数据和工具反馈。publicfinalclassInterruptionMetadataextendsNodeOutput{privatefinalMapString,Objectmetadata;// 自定义元数据privateListToolFeedbacktoolFeedbacks;// 工具反馈列表privateListToolCalltoolsAutomaticallyApproved;// 自动批准的工具}//...............ToolFeedback结构publicstaticclassToolFeedback{Stringid;// 工具调用 IDStringname;// 工具名称Stringarguments;// 工具参数FeedbackResultresult;// 审批结果: APPROVED/REJECTED/EDITEDStringdescription;// 描述信息}使用场景// 处理中断FluxNodeOutputfluxagent.stream(删除重要文件);flux.filter(o-oinstanceofInterruptionMetadata).map(o-(InterruptionMetadata)o).subscribe(im-{System.out.println(中断节点: im.node());// 查看需要审批的工具for(ToolFeedbackfb:im.toolFeedbacks()){System.out.println(工具: fb.getName());System.out.println(参数: fb.getArguments());System.out.println(审批结果: fb.getResult());}});6. 演示案例6.1 监控完整执行过程示例代码ReactAgentagentReactAgent.builder().name(my-agent).model(zhiPuAiChatModel).methodTools(dateTimeTools).build();FluxNodeOutputfluxagent.stream(帮我查询当前时间);flux.subscribe(output-{System.out.println( 节点输出 );System.out.println(节点: output.node());System.out.println(是开始: output.isSTART());System.out.println(是结束: output.isEND());if(outputinstanceofStreamingOutput?so){System.out.println(输出类型: so.getOutputType());if(so.message()!null){System.out.println(消息类型: so.message().getMessageType());}}// Token 统计if(output.tokenUsage()!null){System.out.println(Token 使用: output.tokenUsage().getTotalTokens());}});__START__节点输出节点输出节点:__START__ 是开始:true是结束:false_AGENT_HOOK_节点输出节点输出节点:_AGENT_HOOK_InstructionAgentHook.before 是开始:false是结束:false输出类型:AGENT_HOOK_FINISHED最后的__END__节点输出节点输出节点:__END__ 是开始:false是结束:true6.2 只提取 AI 消息示例代码FluxNodeOutputfluxagent.stream(写一首诗);flux.filter(o-oinstanceofStreamingOutput?).map(o-(StreamingOutput?)o).filter(so-so.getOutputType()OutputType.AGENT_MODEL_STREAMING||so.getOutputType()OutputType.AGENT_MODEL_FINISHED).filter(so-so.message()instanceofAssistantMessage).map(so-(AssistantMessage)so.message()).subscribe(am-{System.out.print(am.getText());// 实时打印});输出示例我来为您写一首诗**时光印记**晨光轻抚窗棂纱 岁月如流指尖滑。 往事如烟随风散 新程似锦待开花。 心中常怀千般梦 脚下每踏万重山。 人生短暂须珍惜 把握当下莫蹉跎。 无论风雨兼程路 总有希望在前方。 只要心中有信念 定能照亮前行光。---希望这首诗能给您带来一些美好的感受 我来为您写一首诗**时光印记**晨光轻抚窗棂纱 岁月如流指尖滑。 往事如烟随风散 新程似锦待开花。 心中常怀千般梦 脚下每踏万重山。 人生短暂须珍惜 把握当下莫蹉跎。 无论风雨兼程路 总有希望在前方。 只要心中有信念 定能照亮前行光。---希望这首诗能给您带来一些美好的感受6.3 统计 Token 消耗信息示例代码OptionalNodeOutputnodeOutputagent.invokeAndGetOutput(你好);UsageusagenodeOutput.get().tokenUsage();System.out.println(总 Token: usage.getTotalTokens());System.out.println(输入 Token: usage.getPromptTokens());System.out.println(输出 Token: usage.getCompletionTokens());输出示例总Token:410输入Token:396输出Token:14