最近好几个项目里都碰到同一个诉求内网环境中的 Java 系统想接大模型能力但数据不能出内网成本又不想被 API 调用费拖死。聊下来结论都落在同一套组合上——本地把 DeepSeek、千问这类开源模型跑起来应用侧用 Spring AI 统一接入。这套方案既能保住数据隐私又能让后端团队不绑定某一家云厂商而且现在 Ollama、vLLM 这类工具已经把本地部署的门槛压得很低个人电脑和企业服务器都能跑。这篇文章就完整记录一下我从零开始接入的实践过程包括模型选型、硬件算账、Spring AI 配置、Function Calling、RAG 知识库以及从单机走向多人团队时必然会踩的坑。1. 为什么偏偏是本地大模型从“个人电脑智能化”说起先说动机。很多人第一次想部署本地大模型是因为自媒体一遍遍说“让你的电脑变聪明”但真正落地时发现聪明在哪、怎么接进自己的系统没人讲清楚。我的观点很直接——本地部署不是目的让现有系统多出一个“会思考的接口”才是目的。而 Spring AI 恰恰是把这个接口标准化了的那个角色。当时的实际场景是这样的团队想给内部 OA 加一个智能问答入口让员工问“报销流程是什么”“去年年会订的哪家供应商”大模型需要读取企业内部的制度文档。方案选型时列了三条路调云端 API明文出网法务先否了、私有化部署要买卡有运维成本、本地单机跑小模型先验证能不能用另说。最后选择先本地跑因为验证成本最低。这个选择背后有几笔账值得算清楚。成本账。云端 API 按 token 计费看似便宜但一旦做成全员功能一天几千次调用一个月下来就是不小的开支。而且企业内部知识问答往往要附带长上下文一次问答输入几千 token费用翻倍。本地模型一次性投入硬件之后推理免费按照 200 人团队使用一年来算只要调用频率够高本地部署很快就摊平了成本。隐私账。企业内部文档、代码片段、财务数据很多都不适合放到第三方 API 上。哪怕云厂商承诺不用于训练合规这一关也很难过。本地部署让数据从物理上不离开内网这是很多企业选择本地模型的头号原因。技术账。本地模型虽然单次能力不如云端最强模型但胜在可控、可定制、可离线。你可以换基础模型可以微调可以在 prompt 层随便折腾不会有账号被封或者接口限流的担忧。那 Spring AI 在这里面解决什么它解决的是“换模型不换代码”的问题。Java 生态里接入 AI 最让人头疼的就是各家 SDK 不统一今天写 OpenAI 的明天换百炼代码就要重构。Spring AI 在 Model、ChatClient、VectorStore 等层次上做了统一抽象底层换成 Ollama、OpenAI、百炼、DeepSeek业务代码基本不用动。这对中大型 Java 项目来说价值比那点封装代码大得多。2. 先把模型跑起来机器配置算账、Ollama 安装与模型选择接入 Spring AI 之前得先让本地模型能对话。很多人一上来就 ollama run 一个 70B 模型然后爆显存再然后得出结论本地大模型不靠谱。实际是选型选错了。2.1 硬件账本显存是硬约束别只盯着 CPU 和内存大模型推理吃的是显存。模型权重放不进显存就只能挤内存一旦走内存速度会掉到每秒几个 token体验直接不可用。所以第一步就是对着模型大小算显存。粗略公式模型文件大小就是权重占用的磁盘空间加上 1~2GB 的 KV Cache 预留就是你需要的显存底线。拿常见量化模型举例模型显存需求Q4 量化最低显卡建议7B / 8BDeepSeek-R1-Distill、Qwen2.5-7B6~8GB8GB 以上14BDeepSeek-R1-Distill-14B、Qwen2.5-14B10~12GB16GB32BQwen2.5-32B20~22GB24GB或者双卡70B / 72B40GB 以上双卡/多卡或者干脆用 vLLM 部署个人电脑想跑得舒服优先看 7B~14B 这个档位。8GB 显存的笔记本也能跑 7B但上下文窗口拉长后速度会明显下降16GB 显存比如一块 4090 笔记本卡或 4060Ti 16G跑 14B 是比较舒服的组合。这里有个经验不要一看到 DeepSeek 就想去跑官方那个 671B 的大模型那需要至少一台 8 卡 A100/H100 的服务器。个人和中小企业用的都是蒸馏版或者量化版比如deepseek-r1:14b是基于 Qwen 蒸馏出来的 14B 模型能力已经覆盖大多数日常推理和代码场景。2.2 Ollama 安装与模型拉取Ollama 是目前个人电脑上部署本地模型最省事的工具没有之一。它对底层的 llama.cpp 做了封装自带量化转换、显存管理、API 服务Windows、macOS、Linux 都有安装包。安装完成后终端执行ollama pull deepseek-r1:14b ollama pull qwen2.5:14bollama list # 查看本地已有模型 ollama run deepseek-r1:14b # 直接对话测试 ollama ps # 查看当前加载在显存里的模型和占用ollama pull拉取的是 GGUF 量化格式。默认一般拉的是 Q4_K_M也就是 4-bit 量化质量和显存占用平衡得比较好。拉完跑一下ollama run确认能正常对话这一步就算完成。Ollama 装好后会自动监听11434端口这个端口就是后续 Spring AI 要连接的底座。需要注意如果机器上已经跑了很多服务11434 被占用可以通过环境变量OLLAMA_HOST修改监听地址例如OLLAMA_HOST0.0.0.0:11435。2.3 量化等级怎么选Q4 与 Q8 的体验差异很多新手不知道 GGUF 量化是什么这里用大白话解释大模型权重是大量浮点数完整精度 FP16 占 2 字节/参数如果压缩成 4-bit 量化内存占用降到接近 1/4代价是模型回答质量略降。量化等级相对体积显存占用回答质量适用场景FP16100%最高最佳显存充足的服务器Q8_0~55%中等接近无损显存有余量时推荐Q4_K_M~35%低可接受复杂推理略降个人电脑首选Q3 / Q2~25%最低明显下降显存不足时的妥协方案我个人的建议个人电脑无脑选 Q4_K_M体验和显存占用最均衡。企业服务器如果显存有富余优先 Q8_0质量更稳。不要在 Q4 和 Q8 之间反复纠结先跑起来再说后面换模型文件只是换一个 tag 的事。3. Spring AI 接入实战从配置文件到冒烟测试的完整链路模型在本地跑通了接下来才是重头戏——用 Spring AI 把它接进 Java 后端。3.1 Spring AI 是什么为什么选它Spring AI 是 Spring 官方推出的 AI 应用开发框架核心思路是把“调用大模型”封装成类似JdbcTemplate那样统一的访问方式。你面对的是ChatClient而不是某一家厂商的 SDK。底层接 Ollama、OpenAI 兼容接口、阿里百炼、DeepSeek API只需要改配置业务代码几本不动。热词里频繁出现spring ai alibaba、spring ai 2.0这些词我的理解是Spring AI 本身在快速迭代Spring AI Alibaba 是阿里基于 Spring AI 做的百炼适配。用哪个取决于你最终连哪个模型服务。这篇文章以 Ollama 本地模型为主线会单独拿出一节讲云端接入的对比。3.2 引入依赖与配置Maven 项目加 Spring AI BOM 和 Ollama Starter版本以官方 BOM 为准dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency /dependencies配置application.ymlspring: ai: ollama: base-url: http://localhost:11434 chat: options: model: qwen2.5:14b temperature: 0.7 top-p: 0.9 # 非必填多少毫秒内没响应则中断 # timeout: 60s这里有一个新手极容易犯的错spring.ai.ollama.chat.options.model填的模型名称必须和ollama list看到的完全一致多一个空格、少一个 tag 都会报模型不存在。3.3 同步调用与流式调用业务代码里最常用的是注入ChatClient。Spring AI 的ChatClient提供了流式 API 的编程接口同步和流式之间只差一个方法名Service public class LlmService { private final ChatClient chatClient; public LlmService(ChatClient.Builder builder) { this.chatClient builder.build(); } public String chat(String message) { return chatClient.prompt() .user(message) .call() .content(); } public FluxString stream(String message) { return chatClient.prompt() .user(message) .stream() .content(); } }同步调用适合内部接口比如定时任务生成摘要流式调用适合 Web 前端因为它能做到一个字一个字地往外吐用户等待体感好得多。Controller 这样写RestController RequestMapping(/api/ai) public class AiChatController { private final LlmService llmService; public AiChatController(LlmService llmService) { this.llmService llmService; } PostMapping(/chat) public ApiResponseString chat(RequestBody ChatRequest request) { return ApiResponse.ok(llmService.chat(request.message())); } PostMapping(value /chat/stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxServerSentEventString stream(RequestBody ChatRequest request) { return llmService.stream(request.message()) .map(token - ServerSentEvent.builder(token).build()); } }流式接口返回TEXT_EVENT_STREAM_VALUE前端用EventSource或fetch的 ReadableStream 就能直接接。这在 Spring AI 里属于最基础的用法但能把这一条链路跑通你已经完成了从模型到业务系统的第一座桥。3.4 冒烟测试写完后启动 Spring Boot 应用用 curl 做一次冒烟测试curl -X POST http://localhost:8080/api/ai/chat \ -H Content-Type: application/json \ -d {message:用一句话介绍你自己}正常的话几秒内会返回一段文本。第一次调用可能会慢因为 Ollama 需要在首次请求时把模型加载进显存后续调用就快了。如果请求报错优先去看 Ollama 的日志。Spring AI 侧的问题一般集中在版本不匹配我会在最后一节专门讲避坑。4. 两种接入路线的取舍本地 Ollama 与云端百炼/DeepSeek API前面说的都是本地模型但实际项目中云端 API 仍然很重要。热词里反复出现“spring alibaba ai 连接百炼 qwen3.7”“deepseek api 如何调用”说明大量 Java 开发者也在关注云端路线。4.1 Spring AI Alibaba 对接百炼 Qwen如果你决定用阿里云百炼的 Qwen 系列Spring AI Alibaba 提供了现成的 Starterdependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter/artifactId /dependencyspring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus用这种方式业务代码层面和 Ollama 那条线几乎一样都是ChatClient。这也是我说 Spring AI 最大价值在于统一抽象的原因——项目早期接百炼中期想换成自建 vLLM代码不用动。4.2 DeepSeek API 的接入方式DeepSeek 官方 API 走的是 OpenAI 兼容协议所以接入 Spring AI 有两条路一是使用专门的spring-ai-starter-model-deepseek如果当时版本已提供二是直接配置为 OpenAI 兼容接口spring: ai: openai: base-url: https://api.deepseek.com api-key: ${DEEPSEEK_API_KEY} chat: options: model: deepseek-chat注意当base-url指到 DeepSeek 或 OpenAI 这类云端服务时api-key是必须的。反过来本地 Ollama 根本不需要 API Key。这个问题我见过太多次——本地 Ollama 配得好好的后来为了测试云端模型往配置里加了spring.ai.openai.api-key再把base-url来回改结果请求全部串到云端去了自己还不知道。4.3 本地与云端的对照选择维度本地 Ollama / vLLM云端百炼 / DeepSeek API初始成本硬件投入一次性几万到几十万零起步按 token 付费边际成本极低电费可忽略用量大了成本可观数据隐私数据不出内网依赖厂商合规承诺模型能力上限受限于本地硬件可用最新最强模型运维复杂度需要自己管模型、显存、升级厂商兜底高并发支撑需要自建推理集群弹性扩缩容我的建议是不要二选一做混合路由。把敏感数据、高频简单问答放到本地模型把复杂推理、长文档理解、代码生成这类对模型能力要求高的任务放到云端。很多团队用一个很简单的策略System Prompt 里判断问题类别或者业务侧根据接口类型路由。Spring AI 的ChatClient本身不限制你创建多个实例你可以分别注入localChatClient和cloudChatClient业务层按规则选择。5. 让模型真正干活Function Calling 与本地知识库 RAG模型只会聊天是没有价值的真正接入业务系统关键在两个能力一个是让模型能调用你的业务方法Function Calling / Tool Calling另一个是让它能回答你私域知识的问题RAG。5.1 Function Calling 配置什么叫 Function Calling简单说模型回答“北京天气怎么样”时不再凭空编而是先生成“调用天气查询函数并传参 city北京”的结构化指令由你的后端代码去查 API再把结果给模型模型基于结果组织话术。Spring AI 里用Tool注解直接暴露方法Component public class WeatherTools { Tool(查询指定城市当天的天气) public String currentWeather(String city) { // 这里可以调用你现有的 HTTP API、数据库查询 return 晴天气温 26 度微风; } }调用时String result chatClient.prompt() .user(北京天气怎么样) .tools(new WeatherTools()) .call() .content();走通这一步的意义很大本地模型从“知识库型助手”变成了“能操作系统的助手”。你可以用它查库存、发审批、拉报表甚至可以把它嵌进业务流程让用户用自然语言触发一段工作流。热词里提到“dify 工作流转成 Spring AI Java 代码”本质上就是把可视化编排的节点用 Java 重写而 Tool Calling 就是那些节点的执行器。需要注意不是所有本地模型都支持 Tool Calling。Qwen2.5 系列和 DeepSeek-R1 蒸馏版都可以但部分老模型不支持。用 Ollama 跑之前先看一眼模型标签页的工具支持说明。5.2 本地知识库搭建RAG 的最小可行方案RAG检索增强生成是本地大模型在企业场景最有价值的应用。流程不复杂文档预处理把 PDF、Word、TXT 切分成段落每段几百到一千字向量化用 Embedding 模型把每段文字变成向量存向量库支持相似度检索的数据库检索用户提问时把问题向量化在向量库里找最相关的几段生成把检索到的段落和原始问题一起塞进 prompt让模型基于资料回答。Spring AI 里可以把 Embedding 模型也接在 Ollama 上ollama pull bge-m3Spring AI 配置Bean EmbeddingModel embeddingModel(OllamaApi ollamaApi) { return new OllamaEmbeddingModel(ollamaApi, OllamaOptions.builder().model(bge-m3).build()); } Bean VectorStore vectorStore(EmbeddingModel embeddingModel) { return SimpleVectorStore.builder(embeddingModel).build(); }向量库用SimpleVectorStore内存实现做验证够了生产环境建议上 PGVector 或 Milvus。查询的时候用 Spring AI 的QuestionAnswerAdvisorSearchRequest request SearchRequest.builder() .query(保修期多久) .topK(5) .build(); String answer chatClient.prompt() .user(根据资料回答保修期多久) .advisors(new QuestionAnswerAdvisor(vectorStore, request)) .call() .content();这段代码的背后逻辑是Spring AI 自动把用户问题拿去向量库检索把命中的文档片段插入 prompt然后让模型回答。对使用者来说效果就是“这个 AI 懂我们公司”。这里提醒一句RAG 的效果上限取决于 Embedding 模型和切分策略而不取决于生成模型。很多时候你觉得 AI 回答得不对不是大模型不行而是你检索回来的资料不对。先检查切分是否拆断了语义再检查检索到的 topK 是否相关。5.3 本地知识库在个人电脑上的玩法如果你的电脑只有 8GB 显存跑不了 7B 以上也可以玩 RAG。Embedding 模型很小BGE-M3 也就 1~2GB生成模型用 Qwen 7B 的 Q4 量化整体流程完全跑得动。我经常在笔记本上挂一个几百页的技术文档问问题比翻文档快太多这是个人电脑最实在的智能化方向。6. 从单机到团队vLLM 部署、并发容量估算与运维成本个人电脑上 Ollama 跑通了下一步是团队使用。这也是热词里“搭建一个200人用的本地大模型需要多少钱”“4显卡部署”背后的问题。6.1 什么时候必须换 vLLMOllama 在单机、低并发下体验很好但一旦到了二三十人同时用Ollama 默认的并发处理能力就不够了表现为排队耗时、首字延迟拉高。这个时候换 vLLM 是主流选择。vLLM 的优势是 PagedAttention 显存管理能把吞吐量提上去并且原生兼容 OpenAI API。vLLM 启动命令示例假设有两张 GPUpython -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-14B-Instruct \ --served-model-name qwen2.5-14b \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.9 \ --port 8000Spring AI 接 vLLM 的时候不需要 Ollama Starter而是用 OpenAI 兼容配置spring: ai: openai: base-url: http://localhost:8000/v1 api-key: EMPTY chat: options: model: qwen2.5-14b注意vLLM 的--served-model-name要和 Spring AI 配置里的 model 一致否则请求会被拒绝。6.2 200 人团队的容量估算容量估算我先给一个经验值再解释逻辑一个 200 人的企业工具场景峰值并发一般不会超过总人数的 10%也就是 20 个并发请求。20 并发对体感流畅来说意味着系统每秒能处理 2~5 个请求QPS 2~5每个请求响应时间控制在 5 秒以内。这个目标下硬件怎么选只做知识问答、轻量办公辅助14B Q4 量化模型 一张 24GB 显卡如 4090、L20、3090就能扛住需要代码生成、长文档总结32B~72B 模型至少需要两张 24GB 卡或者一张 48GB 卡用 vLLM 做 Tensor Parallel 并行追求高质量输出比如 DeepSeek-R1 的复杂推理70B 量化也需要 40GB 显存考虑 2×24GB 或 2×48GB。以上说的都是推理显存KV Cache 还会随并发和上下文长度增长。因此我一般建议显存要留有 20% 余量。4 张显卡跑 14B 模型绰绰有余跑 70B 则是标配热词里“4显卡部署大模型”通常就是这么来的。6.3 二三十万买硬件部署大模型运维工作量有多大热词里有个很直白的问题“如果本地花了二三十万买硬件部署本地大模型会有运维工作量吗”我的回答是有但和训练大模型比不值一提和企业现有后端服务比也高不到哪去。运维工作主要集中在这几块模型生命周期模型文件动辄几十 GB更新、回滚、多版本并存需要管理推理服务监控显存占用、GPU 温度、QPS、错误率镜像 Prometheus Grafana 一套下来业务迭代prompt 调优、RAG 切分策略调整、工具函数新增这些比部署本身更花时间稳定性Ollama 或 vLLM 进程挂掉、显存泄漏、OOM都需要处理。我的建议是别一开始就上 K8s。二三十万预算里你先留出 20% 的预算给运维工具和人力而不是全砸硬件。先用一台物理机 Docker Compose 把 Ollama/vLLM、向量库、应用服务编排好观察两个月的真实用量再决定要不要扩容。很多人高估了模型推理的资源需求低估了 RAG 数据准备和权限管理的复杂度。7. 避坑记录版本兼容、流式乱码、显存不足与内容安全所有踩过的坑值得单独写一节。这些坑单看文档都找不到答案都是实际跑过才明白的。7.1 Spring AI 与 Ollama 的版本兼容问题Spring AI 迭代非常快1.0 时代对接 Ollama 的/api/chat接口到了 2.0 某些模型配置、pom 坐标都有调整。而 Ollama 本身也在频繁更新。两边版本一错位最常见的错误就是启动报400 Bad Request或Unknown field。我的处理原则是优先保证 Spring AI 版本和 Spring Boot 版本匹配再固定一个 Ollama 版本。别一上来都用 latest会给自己埋雷。如果 Ollama 侧升级后 Spring AI 报错最简单的临时方案是降级或者锁定 Ollama 版本号等 Spring AI 侧发版再升级。7.2 流式返回乱码与连接断开本地模型流式返回时如果前端拿到的是乱码多半是 HTTP 响应没有明确 charset。Spring Boot 里确认编码server: servlet: encoding: charset: UTF-8 force: true如果是流式中途断开一般是客户端超时设置太短。Flux 接口默认响应是长连接代理层Nginx的proxy_read_timeout如果设成 60 秒模型生成慢一点就断调成 300 秒基本能覆盖本地模型的生成速度。7.3 Ollama 在 Windows 与 WSL2 下的显存资源问题Windows 用户经常遇到这种情况ollama run显示模型已加载但回答奇慢无比。打开任务管理器一看GPU 占用不到 10%内存倒是飙升。这是因为 Ollama 没有用上 GPU退回了 CPU 推理。排查顺序先ollama ps看模型加载在哪个设备如果显示100% CPU说明 GPU 没被识别。Windows 原生版 Ollama 一般会自动识别 NVIDIA 显卡但如果你把 Ollama 装进了 WSL2就要确保 Windows 的 NVIDIA 驱动和 WSL 的 CUDA 环境匹配。Ollama 内部自带 CUDA runtime不需要手动装完整的 CUDA Toolkit但驱动一定要新。另外注意如果机器上同时跑着 Ollama 和其他吃显存的任务模型可能因为显存不够直接加载失败。可以用环境变量限制OLLAMA_MAX_LOADED_MODELS1 OLLAMA_KEEP_ALIVE5m OLLAMA_NUM_PARALLEL2OLLAMA_KEEP_ALIVE控制了模型在显存中驻留的时间。调到5m表示 5 分钟没人用就卸载释放显存给其他任务。7.4 内容安全护栏的正确做法既然提到了“去除限制词”“破甲”这类搜索热词我必须说明一点本地部署大模型很多人以为可以随意绕过模型的安全约束这在任何正式场合都不应该做。模型的安全限制是一道护栏它的作用不仅是合规更是防止企业知识库被诱导输出不该输出的内容。正确的做法不是“破限制”而是在应用层建护栏System Prompt 里明确“你只能基于提供的资料回答”“遇到不清楚的问题要明确说不”在 Spring AI 的 Advisors 层做输入/输出过滤拦截包含敏感内容的会话记录所有对话日志方便审计对企业内部权限做隔离不同的用户组只能检索到对应范围内的知识。Spring AI 的 Advisor 机制可以拦截请求和响应这是做内容安全的正规路子。把模型接入生产环境之前先想清楚如果用户问了一个越权问题系统应该怎样拒绝这比模型本身的能力更重要。部署本地化大模型不等于把责任也本地化掉。最后补充一点个人体会这套方案从个人电脑上的 Ollama 一路走到团队级 vLLM 部署本质上是在解决同一个问题让大模型成为后端系统的一个普通组件。我的整体感觉是模型部署环节的难度已经极大降低真正的工程量在接入业务、组织知识、治理权限上。与其纠结“本地跑 32B 还是 70B”不如先把 14B 的模型接进你现有的一个业务流程里跑通一次。等你亲眼看到模型能调用你自己的业务方法、能回答你企业文档里的真实问题时你自然会知道下一步该往哪个方向扩。这也是我这两年做 AI 应用落地最深的体会——先从最小闭环开始永远别在选模型阶段就停下来。
阅读完成 · 觉得有帮助?