003 Spring AI Alibaba框架整合百炼大模型平台 — Memory会话记忆、Tool工具、RAG增强检索、ReAct智能体

Spring AI Alibaba框架整合百炼大模型平台

文章目录

版本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;
    }
}
相关推荐
F_U_N_2 小时前
从文档解析到知识运营:AI闭环实践手册
人工智能
PD我是你的真爱粉2 小时前
AI Agent 完全指南:LangChain Agent、ReAct、Copilot-Agent 模式、Manus、Computer Use 与记忆机制
人工智能·react.js·langchain
黑金IT2 小时前
从“视觉断言”到“自动化指挥”:Qwen3-V2 如何终结 AI 的随机性
运维·人工智能·自动化
东北洗浴王子讲AI2 小时前
GPT-5.4辅助机器学习论文写作:从构思到发表的全流程指南
人工智能·gpt·自然语言处理
凤年徐2 小时前
OpenClaw深度解析:“数字龙虾”何以引爆AI Agent时代?安全危机与未来之战
人工智能·安全
英俊潇洒美少年2 小时前
Vue3 实现 AI 流式打字机(SSE+时间切片模拟 React 并发)工程化完整版
前端·人工智能·react.js
帮我吧智能服务平台2 小时前
装备制造服务数字化痛点破解:大模型+协同工具的实战应用
大数据·人工智能·制造
胡单纯2 小时前
AI 直接解析 PDF 文档!OpenClaw 2026.3.3 新功能实测太强了
数据库·人工智能·pdf
盟接之桥2 小时前
盟接之桥®说制造:从“制造”到“智造”,以品类品牌重塑制造业的生态未来
大数据·网络·人工智能·学习·制造