
开工写这篇之前我先说一句自己的判断如果你是个 Java 后端开发者过去一年已经被 ChatGPT、DeepSeek 和各种 AI 编程助手轰炸得有点焦虑又不想为了接一个大模型接口就把原来的 Spring Boot 项目推倒重来那 Spring AI 应该是目前让你“平滑上车”的最好选择之一。这篇博客不是官方文档的翻译也不是培训视频的笔记而是我基于实际项目经验整理的一份入门实战指南。我会从 Spring AI 到底是什么讲起然后拆解它的核心对象、常用功能、本地模型对接步骤最后把我在真实项目中踩过的坑和排查思路一并倒出来。不管你之前有没有接触过 AI 开发只要会用 Spring Boot 写接口跟着这篇文章走一遍基本就能在自己的业务代码里把大模型能力用起来了。1. Spring AI 是什么先搞清楚它在整个 AI 开发生态里的位置1.1 从“模型 SDK 混乱”到“统一抽象”很多人第一次听说 Spring AI 的时候下意识会把它理解成“又一个封装大模型的 SDK”。这么说也不能算错但我更愿意把它比作当年 JDBC 在数据库领域做的事情——在你面对 MySQL、Oracle、PostgreSQL 各不相同的客户端 API 时JDBC 提供了一套统一的接口规范让你写一份代码就能对接多种数据库。Spring AI 想做的是同样的事只不过这次它统一的是大模型。现在的实际情况是OpenAI 有 openai-javaDeepSeek 有自己的 SDK通义千问有 DashScope SDK本地跑模型还有 Ollama 的 Java 客户端。你要是每个模型都接一遍光是学这些 SDK 的用法、处理各家不同的鉴权方式和参数命名就够喝一壶的。更头疼的是今天测试用 OpenAI明天老板说要换成国产模型业务代码几乎要重写。Spring AI 的核心设计就是“接口 实现”。它定义了 ChatModel、EmbeddingModel 这些顶层接口然后针对 OpenAI、Ollama、通义千问、DeepSeek 等各家模型分别提供对应的实现类。你在业务代码里面向的是统一的 ChatModel 接口真正调用哪家模型由配置文件里的参数决定。切换模型的时候改配置、换依赖业务代码几乎不用动。我用一个场景帮大家直观理解一下你写了一个聊天机器人服务底层用的是 Spring AI 的 ChatModel 接口。一开始配置的是 OpenAI 的 GPT-4o线上跑了一段时间觉得成本太高想换成 DeepSeek 或者本地模型。如果你用的是 Spring AI改的就是 application.yml 里 model 相关的几个配置项和 pom.xml 里的依赖坐标如果你直接调 OpenAI 官方 SDK那恭喜你大半个 service 层都要重写。1.2 核心对象ChatModel 与 EmbeddingModelSpring AI 抽象出来的核心对象并不多入门阶段你只要先抓住两个ChatModel 和 EmbeddingModel。ChatModel 是最常用的它封装了与大模型进行多轮对话的能力。你可以把它理解为“聊天模型的统一入口”底层不管是 GPT、DeepSeek、通义千问还是本地部署的模型只要提供了对应的 Spring AI 实现你调用的方法签名都是一样的。最基础的操作就像用 JdbcTemplate 执行 SQL 一样简单注入 ChatModel调用 call 方法传入 prompt拿到响应。EmbeddingModel 则是做文本向量化的接口。它把一段文字转换成一串浮点数数组也就是向量。向量化是 RAG检索增强生成的基础后面我会专门聊到。简单来说如果你想让大模型“读懂”你自己业务系统里的文档、商品信息、工单记录等私有数据就需要用 EmbeddingModel 把文档切成小块并转成向量存进向量数据库查询时再把用户的问题也转成向量通过相似度检索找到相关内容送回给大模型。Spring AI 每个 Starter 包里都内置了对应模型实现的自动装配逻辑。比如你引入 spring-ai-starter-model-openaiSpring Boot 启动时就会自动创建 OpenAI 的 ChatModel、EmbeddingModel Bean。你不需要手动 new 任何实现类直接用 Autowired 或构造器注入就行这一点和用 Spring Boot 操作 Redis、MySQL 的感觉是一样的。1.3 为什么选 Spring AI 而不是直接调 SDK市面上已经有不少大模型 SDK很多人会问我直接调 FastJSON HttpClient 也能把请求发到模型那边为什么要多引入一个框架我的看法是Spring AI 的价值不在于“把请求发出去”这一步而在于它把 AI 集成真正变成了 Spring 生态的一部分。主要体现在几个方面。第一统一配置与依赖管理。不同模型的 API 地址、密钥、超时时间、模型名称这些参数在 Spring AI 里都收敛到 application.yml 中通过 spring.ai.model 前缀统一管理。配置项被标准化之后方便做多环境切换测试环境用免费模型生产环境切到商用模型改配置即可。第二和 Spring Boot 的深度整合。Spring AI 的自动配置、Starter 机制、属性绑定、条件装配这些能力都和你平时用的其他 Spring Boot 组件无缝衔接。你可以在项目里用 Spring 的 Retryable 给模型调用加重试用 Actuator 做健康检查用 AOP 做调用日志埋点这些和模型本身无关的工程能力都是现成的。第三官方在持续推进高层抽象能力。比如 Prompt Template、结构化输出、向量数据库抽象、Function Calling、Agent 相关的支持这些都是普通 SDK 不具备的。你直接调 SDK 可能也能实现同样的效果但需要用大量胶水代码去处理而 Spring AI 把这条链路打通了。当然Spring AI 也不是万能的。如果项目本身不是 Java 技术栈或者你只需要在一个一次性脚本里简单调一次模型那完全没有必要为了用 Spring AI 而用。它更适合的是那种“把 AI 能力作为后端服务的一部分深度嵌入到业务系统中”的场景。2. Spring AI 核心功能拆解不只是“发个消息给大模型”2.1 ChatModel 基础使用与 Prompt Template先从一个最基础的例子看起。引入 Spring AI 的 OpenAI Starter这里以 OpenAI 兼容协议为例后面接 DeepSeek 也会用到然后在 Service 里注入 ChatModel调用 call 方法。Service public class ChatService { private final ChatModel chatModel; public ChatService(ChatModel chatModel) { this.chatModel chatModel; } public String chat(String message) { return chatModel.call(message); } }这段代码看起来简单得有点不太真实但它确实能跑通。你需要做的就是在 application.yml 里配置好 api-key 和模型名称。底层发生了什么Spring AI 会把你传入的字符串封装成一个 Prompt 对象然后调用对应的模型实现把模型的文本响应直接返回。实际项目中我们很少直接传一个裸字符串给模型因为业务场景里通常需要把用户输入和其他背景信息拼到一起生成完整的提示词。传统的做法是用字符串拼接比如 String prompt 你是一个客服助手请回答 userMessage如果提示词比较复杂拼接起来不仅难看还特别容易出错。Spring AI 提供了 PromptTemplate 来替代手工拼接。它的用法和 Spring 的 RestTemplate 有点像也支持占位符替换PromptTemplate promptTemplate new PromptTemplate( 你是一个专业客服助手。 用户的问题是{question} 请用简洁、友好的语气回答。 ); Prompt prompt promptTemplate.create(Map.of(question, userQuestion)); String response chatModel.call(prompt).getResult().getOutput().getText();用占位符的好处是提示词模板可以单独维护甚至放到数据库或配置中心里动态加载。业务代码只负责传参数不会因为提示词改动而跟着改这个对于经常要调 prompt 的团队来说非常实用。2.2 结构化输出让大模型返回 JSON 对象而不是飘忽文本调用大模型最让人头疼的问题之一就是返回结果“不稳定”。你明明让它返回 JSON它可能给你回一段带解释的文本或者 JSON 格式不规范、字段名对不上。如果直接把模型的输出交给前端解析随时可能崩。Spring AI 提供了结构化输出Structured Output能力让你可以直接把模型的输出映射成一个 Java 对象。核心工具是 BeanOutputConverter。看个例子record ProductInfo(String name, String category, BigDecimal price) {}假设你有一个需求让模型从一段商品描述中提取商品名称、分类和价格。传统写法大概是把 JSON 结构说明写死在提示词里然后把模型的返回结果用 Jackson 手动解析中间还要处理 markdown 代码块包裹等问题。用 Spring AI 的写法是这样BeanOutputConverterProductInfo converter new BeanOutputConverter(ProductInfo.class); String formatInstruction converter.getFormatInstruction(); PromptTemplate promptTemplate new PromptTemplate( 从下面的商品描述中提取信息{productDescription} 输出要求{format} , Map.of( productDescription, description, format, formatInstruction )); Prompt prompt new Prompt(promptTemplate.createMessage()); ChatResponse response chatModel.call(prompt); ProductInfo productInfo converter.convert(response.getResult().getOutput().getText());这里的关键是 converter.getFormatInstruction()它会根据你的 Java 类型自动生成一段结构描述告诉模型“你要返回符合这个结构的 JSON”。模型返回后converter.convert 负责把 JSON 解析成 ProductInfo 对象。我用这个功能做过商品信息抽取、用户反馈分类、工单结构化提取整体体验是比“纯提示词 手动解析”方案稳定得多虽然偶尔还是会有模型输出不符合格式的情况但配合重试机制、或者提示词里多给一个 few-shot 示例基本能解决绝大多数问题。2.3 向量库与 RAG给大模型接上“外部记忆”把大模型接进业务系统之后你很快会遇到第二个问题大模型的知识截止于训练数据它不知道你公司内部的规章制度、商品库存、历史工单这些私有数据。你要么每次把相关资料拼进提示词但超长上下文既贵又容易超出模型的 token 上限要么用 RAG 方案把文档向量化存进向量数据库查询时先检索最相关的片段再一起提交给模型。Spring AI 对 RAG 的抽象主要体现在 VectorStore 接口上。它屏蔽了不同向量数据库的差异无论你用的是 Redis、Pgvector、Milvus 还是 Chroma面向业务代码的操作都是一样的写入文档向量、相似度检索。一个最简的 RAG 流程可以这样理解// 1. 把文档切块、向量化写入向量库 ListDocument documents List.of( new Document(员工请假制度事假需提前一天申请年假需提前三天申请。) ); vectorStore.add(documents); // 2. 用户提问时先检索相关片段 ListDocument similarDocs vectorStore.similaritySearch( SearchRequest.builder() .query(我明天有事想请假需要提前多久) .topK(3) .build() ); String context similarDocs.stream() .map(Document::getContent) .collect(Collectors.joining(\n)); // 3. 把检索结果拼入提示词再调用模型 PromptTemplate promptTemplate new PromptTemplate( 基于以下资料回答用户问题 {context} 用户问题{question} , Map.of(context, context, question, userQuestion));RAG 解决的核心问题有两个一是知识实时性模型不懂的新信息可以通过文档更新来覆盖二是幻觉问题模型不再凭空发挥而是基于检索到的资料生成答案。当然RAG 的工程化落地比上面这个例子要复杂得多比如文档切块的粒度、向量检索的阈值、多路召回的策略都会影响最终效果这个我后面会用专门的章节说明。2.4 Function Calling让大模型调用你的 Java 方法如果说 RAG 是给大模型接上了“静态记忆”那 Function Calling 就是给大模型装上了“手脚”让它能主动调用外部工具。典型场景用户问“帮我查一下订单物流进度”模型本身并不知道你的订单系统里有什么数据但它可以识别出用户意图触发你注册好的查询方法拿结果后再组织语言回复。Spring AI 里实现 Function Calling 非常简单核心就是 Tool 注解。Service public class OrderTools { Tool(name query_order_status, description 根据订单号查询订单当前状态) public String queryOrderStatus(ToolParam(订单号) String orderId) { // 调用自己的订单服务 return orderService.getStatusByOrderId(orderId); } }然后在你调用模型时告诉 ChatModel 这个工具在哪ChatClient chatClient ChatClient.builder(chatModel) .defaultTools(new OrderTools()) .build(); String response chatClient.prompt() .user(帮我查一下订单 20241111001 的状态) .call() .content();整个过程的内部逻辑是Spring AI 会把你带有 Tool 注解的方法描述包括方法名、参数、说明转换成模型能理解的 JSON Schema随着请求一起发给大模型。模型判断需要调用工具时不会直接回答问题而是返回一个工具调用请求Spring AI 拦截到这个请求反射调用你的 Java 方法拿到方法返回结果后再把它作为上下文回传给模型最终生成面向用户的回答。Function Calling 是把大模型从“聊天机器”升级为“业务执行者”的关键能力。以后你要是想做一个自然语言查天气、查库存、下单的 AI 助手本质上都是靠 Function Calling 把用户的自然语言指令映射到具体业务方法上。3. 对接本地部署的 DeepSeek从 0 到 1 的完整实操3.1 为什么选择本地部署模型既然 Spring AI 可以对接这么多模型那为什么我会特别强调“Spring AI 对接本地部署的 DeepSeek”这个场景主要原因有三个。一是数据私密性。很多企业内部数据不允许离开自己的内网环境直接调用外部 API 过不了合规审查。本地部署模型后所有请求都发生在本机或内网服务器数据不出域。二是零接口费用。本地模型跑起来后推理费用基本只有电费对于原型验证、测试环境、高频低价值场景特别友好。三是学习成本低。你不需要注册各种云平台、申请 API Key装好 Ollama、拉一个模型下来就能开始调对刚接触 Spring AI 的开发者来说这是上手成本最低的路径。当然本地部署的模型和云端大模型对比在推理速度、生成质量上还是有差距的。所以我的建议是本地部署适合开发调试和私有化场景生产环境的大流量业务还是根据实际需求选云厂商模型。3.2 环境准备安装 Ollama 并拉取 DeepSeek 模型本地跑 DeepSeek我用的是 Ollama因为它安装简单、命令少而且天然兼容 OpenAI 的 API 协议Spring AI 可以直接通过 OpenAI 兼容模式对接。安装完成之后拉取模型ollama pull deepseek-r1:7b拉取时间取决于网络状况模型包有几个 GB耐心等待即可。拉完后验证一下服务是否正常直接命令行测试ollama run deepseek-r1:7b 你好请做一个自我介绍如果模型能正常回复说明本地推理服务已经就绪。Ollama 默认监听 11434 端口Spring AI 里的配置会用到这个地址。3.3 Spring Boot 项目接入配置与代码实现下面我们新建一个 Spring Boot 项目引入 OpenAI Starter因为 Ollama 兼容 OpenAI 协议这里不需要专门的 Ollama Starter然后在 application.yml 里把 base-url 指向本地服务。pom.xml 中加入依赖以 Spring Boot 3.3.x 为例dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId version1.0.0/version /dependency如果你用的 Spring Boot 版本是 3.4.x建议换成 1.0.0 以上版本具体版本兼容关系可以在 Spring AI 官方文档里查看我后面也会讲到。application.yml 配置spring: ai: openai: base-url: http://localhost:11434/v1 api-key: ollama chat: options: model: deepseek-r1:7b temperature: 0.7这里有几个关键配置要解释一下。base-url 必须带上 /v1 后缀因为 Ollama 的 OpenAI 兼容接口路径就是 /v1/chat/completions。api-key 随便填一个非空字符串就行Ollama 本地服务不做鉴权但 Spring AI 的自动配置会要求这个属性不能为空否则启动报错。接下来写一个 Controller 测试RestController RequestMapping(/api/chat) public class ChatController { private final ChatModel chatModel; public ChatController(ChatModel chatModel) { this.chatModel chatModel; } PostMapping public String chat(RequestBody String message) { return chatModel.call(message); } }启动应用后用 curl 或者 Postman 发一个 POST 请求curl -X POST http://localhost:8080/api/chat \ -H Content-Type: text/plain \ -d 用一句话介绍你自己如果配置没问题你就能在响应里看到本地 DeepSeek 模型生成的回复了。整个过程其实就是Spring AI 把请求包装成 OpenAI 协议格式发给 Ollama 的本地服务Ollama 转发给 DeepSeek 模型进行推理结果原路返回。链路不复杂但理解这条链路对后面排查问题非常有帮助。3.4 连接远程 Linux 服务器的本地模型有朋友可能会问我不想把模型部署在本地 Windows 笔记本上而是放在一台 Linux 服务器上Spring Boot 应用在另一台机器怎么连思路还是一样的只是需要把 Ollama 服务暴露到局域网或内网。默认情况下Ollama 只监听 127.0.0.1需要修改环境变量让它监听所有网卡地址。Linux 下可以通过 systemd 配置# 设置环境变量 OLLAMA_HOST0.0.0.0 # 重启 ollama 服务 systemctl edit ollama systemctl restart ollama之后在 Spring Boot 的 application.yml 里把 base-url 改成 http://服务器内网IP:11434/v1 即可。这里提醒一句内网部署不需要担心外网暴露问题但如果这台服务器有公网 IP请务必做好防火墙策略别把 Ollama 暴露在公网上这个服务本身没有任何身份认证裸奔在公网等于把自己家的模型推理能力开放给全网。4. Spring AI Alibaba 与 Spring AI Skill面向生产的选择4.1 Spring AI Alibaba国内云生态的官方适配如果你关注过 Spring AI 的发布节奏应该知道社区里还有一个非常有分量的子项目Spring AI Alibaba。它是由阿里巴巴开源团队维护的 Spring AI 扩展模块专门针对阿里云的“百炼”平台和通义千问系列模型做了深度集成。它的核心价值是什么首先它让 Spring AI 对接国内云厂商模型的过程变得非常简单。你不需要自己去研究 DashScope 的鉴权细节引入 spring-ai-alibaba-starter 之后配置几个参数就能用通义千问、DeepSeek、Llama 等百炼平台上托管的模型。其次它对生产环境的一些痛点做了补齐比如更完善的重试机制、更稳定的模型调用链路、对阿里云内部组件如阿里云 OSS、SchedulerX的集成等。一个典型的配置长这样spring: ai: dashscope: api-key: sk-xxxx chat: options: model: qwen-plus本质上它和前面用 OpenAI 兼容协议对接 Ollama 是同一套抽象模型区别在于是谁提供了 ChatModel 的实现以及这个实现的代码路径上做了哪些针对性优化。理解了这个点你就能明白 Spring AI 生态里各个 Starter 之间的关系它们都是 ChatModel 的具体实现业务代码不感知差异。4.2 DashScope 平台模型部署与对接要点阿里云百炼平台DashScope上的模型不需要你自己部署只需要在控制台开通服务、拿到 API Key就能直接调用。Spring AI Alibaba 这个场景适用于不想维护本地模型、又希望用国内模型服务的生产项目。具体对接流程不复杂开通百炼平台创建一个 API-KEY。在项目中引入 spring-ai-alibaba-starter。在 application.yml 中配置 api-key 和 model 名称。dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter/artifactId version1.0.0/version /dependencyspring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus temperature: 0.8如果同一个项目里既要接百炼又要接本地 Ollama可以通过 Qualifier 或者 Primary 区分不同的 ChatModel Bean这个在 Spring AI 里是支持的。4.3 Spring AI Skill把模型能力封装成可复用的“技能”最近“Spring AI Skill”这个词热度涨得很快。其实它不是 Spring AI 的一个独立框架而是 Spring AI 在 1.0 版本之后逐步完善的一套“技能装配”思路把 Prompt Template、Function Calling、RAG 这些底层的可编程单元组合成一个面向业务的高层单元。举个例子。你做了一个公司内部知识库问答机器人如果是一段一段写代码每次都要处理提示词模板、检索向量库、拼上下文。如果把这个能力封装成一个 skill比如叫“hrPolicyAssistant”那么在这个技能内部你预定义了提示词模板、注册了查询考勤制度的工具方法、配置了员工手册的向量检索逻辑。调用方只需要传入用户问题技能内部帮你完成整个编排。Component public class HrAssistantSkill { private final ChatClient chatClient; public HrAssistantSkill(ChatClient.Builder builder) { this.chatClient builder .defaultSystem(你是一个HR政策助理回答必须基于提供的资料。) .defaultTools(new HrPolicyTools()) .build(); } public String ask(String question) { return chatClient.prompt() .user(question) .call() .content(); } }这个封装的意义在于AI 能力的调用逻辑不再是散落在 Controller 和 Service 里的零散代码而是按照“能力域”组织起来每个技能边界清晰、可测试、可复用。后面系统里的 AI 能力越接越多这种基于技能的组织方式会让代码维护轻松很多。5. 实战经验与踩坑记录Spring AI 常见问题排查5.1 依赖版本与自动配置的坑Spring AI 的版本迭代节奏非常快从里程碑版本到 1.0.0 正式版API 发生过一些调整。网上很多教程用的是旧版本写法粘贴到你项目里可能编译不过或者运行报错。我整理了几个典型的坑。一个常见问题是“找不到 ChatModel 的 Bean”。这种情况多半是依赖引入不对或者 Starter 包版本和 Spring Boot 版本不兼容。Spring AI 的每个版本都对 Spring Boot 版本有明确要求比如 Spring AI 1.0.0 要求 Spring Boot 3.4.x你如果用的是 3.2.x就可能出现自动配置类没有被加载的情况。我的建议是直接到 Spring Initializr 上勾选 Spring AI 依赖生成项目用官方推荐的版本组合别自己拼版本号。还有一个问题是“启动成功但调用时报 401 Unauthorized”。如果你配置的是 OpenAI 兼容协议对接本地或第三方服务检查一下 api-key 是否为空字符串。Spring AI 在客户端初始化时如果发现 api-key 为空可能会走某个默认的无效逻辑表现形式就是请求发出去被对方拒了。本地 Ollama 对接时api-key 填一个任意非空字符串就行。5.2 模型名、参数与上下文管理的坑Ollama 拉取的模型有具体标签比如 deepseek-r1:7b。如果你在配置里写成 deepseek-r1 或者 deepseek-r1:latestOllama 可能返回 404因为模型标签对不上。我踩过一次排查了半天才发现是标签少写了一个版本后缀。建议配置后先直接在 Ollama 上ollama list看一眼准确的模型名。temperature 参数也对结果影响很大。这个参数控制模型输出的随机性取值 0 到 2值越大答案越多变。做结构化输出、信息抽取时建议把 temperature 调到 0 或 0.2减少随机性带来的格式不稳定做闲聊、创意写作可以调到 0.8 左右。参数不是越高越好按场景调。再就是上下文管理。默认情况下Spring AI 的 chatModel.call(message) 是无状态的也就是说模型不记得你上一条消息说了什么。很多新手做聊天机器人时发现模型“记忆力”很差原因就在这里。你需要引入 Spring AI 的 ChatMemory 机制它提供了消息窗口记忆功能像 MessageWindowChatMemory 可以帮你维护最近 N 轮对话ChatMemory chatMemory MessageWindowChatMemory.builder() .maxMessages(20) .build(); ChatClient chatClient ChatClient.builder(chatModel) .defaultChatMemory(chatMemory) .build();这里有个工程上要特别注意的点如果系统是多人同时在线的ChatMemory 必须按会话 ID 隔离否则不同用户的消息会混在一起。生产环境建议把 ChatMemory 的存储实现放到 Redis 等外部存储中而不是默认的内存实现否则服务重启后所有对话上下文都丢了。5.3 结构化输出不稳定的兜底策略虽然 BeanOutputConverter 能解决大部分结构化输出问题但在实际使用中我还是遇到过模型偶尔不按格式返回的情况比如输出里带了解释文字、JSON 被 markdown 代码块包裹、或者字段值为 null。我的兜底方案是组合拳一是在提示词里明确“只输出 JSON不要任何额外文字”并给一个 few-shot 示例二是对模型的原始输出做一次预处理把 markdown 代码块剥掉三是解析失败时做一次重试重试次数控制在 2 到 3 次以内。重试逻辑用一个简单的循环就能实现不需要引入额外框架。int maxRetries 3; for (int i 0; i maxRetries; i) { String raw chatModel.call(prompt).getResult().getOutput().getText(); String cleaned raw.replaceAll(json|, ).trim(); try { return objectMapper.readValue(cleaned, ProductInfo.class); } catch (JsonProcessingException e) { // 记录日志继续重试 } } throw new BusinessException(模型结构化输出多次解析失败请稍后重试);5.4 RAG 与向量化的几个实战建议RAG 落地时我交过不少学费最核心的体会是检索质量决定回答质量。如果你检索出来的片段和问题不相关提示词写得再好也没用。文档切块的粒度是第一个影响因素。切得太小单个片段信息量不足召回结果不完整切得太大容易把不相关内容混在一起还浪费 Token。我常用的策略是先按章节或段落粗切再根据嵌入模型的最大输入长度二次调整块大小控制在 300 到 500 字之间块与块之间保留少量重叠避免关键信息被切断。相似度检索的阈值也要注意。topK 不是越大越好你让人家回答一个问题结果它翻了 10 段文档进来里面只有两段是相关的剩下的全是干扰信息模型的回答反而变差了。我一般先设置 topK 3然后根据线上反馈调整同时可以对相似度分数设一个下限低于阈值就直接告知用户“库中没有找到相关资料”而不是强行让模型编一个答案。另外不同嵌入模型的向量维度不同如果你要切换本地 Ollama 的嵌入模型就得注意向量数据库里已有的数据维度是否匹配。维度不一致时相似度检索会直接报错这是新手比较容易踩的坑。6. 聊聊我对 Spring AI 现状与选型的一些思考写了这么多最后说点偏个人向的看法不涉及任何厂商的立场纯粹是这一年多来在多个项目里用 Spring AI 的一些体感。Spring AI 目前的定位很清晰它就是 Spring 生态在 AI 时代的“基础设施层”。它的目标不是跟 LangChain 这类框架比“谁的功能多”而是让 Java 开发者用最熟悉的 Spring 姿势接入 AI 能力。这一点我觉得它做到了而且完成度在不断提高。但从另一个角度看Spring AI 的很多高级功能仍然处于快速迭代阶段API 变动频率偏高如果你要用它做大型生产项目团队里最好有一个人能持续跟踪版本变化提前评估升级影响。我个人的建议是如果你正在新起一个 Java 后端项目对 AI 能力有刚需那 Spring AI 可以作为首选方案因为它帮你把模型接入、提示词管理、结构化输出、RAG、工具调用这些链路统一起来了后面无论是换模型还是加能力都留了足够的扩展空间。如果你的项目只是想在某个角落简单调一次大模型接口几十行代码能解决的事确实不必引入整个框架。还有一件事我特别想强调模型能力、提示词设计和代码工程三者的关系。Spring AI 解决的是“代码工程”这个层面但最终效果的上限还是由你选的模型和提示词质量决定的。同一个模型提示词写得专业和随便写效果差距极其明显。所以别把 Spring AI 当成“魔法框架”它只是把工程复杂度降下来的工具该打磨的提示词、该设计的检索策略一个都不能少。刚开始接触 Spring AI 的朋友我建议你从小项目入手先用 Ollama 跑一个本地模型照着这篇文章的代码写一个最简单的聊天接口感受一下整个链路跑通是什么感觉。之后再逐步加上 Prompt Template、结构化输出、RAG、Function Calling 这些能力。这个过程不需要花一分钱就能把 Spring AI 的核心用法全部摸一遍。最后分享一个我实际开发中养成的习惯所有调用大模型的方法我都会把请求参数、响应内容、耗时和 Token 消耗统一打到日志里。这么做刚开始会觉得啰嗦但真的排查问题的时候就知道有多香了——模型返回内容对不对、是不是超时了、Token 消耗是不是异常膨胀翻日志一目了然。AI 项目的排错逻辑和传统接口项目不太一样模型的输出是不可控的只有把过程数据完整记录下来你才能知道问题出在提示词、出在检索、还是出在模型本身。这个习惯从你写第一行 Spring AI 代码的时候就可以开始培养了。