
1. Java 开发者切入 AI 的真实路径拆解1.1 为什么 Java 开发者不需要从零学 Python我做了十多年 Java 后端这两年身边问得最多的一句话就是搞 AI 是不是必须转 Python我的答案一直很明确——不需要从零转但需要补一层认知。Java 开发者的优势在于工程化能力、类型系统、并发模型、Spring 生态的依赖注入和事务管理这些东西在把 AI 能力落地成企业级服务时比会写几行 notebook 值钱得多。真正要补的不是语言而是三块认知第一块是大模型的基本交互范式也就是 prompt、token、上下文窗口、温度这些概念第二块是向量与检索也就是 embedding、相似度、向量库第三块是编排与工具调用也就是常说的 agent 和 function calling。这三块补完你原来写的 Controller、Service、Repository 那套分层思路完全可以平移过来只不过 Repository 后面接的不再只是 MySQL还可能是向量库和模型 API。我自己的切入顺序是这样的先用 Spring Boot 写一个最简单的接口把用户输入转发给模型把返回打印出来跑通“请求-模型-响应”这条链路。这一步大概半天就能完成目的是打破“AI 很神秘”的心理门槛。跑通之后再去理解流式输出、多轮对话、上下文管理最后才碰 RAG 和 agent。很多人一上来就啃 RAG 论文结果连一次完整的模型调用都没跑过这是典型的顺序错误。提示不要一上来就纠结选哪个框架。先把一次裸调用跑通框架只是帮你把重复代码封装起来理解成本远低于你的想象。1.2 从 CRUD 思维到 AI 应用思维的三个转变第一个转变是从确定性到概率性。你写if (user.getAge() 18)结果是确定的但模型输出是概率性的同样的输入可能给出不同措辞。这意味着你的代码里必须增加校验层、重试层和兜底层不能假设模型一定返回你想要的格式。我通常会在 prompt 里强制要求 JSON 输出然后在 Java 侧用 Jackson 反序列化失败就重试一次再失败就走降级逻辑。第二个转变是从同步阻塞到流式消费。传统接口是请求进来、查库、返回AI 场景下模型生成可能要几秒到几十秒用户等不了。所以流式输出SSE 或 WebSocket几乎是标配。Spring 里用SseEmitter或者 WebFlux 的FluxString都能做关键是把模型返回的 token 流一段段推给前端让用户看到字在“打字”。第三个转变是从单次调用到上下文编排。一次对话不是孤立的历史消息、系统提示、检索到的知识片段都要拼进上下文。这就涉及 token 预算管理——上下文窗口是有限的塞太多历史会挤掉检索内容塞太多检索内容又会超出窗口。我一般会给历史消息设一个滑动窗口比如保留最近 10 轮检索片段控制在 3 到 5 条每条不超过 500 字。这三个转变想清楚了你再看 Spring AI 或 LangChain4j 的文档会发现它们解决的正是这些工程问题而不是什么高深算法。1.3 工具链选型的核心判断标准选型这件事我踩过坑所以现在只看四个维度生态契合度、抽象层次、可观测性、社区活跃度。生态契合度指的是它跟你现有 Spring Boot 项目能不能无缝集成抽象层次指的是它封装得太浅你要写一堆胶水代码封装得太深你又没法控制细节可观测性指的是能不能方便地看到每次调用的 token 消耗、耗时、prompt 内容社区活跃度决定了你遇到问题能不能搜到答案。按这四个维度Spring AI 和 LangChain4j 是目前 Java 侧最主流的两条路线。Spring AI 的优势是跟 Spring Boot 天然一体配置化程度高适合已经在用 Spring 生态的团队LangChain4j 的优势是抽象更贴近 LangChain 的概念体系链式编排和 RAG 组件更丰富适合需要灵活组合各种能力的场景。我的建议是如果你的项目就是标准 Spring Boot 微服务优先 Spring AI如果你要做复杂的多步编排和 RAG 实验LangChain4j 的组件会更顺手。维度Spring AILangChain4j集成方式Spring Boot Starter配置驱动手动构建组件代码驱动抽象层次较高约定优于配置中等灵活但需自己组装RAG 支持内置 VectorStore 抽象内置 EmbeddingStore 与检索器流式输出原生支持 Flux/SseEmitter支持 StreamingChatLanguageModel适合场景企业级标准微服务复杂编排与实验性项目这张表不是让你二选一而是让你知道什么场景下哪个更省事。实际项目里我甚至见过两个混用的用 Spring AI 管模型调用用 LangChain4j 管检索链也不是不行只是维护成本会高一些。2. 核心概念与最小可运行示例2.1 大模型调用的本质一次 HTTP 请求很多人把大模型想得太玄其实从 Java 开发者视角看调用模型就是一次 HTTP POST请求体里放 prompt响应体里拿文本。以 OpenAI 兼容接口为例请求大概是这样的结构model指定模型名messages是一个数组每个元素有role和contenttemperature控制随机性stream控制是否流式。{ model: your-model-name, messages: [ {role: system, content: 你是一个严谨的 Java 技术助手}, {role: user, content: 解释一下 Spring 的依赖注入} ], temperature: 0.7, stream: false }理解了这个结构你就理解了 90% 的模型调用。所谓多轮对话就是把历史消息按顺序塞进messages数组所谓系统提示就是role为system的那条消息所谓流式就是把stream设为 true然后按 SSE 格式逐块读取。在 Spring AI 里这套东西被封装成了ChatClient。你可以这样写RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient builder .defaultSystem(你是一个严谨的 Java 技术助手) .build(); } GetMapping(/chat) public String chat(RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } }这段代码背后做的就是上面那个 HTTP 请求只不过 Spring AI 帮你处理了序列化、鉴权、重试。我建议每个刚入门的人都先手写一次 HTTP 调用再用框架这样出问题时你知道该去哪一层排查。2.2 Prompt 工程在 Java 侧的落地方式Prompt 工程不是玄学本质是把模糊需求翻译成模型能稳定执行的指令。我在 Java 项目里通常把 prompt 分成三层系统层、任务层、数据层。系统层定义角色和边界任务层描述具体要做什么数据层放用户输入和检索到的上下文。一个我常用的模板是这样的String systemPrompt 你是一个企业知识库助手。 规则 1. 只根据提供的参考资料回答不要编造。 2. 如果参考资料中没有答案明确说资料中未找到相关信息。 3. 回答使用中文分点陈述每点不超过两句话。 ; String userPrompt 参考资料 %s 用户问题%s .formatted(context, question);这里的关键是规则要具体、可验证。“不要编造”太模糊“资料中没有答案就说未找到”才是可执行的。我见过太多人写“请准确回答”这种指令模型根本不知道什么叫准确。还有一个技巧是用分隔符隔离数据。上面用“参考资料”和“用户问题”把两部分分开能有效降低 prompt 注入的风险。如果用户输入里包含“忽略以上指令”这类内容分隔符能帮模型区分哪些是指令、哪些是数据。注意prompt 里不要放敏感信息。即使是内部系统也要假设模型服务方可能记录请求内容用户隐私数据要么脱敏要么在本地模型上处理。2.3 向量与 EmbeddingRAG 的地基RAG 的核心思路一句话说清先把知识切成小块存进向量库用户提问时先检索最相关的几块再把这几块和问题一起交给模型生成答案。这样做的好处是模型不需要记住所有知识只需要根据检索到的片段组织语言既降低了幻觉又让知识可以随时更新。Embedding 是把文本变成一串浮点数的过程比如一段 500 字的文本可能变成 1536 维的向量。语义相近的文本向量距离也近。检索时把用户问题也转成向量然后在向量库里找距离最近的几条这就是语义检索。在 Java 侧LangChain4j 的写法大概是这样EmbeddingModel embeddingModel new AllMiniLmL6V2EmbeddingModel(); EmbeddingStoreTextSegment store new InMemoryEmbeddingStore(); // 入库 Document doc Document.from(Spring AI 是 Spring 生态的 AI 应用框架); ListTextSegment segments new DocumentSplitter(500, 50).split(doc); for (TextSegment segment : segments) { Embedding embedding embeddingModel.embed(segment).content(); store.add(embedding, segment); } // 检索 Embedding queryEmbedding embeddingModel.embed(什么是 Spring AI).content(); ListEmbeddingMatchTextSegment matches store.findRelevant(queryEmbedding, 3);这里有两个参数值得说分块大小和重叠长度。分块太大检索精度下降分块太小语义不完整。我一般从 500 字、重叠 50 字开始试根据实际召回效果调整。重叠是为了防止一句话被切断导致语义丢失。2.4 从零搭一个本地 RAG 知识库的最小闭环如果你想快速验证 RAG 效果又不想依赖外部服务可以用 Ollama 在本地跑一个模型配合 LangChain4j 搭最小闭环。步骤是这样的第一步本地安装 Ollama 并拉取一个模型比如ollama pull qwen2.5。第二步在 Java 项目里引入 LangChain4j 的 Ollama 依赖。第三步写一个OllamaChatModel和一个OllamaEmbeddingModel。第四步把本地文档读进来、切块、入库。第五步写一个检索加生成的接口。OllamaChatModel chatModel OllamaChatModel.builder() .baseUrl(http://localhost:11434) .modelName(qwen2.5) .build(); OllamaEmbeddingModel embeddingModel OllamaEmbeddingModel.builder() .baseUrl(http://localhost:11434) .modelName(nomic-embed-text) .build();这个闭环跑通之后你就有了一个完全本地的知识库问答系统。它的价值不在于性能多强而在于让你完整走一遍“文档-切块-向量-检索-生成”的流程后面换成云端模型或生产级向量库只是替换组件而已。提示本地模型的效果和速度取决于你的机器配置。如果只是验证流程用 7B 级别的模型就够了不要一上来就上大参数模型加载慢还占内存。3. RAG 进阶从能跑到好用之间的差距3.1 检索质量决定 RAG 上限RAG 项目做多了会发现一个规律生成质量的上限由检索质量决定。模型再强检索回来的片段不相关它也编不出正确答案。所以优化 RAG 的重心应该放在检索环节而不是反复调 prompt。检索质量差通常有三个原因分块策略不合理、embedding 模型不适合中文、检索数量设置不当。分块策略我前面说了500 字加 50 字重叠是个稳妥起点。embedding 模型方面中文场景建议用专门优化过中文的模型通用英文模型在中文上的语义区分度会明显下降。检索数量方面返回 3 条和返回 10 条效果差别很大返回太多会引入噪声返回太少可能漏掉关键信息我一般先用 5 条做基线。还有一个容易被忽略的点是元数据过滤。如果你的知识库有分类、时间、权限等维度检索时先按元数据过滤再算相似度能大幅提升相关性。比如用户问的是 2024 年的政策你就不该把 2020 年的文档检索出来。3.2 混合检索与重排序的实用价值纯向量检索有个短板对关键词不敏感。用户搜一个专有名词向量检索可能返回语义相近但术语不对的片段。解决办法是混合检索也就是向量检索加关键词检索两路结果合并后再重排序。关键词检索可以用 Lucene 或 Elasticsearch 的 BM25向量检索用向量库两路各取前 20 条合并去重后用重排序模型打分取前 5 条给模型。重排序模型rerank的作用是更精细地判断问题和片段的匹配度它比向量相似度更准但计算更慢所以只用在候选集上。在 LangChain4j 里可以用EmbeddingStoreContentRetriever配合自定义的ContentAggregator实现混合检索。Spring AI 侧则可以通过VectorStore的similaritySearch加上自定义过滤逻辑来近似实现。这块没有开箱即用的完美方案需要根据你的数据特点调。检索方式优势短板适用场景纯向量检索语义理解好关键词不敏感自然语言提问纯关键词检索术语精确语义泛化差专有名词查询混合检索兼顾两者实现复杂生产级知识库加重排序精度最高延迟增加对准确率要求高3.3 评估 RAG 效果的几个可量化指标RAG 不能凭感觉说“好像还行”得有指标。我常用的有三个命中率、召回率、答案忠实度。命中率是检索结果里包含正确答案的比例召回率是正确答案被检索到的比例忠实度是生成答案里有多少内容真正来自检索片段。测命中率和召回率需要准备一批问题和标准答案然后跑检索看结果。忠实度可以用另一个模型来打分让它判断答案是否基于给定片段。这套评估流程搭起来要花点时间但它是你判断优化是否有效的唯一依据。没有评估你改一个参数觉得变好了可能只是心理作用。我一般会准备 50 到 100 个测试问题覆盖常见问法和边界情况每次调整分块、检索数量、重排序策略后都跑一遍记录指标变化。这个过程很枯燥但比盲目调参靠谱得多。3.4 知识库更新的工程化处理知识库不是建一次就完事文档会更新、会新增、会删除。工程化处理的核心是增量更新和版本管理。增量更新指的是只处理变化的文档而不是每次全量重建向量库。版本管理指的是保留文档的历史版本检索时能区分新旧。实现上我会给每个文档算一个内容哈希入库时记录哈希值。更新时对比哈希变了才重新切块和向量化。删除时根据文档 ID 删掉对应的向量。这样一套下来知识库的维护成本会低很多。还有一个细节是切块的一致性。如果分块策略变了旧向量和新向量就不在一个语义空间里检索会出问题。所以分块策略一旦确定要么不改要么全量重建。我一般会在项目初期多试几种策略定下来之后就锁死。4. Agent 与工具调用让模型动手做事4.1 Function Calling 的基本原理Function Calling 让模型不只是聊天还能调用你定义好的 Java 方法。流程是这样的你把可用的方法描述成 JSON Schema 告诉模型模型判断需要调用哪个方法、传什么参数你的代码执行方法把结果再喂回模型模型据此生成最终回答。在 Spring AI 里可以用Tool注解标记方法Component public class WeatherTools { Tool(description 根据城市名查询当前天气) public String getWeather(ToolParam(description 城市名称) String city) { // 实际调用天气 API return city 今天晴25 度; } }然后在 ChatClient 里注册这个工具模型就会在需要时自动调用。这里的关键是方法描述要清晰模型靠描述来判断什么时候调用。描述写“查询天气”不如写“根据城市名查询当前天气返回温度和天气状况”。4.2 Agent 编排的常见模式Agent 编排我见过三种典型模式ReAct 模式、Plan-and-Execute 模式、多 Agent 协作模式。ReAct 是边想边做模型先推理下一步该干什么然后调用工具看结果再推理循环直到得出答案。Plan-and-Execute 是先制定完整计划再逐步执行适合步骤明确的复杂任务。多 Agent 协作是把不同职责拆给不同 Agent比如一个负责检索、一个负责计算、一个负责汇总。Java 侧实现这些模式LangChain4j 的AiServices和Agent抽象比较顺手Spring AI 则更偏向单次工具调用。我的经验是简单场景用单次工具调用就够了别过度设计真正需要多步推理的场景再上 Agent 编排而且要加最大步数限制防止模型陷入死循环。注意Agent 调用工具是有副作用的比如下单、发邮件。生产环境一定要加人工确认或权限校验不能让模型自主执行敏感操作。4.3 工具调用的安全边界设计工具调用最大的风险是模型误调用或恶意调用。防护措施有三层第一层是工具白名单只暴露必要的工具危险操作不暴露第二层是参数校验模型传的参数要在 Java 侧再校验一遍不能信任第三层是权限控制工具执行前检查当前用户有没有权限。我还会给工具调用加审计日志记录谁在什么时候调了什么工具、传了什么参数、返回了什么结果。出了问题能追溯也能用来分析模型的行为模式。5. 常见问题与排查技巧实录5.1 模型返回格式不稳定的处理最常见的问题是模型不按要求的 JSON 格式返回多一个逗号、少一个引号都会导致反序列化失败。我的处理方式是prompt 里明确要求“只返回 JSON不要任何解释”然后用 Jackson 解析失败就重试一次重试时在 prompt 里加上“上次返回格式错误请严格按 JSON 格式返回”。如果两次都失败就走降级逻辑返回一个默认结构。还有一个技巧是用模型的 JSON mode如果模型支持的话开启后它会强制输出合法 JSON。Spring AI 和 LangChain4j 都支持配置这个选项。5.2 上下文超长的截断策略上下文窗口有限历史消息加检索片段很容易超。我的截断策略是系统提示永远保留最近 3 轮对话保留检索片段按相关性排序后取前 N 条超出预算就从最旧的历史开始删。预算计算可以用 tokenizer 精确算也可以按字符数粗略估中文大概 1 个字 1 个 token英文 4 个字符 1 个 token。5.3 流式输出中断的排查流式输出中断通常有三个原因网络超时、模型服务限流、客户端断开。排查时先看服务端日志有没有异常再看模型服务返回的状态码。如果是超时调整客户端和服务端的超时配置如果是限流加退避重试如果是客户端断开服务端要能感知并释放资源。问题现象可能原因排查方向返回格式错误prompt 不明确检查 prompt 约束上下文超长历史或检索过多检查 token 预算流式中断超时或限流检查网络与服务端日志检索不相关分块或模型问题检查分块策略与 embedding工具误调用描述不清检查工具描述与白名单5.4 本地模型与云端模型的取舍本地模型的好处是数据不出内网、无调用成本、可离线短板是效果和速度受硬件限制。云端模型效果好、速度快但有调用成本和数据合规问题。我的建议是开发和验证阶段用本地模型快速迭代生产环境根据数据敏感度选择敏感数据用本地非敏感用云端或者混合部署。6. 学习路线与资源组织6.1 分阶段的学习计划第一阶段跑通模型调用理解 prompt、token、流式输出大概一周。第二阶段理解 embedding 和向量检索搭一个最小 RAG大概两周。第三阶段学习工具调用和 Agent 编排做一个能查数据、能算数的助手大概两周。第四阶段深入 RAG 优化做混合检索和重排序建立评估体系大概一个月。这个节奏不绝对但顺序别乱每一步都建立在前面基础上。6.2 值得反复读的文档与源码Spring AI 和 LangChain4j 的官方文档是必读的但不要只读不练。我的习惯是读完一个模块就去写一个最小示例跑通了再读下一个。源码方面重点看ChatClient和EmbeddingStore的实现理解它们怎么封装 HTTP 调用和向量计算。看源码不是为了改它而是为了出问题时知道去哪找原因。6.3 从 Demo 到生产的差距Demo 能跑不代表能上生产。生产环境要考虑并发调用下的限流和熔断、模型调用的成本监控、prompt 的版本管理、检索结果的质量监控、用户反馈的收集闭环。这些工程问题才是 Java 开发者的主战场也是你相对于纯算法背景的人的优势所在。我在实际项目里的体会是AI 应用的难点从来不在模型本身而在怎么把模型能力稳定、可控、可观测地集成进现有系统。Java 开发者在这件事上有天然优势缺的只是对 AI 交互范式的理解。把这一层补上剩下的都是你熟悉的东西。