【NexLM 开源系列】如何封装多个大模型 API 调用
🌟 在这系列文章中,我们将一起探索如何搭建一个支持大模型集成项目 NexLM 的开发过程,从 架构设计 到 代码实战,逐步搭建一个支持 多种大模型(GPT-4、DeepSeek 等) 的 一站式大模型集成与管理平台,并集成 认证中心、微服务、流式对话 等核心功能。
🔍 从架构设计到代码实现,一起探讨如何应对不同技术挑战,最终打造出高效、可扩展的大模型平台,目前项目基础架构已经搭建完成。
系列目录规划:
- NexLM:从零开始打造你的专属大模型集成平台 ✅
- Spring Boot + OpenAI/DeepSeek:如何封装多个大模型 API 调用 ✅
- 微服务 + 认证中心:如何保障大模型 API 的安全调用
- 支持流式对话 SSE & WebSocket:让 AI 互动更丝滑
- 缓存与性能优化:提高 LLM API 响应速度
- NexLM 开源部署指南(Docker)
第二篇:Spring Boot + OpenAI/DeepSeek:如何封装多个大模型 API 调用
🎯 如何让你的项目支持 OpenAI、DeepSeek、本地大模型等多种 LLM?
🎯 如何封装 API,做到扩展性强、调用方便?
🎯 这篇文章带你一步步搭建通用 LLM 调用服务!
为什么要封装 LLM API?
在大模型开发中,我们往往需要 支持多个模型,例如:
- GPT-4(OpenAI) :行业最强模型之一,但 API 价格较贵
- DeepSeek:性价比高,部分场景效果接近 GPT-4
- 本地大模型(如 ChatGLM) :适合私有化部署,数据安全
如果在代码里直接写多个 API 请求,会导致 代码冗余、扩展性差。我们需要一个 统一封装的 LLM API 调用层,让项目可以随时切换模型,甚至同时支持多个模型。
效果展示

DeepSeek API 调用交互稍微有点点耗时…目前还没有支持流式输出效果(下一期优化),代码仓库地址:https://github.com/pitt1997/NexLM
代码实现
实现简单的 AI 接口调用还是比较简单,定义接口和具体接口的调用逻辑即可,下面是代码演示。

1)Controller 层
ChatController 定义一个页面的路由地址
@RestController
public class ChatController {/*** chat页面*/@GetMapping("/auth/chat")public ModelAndView chat(ModelAndView modelAndView) {modelAndView.setViewName("ftl/chat");return modelAndView;}
}
ChatApiController 定义一个 API 交互接口。
@RestController
@RequestMapping("/api/ai")
public class ChatApiController {@Autowiredprivate ChatService chatService;@Autowiredprivate JwtTokenProvider jwtTokenProvider;@PostMapping("/chat")public ResponseEntity<String> chat(@RequestBody ChatRequest request, @RequestHeader("Authorization") String token) {// TODO 测试放行(后续加上JWT票据认证)if (token.startsWith("Bearer ")) {return ResponseEntity.ok(chatService.callAIModel(request.getMessage(), request.getModelType()));}// 认证中心解析 JWT 验证权限SessionUser sessionUser = jwtTokenProvider.validateUserToken(token);if (sessionUser == null) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");}return ResponseEntity.ok(chatService.callAIModel(request.getMessage(), request.getModelType()));}
}
2)VO 层
请求对象,主要定义输入的消息和模型的类型。
@Data
public class ChatRequest {private String message; // 用户输入的消息private String modelType; // 选择的大模型,例如 "chatgpt" 或 "local"
}
3)Service 层
这里封装不同模型的调用实现。
@Service
public class ChatService {@Autowiredprivate OpenAIClient openAIClient;@Autowiredprivate DeepSeekClient deepSeekClient;@Autowiredprivate LocalLLMClient localLLMClient;public String callAIModel(String prompt, String modelType) {if ("chatgpt".equalsIgnoreCase(modelType)) {return openAIClient.chat(prompt);} else if ("deepseek".equalsIgnoreCase(modelType)) {return deepSeekClient.chat(prompt);} else if ("local".equalsIgnoreCase(modelType)) {return localLLMClient.chat(prompt);}return "Invalid Model Type";}
}
4)调用大模型接口 API
实现具体大模型的 API 调用逻辑。以 DeepSeek 为例,注意 DeepSeek 需要提前在官网注册密钥。(注:按照官网要求的请求参数格式进行请求即可,注意需要正确解析返回结果中的内容)。
@Component
public class DeepSeekClient {private static final String API_URL = "https://api.deepseek.com/chat/completions"; // DeepSeek API 地址private static final String API_KEY = "你的 DeepSeek API Key"; // 替换为你的 API Keyprivate static final String MODEL = "deepseek-chat"; // deepseek-v3 / deepseek-r1public String chat(String prompt) {RestTemplate restTemplate = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.add("Authorization", "Bearer " + API_KEY);Map<String, Object> body = new HashMap<>();body.put("model", MODEL); // deepseek-v3 / deepseek-r1body.put("temperature", 0.7); // 可调整温度body.put("max_tokens", 2048); // 控制回复长度List<Map<String, String>> messages = Arrays.asList(new HashMap<String, String>() {{put("role", "user");put("content", prompt);}});body.put("messages", messages);HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);ResponseEntity<String> response = restTemplate.exchange(API_URL, HttpMethod.POST, request, String.class);return extractContent(response.getBody());}// 解析一下大模型返回结果的json参数。private String extractContent(String responseBody) {try {ObjectMapper objectMapper = new ObjectMapper();JsonNode root = objectMapper.readTree(responseBody);return root.path("choices").get(0).path("message").path("content").asText();} catch (Exception e) {e.printStackTrace();return "Error parsing response";}}
}
OpenAI 调用逻辑基本一致。
@Component
public class OpenAIClient {private static final String API_URL = "https://api.openai.com/v1/chat/completions";private static final String API_KEY = "你的 OpenAI API Key"; // 请替换为你的 API Keypublic String chat(String prompt) {RestTemplate restTemplate = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.add("Authorization", "Bearer " + API_KEY);Map<String, Object> body = new HashMap<>();body.put("model", "gpt-4");List<Map<String, String>> messages = Arrays.asList(new HashMap<String, String>() {{put("role", "user");put("content", prompt);}});body.put("messages", messages);HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);ResponseEntity<String> response = restTemplate.exchange(API_URL, HttpMethod.POST, request, String.class);return response.getBody();}
}
5)调用本地模型
也可以调用本地部署的大模型(需要提前部署本地大模型,可以看我之前的文章部署方法)。
@Component
public class LocalLLMClient {private static final String LOCAL_MODEL_URL = "http://localhost:5000/api/chat";public String chat(String prompt) {RestTemplate restTemplate = new RestTemplate();Map<String, String> requestBody = new HashMap<>();requestBody.put("prompt", prompt);ResponseEntity<String> response = restTemplate.postForEntity(LOCAL_MODEL_URL, requestBody, String.class);return response.getBody();}
}
3. 前端页面(HTML + JavaScript)
一个简单的 HTML 页面,输入问题后,调用后端 API 获取大模型的回答(暂时使用 HTML 做一个演示)。
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>AI 聊天</title><style>body {font-family: Arial, sans-serif;background: #f4f4f4;margin: 0;padding: 0;display: flex;justify-content: center;align-items: center;height: 100vh;}.chat-container {width: 500px;background: #fff;padding: 20px;border-radius: 10px;box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);}h1 {text-align: center;color: #333;}.input-group {margin: 15px 0;}input, select, button {width: 100%;padding: 10px;margin-top: 5px;border: 1px solid #ccc;border-radius: 5px;}button {background: #007bff;color: white;cursor: pointer;}button:hover {background: #0056b3;}.response {margin-top: 20px;background: #eef;padding: 10px;border-radius: 5px;min-height: 50px;}</style>
</head>
<body>
<div class="chat-container"><h1>大模型 AI 聊天</h1><div class="input-group"><label>输入你的问题:</label><input type="text" id="message" placeholder="请输入问题"></div><div class="input-group"><label>选择模型:</label><select id="modelType"><option value="chatgpt">ChatGPT</option><option value="deepseek">DeepSeek</option><option value="local">本地模型</option></select></div><button onclick="sendMessage()">发送</button><div class="response" id="response">AI 回复将在这里显示...</div>
</div><script>async function sendMessage() {const message = document.getElementById('message').value;const modelType = document.getElementById('modelType').value;// 你的 JWT 令牌const token = getCookie('JSESSIONID'); // 这里应从登录系统获取console.log('JWT Token:', token); // 打印 token 值const response = await fetch('/web/api/ai/chat', {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': `Bearer B6FC7391D7856A16391F9860DA5DA3B8}`},body: JSON.stringify({ message, modelType })});const text = await response.text();document.getElementById('response').innerText = text;}function getCookie(name) {const cookies = document.cookie.split(';');for (let cookie of cookies) {const [cookieName, cookieValue] = cookie.trim().split('=');if (cookieName === name) {return decodeURIComponent(cookieValue);}}return null;}
</script>
</body>
</html>
4. 认证中心(JWT 解析示例)
接口调用时应当校验当前用户的票据(登录时会存储用户会话票据信息)。
// 认证中心解析 JWT 验证权限SessionUser sessionUser = jwtTokenProvider.validateUserToken(token);if (sessionUser == null) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");}
5. 效果测试
🔥 现在,你的 AI 聊天前后端已完成!
- 直接访问登录
http://localhost:8080/web/auth/login页面 - 输出用户名/密码:admin/123456
- 跳转大模型页面,选择对应大模型,开始对话
6. 总结
- 架构:微服务 + 认证中心 + API 网关 + 本地/远程大模型
- Java 代码:完整的 Controller、Service、API 调用示例
- 前端:简单 HTML + JS 渲染
- 认证:JWT 校验
这样,你的系统可以支持用户认证,并调用本地或第三方大模型进行 AI 交互。
结语
本篇文章,我们介绍了 如何封装多个 LLM(大模型) API 调用 。
但是,目前的 对话是一次性返回的,后续我们将完善 微服务 + 认证中心:如何保障大模型 API 的安全调用, 并且支持流式对话(SSE)增加 WebSocket 实时消息实现 实时输出 AI 回复!敬请期待!
📌 下一章预告:SSE + WebSocket 实现流式对话!
📢 你对这个项目感兴趣吗?欢迎 Star & 关注! 📌 GitHub 项目地址
相关文章:
【NexLM 开源系列】如何封装多个大模型 API 调用
🌟 在这系列文章中,我们将一起探索如何搭建一个支持大模型集成项目 NexLM 的开发过程,从 架构设计 到 代码实战,逐步搭建一个支持 多种大模型(GPT-4、DeepSeek 等) 的 一站式大模型集成与管理平台ÿ…...
Git和GitHub基础教学
文章目录 1. 前言2. 历史3. 下载安装Git3.1 下载Git3.2 安装Git3.3 验证安装是否成功 4. 配置Git5. Git基础使用5.1 通过Git Bash使用5.1.1 创建一个新的仓库。5.1.1.1 克隆别人的仓库5.1.1.2 自己创建一个本地仓库 5.1.2 管理存档 5.2 通过Visual Studio Code使用 6. Git完成远…...
笔记六:单链表链表介绍与模拟实现
在他一生中,从来没有人能够像你们这样,以他的视角看待这个世界。 ---------《寻找天堂》 目录 文章目录 一、什么是链表? 二、为什么要使用链表? 三、 单链表介绍与使用 3.1 单链表 3.1.1 创建单链表节点 3.1.2 单链表的头插、…...
坐落于杭州的电商代运营公司品融电商
坐落于杭州的电商代运营公司品融电商 在中国电商行业蓬勃发展的浪潮中,品融电商(PINKROON)作为一家扎根杭州的新锐品牌管理公司,凭借其独特的全域增长方法论和实战经验,迅速崛起为行业标杆。自2020年成立以来&#x…...
微前端之 Garfish.js 的基础使用教程和进阶配置
前言 在现代前端开发中,微前端架构逐渐成为一种流行的解决方案。它允许将大型应用拆分成多个小型独立的子应用,从而提高开发效率和可维护性。Garfish.js 是一个强大的微前端框架,可以帮助我们轻松实现这一架构。在本文中,通过一个…...
图像的特征
图像的特征主要包括以下几类: 1. 颜色特征: 直方图:描述图像中颜色的分布。 颜色矩:通过颜色的均值、方差等统计量表示颜色分布。 主色调:图像中占主导地位的颜色。 2. 纹理特征: 灰度共生矩阵࿰…...
Spring上下文工具类
文章目录 获取ip地址请求上下文相关Spring上下文获取Bean对象 获取ip地址 public class IpUtils {private IpUtils() {}/*** 获取请求ip地址** return {link String}*/public static String getIpAddress() {HttpServletRequest request RequestContextHolderUtils.getReques…...
JSONUtil InvocationTargetException: null
使用JSONUtil时候报错,一般这时候你检查下自己代码里是不是重写了get或者set InvocationTargetException 是 Java 中的一个异常,通常在使用反射(Reflection)或动态代理(Dynamic Proxy)时抛出。它表示在调用…...
高压为什么cover不住低压的hold问题
常规下我们认为hold问题常发生在高压下,但很多情况下高压的hold无法cover低压的hold hold slack (tlaunch -tcapture) (tcqtcomb) -thold -tuncertainty (tlaunch -tcapture):代表时钟skew (tcqtcomb):代表data path的长度 thold:代表查表…...
【算法学习之路】8.栈和队列
栈和队列 前言一.简介二.题目12 前言 我会将一些常用的算法以及对应的题单给写完,形成一套完整的算法体系,以及大量的各个难度的题目,目前算法也写了几篇,题单正在更新,其他的也会陆陆续续的更新,希望大家点…...
OpenMCU(三):STM32F103 FreeRTOS移植
概述 本文主要描述了STM32F103移植FreeRTOS的简要步骤。移植描述过程中,忽略了Keil软件的部分使用技巧。默认读者熟练使用Keil软件。本文的描述是基于OpenMCU_RTOS这个工程,该工程已经下载放好了移植STM32F103 FreeRTOS的所有文件 OpenMCU_RTOS工程的愿景…...
大数据 spark hive 总结
Apache Spark 简介 是一个开源的统一分析引擎,专为大规模数据处理而设计。它提供了高级API,支持Java、Scala、Python和R语言,并且包含了一个优化过的执行引擎,该引擎支持循环计算(如机器学习算法)和交互式…...
小程序开发总结
今年第一次帮别人做小程序。 从开始动手到完成上线,一共耗时两天。AI 让写代码变得简单、高效。 不过,小程序和 Flutter 等大厂开发框架差距实在太大,导致我一开始根本找不到感觉。 第一,IDE 不好用,各种功能杂糅在…...
QLoggingCategory类使用
QLoggingCategory类使用 QLoggingCategory的概述 QLoggingCategory是Qt的日志策略类;可以通过声明不同的日志策略对象来输出不同的日志信息。打印信息类型如下:宏 Q_DECLARE_LOGGING_CATEGORY(name) 定义一个返回QLoggingCategory对象函数,…...
GPU加速生信分析-宏基因组MAG去污染
Deepurify利用多模态深度语言模型来过滤污染的基因组,从而提高了宏基因组组装基因组(MAGs)的质量,并且可以利用GPU加速。 宏基因组组装的基因组 (MAG) 为使用宏基因组测序数据探索微生物暗物质提供了有价值…...
数据结构(蓝桥杯常考点)
数据结构 前言:这个是针对于蓝桥杯竞赛常考的数据结构内容,基础算法比如高精度这些会在下期给大家总结 数据结构 竞赛中,时间复杂度不能超过10的7次方(1秒)到10的8次方(2秒) 空间限制&#x…...
从0到1入门Linux
一、常用命令 ls 列出目录内容 cd切换目录mkdir创建新目录rm删除文件或目录cp复制文件或目录mv移动或重命名文件和目录cat查看文件内容grep在文件中查找指定字符串ps查看当前进程状态top查看内存kill终止进程df -h查看磁盘空间存储情况iotop -o直接查看比较高的磁盘读写程序up…...
灰色地带规避:知识产权校验API的商标库模糊匹配算法
在反向海淘或其他电商业务场景中,为了规避知识产权方面的灰色地带,开发知识产权校验 API 并运用商标库模糊匹配算法是很有必要的。以下将详细介绍商标库模糊匹配算法的设计与实现: 算法设计思路 商标库模糊匹配算法的核心目标是在给定一个待匹…...
React:类组件(中)
dangerouslySetInnerHTML React写进{}内的东西,不允许被当作代码块解析,是为了防止xss攻击和代码注入 XSS(跨站脚本攻击,Cross-Site Scripting) 是一种常见的安全漏洞,攻击者通过注入恶意脚本到网页中&…...
第六次CCF-CSP认证(含C++源码)
第六次CCF-CSP认证 数位之和(easy)思路及AC代码遇到的问题 开心消消乐(easy)思路及AC代码 画图(mid)思路及AC代码 数位之和(easy) 题目链接 思路及AC代码 既然题目要求我们输出各位…...
SpringBoot 如何调用 WebService 接口
前言 调用WebService接口的方式有很多,今天记录一下,使用 Spring Web Services 调用 SOAP WebService接口 一.导入依赖 <!-- Spring Boot Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId…...
算法 之 树形dp 树的中心、重心
文章目录 重心实践题目小红的陡峭值 在树的算法中,求解树的中心和重心是一类十分重要的算法 求解树的重心 树的重心的定义:重心是树中的一个节点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点…...
Docker 配置镜像源
》》Daemon {"registry-mirrors": ["https://docker.1ms.run","https://docker.xuanyuan.me"] }》》》然后在重新 docker systemctl restart docker...
Linux 离线部署Ollama和DeepSeek-r1模型
都在复制粘贴联网状态下linux部署deepseek,离线状态下需要下载Ollama和DeepSeek模型,然后将下载包上传到linux中。 1、下载Ollama https://github.com/ollama/ollama/releases 注意:如果CentOS7建议安装V0.5.11版本,V0.5.13需要…...
SQLAlchemy系列教程:如何执行原生SQL
Python中的数据库交互提供了高级API。但是,有时您可能需要执行原始SQL以提高效率或利用数据库特定的特性。本指南介绍在SQLAlchemy框架内执行原始SQL。 在SQLAlchemy中执行原生SQL SQLAlchemy虽然以其对象-关系映射(ORM)功能而闻名ÿ…...
RuleOS:区块链开发的“新引擎”,点燃Web3创新之火
RuleOS:区块链开发的“新引擎”,点燃Web3创新之火 在区块链技术的浪潮中,RuleOS宛如一台强劲的“新引擎”,为个人和企业开发去中心化应用(DApp)注入了前所未有的动力。它以独特的设计理念和强大的功能特性&…...
【编译器】VSCODE烧录ESP32-C3——xiaozhi智能聊天机器人固件
【编译器】VSCODE烧录ESP32-C3——xiaozhi智能聊天机器人固件 文章目录 [TOC](文章目录) 前言一、方法一:使用固件烧录工具1. 安装CH340驱动2. 打开FLASH_DOWNLOAD文件3. 选择芯片类型和烧录方式4. 选择烧录文件5. 参数配置 二、方法二:VSCODE导入工程1.…...
设计模式文章汇总-Golang语言实现
Golang学习笔记_27——单例模式 Golang学习笔记_28——工厂方法模式 Golang学习笔记_29——抽象工厂模式 Golang学习笔记_30——建造者模式 Golang学习笔记_31——原型模式 Golang学习笔记_32——适配器模式 Golang学习笔记_33——桥接模式 Golang学习笔记_34——组合模式 Gola…...
显式 GC 的使用:留与去,如何选择?
目录 一、什么是显式 GC? (一) 垃圾回收的基本原理 (二)显式 GC 方法和行为 1. System.gc() 方法 2. 显式 GC 的行为 (三)显式 GC 的使用场景与风险 1. JVM 如何处理显式 GC 2. 显式 GC…...
SpringMVC概述以及入门案例
目录 SpringMVC概述 为什么需要Spring MVC? SpringMVC入门 工作流程分析 SpringMVC概述 SpringMVC技术与Servlet技术功能等同,均属于Web层开发技术。SpringMVC是一种基于java实现MVC模型的轻量级Web框架。 为什么需要Spring MVC? 在传统J…...
