从大模型 API 生态到 Spring AI:接口、平台与框架三层怎么串起来
一、开始之前五个真实困惑在正式展开之前先问你五个问题——这是开发者在接触大模型应用时最常卡住的地方。读完这篇文章希望每一个你都能自己回答出来。困惑一「ChatGPT」「GPT-4o」「OpenAI」是同一个东西吗不是。「OpenAI」是公司名「GPT-4o」是模型名「ChatGPT」是基于 GPT 系列模型构建的用户产品。类比一下就像「华为」是公司「麒麟 9000」是芯片「Mate 60 Pro」是产品。你在 ChatGPT 网页上体验的能力和你调 OpenAI API 得到的原始模型能力是两回事。同理「通义千问」是模型「阿里云百炼」是平台它们在不同层次上。困惑二文档说「支持 OpenAI 兼容格式」到底兼容了什么很多开发者以为「兼容 OpenAI」意味着「性能一样」「效果相同」甚至「用同一个 API Key」。实际上「兼容」指的是请求和响应的 JSON 字段形状路径一样如/v1/chat/completions、字段名一样messages、model、choices……仅此而已。模型是哪家的、效果如何、Key 是哪个账号的——这些与「兼容」无关。真正的意义是你同一套 HTTP 调用习惯可以直接迁移只需改 base URL 和 Key。困惑三「阿里云百炼」是一个模型还是一个平台里面的「通义千问」又是什么「百炼」是阿里云提供的模型服务平台它聚合了通义千问阿里自研、智谱、Llama 等多种模型对外统一暴露 OpenAI 兼容的 HTTP 接口。「通义千问」是模型本身——你可以通过百炼平台来调它也可以单独通过通义千问的 API 来调它是两条路。平台是运维和接入的入口模型是能力本身两者一定要分清。困惑四我在 Coze 里创建了一个 Bot把它发布成 API这和直接调 GPT-4o 的接口一样吗不一样。Coze 的 Bot API 里有bot_id、会话 ID 等平台特有字段因为你调用的本质是「一个在 Coze 平台上编排好的智能体实例」而不是「一个裸模型」。同样道理你在某平台创建的「知识库问答助手」调它的接口时走的是平台编排逻辑不是原始 LLM 的 chat completion 接口。把这两者混淆会让你在 Spring AI 里配置一个「模型接入」却发现完全对不上号。困惑五我已经用 Spring AI 了上面这些还需要懂吗必须懂。Spring AI 帮你屏蔽了 HTTP 细节和厂商差异但它屏蔽不了「你必须知道自己在调什么」这件事。你仍然需要告诉它厂商的 base URL 是什么、model 名叫什么、这个接口是 Chat 还是 Embedding——这些都要写进配置。框架减少了重复代码但不代替你对接口层的理解。这五个困惑归根结底来自同一个问题大模型生态里的名词混在一起让人分不清它们属于哪一层。接下来就用三层结构把这些概念各归其位。二、三层结构一张认知地图把大模型开发生态拆成三层来理解是最有效的认知框架第一层规范层接口长什么样——请求和响应的 JSON 格式约定 第二层服务层谁在提供端点——平台和模型服务商 第三层开发层你怎么写代码——SDK 与框架下图是这三层的结构关系一个类比帮助记忆OpenAI 兼容格式像国标插头规格定义了插头的形状百炼、智谱、DeepSeek这类服务是按这个规格生产的电器可以直接插Coze Bot是用自家规格的电器需要转接头Spring AI则是排插——不管插什么规格的电器都能统一管理。三、第一层接口长什么样3.1 OpenAI 兼容格式——事实标准在大量模型服务文档里你会看到「支持 OpenAI 兼容」或「兼容 OpenAI Chat Completions API」这样的描述。这个格式的核心特征端点路径POST /v1/chat/completions请求体结构{ model: your-model-id, messages: [ { role: system, content: 你是一个专业的助手。 }, { role: user, content: 用一句话解释什么是向量数据库。 } ], temperature: 0.7, stream: false }响应体结构非流式{ id: chatcmpl-xxx, object: chat.completion, choices: [ { index: 0, message: { role: assistant, content: 向量数据库是一种专门存储和检索高维向量数据的数据库用于语义搜索和相似度匹配。 }, finish_reason: stop } ], usage: { prompt_tokens: 45, completion_tokens: 28, total_tokens: 73 } }几个需要理解的字段messages是一个数组按时间顺序记录整个对话历史。role分三种system系统指令定义模型行为、user用户输入、assistant模型上一轮的回答。多轮对话时你需要把上一轮的assistant回答追加进数组再发下一次请求——模型本身是无状态的上下文由调用方维护。temperature控制随机性0 最确定性1 最随机一般设 0.7 左右。stream设为true时响应变成 SSE 流式输出。usage记录本次请求消耗的 token 数是计费和监控的依据。「兼容」的真正含义只要一家服务提供商的 HTTP API 能接受上面这个请求体、返回上面这个响应结构就称为「OpenAI 兼容」。你同一套代码改一下 base URL 和 API Key就可以切换到这家服务。换模型后是否效果一样、上下文记忆是否一致——那是模型本身的问题和「兼容」无关。多轮对话上下文由调用方维护模型本身是无状态的——它不记得上一次你问过什么。每次请求你需要把完整的对话历史放进messages数组一起发过去。对话轮次越多数组越长每轮的输入 token 也越多。用 Spring AI 实现多轮对话大致如下ListMessage history new ArrayList(); // 第一轮 history.add(new UserMessage(Java 和 Go 最大的区别是什么)); String reply1 chatClient.prompt(new Prompt(history)).call().content(); history.add(new AssistantMessage(reply1)); // 把模型回答追加进历史 // 第二轮 —— 因为 history 里有上文模型知道「Java 和 Go」指的是什么 history.add(new UserMessage(那在并发场景下哪个更好用)); String reply2 chatClient.prompt(new Prompt(history)).call().content(); history.add(new AssistantMessage(reply2));需要注意长对话会让history无限增长最终超出模型的上下文窗口限制。生产场景里通常需要对 history 做截断或摘要只保留最近 N 轮或者引入会话管理机制。3.2 厂商自有协议——何时出现、如何识别不是所有服务都走 OpenAI 兼容路线以下两种情况会出现自有协议情况一平台特有的编排对象。当你调用的不是一个裸模型而是「一个在某平台上创建好的 Bot 或智能体实例」时请求里必然出现平台专属字段如bot_id、app_id、workflow_id等。这类接口的语义是「运行这个编排好的产品」而非「调这个模型做 chat completion」。情况二平台特有能力的扩展字段。一些厂商在 OpenAI 兼容格式基础上增加了自定义参数如联网搜索开关、知识库 ID、输出格式强制 JSON 等。这类通常兼容核心格式只是额外字段不同。实践建议读任何服务文档时先看请求示例确认messages字段是否是数组对话格式路径是否形如/chat/completions。如果不是——你面对的可能是平台特有接口需要按平台自己的 SDK 或文档处理不要套 OpenAI 客户端。3.3 Embedding 接口是另一类许多开发者把 Chat 和 Embedding 混为一谈以为「一个 API Key 走所有」。实际上 Embedding 是独立端点POST /v1/embeddings { model: text-embedding-xxx, input: 这是需要向量化的文本 }响应返回一个浮点数数组向量用于语义检索不是给人读的文字。在 RAG检索增强生成场景里文档入库走 Embedding对话回答走 Chat Completion它们是两个接口、两个模型甚至可以来自不同厂商这点后面还会提到。四、第二层平台与模型——服务层里有什么4.1 三类服务各有定位理解第二层最重要的一件事同样一个名字可能指模型也可能指平台。把它们分成三类类型 A独立模型提供商直接提供自己研发的模型对外暴露 HTTP 接口通常同时提供 OpenAI 兼容入口和原生入口。你拿到的是直连模型的原始能力。典型特征文档里有「模型列表」每个模型有独立的model_id计费按 token。类型 B聚合/网关平台在一个控制台里接入多家模型自研 第三方统一鉴权和计费对外暴露统一的 OpenAI 兼容接口。你用同一个 Key 和同一个 base URL切换model参数就能调用平台接入的不同模型。典型特征文档里有「模型广场」里面既有自家模型也有第三方模型。类型 C编排/Bot 平台提供的不是裸模型接口而是「已经编排好能力」的平台产品——包含预设提示词、插件、知识库、工作流等。发布为 API 后你调用的是这个编排好的「智能体」请求里需要传bot_id或类似平台标识符。为什么要分清这三类因为选错了类型代码就对不上。如果你想调一个裸模型结果拿到了类型 C 的接口怎么配都跑不起来。4.2 模型类型速查——按「你要解决什么问题」来选现在市面上的模型按能力可以分为以下几类。选模型之前先确定你的任务属于哪一类模型类型你想解决的问题对应接口形态文档里常见标识文本对话问答、摘要、代码生成、写作Chat Completions-chat、instruct、chat推理增强数学推理、逻辑分析、复杂规划Chat Completions思考链-r1、-thinking、o3系列嵌入Embedding语义检索、向量化、RAG 召回Embeddings 接口-embedding、-embed多模态理解图片描述、OCR、图文联合推理Chat Completions含图片输入-vision、-VL、multimodal图像生成文生图、图生图Images 接口dall-e、flux、cogview语音TTS文字转语音、ASR语音转文字Audio 接口tts、whisper、asr视频生成文生视频、图生视频各厂商独立接口sora、hailuo、cogvideox实用建议选模型时先对号入座这张表再看各厂商在这个类目下的具体产品。不要用「文本对话」模型去做向量检索那是完全不同的接口和能力——能力类型变了接口路径和字段就变了即使在同一家厂商也是两个完全不同的 API。4.3 如何读一份厂商文档拿到一份新厂商的文档按这个顺序读效率最高看鉴权方式API Key 放请求头Authorization: Bearer xxx还是 URL 参数还是其他方式。看 base URL这是你在 Spring AI 里要填写的base-url。看模型列表找到你需要能力对应的model_id注意文本对话、Embedding、多模态往往是不同的模型名称。看请求示例确认是 OpenAI 兼容结构还是自有结构。看 usage 字段了解计费单位token 数量及定价这关系到后续的成本控制。五、认识当前主要模型覆盖面与能力地图理解了服务层的三类结构之后面对市面上众多模型与厂商需要两个工具一张覆盖面矩阵帮你快速判断某家厂商能不能满足你的多种需求一张能力类型详表帮你在确定任务类型之后知道这类模型能做什么、典型用在哪里。5.1 厂商覆盖矩阵一眼看清谁能做什么下表是「厂商 × 能力类型」的覆盖矩阵。✓ 表示该厂商有此类型的产品线不代表能力强弱详细参数以各官方文档为准版本会随时间更新。「—」表示该厂商尚未明确提供或资料有限不代表绝对不行。厂商文本对话推理增强多模态理解Embedding图像生成语音视频生成OpenAI✓ GPT 系列✓ o 系列✓ GPT-4o 视觉✓ text-embedding 系列✓ DALL-E✓ Whisper/TTS✓ SoraAnthropic✓ Claude 系列✓ Claude 思考模式✓ Claude 视觉————Google✓ Gemini 系列—✓ Gemini 原生多模态✓ Gemini Embedding✓ Imagen——DeepSeek✓ V3 系列✓ R1—✓———智谱✓ GLM 系列—✓ GLM-4V✓✓ CogView✓ GLM-TTS✓ CogVideoX阿里云 / 通义✓ Qwen 系列—✓ Qwen-VL✓✓——MiniMax✓ M 系列—✓ MiniMax-VL——✓ T2A✓ HailuoKimi / 月之暗面✓ K2 系列—✓ 图文理解————讯飞✓ 星火————✓ 语音识别/合成—字节跳动✓ 豆包系列—————✓ Seedance实用场景如果你的项目需要同时用到对话 Embedding 语音可以从矩阵里快速筛出哪几家能一站式满足减少多厂商接入的运维成本。如果你只需要对话绝大多数厂商都可选此时再结合下面的详表按场景细化。5.2 按任务类型选型关键能力 代表模型 典型场景确定了任务类型之后用这张表了解「这类模型能做什么、典型用在哪」再去对应厂商查具体的model_id和接口文档能力类型代表模型举例关键能力典型场景接口形态文本对话Claude 系列、GPT 系列、DeepSeek-V3、GLM-5、Qwen 系列、Kimi K2、豆包多轮对话、代码生成、长文档摘要、写作润色上下文窗口通常 32K1M智能客服、代码助手、内容生成、知识问答Chat Completions推理增强OpenAI o 系列、DeepSeek-R1、Claude 思考模式链式思考Chain of Thought推理步骤可见更准但更慢数学证明、复杂分析报告、法律条文推理、多步规划Chat Completions响应含思考过程字段多模态理解Gemini 系列、GPT-4o 视觉、GLM-4V、Qwen-VL、MiniMax-VL图文联合理解、OCR 结构化提取、视频帧分析发票 / 合同识别、图表解读、产品图描述、视频内容摘要Chat Completionsmessages 里传图片 / 视频 URL 或 base64嵌入EmbeddingOpenAI text-embedding 系列、Gemini Embedding、各厂商 embedding 系列将文本映射为高维向量输出是浮点数组不生成文字RAG 知识库构建、语义搜索、相似文档推荐Embeddings 接口路径和字段与 Chat 完全不同图像生成DALL-EOpenAI、ImagenGoogle、CogView智谱文生图、图生图、风格迁移输出是图片 URL 或 base64营销素材生成、设计原型、电商商品展示图Images 接口语音 TTS / ASROpenAI WhisperASR/ TTS、MiniMax T2A、讯飞星火语音TTS将文字合成为高自然度语音ASR语音转文字语音助手、有声内容生产、会议转录、无障碍辅助Audio 接口各厂商路径独立非标准化视频生成SoraOpenAI、HailuoMiniMax、CogVideoX智谱、Seedance字节文生视频、图生视频、视频编辑时长通常 560 秒级别广告短片、产品演示、短视频内容原型各厂商独立异步接口提交任务 → 轮询状态 → 取结果各类型关键注意点推理增强响应延迟通常是普通对话的 310 倍不适合需要实时响应的聊天场景适合离线或低频的复杂分析任务。嵌入Embedding和 Chat 是完全不同的接口和模型即使在同一厂商控制台也不能混用。索引构建和在线查询时必须用同一个Embedding 模型——向量空间不同的两个模型算出来的向量无法比较检索结果会完全错误。视频生成是异步任务流不是同步 HTTP 响应。接入方式与其他类型有本质差异需要额外实现任务状态轮询逻辑Spring AI 目前也没有统一抽象需要自行对接各厂商 SDK。5.3 选型流程小结从任务到代码的完整路径① 确认任务类型对话 / 推理 / 多模态 / 嵌入 / 图像 / 语音 / 视频 ↓ ② 查覆盖矩阵筛出满足需求的厂商候选列表 ↓ ③ 查能力详表确认接口形态Chat / Embeddings / Images / Audio / 异步 ↓ ④ 去厂商文档找 base URL model_id 鉴权方式 ↓ ⑤ 填进 Spring AI 配置Chat 和 Embedding 分别是不同的 Bean六、第三层框架与 SDK 在解决什么5.1 不用框架时代码长什么样假设你想在 Java 里调一个 Chat API不用任何框架// 每次调用都要手写 HTTP 请求 HttpClient client HttpClient.newHttpClient(); String body { model: gpt-4o, messages: [{role: user, content: %s}] } .formatted(userInput); HttpRequest request HttpRequest.newBuilder() .uri(URI.create(https://api.openai.com/v1/chat/completions)) .header(Authorization, Bearer apiKey) .header(Content-Type, application/json) .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponseString response client.send(request, HttpResponse.BodyHandlers.ofString()); // 还要自己解析 JSON拿到 choices[0].message.content这段代码本身不难但想象一下换一家厂商时你要改几处提示词模板散落在各处怎么管理多轮对话的 messages 历史谁来维护输出想映射成 Java 对象怎么办工具调用Function Calling怎么实现这就是框架要解决的问题——不是「帮你调 API」而是把上面每一个问题都抽象成可复用的模式让你只关心业务逻辑。5.2 官方 SDK vs 统一框架选哪个官方 SDKSpring AI 等统一框架优点紧贴最新 API新特性支持最快多厂商统一接口切换改配置而不改代码缺点换厂商必须改代码各家 SDK 风格不一新特性可能有滞后需要框架适配适合深度依赖单一厂商、追求最新特性Java 企业项目、预期会换模型或混用多家规则很简单如果你确定只用一家且要用最新特性直接用官方 SDK如果你的 Java 项目未来可能换模型或者已经用了 Spring 生态Spring AI 是更好的选择。两者不互斥也可以在 Spring AI 做不到的边缘场景手动补充 HTTP 调用。七、Spring AI设计理念与核心抽象6.1 设计理念一次编写按需切换Spring AI 的核心理念用一句话概括为不同 AI 模型和能力提供统一的编程接口让切换模型变成改配置而不是改代码。这不是宣传语是可以验证的如果你的业务代码只依赖ChatClient当你把配置里的 base URL 从 OpenAI 改成 DeepSeek理论上代码无需任何改动就能运行。6.2 学习路径四步法学 Spring AI 建议按这个顺序每一步都有明确的目标步骤目标完成标志第一步理解理念搞清楚它在三层架构里处于哪层解决什么问题能用一句话说清「Spring AI 为什么存在」第二步跑通最小 Demo从零搭起一个 Spring Boot 项目打通「配置 → 调用 → 拿到回答」全链路在本地跑起来能看到 AI 的输出第三步掌握核心 20% APIChatClient/PromptTemplate/.tools()/ChatResponse元数据能独立写出业务对话、工具调用的代码第四步举一反三理解扩展点换厂商改配置、换模型改 model 名、换能力换 Bean遇到新需求知道从哪里改、改什么第二步的最小 Demo 长这样pom.xml 引入spring-ai-openai-spring-boot-starter版本以官网为准RestController public class ChatController { private final ChatClient chatClient; // Spring AI 自动注入 ChatClient.Builder由配置文件里的 base-url / api-key 驱动 public ChatController(ChatClient.Builder builder) { this.chatClient builder.build(); } GetMapping(/chat) public String chat(RequestParam String message) { return chatClient.prompt(message).call().content(); } }对应的application.ymlspring: ai: openai: base-url: https://api.openai.com # 改成其他厂商地址即可切换 api-key: ${AI_API_KEY} chat: options: model: gpt-4o这就是完整的 Hello World——一个配置、一个 Controller、三行核心代码。6.3 四个核心抽象先记住这些ChatModel最底层的接口封装了「向某个模型发一次 chat 请求、拿到响应」这件事。不同厂商的适配器OpenAiChatModel、ZhiPuAiChatModel等都实现了这个接口。你一般不直接用它而是通过上层的ChatClient。ChatClient面向业务开发者的门面。你几乎所有的聊天操作都从它开始通过链式调用构建请求、发送、取结果。它内部处理了对ChatModel的调用还统一了提示词、工具调用、输出解析等逻辑。Prompt输入的抽象。可以是一段纯文本也可以是包含系统指令、用户消息、历史对话的完整上下文对象。配合PromptTemplate可以把固定指令和运行时变量分开管理。ChatResponse输出的抽象。不只包含模型生成的文本还包含metadatatoken 用量、模型名、finish reason 等元数据。大多数时候你只要文本少数时候计费统计、调试才需要完整的ChatResponse。这四个类之间的关系业务代码ChatClient门面 / 链式APIChatModel统一接口OpenAiChatModelZhiPuAiChatModelOllamaChatModelOpenAI HTTP API智谱 HTTP APIOllama 本地端点6.4 设计模式为什么这么设计Spring AI 的架构里有几个经典设计模式理解它们能帮你更快找到扩展点门面模式FacadeChatClient把底层复杂性HTTP、序列化、工具调用编排、重试全部封装对外暴露简洁的链式 API。你不需要知道ChatModel怎么实现的用ChatClient就行。适配器模式Adapter每家厂商的 HTTP 响应格式不完全一样即使是 OpenAI 兼容的也可能有细节差异各厂商的ChatModel实现就是适配器——把厂商特有的 HTTP 响应统一适配成ChatResponse让上层代码感知不到差别。建造者模式BuilderChatClient的链式调用.prompt()...call()...content()就是建造者逐步构建请求对象最后一次性发出。模板方法Template MethodPromptTemplate定义了「提示词 固定模板 运行时变量」的结构把变化的部分用户输入和不变的部分系统指令、格式要求分离。6.5 二八原则20% 的 API 覆盖 80% 的场景实际使用中你 80% 的需求只需要这一行String answer chatClient.prompt(userMessage).call().content();剩下 20% 的场景是需要 token 用量统计、需要元数据、需要结构化输出ChatResponse response chatClient.prompt(userMessage).call(); // 取文本内容 String content response.getResult().getOutput().getContent(); // 取 token 用量用于计费统计、配额监控 Usage usage response.getMetadata().getUsage(); long totalTokens usage.getTotalTokens();核心功能使用频率参考功能使用频率说明ChatClient极高所有聊天操作的入口.prompt().call().content()极高最常用链式调用PromptTemplate高动态构建提示词.tools()中让模型调用本地 Java 方法ChatResponse元数据低中token 统计、调试时使用6.6 PromptTemplate把提示词管理起来在生产环境里提示词不应该散落在业务代码里——一方面难以维护另一方面业务逻辑和 AI 指令混在一起修改提示词时要改代码、重新部署。PromptTemplate的思路是把固定指令和运行时变量分开// 模板定义可以外置到配置文件或数据库 String templateText 你是一个专业的代码审查助手。请对以下代码进行分析 编程语言{language} 代码 {code} 请指出潜在的问题并给出改进建议。输出格式为 JSON。 ; PromptTemplate template new PromptTemplate(templateText); Prompt prompt template.create(Map.of( language, Java, code, userSubmittedCode )); String review chatClient.prompt(prompt).call().content();这样提示词的固定部分你是什么助手、输出格式要求和运行时变量语言、代码内容完全分离。如果要调整提示词措辞只改模板字符串不动业务逻辑。6.7 工具调用Function Calling让模型调本地代码工具调用的完整链路如下——模型不直接调用你的代码而是通过「声明意图 框架代理执行」的方式完成本地 Java 方法大模型 APISpring AI 应用用户本地 Java 方法大模型 APISpring AI 应用用户发起问题发送消息 工具描述 schema返回 tool_call 信号 参数执行本地 Java 方法返回执行结果把工具结果作为消息发回生成最终回答返回答案理解这个流程之后再看三件事工具是本地 Java 方法模型拿到的是描述不是代码。你注册工具时Spring AI 会提取方法的名称、参数和描述生成一份 JSON schema 传给模型。模型只知道「有一个叫queryOrderStatus的工具接收orderId参数」不知道方法的具体实现。由模型决定是否调用、调用哪个工具Java 负责执行。当模型判断回答用户问题需要查订单状态时它会在响应里输出「我要调用queryOrderStatus参数是 xxx」Spring AI 框架捕获这个信号自动调用你注册的 Java 方法把结果返回给模型模型再基于结果生成最终回答。工具调用是「模型能力 业务代码」的桥梁。模型的知识截止训练时间不知道你系统里的实时数据。工具调用让模型在需要实时信息时「伸手」取数据而不是胡编。// 定义工具 Bean Description(查询订单状态根据订单 ID 返回当前状态) public FunctionOrderQueryRequest, OrderStatus queryOrderStatus() { return request - orderService.getStatus(request.orderId()); } // 使用时注册工具 String answer chatClient .prompt(我的订单 2024001 现在是什么状态) .tools(queryOrderStatus) .call() .content(); // 模型会自动调用工具你什么都不用多写.tools()用于当次请求注册工具.defaultTools()在ChatClient构建时注册全局默认工具。6.8 从单次对话到智能体工程工具调用让模型能「伸手」拿到实时数据这是一个重要的能力跃迁。在它的基础上还有一个更大的演进值得理解从一次性问答到多步骤自主完成任务。Vibe Coding氛围编程是最早一批人使用 AI 写代码的方式向模型描述需求接受它生成的代码感觉差不多就提交。它的核心特征是「人在循环里做最终判断」模型只负责一次性生成没有反馈回路。写一段简单脚本效果不错但在复杂系统里会产生难以维护的代码——因为模型不知道自己生成的代码「跑不跑得通」「对不对」。Agentic Engineering智能体工程是对这个问题的回应。它的核心是Plan → Act → Observe的循环规划Plan→ 执行Act→ 观察结果Observe→ 再规划 → 再执行……一个具体例子写一个功能并自动验证正确性。Plan分析需求拆分为「写接口 → 写测试 → 运行测试 → 修复问题」几步。Act调用工具写代码本地文件写入工具调用工具执行测试本地命令行工具。Observe测试失败读取错误信息。回到Plan根据错误重新生成修复方案继续循环直到测试通过。与 Spring AI 的关系.tools()就是「Act」阶段的基础设施。当你注册了多个工具查数据库、发邮件、写文件……模型可以在一次对话里多次、顺序调用不同工具每次都把结果作为上下文反馈回来驱动下一步决策。工具调用 → 结果反馈 → 模型再决策这个闭环就是「智能体」最小形态的实现。你不需要一开始就构建多 Agent 的复杂系统。从一个工具开始理解「模型决策 Java 执行 结果反馈」的完整链路就已经站在了智能体工程的起跑线上。6.9 配置切换统一抽象的直接受益Spring AI 切换厂商时改的是配置不是代码spring: ai: openai: # 切换到 DeepSeek把这里改成 https://api.deepseek.com # 切换到 Ollama 本地改成 http://localhost:11434/v1 base-url: https://api.openai.com api-key: ${AI_API_KEY} chat: options: model: gpt-4o # 改成 deepseek-chat 或 llama3这个配置改一改你的业务代码中所有chatClient.prompt(...).call().content()调用都会指向新的模型无需任何代码改动。这就是「统一抽象层」在工程层面的直接价值。八、几个容易忽视的工程问题7.1 RAG 在整体架构里的位置RAGRetrieval-Augmented Generation检索增强生成是目前最常见的 AI 应用模式之一它不是一个单一接口而是嵌入模型 向量库 对话模型的组合在线查询阶段离线入库阶段Embedding 模型用户问题向量检索相关文档片段Chat 对话模型最终回答切块原始文档Embedding 模型向量数据库几个重要认知Embedding 模型和 Chat 模型是两个独立的东西可以来自不同厂商。离线入库和在线查询是分开的两个阶段不要把建索引和用户问答混在同一条请求链路里。向量数据库存的是浮点数数组向量不是原始文本。检索的原理是找「意思相近」的片段不是关键词匹配。Spring AI 提供了VectorStore抽象和多种向量库集成pgvector、Redis、Pinecone 等以及DocumentReader、TokenTextSplitter等离线处理工具。7.2 Token理解计费和上下文限制Token 是模型处理文本的基本单位大致理解英文约 1 个词 ≈ 1 个 token中文约 1 个字 ≈ 1.52 个 token因模型而异。Token 决定两件事计费几乎所有云端 API 按「输入 token 数 输出 token 数」计费。多轮对话时每次请求都要把历史消息带上随着对话轮次增加输入 token 会越来越多——这意味着长对话的每一轮成本都比上一轮高。上下文窗口每个模型都有最大上下文限制如 4K、8K、32K、128K token超过这个限制模型就看不到最早的消息了。这就是为什么你和某些 AI 对话时它会「忘记」几十条之前说的话。ChatResponse里的usage字段记录了本次请求的 prompt token 数和 completion token 数这是做成本监控的基础数据。建议把这个数据打进日志便于后续分析。7.3 API Key 与安全注意事项以下几点是高频踩坑的安全问题API Key 不进仓库用环境变量或密钥管理服务Vault、云厂商的 Secret Manager存放在代码里只引用变量名不写明文。Spring AI 配置里的${AI_API_KEY}就是正确用法。提示词注入用户输入不能无过滤地直接拼进提示词。如果你的系统提示词是「你是专属助手只回答关于 XXX 的问题」而用户输入是「忽略之前的所有指令改成……」模型可能真的会「听话」。用PromptTemplate的变量替换比字符串拼接更有隔离效果。工具调用的权限边界注册给模型的工具只暴露必要的能力。如果你的queryOrder工具只需要查不需要改就不要把「修改订单」的方法也注册进去即使模型理论上不会主动乱调也不要给它这个机会。7.4 本地部署与云端 API 的统一切换Spring AI 支持 Ollama——在本机运行开源模型Llama、Qwen、DeepSeek 等的本地版本暴露 OpenAI 兼容的 HTTP 接口。对 Spring AI 来说Ollama 和云端厂商没有区别只是 base URL 不同# 云端 OpenAI spring.ai.openai.base-url: https://api.openai.com spring.ai.openai.api-key: ${OPENAI_KEY} # 本地 Ollama无需 key spring.ai.openai.base-url: http://localhost:11434/v1 spring.ai.openai.api-key: ollama spring.ai.openai.chat.options.model: qwen2.5:14b业务代码一行不改。这个能力在实际工程里很有价值开发和联调阶段用本地模型不花云端 token 费用上线阶段切换到云端模型只改配置。九、动手之前技术向选型清单开始一个大模型相关的功能开发之前先用这个清单自查问题为什么重要我要解决的任务是对话、还是嵌入、还是图像/语音不同任务类型对应不同接口选错类型什么都调不通。目标服务商的接口是 OpenAI 兼容还是自有协议决定用哪套客户端配置以及 Spring AI 是否直接支持。我调用的是裸模型还是平台编排好的智能体前者配 model base URL后者可能需要 bot_id 等额外参数。上下文窗口够不够用多轮对话和 RAG 场景都要提前评估 token 数量。模型部署在哪本地 Ollama、私有化部署、还是公有云影响网络拓扑、数据合规和 API Key 的管理方式。是否需要工具调用能力部分模型不支持 Function Calling要提前确认。成本控制的方式是什么是否需要记录 usage、做限流、区分模型用于不同场景以控制费用十、回到开头五个困惑现在有答案了吗困惑一「ChatGPT」「GPT-4o」「OpenAI」是同一个东西吗现在你知道公司第二层服务层的角色、模型服务层对外提供的能力类型、产品基于模型构建的用户界面是三个不同层次的事情。「百炼」是平台类型 B「通义千问」是模型两者都在第二层但一个是聚合入口一个是能力本身。困惑二「OpenAI 兼容」兼容了什么现在你知道兼容的是第一层的接口规范——JSON 请求结构字段名、路径不是模型效果、不是账号体系。它的意义是同一套 HTTP 调用习惯可以迁移到兼容的服务商。困惑三平台和模型是同一个东西吗现在你知道服务层分三类——独立模型商直连模型、聚合平台多模型接入口、编排平台智能体产品。「百炼」是聚合平台「通义千问」是模型「Coze 的 Bot」是编排产品三个不同的东西。困惑四Bot 平台 API 和模型 API 是一样的吗现在你知道Bot 平台 API 在第二层属于「类型 C—编排平台」请求里有平台特有字段bot_id等不是标准的 Chat Completions 格式。如果你在 Spring AI 里配置需要确认是否有对应的适配器或者走自定义 HTTP 调用。困惑五用了 Spring AI 还需要懂接口层吗现在你知道Spring AI 是第三层开发框架它屏蔽了 HTTP 细节但要求你正确配置第一层接口形态决定用哪套配置和第二层base URL、model_id、接口类型的信息。框架的作用是让你少写重复代码不是让你省略对接口层的理解。三层一旦分清大模型开发里那些让人困惑的名词和概念就会各归其位。接口是格式约定平台是能力入口框架是代码组织方式——三件事三个层次互不混淆。