Spring AI Alibaba框架整合百炼大模型平台
文章目录
-
- [Spring AI Alibaba框架整合百炼大模型平台](#Spring AI Alibaba框架整合百炼大模型平台)
-
- 1.会话记忆:Memory
- [2.工具篇:Function Calling](#2.工具篇:Function Calling)
- 3.RAG增强检索(知识库)
- [4.ReAct 智能体](#4.ReAct 智能体)
版本springboot 3.5.x, jdk17, springaiAlibaba1.1.2.2
1.会话记忆:Memory
默认情况下,AI 是"鱼的记忆",每次对话都是独立的。要实现多轮对话,我们需要一个"笔记本"(ChatMemory)和一个"秘书"(MessageChatMemoryAdvisor),秘书会在每次提问前把笔记本里的历史记录翻出来一起发给 AI,并在得到回答后把新内容记下来。
java
/**
* 创建 ChatClient.Builder Bean,供需要动态构建 ChatClient 的场景使用
* (例如多轮对话中需要为每个会话创建独立的 ChatClient)
*/
@Bean
public ChatClient.Builder chatClientBuilder(DashScopeChatModel chatModel) {
return ChatClient.builder(chatModel);
}
java
// 旧版写法 (InMemoryChatMemory 已被弃用)
// ChatMemory chatMemory = new InMemoryChatMemory();
java
@RestController
@RequestMapping("/api/memory")
public class MemoryChatController {
private final ChatClient.Builder chatClientBuilder;
private final ChatMemory chatMemory;
// 注入 ChatClient.Builder,方便我们为每个会话动态创建带记忆的 ChatClient
public MemoryChatController(ChatClient.Builder chatClientBuilder) {
this.chatClientBuilder = chatClientBuilder;
// 初始化全局 ChatMemory 实例,设置最大记忆条数
this.chatMemory = MessageWindowChatMemory.builder()
// 会话记忆仓库(全局唯一,Spring AI 官方标准)实际项目中可以用 Redis 等分布式缓存。
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(10)
.build();
}
@PostMapping("/chat")
public Map<String, String> chatWithMemory(@RequestBody MemoryChatRequest request) {
String conversationId = request.getConversationId();
// 2、使用建造者模式创建 Advisor(构造函数已私有,必须用 builder)
MessageChatMemoryAdvisor memoryAdvisor = MessageChatMemoryAdvisor.builder(chatMemory)
.build();
// 3. 为本次请求构建带记忆的 ChatClient
ChatClient sessionClient = chatClientBuilder
//插件
.defaultAdvisors(
//记忆
memoryAdvisor,
// 日志 Advisor
new SimpleLoggerAdvisor()
)
.build();
String response = sessionClient
.prompt()
.user(request.getQuestion())
// Spring AI 自动区分不同会话
.advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, conversationId))
.call()
.content();
return Map.of(
"conversationId", conversationId,
"question", request.getQuestion(),
"answer", response
);
}
/**
* 清除指定会话的记忆
*/
@DeleteMapping("/clear/{conversationId}")
public Map<String, String> clearMemory(@PathVariable String conversationId) {
// 直接调用 ChatMemory 的 clear 方法,按 conversationId 清除
chatMemory.clear(conversationId);
return Map.of(
"status", "success",
"message", "会话 " + conversationId + " 的记忆已清除"
);
}
}
2.工具篇:Function Calling
核心概念:AI 模型的知识有截止日期,也无法获取实时数据。Function Calling 让 AI 能够调用你写的 Java 方法,从而获取实时信息或执行具体操作。
方式一:Tool注解实现(推荐)
java
/**
* 日期时间工具类
*/
@Component
public class DateTimeTools {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
@Tool(description = "获取当前日期和时间")
public String getCurrentDateTime() {
return "当前时间:" + LocalDateTime.now().format(FORMATTER);
}
@Tool(description = "计算两个日期之间的天数差")
public String daysBetween(
@ToolParam(description = "开始日期,格式 yyyy-MM-dd") String startDate,
@ToolParam(description = "结束日期,格式 yyyy-MM-dd") String endDate) {
try {
LocalDateTime start = LocalDateTime.parse(startDate + "T00:00:00");
LocalDateTime end = LocalDateTime.parse(endDate + "T00:00:00");
long days = Math.abs(java.time.Duration.between(start, end).toDays());
return startDate + " 到 " + endDate + " 相差 " + days + " 天";
} catch (Exception e) {
return "日期格式错误,请使用 yyyy-MM-dd";
}
}
}
方式二:BiFunction接口实现
Spring AI 对这种老式写法**不会生成标准 JSON 结构**,会直接把参数拼成**非 JSON 字符串** → 阿里云ai直接返回 400 错误。
java
/**
* 天气查询工具(模拟数据)
* 实现 BiFunction<String, ToolContext, String> 接口,
* 这样可以直接被 FunctionToolCallback 使用。
* 参数说明:
* - String:AI 调用工具时传入的参数(本工具不需要,忽略即可)
* - ToolContext:工具执行上下文,包含会话 ID 等元信息
*/
@Component
public class WeatherTool implements BiFunction<String, ToolContext, String> {
private final Random random = new Random();
private final String[] conditions = {"晴", "多云", "阴", "小雨", "中雨", "雪"};
/**
* BiFunction 的 apply 方法
* @param city AI 提取的城市名称
* @param context 工具上下文
* @return 天气信息字符串
*/
@Override
public String apply(String city, ToolContext context) {
return getWeather(city);
}
/**
* 实际业务方法:查询天气(模拟)
*/
public String getWeather(String city) {
int temp = 15 + random.nextInt(20); // 15~35度
String cond = conditions[random.nextInt(conditions.length)];
int humidity = 30 + random.nextInt(50); // 30%~80%
return String.format("%s天气:%s,温度%d°C,湿度%d%%",
city, cond, temp, humidity);
}
}
工具使用
java
@Slf4j
@RestController
@RequestMapping("/api/tool")
public class ToolChatController {
private final ChatClient chatClient;
private final DateTimeTools dateTimeTools;
private final WeatherTool weatherTool;
public ToolChatController(ChatClient.Builder builder,
DateTimeTools dateTimeTools,
WeatherTool weatherTool) {
this.chatClient = builder.build();
this.dateTimeTools = dateTimeTools;
this.weatherTool = weatherTool;
}
@PostMapping("/chat")
public Map<String, Object> chatWithTools(@RequestBody ToolChatRequest request) {
ChatResponse chatResponse = chatClient.prompt()
.user(request.getQuestion())
.toolCallbacks(ToolCallbacks.from(dateTimeTools,weatherTool)) // 注册工具
.call()
.chatResponse();
// 助手消息
AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
// 输出提示信息
String result = assistantMessage.getText();
return Map.of("question", request.getQuestion(),
"answer", result);
}
@Data
public static class ToolChatRequest {
private String question;
}
}
3.RAG增强检索(知识库)
核心概念:RAG 让 AI 能够回答你私有文档里的内容。首先把文档转成向量存入向量数据库,用户提问时检索最相似的文档片段,连同问题一起发给 AI。
java
/**
* RAG 检索增强生成服务
* 负责:初始化知识库、检索相关文档、构建增强 Prompt。
* - SimpleVectorStore 是内存实现,适合学习和测试
* - 生产环境建议使用 RedisVectorStore、PgVectorStore 等持久化方案
*/
@Service
public class RagService {
private final EmbeddingModel embeddingModel; // 用于将文本转成向量
private VectorStore vectorStore; // 向量数据库
public RagService(EmbeddingModel embeddingModel) {
this.embeddingModel = embeddingModel;
}
/**
* 在 Bean 初始化后执行:加载模拟知识库文档
*/
@PostConstruct
public void initKnowledgeBase() {
// 创建内存向量存储
this.vectorStore = SimpleVectorStore.builder(embeddingModel).build();
// 模拟知识库文档
List<Document> documents = List.of(
new Document("""
Spring AI 是一个为 AI 工程开发提供 Spring 友好 API 和抽象的应用框架。
它简化了将大语言模型集成到 Spring 应用的过程。
核心组件包括:ChatClient、EmbeddingModel、VectorStore 等。
""", Map.of("source", "官方文档", "type", "overview")),
new Document("""
ChatClient 是 Spring AI 推荐的与 AI 模型交互的方式。
它提供了流畅的 API,支持链式调用,并且可以通过 Advisor 扩展功能。
""", Map.of("source", "官方文档", "type", "chatclient")),
new Document("""
Function Calling(工具调用)允许 AI 模型调用你定义的 Java 方法。
使用 @Tool 注解或实现 BiFunction 接口来定义工具。
""", Map.of("source", "官方文档", "type", "function-calling")),
new Document("""
RAG(检索增强生成)技术让 AI 能够基于私有知识库回答问题。
流程:文档分块 → 向量化 → 存入向量数据库 → 检索 → 生成答案。
""", Map.of("source", "官方文档", "type", "rag"))
);
// 将文档存入向量存储(会自动调用 EmbeddingModel 进行向量化)
vectorStore.accept(documents);
System.out.println("✅ 知识库初始化完成,共加载 " + documents.size() + " 篇文档");
}
/**
* 根据用户问题检索最相似的文档片段
*/
public List<Document> searchSimilarDocuments(String query, int topK) {
return vectorStore.similaritySearch(
SearchRequest.builder()
.query(query)
.topK(topK)
.similarityThreshold(0.7) // 相似度阈值,低于此值的结果会被过滤
.build()
);
}
/**
* 构建增强的提示词:将检索到的文档作为上下文,嵌入到问题之前
*/
public String buildPromptWithContext(String question, List<Document> contextDocs) {
String context = contextDocs.stream()
.map(doc -> "- " + doc.getText())
.collect(Collectors.joining("\n\n"));
return """
请严格基于以下【参考信息】回答问题。如果参考信息中没有相关内容,请回答"根据现有知识库,我无法回答这个问题"。
【参考信息】
%s
【用户问题】
%s
""".formatted(context, question);
}
}
使用
java
/**
* RAG 问答控制器
*/
@RestController
@RequestMapping("/api/rag")
public class RagChatController {
private final ChatClient chatClient;
private final RagService ragService;
public RagChatController(ChatClient.Builder builder, RagService ragService) {
this.chatClient = builder.build();
this.ragService = ragService;
}
/**
* RAG 问答接口
* <p>
* {"question": "什么是 Spring AI?"}
*/
@PostMapping("/ask")
public Map<String, Object> ask(@RequestBody RagRequest request) {
// 1. 检索相关文档
List<Document> relevantDocs = ragService.searchSimilarDocuments(request.getQuestion(), 3);
// 2. 构建包含上下文的 Prompt
String enhancedPrompt = ragService.buildPromptWithContext(request.getQuestion(), relevantDocs);
// 3. 调用 AI 生成答案
String answer = chatClient.prompt()
.user(enhancedPrompt)
.call()
.content();
// 4. 提取文档摘要用于返回
List<String> sources = relevantDocs.stream()
.map(doc -> doc.getMetadata().getOrDefault("source", "unknown") + ": " +
doc.getText().substring(0, Math.min(50, doc.getText().length())) + "...")
.toList();
return Map.of(
"question", request.getQuestion(),
"answer", answer,
"sources", sources
);
}
@Data
public static class RagRequest {
private String question;
}
}
测试结果
json
{
"sources": [
"官方文档: Spring AI 是一个为 AI 工程开发提供 Spring 友好 API 和抽象的应用框架。\n它..."
],
"question": "什么是 Spring AI?",
"answer": "Spring AI 是一个为 AI 工程开发提供 Spring 友好 API 和抽象的应用框架,旨在简化将大语言模型集成到 Spring 应用中的过程。其核心组件包括 ChatClient、EmbeddingModel 和 VectorStore 等。"
}
4.ReAct 智能体
核心概念 :Agent 比普通对话更高级。它会自主思考 (Reason)、采取行动 (Act)、观察结果(Observe),循环这个过程直到完成任务。就像一个能自己解决问题的智能助理。
Tool拦截器
java
@Component
public class CustomToolInterceptor extends ToolInterceptor {
@Override
public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {
System.out.println("🔥 [拦截器] 即将调用工具: " + request.getToolName());
System.out.println("🔥 [拦截器] 工具参数: " + request.getArguments());
// 调用 handler 继续执行链,真正调用工具
ToolCallResponse response = handler.call(request);
System.out.println("✅ [拦截器] 工具执行完成,");
System.out.println("✅ [拦截器] 返回结果: " + response.getResult());
return response;
}
@Override
public String getName() {
return "customToolInterceptor";
}
}
ReactAgent
java
/**
* Agent 服务
* <p>
* 使用 ReactAgent 实现 ReAct(Reasoning + Acting)模式。
* <p>
*/
@Slf4j
@Service
public class AgentService {
private final DashScopeChatModel chatModel;
private final WeatherTool weatherTool;
private final DateTimeTools dateTimeTools;
private final CustomToolInterceptor customToolInterceptor;
private ReactAgent agent;
public AgentService(DashScopeChatModel chatModel,
WeatherTool weatherTool,
DateTimeTools dateTimeTools,
CustomToolInterceptor customToolInterceptor) {
this.chatModel = chatModel;
this.weatherTool = weatherTool;
this.dateTimeTools = dateTimeTools;
this.customToolInterceptor = customToolInterceptor;
}
/**
* 初始化 Agent,配置其使用的工具和系统提示词
*/
@PostConstruct
public void init() {
// 2. 构建 ReactAgent
this.agent = ReactAgent.builder()
.name("我的智能助理")
.model(chatModel)
.systemPrompt("""
你是一个智能助理,可以查询天气和时间信息。
当用户询问天气时,请先确认城市名称,然后调用 get_weather 工具。
当用户询问时间时,调用 get_current_time 工具。
回答要简洁、友好,用中文。
""")
.tools(ToolCallbacks.from(weatherTool, dateTimeTools))
.interceptors(customToolInterceptor)
.build();
System.out.println("✅ Agent 初始化完成");
}
/**
* 调用 Agent 处理用户问题
* @param question 用户问题
* @return Agent 的回答
*/
public String chat(String question) throws GraphRunnerException {
return agent.call(question).getText();
}
}
使用
java
/**
* Agent 智能体控制器
*/
@RestController
@RequestMapping("/api/agent")
public class AgentChatController {
private final AgentService agentService;
public AgentChatController(AgentService agentService) {
this.agentService = agentService;
}
/**
* Agent 对话接口
* {"question": "帮我查一下北京和上海的天气,然后告诉我哪个城市更暖和"}
*/
@PostMapping("/chat")
public Map<String, String> agentChat(@RequestBody AgentRequest request) throws GraphRunnerException {
String response = agentService.chat(request.getQuestion());
return Map.of(
"question", request.getQuestion(),
"answer", response
);
}
@Data
public static class AgentRequest {
private String question;
}
}