欢迎光临
我们一直在努力

第 2 篇 Spring AI Alibaba 初体验:原来 Java 也能轻松玩转 AI Agent

🚀 本文目标

1、初步了解 Spring AI Alibaba 框架的定位及提供的核心功能。

2、从零开始搭建一个 ReactAgent 示例。

画板

Spring AI Aliaba 概述

⭐ Spring AI Aliaba 是构建 Agent 智能体应用最简单的方式,只需要 10 行代码!【你信吗?10 行只能基础代码哈 😁】

Spring AI 简介

Spring AI 项目由 Spring 官方开源并维护的 AI 应用开发框架,该项目目标是简化包含 AI 功能的应用程序的开发,避免不必要的复杂性。

该项目从著名的 Python 项目(例如 LangChain 和 LlamaIndex)中汲取灵感,但 Spring AI 并非这些项目的直接移植,而是基于这样的信念:下一波生成式 AI 应用将不仅面向于 Python 开发人员,还将遍及多种编程语言。从本质上讲,Spring AI 解决了 AI 集成的基本挑战:****Connecting your enterprise Data and APIs with the AI Models。

架构设计

🚩 Spring AI Alibaba 项目架构设计:

  • Agent 框架:最上层,提供给开发者使用,是一个以 ReactAgent 设计理念为核心的 Agent 开发框架,使开发者能够构建具备上下文工程和人机交互等核心能力的 Agent。
  • Graph:中间层,是一个低级别的工作流和多 Agent 协调框架,能够帮助开发者实现复杂的应用程序编排,它具备丰富的预置节点和简化图状态定义,Graph 是 Agent 框架的底层运行时基础。【🏋️ Spring AI Alibaba 中的 Graph Runtime 与 Agent Framework 的关系,有些类似于 LangChain 与 LangGraph 的关系】
  • Augementd LLM:最底层,以 Spring AI 框架底层原子抽象为基础,为构建 LLM 应用提供基础抽象,比如模型(Model)、工具(Tool)、模型上下文协议(MCP)、消息(Message)、向量存储(Vector Store)等。

核心功能

  • ReactAgent:构建具有推理和行动能力的智能 Agent,遵循 ReAct(推理+行动)范式,用于迭代解决问题。
  • 多 Agent 编排:使用内置模式(包括 SequentialAgent、ParallelAgent、LlmRoutingAgent、LoopAgent)组合多个 Agent,以执行复杂的任务。
  • 上下文工程:内置快速工程、上下文管理、对话流控制的最佳实践,以提高 Agent 的可靠性和性能。
  • 人机协同:将人工反馈和审批步骤无缝集成到 Agent 工作流程中,从而实现关键工具和操作的监督执行。
  • 流式传输支持:Agent 响应支持实时流式传输。
  • 错误处理:强大的错误恢复和重试机制。
  • 基于 Graph 的工作流:基于 Graph 的工作流运行时和 API,用于条件路由、嵌套图、并行执行和状态管理。可将工作流导出为 PlantUML 和 Mermaid 格式。
  • A2A 支持:通过 Nacos 集成支持 Agent 间通信,实现跨服务的分布式 Agent 协调和协作。
  • 丰富的模型、工具、MCP 支持:利用 Spring AI 的核心理念,支持多种 LLM、工具调用和模型上下文协议。

Agent 设计原则

单 Agent 模式:推荐使用 Agent Framework 内置的 ReActAgent抽象快速构建 Agent 应用。

多 Agent 模式:使用内置模式(包括 SequentialAgent、ParallelAgent、LlmRoutingAgent、LoopAgent)组合多个 Agent,以执行复杂的任务。

精准粒度控制模式:对于某些开发场景而言,直接使用 Graph API 也是可行的,它能提供更灵活的编排、更直接的状态控制,适合于需要超高可靠性、大量自定义逻辑、需要精确控制延时的场合。

ReactAgent 快速开始

🚀 小目标:创建一个支持工具调用的 ReactAgent,当用户询问天气时,能够调用天气工具查询天气并返回给用户。

准备工作

准备 API key

在调用 LLM 时,需要提前准备一个 API Key。有很多种使用 LLM 的方式,比如阿里百炼 DashScope、DeepSeek、Ollama、OpenAI、OpenAI 兼容模型等等。

使用硅基流动(OpenAI 兼容方式),访问 硅基流动注册送2000万Token,即可获得 2000 万 Token,后续可在模型广场种选择各种各样的模型去使用。

在硅基流动 API 秘钥管理页面,可创建 API 秘钥。

添加依赖

首先,添加 Spring AI Alibaba Agent 框架依赖。

<!– Spring AI Alibaba Agent Framework –>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>1.1.0.0-RC2</version>
</dependency>

其次,选择一个 LLM 模型的依赖。有很多种选择,比如 DashScope、Ollama、DeepSeek、OpenAI & OpenAI 兼容等方式。由于我使用的是硅基流动,属于 OpenAI 兼容方式,所以引入 spring-ai-starter-model-openai即可。

<!– 可选方式1:DashScope ChatModel 支持 –>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>1.1.0.0-RC2</version>
</dependency>

<!– 可选方式2:OpenAI & OpenAI 兼容 –>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>1.1.0</version>
</dependency>

构建 Agent – 极速版

下面,直接使用 Java 代码,以最快的速度,构建一个 Agent 并运行。

  • 关键点 1:先构建一个 ChatModel,Agent 需要利用它来与 LLM 交互。
  • 关键点 2:定义一个 Agent,指定 Agent 名称、描述、要使用 LLM、系统提示词、注册的工具信息等。
  • 关键点 3:调用 Agent,查看返回的信息、元数据等信息。
  • 关键点 4:工具的定义方式,并使用 FunctionToolCallback注册一个工具。

package com.ganlan.cloud.ai;

import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.function.BiFunction;

@Slf4j
@SpringBootApplication
public class AiApplication {

public static void main(String[] args) {
SpringApplication.run(AiApplication.class, args);
}

@PostConstruct
public void init() {
// 关键点1:构建ChatModel
OpenAiChatOptions options = OpenAiChatOptions
.builder()
.model(System.getenv("MODEL"))
.build();
OpenAiApi openAiApi = OpenAiApi.builder()
.baseUrl(System.getenv("BASE_URL"))
.apiKey(System.getenv("API_KEY"))
.build();
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.defaultOptions(options)
.openAiApi(openAiApi)
.build();

// 关键点2:定义Agent并注册工具
ReactAgent reactAgent = ReactAgent.builder()
.name("agent")
.description("这个一个Agent,支持调用工具获取天气")
.model(chatModel)
.systemPrompt("你是一个小助手,支持调用getWeatherForLocation工具查询天气")
.saver(new MemorySaver())
.tools(FunctionToolCallback
.builder("getWeatherForLocation", new WeatherTool())
.description("根据给定的城市获取天气信息")
.inputType(WeatherToolRequest.class)
.build()
)
.build();

// 关键点3:调用Agent,并返回相关信息
AssistantMessage assistantMessage = null;
try {
assistantMessage = reactAgent.call("合肥天气怎么样?");
log.info("Agent 是否调用了工具:{},{}", assistantMessage.hasToolCalls(), assistantMessage.getToolCalls());
log.info("Agent 元数据:{}", assistantMessage.getMetadata());
log.info("Agent 响应内容:{}, {}", assistantMessage.getMessageType(), assistantMessage.getText());
} catch (GraphRunnerException e) {
throw new RuntimeException(e);
}
}

// 关键点4:天气工具定义
public class WeatherTool implements BiFunction<WeatherToolRequest, ToolContext, String> {
@Override
public String apply(WeatherToolRequest request, ToolContext toolContext) {
return request.city() + " 天气阳光明媚啊!";
}
}

// 天气工具请求
public record WeatherToolRequest(@ToolParam(description = "城市名称") String city) {

}

}

至此,Agent 智能体已经入门啦!🚀🚀🚀

构建 Agent – 标准版

上述 Agent 方式,基本类似于纯 Java 方式使用,下面我们用标准的 SpringBoot 方式来构建一个 Agent,功能上依然是一个天气预报 Agent。

配置 Agent

代码解读:

1、直接注入 ChatModel,因为 spring-ai-starter-model-openai会自动装配 OpenAIChatModel。

2、使用 Bean 创建 ReactAgent,便于后续 Controller 进行调用。

3、ReactAgent创建逻辑

  • 基本信息:名称、描述、系统提示词、使用的模型
  • 记忆保存:MemorySaver在内存中保存记忆,支持在 MySQL 等数据库中保存。与 RunnableConfig配合使用,其会有 threadId表示是哪一个用户。
  • 工具注册:注册工具 getWeatherTool 信息。
  • Hooks 设置:支持配置各种 Hooks,比如限制 Agent 调用次数。
  • 输出类型:定义 Agent 最终返回的 Java 类型信息。

package com.ganlan.cloud.ai.config;

import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.agent.hook.Hook;
import com.alibaba.cloud.ai.graph.agent.hook.hip.HumanInTheLoopHook;
import com.alibaba.cloud.ai.graph.agent.hook.hip.ToolConfig;
import com.alibaba.cloud.ai.graph.agent.hook.modelcalllimit.ModelCallLimitHook;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.ganlan.cloud.ai.response.AgentResponse;
import com.ganlan.cloud.ai.tool.WeatherTool;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@RequiredArgsConstructor
@Configuration
public class AgentConfiguration {

private final ChatModel chatModel;

@Bean
public ReactAgent weatherAgent() {
ModelCallLimitHook modelCallLimitHook = ModelCallLimitHook.builder()
.runLimit(5) // 限制最多调用 5 次
.exitBehavior(ModelCallLimitHook.ExitBehavior.ERROR) // 超出限制时抛出异常
.build();

return ReactAgent.builder()
.name("weather_agent")
.description("这是一个天气预报Agent")
.systemPrompt("你是一个天气预报小助手,支持调用getWeatherTool工具查询天气")
.model(chatModel)
.saver(MemorySaver.builder().build())
.tools(FunctionToolCallback
.builder("getWeatherTool", new WeatherTool())
.description("根据给定的城市获取天气信息")
.inputType(WeatherTool.Request.class)
.build()
)
.hooks(modelCallLimitHook)
.outputType(AgentResponse.class)
.build();
}

}

AgentResponse 类定义如下:最终 Agent 会将模型响应信息,转换为如下类对象。

/**
* Agent响应信息
*
* @param weatherInfo 天气信息
* @param otherInfo 其它信息
*/

public record AgentResponse(String weatherInfo, String otherInfo) {
}

定义工具

下面的类,定义了一个工具,包括工具入参,工具实现逻辑。

package com.ganlan.cloud.ai.tool;

import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.tool.annotation.ToolParam;

import java.util.function.BiFunction;

/**
* 天气查询工具
*/

public class WeatherTool implements BiFunction<WeatherTool.Request, ToolContext, String> {
@Override
public String apply(Request request, ToolContext toolContext) {
return request.city() + ":天气晴天,20~25摄氏度!";
}

public record Request(@ToolParam(description = "城市名称") String city) {

}

}

创建 Controller

编写一个 Controller,实现对 Agent 的调用。这里使用底层的 invoke 方法,会返回所有信息。同时传入了 RunnableConfig 指定了用户信息,实现记忆的保存。

package com.ganlan.cloud.ai.controller;

import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
@Controller
@RequestMapping("/weather-agent")
public class AgentHelloWorldController {

private final ReactAgent weatherAgent;

@GetMapping
public ResponseEntity<String> hello(@RequestParam(value = "query", defaultValue = "合肥天气怎么样?", required = false) String query) {
RunnableConfig runnableConfig = RunnableConfig.builder().threadId("user_1").build();
try {
Optional<OverAllState> overAllState = weatherAgent.invoke(query, runnableConfig);
return ResponseEntity.ok(overAllState.toString());
} catch (GraphRunnerException e) {
log.error("Agent执行失败", e);
return ResponseEntity.internalServerError().body("Agent执行失败:" + e.getMessage());
}
}

}

测试 Agent

直接调用 http://127.0.0.1:18081/weather-agent?query=上海天气怎么样即可看到返回结果。

上述返回结果,我们对它分析一下:

1、第 1 个 message(USER):用户问的问题,包括了 Spring AI Alibaba 添加的默认提示词,要求必须返回 JSON 格式。并给出了 JSON 格式的定义信息。

2、第 2 个 message(ASSISTANT):模型给出的恢复,要求调用工具,并给出了工具调用明细信息。

3、第 3 个 message(TOOL):开始调用工具并得到结果。

4、第 4 个 message(ASSISTANT):模型拿到工具结果后,将结果转换为预期类型返回出来。同时,停止本次调用。

{
"data": {
"input": "上海天气怎么样?",
"messages": [
{
"messageType": "USER",
"metadata": {"messageType": "USER"},
"media": [],
"text": "上海天气怎么样?\\r\\nYour response should be in JSON format.\\nDo not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.\\nDo not include markdown code blocks in your response.\\nRemove the ```json markdown from the output.\\nHere is the JSON Schema instance your output must adhere to:\\n```{\\r\\n \\"$schema\\" : \\"https://json-schema.org/draft/2020-12/schema\\",\\r\\n \\"type\\" : \\"object\\",\\r\\n \\"properties\\" : {\\r\\n \\"otherInfo\\" : {\\r\\n \\"type\\" : \\"string\\"\\r\\n },\\r\\n \\"weatherInfo\\" : {\\r\\n \\"type\\" : \\"string\\"\\r\\n }\\r\\n },\\r\\n \\"required\\" : [ \\"otherInfo\\", \\"weatherInfo\\" ],\\r\\n \\"additionalProperties\\" : false\\r\\n}```\\n"
},
{
"messageType": "ASSISTANT",
"metadata": {"role": "ASSISTANT","messageType": "ASSISTANT","finishReason": "TOOL_CALLS","refusal": "","index": 0,"annotations": [{}],"id": "019b8e8d9eee0c60348bf29133cab407"},
"toolCalls": [{"id": "019b8e8db8cd29247a4657006bc41df8","type": "function","name": "getWeatherTool","arguments": "{\\"city\\": \\"上海\\"}"}],
"media": [],
"text": ""
},
{
"responses": [{"id": "019b8e8db8cd29247a4657006bc41df8","name": "getWeatherTool","responseData": "\\"上海:天气晴天,20~25摄氏度!\\""}],
"metadata": {"messageType": "TOOL"},
"messageType": "TOOL",
"text": ""
},
{
"messageType": "ASSISTANT",
"metadata": {"role": "ASSISTANT","messageType": "ASSISTANT","finishReason": "STOP","refusal": "","index": 0,"annotations": [{}],"id": "019b8e8ce7d64d7d6246fc836f464231"},
"toolCalls": [],
"media": [],
"text": "{\\n \\"otherInfo\\": \\"上海天气预报\\",\\n \\"weatherInfo\\": \\"天气晴天,20~25摄氏度!\\"\\n}"
}
]
}
}

如果继续调用 Agent,就会发现它会记录所有的历史消息。

至此,一个简单的,标准版的 Agent 完成!!!🚀🚀🚀

入坑指南

报错:Conversion from JSON to String failed

使用官方的快速示例(即如下代码),发现 Agent 调用会报错🤒:Conversion from JSON to String failed。

// 初始化 ChatModel
DashScopeApi dashScopeApi = DashScopeApi.builder()
.apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
.build();

ChatModel chatModel = DashScopeChatModel.builder()
.dashScopeApi(dashScopeApi)
.build();

// 定义天气查询工具
public class WeatherTool implements BiFunction<String, ToolContext, String> {
@Override
public String apply(String city, ToolContext toolContext) {
return "It's always sunny in " + city + "!";
}
}

ToolCallback weatherTool = FunctionToolCallback.builder("get_weather", new WeatherTool())
.description("Get weather for a given city")
.inputType(String.class)
.build();

// 创建 agent
ReactAgent agent = ReactAgent.builder()
.name("weather_agent")
.model(chatModel)
.tools(weatherTool)
.systemPrompt("You are a helpful assistant")
.saver(new MemorySaver())
.build();

// 运行 agent
AssistantMessage response = agent.call("what is the weather in San Francisco");
System.out.println(response.getText());

排查原因:上述代码使用了 FunctionToolCallback,其调用代码中,会将工具的输入信息从 JSON 转换为目标类型,而目标类型是一个 String,JSON 直接转换为 String 就报错了。

@Override
public String call(String toolInput, @Nullable ToolContext toolContext) {
Assert.hasText(toolInput, "toolInput cannot be null or empty");

logger.debug("Starting execution of tool: {}", this.toolDefinition.name());

I request = JsonParser.fromJson(toolInput, this.toolInputType);
O response = callMethod(request, toolContext);

logger.debug("Successful execution of tool: {}", this.toolDefinition.name());

return this.toolCallResultConverter.convert(response, null);
}

解决办法:将工具请求信息使用 JavaBean 对象来接收,这样就能将 json 中的字段映射到 JavaBean 中的字段了。

// 天气工具定义
public class WeatherTool implements BiFunction<WeatherToolRequest, ToolContext, String> {
@Override
public String apply(WeatherToolRequest request, ToolContext toolContext) {
return request.city() + " 天气阳光明媚啊!";
}
}

// 天气工具请求
public record WeatherToolRequest(@ToolParam(description = "城市名称") String city) {

}

参考

1.概览 | Spring AI Alibaba

2.快速开始 | Spring AI Alibaba

相关博文

1.第 1 篇 Spring AI Aliaba – AI 快速体验 2.第 2 篇 Spring AI Alibaba 初体验:原来 Java 也能轻松玩转 AI Agent 3.第 3 篇 Spring AI – Model API 入门指南 4.第 4 篇 深入理解 Spring AI ChatClient:一篇就够了,比官方文档更友好 5.第 5 篇 Spring AI – Tool Calling 全面解析:从基础到高级应用 6.第 6 篇 AI调用外部工具就这么简单:Spring AI Alibaba 工具集成指南 7.第 7 篇 Spring AI 结构化输出完全指南:从原理到最佳实践

赞(0)
未经允许不得转载:171主机测评 » 第 2 篇 Spring AI Alibaba 初体验:原来 Java 也能轻松玩转 AI Agent
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址