AI 大模型统一集成|Spring AI + DeepSeek 实战接入指南

发布于:2025-06-09 ⋅ 阅读:(21) ⋅ 点赞:(0)

AI 大模型统一集成|Spring AI + DeepSeek 实战接入指南

🌟 在这系列文章中,我们将一起探索如何搭建一个支持大模型集成项目 ChatAI 的开发过程,从 架构设计 到 代码实战,逐步搭建一个支持 多种大模型(GPT-4、DeepSeek 等) 的 一站式大模型集成与管理平台,并集成 认证中心、微服务、流式对话 等核心功能。

本文将结合 Spring AI 与大语言模型,演示如何借助 Tool Calling 实现系统 API 的调用,并支持多轮对话中的上下文记忆处理,从而构建更智能、实用的企业级 AI 应用。并成功打通 DeepSeek 大模型,实现统一的 AI 接入方案。


✨ 为什么选择 Spring AI + DeepSeek?

在多模型百花齐放的时代,OpenAI、DeepSeek、Moonshot、Claude 各有千秋,我们如何实现统一接入、灵活切换、快速调用

Spring 团队开源的 Spring AI 正是为此而生,它提供了统一的编程模型,支持主流 AI 平台(OpenAI、Azure、HuggingFace、DeepSeek 等)。

而 DeepSeek 则是近年来在中文语义、代码能力方面表现优异的国产大模型,支持 OpenAI 接口风格,适配性强。


⚙️ Spring AI 工具调用

Spring AI 工具调用与不同 AI 模型一起使用的更多信息,请遵循工具调用文档
在这里插入图片描述

工具元数据注入

在发起 Chat Request 时,将工具描述(name/description)、参数结构(input schema)等元数据封装至请求体,建立大模型的工具调用能力基线。每个工具定义都包含输入参数的名称、描述和方案。

模型决策响应

当模型决定调用工具时,它会发送一个响应,其中包含工具名称和输入参数,这些参数在定义的架构之后建模。
大模型根据上下文推理生成工具调用指令(tool_calls字段),返回包含选定工具名称及结构化参数的中间响应。

服务端路由执行

应用程序负责使用工具名称来识别并使用提供的输入参数执行工具。
Spring AI 模块解析工具调用指令,通过服务发现机制定位目标工具实例,注入参数并触发同步/异步执行。

执行结果标准化

工具调用的结果由应用程序处理。
工具返回原始执行结果后,系统进行数据类型校验、异常捕获和JSON序列化处理,生成模型可解析的标准化数据结构。

上下文增强推理

应用程序将工具调用结果发送回模型。
将标准化结果作为新增上下文(tool_outputs)回传大模型,触发基于增强上下文的二次推理流程。

终端响应生成

模型综合初始请求与工具执行结果,生成最终自然语言响应,完成工具增强的对话闭环。

🔧 环境准备

✅ 依赖版本

  • Spring Boot: 3.4.1
  • Spring AI: 1.0.0-M6
  • JDK: 21

✅ Maven 依赖配置

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-dependencies</artifactId>
	<version>3.4.1</version>
	<type>pom</type>
	<scope>import</scope>
</dependency>
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
  <version>1.0.0-M6</version>
</dependency>

虽然依赖名为 openai,但因为 DeepSeek 模拟了 OpenAI 接口协议,所以可以通用。


⚙️ DeepSeek 接口说明

DeepSeek 官方接口文档:https://api-docs.deepseek.com/zh-cn/

在这里插入图片描述

DeepSeek 官方提供的 API 接口地址:

https://api.deepseek.com

🛠️ Spring AI 配置 DeepSeek

application.yml 中添加如下配置:

spring:
  ai:
    openai:
      api-key: sk-xxx  # deepseek 密钥
      base-url: https://api.deepseek.com
      chat:
        options:
            model: deepseek-reasoner

📦 构建一个 Spring AI 工具调用

SpringAiAiInvoke

代码很简单,由于引入 spring ai 的 stater 之后按照模型配置接入即可。然后注入一个 ChatModel 。我们测试接口可以将实现类继承接口 CommandLineRunner 可在 SpringBoot 启动时自动执行方便查看效果。

import jakarta.annotation.Resource;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

/**
 * Spring AI 框架调用 AI 大模型(Deepseek)
 */
@Component
public class SpringAiAiInvoke implements CommandLineRunner {

    @Resource
    private ChatModel deepseekChatModel;

    @Override
    public void run(String... args) throws Exception {
        AssistantMessage assistantMessage = deepseekChatModel.call(new Prompt("你好!"))
                .getResult()
                .getOutput();
        System.out.println("DeepSeek大模型调用结果:" + assistantMessage.getText());
    }
}

执行测试结果:

在这里插入图片描述


📦 Spring AI 上下文记忆

大型语言模型 (LLM) 是无状态的,这意味着它们不会保留有关以前交互的信息。当你希望在多个交互中维护上下文或状态时,这可能是一个限制。为了解决这个问题,Spring AI 提供了聊天内存功能,允许你在与 LLM 的多次交互中存储和检索信息。

可以基于 ChatMemory 抽象实现各种类型的内存以支持不同的使用案例。 消息的底层存储由ChatMemoryRepository,其唯一职责是存储和检索消息。这取决于ChatMemoryimplementation 来决定要保留哪些消息以及何时删除它们。策略示例可能包括保留最后 N 条消息、将消息保留一段时间或将消息保持在某个Tokens限制内。

其中 MessageChatMemoryAdvisor 实现聊天记忆,InMemoryChatMemory 为 SpringAI 自带的实现(基于内存)。可以使用 CHAT_MEMORY_CONVERSATION_ID_KEY 参数指定对话ID,不同的会话ID用于隔离记忆。

@Component
@Slf4j
public class AgentApp {

    private final ChatClient chatClient;

    private static final String SYSTEM_PROMPT = "你是一个专业的天气助手,必须用中文回答,回答需包含温度、湿度和风力。";

    /**
     * 初始化 ChatClient
     *
     * @param deepseekChatModel
     */
    public AgentApp(ChatModel deepseekChatModel) {
        // 初始化基于内存的对话记忆
        ChatMemory chatMemory = new InMemoryChatMemory();
        chatClient = ChatClient.builder(deepseekChatModel)
//                .defaultSystem(SYSTEM_PROMPT)
                .defaultAdvisors(
                        new MessageChatMemoryAdvisor(chatMemory),
                        // 自定义日志 Advisor,可按需开启
                        new LoggerAdvisor()
//                        // 自定义推理增强 Advisor,可按需开启
//                       ,new ReReadingAdvisor()
                )

                .build();
    }

    /**
     * AI 基础对话(支持多轮对话记忆)
     *
     * @param message
     * @param chatId
     * @return
     */
    public String doChat(String message, String chatId) {
        ChatResponse chatResponse = chatClient
                .prompt()
                .user(message)
                .advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .call()
                .chatResponse();
        String content = chatResponse.getResult().getOutput().getText();
        log.info("content: {}", content);
        return content;
    }

    /**
     * AI 基础对话(支持多轮对话记忆)
     *
     * DeepSeek Reasoner 严格要求 system 消息必须是第一条 显示声明
     *
     * @param message
     * @param chatId
     * @return
     */
    public String doChatDeepSeek(String message, String chatId) {
        ChatResponse chatResponse = chatClient
                .prompt()
                // .system(SYSTEM_PROMPT)
                .user(message)
                .advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .call()
                .chatResponse();
        String content = chatResponse.getResult().getOutput().getText();
        log.info("content: {}", content);
        return content;
    }
}

LoggerAdvisor 可以打印大模型内容。这里补充一下 Advisor 的作用,可以理解为作为模型调用前或者调用后的拦截器。


import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.advisor.api.*;
import org.springframework.ai.chat.model.MessageAggregator;
import reactor.core.publisher.Flux;

/**
 * 自定义日志 Advisor
 * 打印 INFO 级别日志、只输出单次用户提示词和 AI 回复的文本
 */
@Slf4j
public class LoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private AdvisedRequest before(AdvisedRequest request) {
        log.info("AI Request: {}", request.userText());
        return request;
    }

    private void observeAfter(AdvisedResponse advisedResponse) {
        log.info("AI Response: {}", advisedResponse.response().getResult().getOutput().getText());
    }

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

        advisedRequest = before(advisedRequest);

        AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);

        observeAfter(advisedResponse);

        return advisedResponse;
    }

    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

        advisedRequest = before(advisedRequest);

        Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);

        return new MessageAggregator().aggregateAdvisedResponse(advisedResponses, this::observeAfter);
    }

}

接下来使用 Junit 测试看看效果。

@SpringBootTest
class AgentAppTest {

    @Resource
    private AgentApp agentApp;

    @Test
    void doChat() {
        String chatId = UUID.randomUUID().toString();
        // 第一轮
        String message = "你好,我是小李。";
        String answer = agentApp.doChatDeepSeek(message, chatId);
        // 第二轮
        message = "今天天气如何?";
        answer = agentApp.doChatDeepSeek(message, chatId);
        Assertions.assertNotNull(answer);
        // 第三轮
        message = "你知道叫什么来着?刚跟你说过。";
        answer = agentApp.doChatDeepSeek(message, chatId);
        Assertions.assertNotNull(answer);
    }
}

自测效果:

在这里插入图片描述

通过连续对话3次,最后一次提问此前给过的信息,可以看到大模型的结果中正常回答,说明聊天记忆功能已生效。

💡 注意事项

DeepSeek 使用 OpenAI 接口兼容层

  • 实际上是伪装成 OpenAI,所以你必须手动设置 base-urlapi-key
  • 模型名称建议使用deepseek-chatdeepseek-coder(根据模型需求场景)。

🔄 后续如何扩展?

  • ✅ 替换为 Claude、Gemini、Moonshot 等其他模型,只需改配置即可;
  • ✅ 接入 RAG(支持 Embedding);
  • ✅ 搭建前端 Chat UI(如:Cursor、NextChat、ChatUI);
  • ✅ 封装为微服务,支持多用户、多模型切换。

📢 你对这个项目感兴趣吗?欢迎 Star & 关注! 📌 GitHub 项目地址 🌟 你的支持是我持续创作的动力,欢迎点赞、收藏、分享!

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到