Java 开发者想学大模型应用,最容易踩的第一个坑不是代码写不出来,而是路线选错。一搜教程,前排基本都是 Python 和 LangChain,等自己动手才发现,LangChain 的 Agent 循环、工具调用、记忆管理,在 Java 项目里并没有现成的一比一对应方案。Spring AI 的出现,才让 Java 后端有了一条真正能落地的接入路线。这篇内容围绕 Java + 大模型这个主题,按我实际跑过的顺序整理 Spring AI、Spring AI Alibaba、Agent、Function Calling、RAG 和面试高频点,尽量直接落到能写代码、能调试、能讲清楚原理的程度。
1. 先分清楚:Java 后端做 AI 应用,不是照搬 LangChain
1.1 LangChain 是 Python 生态,直接抄会出问题
LangChain 的定位是 Python 生态里的大模型应用框架,核心价值是把模型调用、提示词管理、工具调用、记忆、向量检索这些零散动作封装成链式流程。很多 Java 开发者看到“LangChain + Agent”教程,第一反应是用 Java 重写一套,结果发现接口命名、抽象模型、异步回调方式都不一样,强行迁移的成本非常高。
原因在于,LangChain 的设计依赖 Python 的动态类型、装饰器和异步生态。Java 这边想要类似能力,更现实的做法是借助 Spring AI,或者选择 LangChain4j。如果 Java 项目里强行维护一套桥接层去对接 Python 版 LangChain,后面维护会非常痛苦,社区示例也几乎不会给 Java 版本。
1.2 Spring AI 到底解决什么问题
Spring AI 是面向 Java/Spring 生态的 AI 应用框架,目标不是复刻 LangChain,而是把模型接入、Prompt 管理、结构化输出、工具调用、向量存储、RAG 这些能力,统一成 Spring Boot 风格。你在其他语言里看到的那套概念,在 Spring AI 里基本都能找到对应组件,只是命名和调用方式不同。
Spring AI Alibaba 是阿里在 Spring AI 基础上的适配和扩展,主要补强了国产模型接入、图编排 Graph、Agent 编排、本地模型适配等能力。两者的关系可以理解成:Spring AI 做通用底座,Spring AI Alibaba 做更贴近国内生产环境的增强。
选型判断其实不难:
- 如果公司已经用 Spring Boot,模型既有海外 API 也有国产 API,优先 Spring AI + Spring AI Alibaba。
- 如果只是学习,本地用 Ollama 部署模型,单独用 Spring AI 就够了,配置更简单。
- 如果团队主力语言是 Python,LangChain 原生当然合适,但那属于另一个技术栈,不要混着决策。
2. 环境准备:模型从哪来,依赖怎么配,参数先设哪几个
2.1 先定模型来源:本地部署、免费 API、商用 API
做大模型应用,第一步不是写代码,而是确定模型从哪里来。这个决定会影响后面所有配置和排错方式。
本地部署最常见的方案是 Ollama。好处是数据不出内网、没有按 Token 计费的压力,适合开发和测试。缺点是显存、内存、磁盘直接决定模型上限,显存不足时只能跑小参数模型,能力和生成速度都有限。如果你只是学习,可以先在本机部署一个 7B 左右的中小模型,能跑通流程就行。
免费 API 也值得用。不少模型服务商提供学习用额度或免费模型接口,适合做 Demo、跑通概念验证、准备面试项目。商用 API 则适合生产环境,稳定性和吞吐有保障,但需要盯着成本和限流。
判断标准很简单:如果只是本地验证,先选最容易跑通的入口,不需要一上来就追求生产级配置。等流程确认没问题,再切到目标模型服务。
2.2 Java 版本和 Spring Boot 项目初始化
新项目一般建议 Java 17 起步,Spring Boot 3.x。原因很直接:Spring AI 对 Jakarta EE 和 Spring 6 的依赖比较明显,如果你的机器还在 Java 8,就要先升级环境,否则依赖冲突会非常难查。
初始化项目时,重点不是生成的模板,而是依赖清单。典型依赖包括:
- Spring AI 对应模型供应商的 starter,比如 OpenAI 兼容接口或 Ollama。
- 如果走 Spring AI Alibaba 的 DashScope,则引入对应 qwen 模型 starter。
- 后续用工具调用、向量存储时,再按需加对应模块。
这里有一个非常容易踩的点:Spring AI 不同版本的包名和配置项变化比较快。网上很多教程是旧版本写法,配置文件里的前缀、密钥字段可能在新版里已经改掉。落地前务必要看当前稳定版本官方文档,不要直接复制旧博客。
2.3 配置文件里的关键项
一个最简配置一般包含这几类:
| 配置项 | 作用 | 常见取值 |
|---|---|---|
| base-url | 模型服务地址 | 本地 Ollama 通常是http://localhost:11434 |
| api-key | 访问密钥 | 本地部署写任意占位符即可,云端服务写真实 Key |
| model | 模型名称 | 如qwen2.5:7b、gpt-4o-mini、qwen-plus |
| timeout | 请求超时 | 建议至少 60 秒,长文本要更长 |
| max-tokens | 单次最大生成长度 | 根据任务类型,一般 512 到 4096 |
配置不是越多越好。第一次先跑通对话,只要 base-url、api-key、model 三个正确就行。这里不要急着调 temperature、top_p,默认值足够验证流程。很多刚入门的人一上来就把温度调来调去,结果问题根本不在随机性,而是模型名写错或者地址不通。
3. 第一个可运行 Demo:把对话跑通,再谈 Agent
3.1 最小代码结构
Spring AI 里最核心的对象是 ChatClient。新版中推荐用ChatClient.builder(...)创建客户端,然后调用类似prompt().user("你好").call().content()的方法发起请求。
一个最简流程可以拆成三步:
- 配置一个 ChatModel Bean,让 Spring AI 根据当前配置自动创建。
- 注入 ChatClient。
- 写一个接口或命令行测试类,发起一条用户消息并打印结果。
关键点是先确认“模型服务能被访问”这件事,而不是先写复杂的技能调用和向量逻辑。很多问题在一开始就出现,大部分是 base-url 写错、api-key 为空、网络不通或模型名不对。我一般会先用 curl 直接请求模型服务,确认服务本身活着,再检查 Spring 配置。
3.2 普通输出和流式输出要分开对待
对话接口有两种模式。
- 同步输出:逻辑简单,适合测试和后台处理。
- 流式输出:适合前端页面类似打字机效果,用户体验好,但编程模型复杂。
流式输出需要处理响应式类型,开发阶段先别混在一起。先跑同步模式,确认模型能正常返回,再处理流式。面试时能讲清楚两者区别和适用场景,比背代码更有用。
3.3 验证不是只看返回内容
跑通之后至少要看四点:
- 返回内容是否完整。
- 请求耗时和日志有没有异常。
- 打印的调用链是否清晰。
- 多次调用是否稳定。
如果日志里出现超时,先看模型服务是否真的启动,再看网络和 base-url,最后看模型名。这个顺序不要反过来,因为大多数超时不是模型能力问题,而是服务没起来或地址配置不对。
4. 让模型真正“做事”:Function Calling 是 Agent 的地基
4.1 为什么普通 Prompt 不够
直接让模型生成文本很简单,但业务场景需要模型去查数据库、调用订单系统、操作外部接口。这时候如果只靠 Prompt,模型只能“编答案”,不能真正执行动作。
Function Calling 的意义是:模型根据用户问题和系统注册的工具描述,自己决定应该调用哪个函数,然后把参数以 JSON 形式返回,由 Java 代码真正执行。这一步是 Agent 的基础,没有它,Agent 只会停留在“看起来会聊天”的阶段。
4.2 注册工具的最小实现
在 Spring AI 里,通常做一个普通 Java Bean,方法上加@Tool注解,并在 ChatClient 调用时把该类的实例作为工具传入。模型在需要时生成带参数的工具调用,框架负责把 JSON 参数解析出来,再执行对应方法。
这里有几个经验:
- 方法名和描述要写得非常清楚。模型靠描述判断是否调用,描述含糊,模型就会乱选。
- 参数类不要设计太复杂的嵌套结构。JSON Schema 越简洁,模型生成参数的准确率越高。
- 单个工具方法内部不要做太重的阻塞操作,否则会拖慢整个对话。
4.3 Agent 循环到底是什么
Agent 不是一个大而全的神秘框架,它本质上是“模型 + 工具 + 循环”的组合。一次典型循环是:
- 用户发消息。
- 模型判断是否需要调用工具。
- 如果需要,框架生成工具调用请求并执行。
- 把工具返回结果放回上下文,继续让模型生成最终回复。
- 如果还需要其他工具,模型继续发起调用,直到不需要为止。
工具越多,循环越复杂,也越容易出现“模型反复调用同一个工具”或者“上下文过长”的问题。接手一个 Agent 项目,先看循环次数上限、超时时间和工具数量,不要直接调并发。
4.4 从单工具到多工具的边界
多工具 Agent 看起来强大,实际上对模型能力和工具描述要求更高。如果模型参数量小,工具描述又互相冲突,就会频繁出现调用错误。
更稳妥的做法是:先做一个工具,跑通单次调用,再逐步加第二个、第三个。每加一个工具都要验证模型能否正确选择。不要因为框架支持十个工具就一次性塞进来,工具越多,排查越复杂。
注意:这里不要一上来就做多工具编排。先让一个工具稳定被调用,再扩展成多个。
5. 记忆、RAG 和 Spring AI Alibaba Graph:把单轮对话变成可用系统
5.1 对话记忆不是聊天记录
很多初学者把“记忆”理解成保存聊天记录。真正要解决的问题有两个:一是模型上下文窗口有限,不能无限拼接历史;二是不同业务需要不同粒度的记忆策略。
常见做法是用会话记忆组件保存 session 内的消息,同时控制保留的轮数。对超长历史,可以抽取摘要后再放入上下文。这部分不需要自己造轮子,但要理解是什么策略,面试会问。
5.2 RAG 先小样本验证
RAG 是检索增强生成,核心是先把资料切片、转成向量、存入向量数据库,用户提问时先做相似度检索,再把命中片段拼到 Prompt 里让模型生成。
为什么一定要先小样本验证?因为 RAG 的链路长,每一环都可能出问题。切片太大会把无关内容带进来,太小又会让语义不完整;向量模型选择不合适,中文相似度可能匹配不准;召回分数阈值设置不对,会导致答案缺失。
我自己验证时的顺序是:
- 准备 20 到 50 条高质量文档,手工确认没有乱码。
- 跑一次检索,看召回片段是不是真的和问题相关。
- 再让模型基于片段作答,对比不经过 RAG 的答案。
如果出现“答非所问”,大概率不是模型问题,而是召回没命中。先看检索结果,再调 Prompt,不要一上来就换大模型。
5.3 Spring AI Alibaba Graph 解决什么
Graph 是一种编排方式,把流程建模成节点和边。相比一个长 Prompt 或者一串 if/else,它更适合多步骤、分支、条件判断的任务。
Spring AI Alibaba Graph 的价值在于:Java 生态里也能用类似 LangGraph 的思维去设计 Agent 流程,而不是把所有逻辑写在一个 Controller 方法里。LangGraph 和 LangChain 的关系可以理解成:LangChain 提供组件,LangGraph 提供状态图和循环控制。Java 侧虽然没有完全一致的一比一框架,但 Spring AI Alibaba Graph 承担了相似的角色。
5.4 不要为了 Graph 而 Graph
如果任务只是“翻译一句话”“总结一段文本”,用 ChatClient 就够了。Graph 适合有明确分支和状态流转的场景,比如工单流转、审批流程、多轮信息收集。要不要上 Graph,取决于有没有真实的状态迁移需求,而不是因为看到别人用了所以也加一个。
6. 当多个功能都能跑通:接口封装、批量任务和重试策略
6.1 Controller 里不要堆 AI 逻辑
AI 功能跑通后,最先出问题的就是项目结构。把 ChatClient、工具调用、向量检索全部堆在一个 Controller 里,短期能跑,后面改 Prompt 要重启,改参数要全量回归,加新模型也没法替换。
建议最少分三层:
- 接口层:接收请求、返回结果、做参数校验。
- 服务层:编排 ChatClient、工具、RAG、记忆。
- 模型层:负责模型接入、工具注册、Prompt 模板。
这样分层之后,即使后面要把 Ollama 换成云端 API,也只需要改模型层配置,接口层和服务层不用动。
6.2 批量和异步:不要一条一条阻塞调用
如果业务需要批量处理文本,比如给一百篇文章生成摘要,不要用 for 循环同步请求。常见做法是引入任务队列,用固定线程池异步处理,把任务状态写入数据库。
真正重要的参数有两个:并发数和重试次数。不要一上来就开最大并发,先观察模型服务端吞吐。本地部署时,显存决定并发上限;云端 API 时,配额和限流决定上限。开太大只会让请求全部超时。
6.3 失败重试要看错误类型
不是所有失败都值得重试:
- 超时需要重试,但要看是不是服务端负载过高。
- 认证错误,重试没有意义。
- 模型名错误或参数校验失败,重试只会浪费成本。
- 工具执行报错,要优先看方法内部异常,而不是模型服务。
输出命名也要提前定好。批量任务如果所有结果都写到一个文件,后面很难定位是哪一条失败。建议输出文件名带上任务 ID 或序号,日志里也要能根据请求 ID 串起来。
7. 面试版:Java + 大模型,怎么把项目讲清楚
7.1 先把概念讲准确
面试里最怕的不是不会,而是把概念说模糊。
- 说“我调了大模型 API”,不如说“我用 Spring AI 统一接了 OpenAI 兼容接口和本地 Ollama 模型,通过配置文件切换模型源”。
- 说“我做了 Agent”,不如说“我实现了基于 Function Calling 的工具调用循环,模型可以自主选择查询订单接口和库存接口”。
- 说“我做了 RAG”,不如说“我把产品文档切片后存入向量库,检索 Top-K 拼接到 Prompt,并做了命中分数阈值过滤”。
面试官想听的不是名词,而是你清楚每一步在干什么。
7.2 高频问题清单
| 问题 | 回答方向 |
|---|---|
| 大模型 API 调用和普通 HTTP 请求有什么区别 | 长连接、流式返回、Token 限制、上下文管理与重试策略 |
| 什么是 Function Calling | 模型生成结构化工具调用参数,由代码执行真实动作 |
| Agent 和普通接口编排的区别 | 由模型决定调用路径,而不是代码里写死 if/else |
| RAG 解决了什么问题 | 把私有知识通过检索注入上下文,避免重新训练 |
| 微调适合什么场景 | 固定格式、特定风格、领域术语;数据量少时优先 RAG |
| 上下文超长怎么办 | 摘要、裁剪、按需检索、滑动窗口 |
7.3 项目描述要注重边界
很多人在简历上写“使用 LangChain + Agent 开发智能客服”,面试官一问“为什么不用 Spring AI”,就答不上来。更合适的写法是:把技术选型、单轮能力、多轮记忆、工具调用、失败排查都讲清楚,甚至主动说“当时用了三个工具,其中一个召回不稳定,我们通过增加描述和调低阈值解决了”。面试官更在意你踩过的坑和处理方式,而不是你用了多少个框架。
7.4 成本也是面试点
“大模型还用得起吗”这种热搜背后,是成本焦虑。面试时能聊清楚成本模型会很加分:
- 输入 Token 和输出 Token 计价通常不同。
- 缓存命中可以降低成本。
- 本地部署是固定硬件成本,API 是按量成本。
- 批量任务先小样本估算 Token 消耗,再做全量。
如果能说出“当时用了哪些手段控制成本”,比背一堆模型参数更让面试官印象深刻。
8. 踩坑清单:最容易出问题的还是环境和输入
8.1 启动报错和连接失败
常见问题包括:
- 版本不兼容:Spring Boot 2.x 跑 Spring AI 会报一堆方法找不到。
- 配置前缀不对:老教程的配置项和新版本不一致。
- base-url 忘了加端口。
- 本地模型服务没有启动。
处理顺序是:先看启动日志第一处异常,再看配置项是否加载,最后用命令行或 curl 直接请求模型服务,确认服务本身可用。不要把时间花在反复重启应用上。
8.2 输出为空和格式乱
模型返回为空,先检查 max-tokens 是否太小,再看是否触发内容过滤,最后看流式调用是否没有正确订阅。输出格式乱,优先用结构化输出或 JSON Schema,而不是在 Prompt 里写“请用 JSON 返回”。靠提醒词约束格式,远不如用框架的 Schema 约束稳定。
8.3 性能问题
如果觉得响应特别慢,不要只调大并发。先加日志,看耗时分布:
- 模型服务生成耗时。
- 工具调用耗时。
- 向量检索耗时。
- 网络传输耗时。
哪一段最慢就优化哪一段。本地模型慢,多数是显存不足,模型被换出到内存;云端慢,要看是否触发限流。
8.4 一个常用的验证顺序
我一般遇到问题不会乱改参数,按这个顺序排查:
- 复现路径是否确定。
- 输入内容、输入格式是否稳定。
- 模型服务本身是否正常。
- 框架是否正常调用。
- 业务代码是否有异常。
这条链路适用于对话、工具、RAG、批量任务,基本能覆盖大部分问题。很多人浪费时间的根源,是跳过前两步直接改模型参数,结果问题根本不在生成质量。
最后再说一句实在的。Java 开发者入局大模型,不需要把 Python 生态所有名词都背一遍,也不需要从 LangChain 源码开始啃。真正决定项目能不能落地的,是模型来源是否稳定、工具调用是否可监控、批量任务是否有重试、Prompt 和参数是否可配置。先把一条对话跑通,再逐步加工具、记忆和检索,比一开始就堆一堆框架名词靠谱得多。