侧边栏壁纸
博主头像
牧云

怀璧慎显,博识谨言。

  • 累计撰写 139 篇文章
  • 累计创建 13 个标签
  • 累计收到 8 条评论

目 录CONTENT

文章目录

Agents

秋之牧云
2026-04-03 / 0 评论 / 0 点赞 / 1 阅读 / 0 字

Agents 将大语言模型与工具结合,创建具备任务推理、工具使用决策、工具调用的自动化系统,系统具备持续推理、工具调用的循环迭代能力,直至问题解决。

Spring AI Alibaba 提供了基于 ReactAgent 的生产级 Agent 实现。

一个 LLM Agent 在循环中通过运行工具来实现目标。Agent 会一直运行直到满足停止条件 —— 即当模型输出最终答案或达到迭代限制时。

ReactAgent 理论基础

什么是 ReAct

ReAct(Reasoning + Acting)是一种将推理和行动相结合的 Agent 范式。在这个范式中,Agent 会:

  1. 思考(Reasoning):分析当前情况,决定下一步该做什么

  2. 行动(Acting):执行工具调用或生成最终答案

  3. 观察(Observation):接收工具执行的结果

  4. 迭代:基于观察结果继续思考和行动,直到完成任务

这个循环使 Agent 能够:

  • 将复杂问题分解为多个步骤

  • 动态调整策略基于中间结果

  • 处理需要多次工具调用的任务

  • 在不确定的环境中做出决策

ReactAgent 的工作原理

Spring AI Alibaba 中的ReactAgent 基于 Graph 运行时构建。Graph 由节点(steps)和边(connections)组成,定义了 Agent 如何处理信息。Agent 在这个 Graph 中移动,执行如下节点:

  • Model Node (模型节点):调用 LLM 进行推理和决策

  • Tool Node (工具节点):执行工具调用

  • Hook Nodes (钩子节点):在关键位置插入自定义逻辑

ReactAgent 的核心执行流程:

https://java2ai.com/img/agent/agents/reactagent.png

核心组件

Model(模型)

Model 是 Agent 的推理引擎。Spring AI Alibaba 支持多种配置方式。

基础模型配置

最直接的方式是使用 ChatModel 实例:

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;

// 创建 DashScope API 实例
DashScopeApi dashScopeApi = DashScopeApi.builder()
  .apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
  .build();

// 创建 ChatModel
ChatModel chatModel = DashScopeChatModel.builder()
  .dashScopeApi(dashScopeApi)
  .build();

// 创建 Agent
ReactAgent agent = ReactAgent.builder()
  .name("my_agent")
  .model(chatModel)
  .build();

高级模型配置

通过 ChatOptions 可以精细控制模型行为:

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;

ChatModel chatModel = DashScopeChatModel.builder()
  .dashScopeApi(dashScopeApi)
  .defaultOptions(DashScopeChatOptions.builder()
	.withModel(DashScopeChatModel.DEFAULT_MODEL_NAME)
      .withTemperature(0.7)    // 控制随机性
      .withMaxToken(2000)      // 最大输出长度
      .withTopP(0.9)           // 核采样参数
      .build())
  .build();

常用参数说明

  • temperature:控制输出的随机性(0.0-1.0),值越高越有创造性

  • maxTokens:限制单次响应的最大 token 数

  • topP:核采样,控制输出的多样性

  • 更多参数请参考 ChatModel 适配

Tools(工具)

工具赋予 Agent 执行操作的能力,支持顺序执行、并行调用、动态选择和错误处理。

定义和使用工具

import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.ai.chat.model.ToolContext;
import java.util.function.BiFunction;

// 定义工具(示例:仅一个搜索工具)
public class SearchTool implements BiFunction<String, ToolContext, String> {
  @Override
  public String apply(String query, ToolContext context) {
      // 实现搜索逻辑
      return "搜索结果: " + query;
  }
}

// 创建工具回调
ToolCallback searchTool = FunctionToolCallback.builder("search", new SearchTool())
  .description("搜索工具")
  .build();

// 在Agent中使用
ReactAgent agent = ReactAgent.builder()
  .name("search_agent")
  .model(chatModel)
  .tools(searchTool)
  .build();

工具错误处理

import com.alibaba.cloud.ai.graph.agent.interceptor.ToolInterceptor;
import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallRequest;
import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallResponse;
import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallHandler;

public class ToolErrorInterceptor extends ToolInterceptor {
  @Override
  public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {
      try {
          return handler.call(request);
      } catch (Exception e) {
          return ToolCallResponse.of(request.getToolCallId(), request.getToolName(),
              "Tool failed: " + e.getMessage());
      }
  }

  @Override
  public String getName() {
      return "ToolErrorInterceptor";
  }
}

ReactAgent agent = ReactAgent.builder()
  .name("my_agent")
  .model(chatModel)
  .interceptors(new ToolErrorInterceptor())
  .build();

ReAct 循环示例:Agent 自动交替进行推理和工具调用,直到获得最终答案。

用户: 查询杭州天气并推荐活动
→ [推理] 需要查天气 → [行动] get_weather("杭州") → [观察] 晴,25°C
→ [推理] 需要推荐活动 → [行动] search("户外活动") → [观察] 西湖游玩...
→ [推理] 信息充足 → [行动] 生成答案

System Prompt(系统提示)

System Prompt 塑造 Agent 处理任务的方式。

基础用法

通过 systemPrompt 参数提供字符串:

ReactAgent agent = ReactAgent.builder()
  .name("my_agent")
  .model(chatModel)
  .systemPrompt("你是一个专业的技术助手。请准确、简洁地回答问题。")
  .build();

使用 instruction

对于更详细的指令,使用 instruction 参数:

String instruction = """
  你是一个经验丰富的软件架构师。

  在回答问题时,请:
  1. 首先理解用户的核心需求
  2. 分析可能的技术方案
  3. 提供清晰的建议和理由
  4. 如果需要更多信息,主动询问

  保持专业、友好的语气。
  """;

ReactAgent agent = ReactAgent.builder()
  .name("architect_agent")
  .model(chatModel)
  .instruction(instruction)
  .build();

动态 System Prompt

使用 ModelInterceptor 实现基于上下文的动态提示:

import com.alibaba.cloud.ai.graph.agent.interceptor.ModelInterceptor;
import com.alibaba.cloud.ai.graph.agent.interceptor.ModelRequest;
import com.alibaba.cloud.ai.graph.agent.interceptor.ModelResponse;
import com.alibaba.cloud.ai.graph.agent.interceptor.ModelCallHandler;
import org.springframework.ai.chat.messages.SystemMessage;

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);
  }

  @Override
  public String getName() {
      return "DynamicPromptInterceptor";
  }
}

ReactAgent agent = ReactAgent.builder()
  .name("adaptive_agent")
  .model(chatModel)
  .interceptors(new DynamicPromptInterceptor())
  .build();

调用 Agent

基础调用

使用 call 方法获取最终响应:

import org.springframework.ai.chat.messages.AssistantMessage;

// 字符串输入
AssistantMessage response = agent.call("杭州的天气怎么样?");
System.out.println(response.getText());

// UserMessage 输入
UserMessage userMessage = new UserMessage("帮我分析这个问题");
AssistantMessage response = agent.call(userMessage);

// 多个消息
List<Message> messages = List.of(
  new UserMessage("我想了解 Java 多线程"),
  new UserMessage("特别是线程池的使用")
);
AssistantMessage response = agent.call(messages);

获取完整状态

使用 invoke 方法获取完整的执行状态:

import com.alibaba.cloud.ai.graph.OverAllState;
import java.util.Optional;

Optional<OverAllState> result = agent.invoke("帮我写一首诗");

if (result.isPresent()) {
  OverAllState state = result.get();

  // 访问消息历史
  Optional<Object> messages = state.value("messages");
  List<Message> messageList = (List<Message>) messages.get();

  // 访问自定义状态
  Optional<Object> customData = state.value("custom_key");

  System.out.println("完整状态:" + state);
}

使用配置

通过 RunnableConfig 传递运行时配置:

import com.alibaba.cloud.ai.graph.RunnableConfig;

String threadId = "thread_123";
RunnableConfig runnableConfig = RunnableConfig.builder()
  .threadId(threadId)
  .addMetadata("key", "value")
  .build();

AssistantMessage response = agent.call("你的问题", runnableConfig);

高级特性

结构化输出

在某些情况下,你可能希望 Agent 以特定格式返回输出。ReactAgent 提供了两种策略。

使用 outputType

通过 Java 类定义输出结构,Agent 会自动生成对应的 JSON Schema:

public class PoemOutput {
  private String title;
  private String content;
  private String style;

  // Getters and Setters
  public String getTitle() { return title; }
  public void setTitle(String title) { this.title = title; }

  public String getContent() { return content; }
  public void setContent(String content) { this.content = content; }

  public String getStyle() { return style; }
  public void setStyle(String style) { this.style = style; }
}

ReactAgent agent = ReactAgent.builder()
  .name("poem_agent")
  .model(chatModel)
  .outputType(PoemOutput.class)
  .saver(new MemorySaver())
  .build();

AssistantMessage response = agent.call("写一首关于春天的诗");
// 输出会遵循 PoemOutput 的结构
System.out.println(response.getText());

使用 outputSchema

使用 BeanOutputConverter 生成输出 Schema,提供类型安全和自动 schema 生成:

import org.springframework.ai.converter.BeanOutputConverter;

// 定义输出类型
public static class TextAnalysisResult {
  private String summary;
  private List<String> keywords;
  private String sentiment;
  private Double confidence;

  // Getters and Setters
  public String getSummary() { return summary; }
  public void setSummary(String summary) { this.summary = summary; }
  public List<String> getKeywords() { return keywords; }
  public void setKeywords(List<String> keywords) { this.keywords = keywords; }
  public String getSentiment() { return sentiment; }
  public void setSentiment(String sentiment) { this.sentiment = sentiment; }
  public Double getConfidence() { return confidence; }
  public void setConfidence(Double confidence) { this.confidence = confidence; }
}

// 使用 BeanOutputConverter 生成 outputSchema
BeanOutputConverter<TextAnalysisResult> outputConverter = new BeanOutputConverter<>(TextAnalysisResult.class);
String format = outputConverter.getFormat();

ReactAgent agent = ReactAgent.builder()
  .name("analysis_agent")
  .model(chatModel)
  .outputSchema(format)
  .saver(new MemorySaver())
  .build();

AssistantMessage response = agent.call("分析这段文本:春天来了,万物复苏。");

选择建议

  • outputType:类型安全,适合结构固定的场景(推荐

  • outputSchema:使用 BeanOutputConverter 生成时提供类型安全,手动提供字符串时灵活性高,适合动态或复杂的输出格式

Memory(记忆)

Agent 通过状态自动维护对话历史。使用 MemorySaver 配置持久化存储。

import com.alibaba.cloud.ai.graph.checkpoint.config.SaverConfig;
import com.alibaba.cloud.ai.graph.checkpoint.constant.SaverEnum;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;

// 配置内存存储
ReactAgent agent = ReactAgent.builder()
  .name("chat_agent")
  .model(chatModel)
  .saver(new MemorySaver())
  .build();

// 使用 thread_id 维护对话上下文
RunnableConfig config = RunnableConfig.builder()
  .threadId("user_123")
  .build();

agent.call("我叫张三", config);
agent.call("我叫什么名字?", config);  // 输出: "你叫张三"

生产环境:使用 RedisSaverMongoSaver 等持久化存储替代 MemorySaver

Hooks(钩子)

Hooks 允许在 Agent 执行的关键点插入自定义逻辑。

Hook 类型与使用

import com.alibaba.cloud.ai.graph.agent.hook.*;
import com.alibaba.cloud.ai.graph.agent.hook.messages.MessagesModelHook;
import com.alibaba.cloud.ai.graph.agent.hook.messages.AgentCommand;
import com.alibaba.cloud.ai.graph.agent.hook.messages.UpdatePolicy;

// 1. AgentHook - 在 Agent 开始/结束时执行,每次Agent调用只会运行一次
@HookPositions({HookPosition.BEFORE_AGENT, HookPosition.AFTER_AGENT})
public class LoggingHook extends AgentHook {
  @Override
  public String getName() { return "logging"; }

  @Override
  public CompletableFuture<Map<String, Object>> beforeAgent(OverAllState state, RunnableConfig config) {
      System.out.println("Agent 开始执行");
      return CompletableFuture.completedFuture(Map.of());
  }

  @Override
  public CompletableFuture<Map<String, Object>> afterAgent(OverAllState state, RunnableConfig config) {
      System.out.println("Agent 执行完成");
      return CompletableFuture.completedFuture(Map.of());
  }
}

// 2. MessagesModelHook - 在模型调用前后执行(例如:消息修剪),专门用于操作消息列表,使用更简单,更推荐。区别于AgentHook,MessagesModelHook在一次agent调用中可能会调用多次,也就是每次 reasoning-acting 迭代都会执行
@HookPositions({HookPosition.BEFORE_MODEL})
public class MessageTrimmingHook extends MessagesModelHook {
  private static final int MAX_MESSAGES = 10;

  @Override
  public String getName() {
      return "message_trimming";
  }

  @Override
  public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {
      if (previousMessages.size() > MAX_MESSAGES) {
          // 只保留最后 MAX_MESSAGES 条消息
          List<Message> trimmedMessages = previousMessages.subList(
              previousMessages.size() - MAX_MESSAGES, 
              previousMessages.size()
          );
          return new AgentCommand(trimmedMessages, UpdatePolicy.REPLACE);
      }
      // 消息数量未超过限制,直接返回原消息列表
      return new AgentCommand(previousMessages);
  }
}

Hook 执行位置

  • BEFORE_AGENT / AFTER_AGENT:Agent 整体执行前后

  • BEFORE_MODEL / AFTER_MODEL:Agent Loop 循环过程中,每次模型调用前后

Interceptors(拦截器)

Interceptors 提供更细粒度的控制,可以拦截和修改模型调用和工具执行。

使用示例

import com.alibaba.cloud.ai.graph.agent.interceptor.*;

// ModelInterceptor - 内容安全检查
public class GuardrailInterceptor extends ModelInterceptor {
  @Override
  public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
      // 前置:检查输入
      if (containsSensitiveContent(request.getMessages())) {
      	return ModelResponse.of(AssistantMessage.builder().content("检测到不适当的内容").build());
      }

      // 执行调用
      ModelResponse response = handler.call(request);

      // 后置:检查输出
      return sanitizeIfNeeded(response);
  }
}

// ToolInterceptor - 监控和错误处理
public class ToolMonitoringInterceptor extends ToolInterceptor {
  @Override
  public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {
      long startTime = System.currentTimeMillis();
      try {
          ToolCallResponse response = handler.call(request);
          logSuccess(request, System.currentTimeMillis() - startTime);
          return response;
      } catch (Exception e) {
          logError(request, e, System.currentTimeMillis() - startTime);
          return ToolCallResponse.error(request.getToolCall(),
              "工具执行遇到问题,请稍后重试");
      }
  }
}

// 组合使用
ReactAgent agent = ReactAgent.builder()
  .name("my_agent")
  .model(chatModel)
  .interceptors(new GuardrailInterceptor(), new LoggingInterceptor(), new ToolMonitoringInterceptor())
  .saver(new MemorySaver())
  .build();

常见用途

  • ModelInterceptor:内容安全、动态提示、日志记录、性能监控

  • ToolInterceptor:错误重试、权限检查、结果缓存、审计日志

控制与流式输出

迭代控制

通过 Hooks 控制 Agent 的执行迭代,防止无限循环或过度成本。

import com.alibaba.cloud.ai.graph.agent.hook.modelcalllimit.ModelCallLimitHook;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;

// 使用内置的 ModelCallLimitHook 限制模型调用次数
ReactAgent agent = ReactAgent.builder()
  .name("my_agent")
  .model(chatModel)
  .hooks(ModelCallLimitHook.builder().runLimit(5).build())  // 限制最多调用 5 次
  .saver(new MemorySaver())
  .build();
import com.alibaba.cloud.ai.graph.agent.hook.ModelHook;
import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;
import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;
import com.alibaba.cloud.ai.graph.agent.hook.JumpTo;
import org.springframework.ai.chat.messages.AssistantMessage;

// 自定义停止条件:基于状态判断是否继续
@HookPositions({HookPosition.BEFORE_MODEL})
public class CustomStopConditionHook extends ModelHook {

  @Override
  public String getName() {
      return "custom_stop_condition";
  }

  @Override
  public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {
      // 检查是否找到答案,展示使用 OverAllState
      boolean answerFound = (Boolean) state.value("answer_found").orElse(false);
      // 检查错误次数,展示使用 RunnableConfig
      int errorCount = (Integer) config.context().get("error_count").orElse(0);

      // 找到答案或错误过多时停止
      if (answerFound || errorCount > 3) {
          List<Message> messages = new ArrayList<>();
          messages.add(new AssistantMessage(
              answerFound ? "已找到答案,Agent 执行完成。"
                          : "错误次数过多 (" + errorCount + "),Agent 执行终止。"
          ));
          // the messages will be appended to the original message list context.
          return CompletableFuture.completedFuture(Map.of("messages", messages));
      }

      return CompletableFuture.completedFuture(Map.of());
  }

}

// 使用自定义停止条件
ReactAgent agent = ReactAgent.builder()
  .name("my_agent")
  .model(chatModel)
  .hooks(new CustomStopConditionHook())
  .saver(new MemorySaver())
  .build();

流式输出

在 Agent 场景中,流式输出的核心是处理 StreamingOutput 类型。无论是模型推理、工具调用还是 Hook 节点,输出都统一为这个类型。

使用 OutputType 区分输出类型

通过 OutputType 枚举可以区分不同节点的输出,以及判断是流式增量内容还是完成输出:

OutputType

说明

AGENT_MODEL_STREAMING

模型推理的流式增量内容

AGENT_MODEL_FINISHED

模型推理完成,可获取全量内容

AGENT_TOOL_STREAMING

工具调用的流式增量内容

AGENT_TOOL_FINISHED

工具调用完成

AGENT_HOOK_STREAMING

Hook 节点的流式增量内容

AGENT_HOOK_FINISHED

Hook 节点完成

提示

  • 对于 Hook 等通常非流式的节点,直接使用 AGENT_HOOK_FINISHED 读取内容即可

  • 并非所有节点输出都有意义,尤其是 Hook 节点可能产生无效输出,建议通过 OutputType 进行过滤

import reactor.core.publisher.Flux;
import com.alibaba.cloud.ai.graph.NodeOutput;
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
import com.alibaba.cloud.ai.graph.streaming.OutputType;

Flux<NodeOutput> stream = agent.stream("复杂任务");
stream.subscribe(
  output -> {
      // 检查是否为 StreamingOutput 类型
      if (output instanceof StreamingOutput streamingOutput) {
          OutputType type = streamingOutput.getOutputType();
          
          // 处理模型推理的流式输出
          if (type == OutputType.AGENT_MODEL_STREAMING) {
              // 流式增量内容,逐步显示
              System.out.print(streamingOutput.message().getText());
          } else if (type == OutputType.AGENT_MODEL_FINISHED) {
              // 模型推理完成,可获取完整响应
              System.out.println("\n模型输出完成");
          }
          
          // 处理工具调用完成(目前不支持 STREAMING)
          if (type == OutputType.AGENT_TOOL_FINISHED) {
              System.out.println("工具调用完成: " + output.node());
          }
          
          // 对于 Hook 节点,通常只关注完成事件(如果Hook没有有效输出可以忽略)
          if (type == OutputType.AGENT_HOOK_FINISHED) {
              System.out.println("Hook 执行完成: " + output.node());
          }
      }
  },
  error -> System.err.println("错误: " + error),
  () -> System.out.println("Agent 执行完成")
);

消息类型识别

StreamingOutput.message() 返回的消息有多种类型,需要结合 OutputType 和消息元数据进行区分处理:

OutputType

消息类型

判断条件

说明

AGENT_MODEL_STREAMING

/ AGENT_MODEL_FINISHED

模型普通响应

AssistantMessage

metadata.reasoningContent

为空

模型的实际回复内容,通过 getText()

获取

AGENT_MODEL_STREAMING

/ AGENT_MODEL_FINISHED

模型 Thinking

AssistantMessage

metadata.reasoningContent

不为空

模型的思考过程(如 DeepSeek 等支持 Thinking 的模型)

AGENT_MODEL_FINISHED

工具调用请求

AssistantMessage

hasToolCalls()

true

模型请求调用工具,包含工具名称和参数

AGENT_TOOL_FINISHED

工具响应结果

ToolResponseMessage

工具执行后的返回结果

import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.ToolResponseMessage;

// 结合 OutputType 和消息类型进行处理
if (output instanceof StreamingOutput streamingOutput) {
  OutputType type = streamingOutput.getOutputType();
  Message message = streamingOutput.message();
  
  // 处理模型流式输出
  if (type == OutputType.AGENT_MODEL_STREAMING) {
      if (message instanceof AssistantMessage assistantMessage) {
          // 检查是否为 Thinking 消息
          Object reasoningContent = assistantMessage.getMetadata().get("reasoningContent");
          if (reasoningContent != null && !reasoningContent.toString().isEmpty()) {
              System.out.print("[Thinking] " + reasoningContent);
          } else {
              // 普通模型响应(增量内容)
              System.out.print(assistantMessage.getText());
          }
      }
  }
  // 处理模型输出完成
  else if (type == OutputType.AGENT_MODEL_FINISHED) {
      if (message instanceof AssistantMessage assistantMessage) {
          if (assistantMessage.hasToolCalls()) {
              // 工具调用请求
              assistantMessage.getToolCalls().forEach(toolCall -> {
                  System.out.println("[Tool Call] " + toolCall.name() + ": " + toolCall.arguments());
              });
          } else {
              // 模型完整响应
              System.out.println("\n[Model Finished]");
          }
      }
  }
  // 处理工具执行结果
  else if (type == OutputType.AGENT_TOOL_FINISHED) {
      if (message instanceof ToolResponseMessage toolResponse) {
          toolResponse.getResponses().forEach(response -> {
              System.out.println("[Tool Result] " + response.name() + ": " + response.responseData());
          });
      }
  }
}

提示

  • 先判断 OutputType:通过 OutputType 先确定是模型输出、工具输出还是 Hook 输出,再处理具体消息

  • Thinking 消息:部分模型(如 DeepSeek-R1)支持输出思考过程,通过 metadata.reasoningContent 获取

  • 工具调用:工具调用请求通常在 AGENT_MODEL_FINISHED 阶段出现,此时 hasToolCalls() 返回 true

  • 流式 vs 完成STREAMING 阶段内容是增量的,FINISHED 阶段可获取完整消息或执行结果

更多关于 Graph 底层流式输出机制,请参考 Graph 流式输出

0

评论区