Java工程师转型AI Agent开发实战:LangChain4j与Spring AI核心指南

发布时间:2026/10/5 14:16:17
Java工程师转型AI Agent开发实战:LangChain4j与Spring AI核心指南 1. 为什么 Java 工程师转 AI Agent 有天然优势1.1 从 CRUD 到智能体差的不是智商是视角这两年身边不少 Java 老哥都在焦虑同一件事干了五六年业务系统天天写 Controller、Service、Mapper突然发现招聘 JD 上开始出现“熟悉 AI Agent 开发”“有 LangChain 经验优先”这类要求。第一反应通常是——这玩意儿不是 Python 的天下吗我一个写 Java 的凑什么热闹。我一开始也这么想直到真正用 Spring AI 和 LangChain4j 把第一个 Agent 跑起来才发现事情完全不是这样。AI Agent 的本质是什么是一个能自主决策、调用工具、循环执行直到完成目标的程序。你把这个定义拆开看状态管理、流程编排、异常重试、工具注册与调用、并发控制、可观测性——这些全是 Java 工程师干了十几年的老本行。Python 生态在模型训练和实验阶段确实强但 Agent 一旦要上生产要扛并发、要做权限、要接企业已有的鉴权体系和数据库Java 那套工程化能力反而是稀缺资源。所以这篇不是教你从零学 Python而是把 Java 工程师已有的技能树映射到 AI Agent 这个新战场上。核心关键词就几个Java、AI Agent、LangChain4j、Spring AI、ReAct。搞懂这五个词之间的关系转型路径就清晰了一大半。1.2 先搞清楚 Agent 和普通调 API 的区别很多人以为接个大模型 API 就算做 AI 了这跟 Agent 差着十万八千里。普通调用是“你问我答”一问一答就结束。Agent 是“你给目标我自己想办法”。举个具体例子你让普通 API“帮我查下北京天气”它返回一段文字就完事。你让 Agent“帮我安排明天去北京的行程”它会自己拆解成查天气、查航班、查酒店、比对时间、生成方案中间发现航班取消了还会重新查。这个“自己想办法”的能力业界最经典的实现范式就是ReActReasoning Acting。它的循环逻辑是思考Reasoning→ 行动Acting调用工具→ 观察结果Observation→ 再思考直到任务完成或达到最大轮次。LangChain4j 和 Spring AI 都内置了对 ReAct 模式的支持你不需要自己从零实现这个循环但必须理解它的运转机制否则出了问题根本不知道从哪排查。Java 工程师理解 ReAct 有个天然优势它本质上就是一个带状态机的 while 循环加上工具调用的分发逻辑。你写过工作流引擎、写过状态机、写过责任链模式这些经验直接就能迁移过来。区别只在于决策者从硬编码的 if-else 变成了大模型。1.3 技术选型LangChain4j 还是 Spring AI这是转型路上第一个必须做的决定。我的建议是如果你团队已经在用 Spring Boot优先 Spring AI如果你想快速理解 Agent 的底层机制先玩 LangChain4j。LangChain4j 的定位更像“Java 版的 LangChain”抽象层次丰富Agent、Tool、Memory、RAG 这些概念都有对应的接口文档和示例也多适合学习和快速验证想法。它的 API 设计比较贴近 Python 那边的思路你去看 LangChain 的教程很多概念能直接对应上。Spring AI 则是 Spring 官方出手最大的价值是“Spring 味”——自动配置、依赖注入、和 Spring Boot 生态无缝集成。你现有的项目加个 starter 依赖配几行 yml 就能接上大模型。它对企业级开发更友好尤其是需要和现有鉴权、监控、事务体系打通的时候。不过 Spring AI 相对年轻某些高级 Agent 特性可能不如 LangChain4j 丰富需要自己补一些胶水代码。实际项目里我经常两个都用用 LangChain4j 做原型验证跑通了再用 Spring AI 重写成生产版本。别纠结哪个“更好”它们解决的是不同阶段的问题。2. 核心概念拆解把 Agent 的零件一个个讲明白2.1 大模型是大脑但光有大脑干不了活大模型在 Agent 里的角色就是一个“决策中枢”。它负责理解用户意图、决定下一步做什么、判断任务是否完成。但它本身有几个硬伤不知道实时信息、不能操作外部系统、记不住太长的上下文、还可能一本正经地胡说八道。Agent 的整套架构本质上就是在给这个大模型“打补丁”。不知道实时信息给它接搜索工具。不能操作外部系统给它接数据库和 API。记不住上下文给它加 Memory 模块。会胡说加 RAG 让它基于事实回答。所以你看Agent 开发的大部分工作不是在调模型而是在搭这套外围系统。这对 Java 工程师来说反而是好消息——外围系统才是我们的主场。2.2 Tool CallingAgent 的手和脚Tool Calling 是 Agent 最核心的能力。你定义一个 Java 方法加上注解Agent 就能在需要的时候调用它。比如Component public class WeatherTool { Tool(查询指定城市的实时天气) public String getWeather(P(城市名称) String city) { // 调用真实天气 API return weatherApi.query(city); } }这段代码在 LangChain4j 和 Spring AI 里写法略有差异但核心思想一致用注解把普通 Java 方法暴露成 Agent 可调用的工具方法上的描述文字会作为提示词的一部分发给大模型让模型知道这个工具是干什么的、参数是什么。这里有个特别容易踩的坑工具描述写得好不好直接决定 Agent 会不会用、用得对不对。我见过太多人把描述写成“查询天气”结果模型不知道该传什么参数。正确的写法要包含这个工具做什么、什么场景下用、参数的含义和格式。描述写得越清楚模型的调用准确率越高。这跟写 API 文档是一个道理只不过读者从人变成了模型。2.3 Memory让 Agent 记住上下文没有 Memory 的 Agent每次对话都是失忆的。你上一句说“我叫张三”下一句问“我叫什么”它答不上来。Memory 模块就是解决这个问题的它负责把历史对话存起来在每次请求时把相关的历史拼进提示词。Memory 分几种短期记忆当前会话的对话历史、长期记忆跨会话的用户偏好、事实知识、实体记忆提取出的结构化信息比如用户的名字、订单号。实现上短期记忆最简单存个 List 就行长期记忆通常要接向量数据库做语义检索。Java 工程师做 Memory 有个天然优势你熟悉各种缓存和存储方案。Redis、Caffeine、PostgreSQL 这些你本来就在用把对话历史存进去、按会话 ID 检索出来这套逻辑你闭着眼睛都能写。难点不在存储在于什么时候该把哪些历史塞进提示词——塞太多浪费 token 还干扰模型塞太少又记不住关键信息。这个平衡需要根据业务场景反复调。2.4 RAG给 Agent 接上私有知识RAG检索增强生成是让 Agent 回答私有领域问题的标准方案。流程是把文档切块、向量化、存进向量库用户提问时先把问题向量化检索出最相关的几个文档块连同问题一起发给大模型让它基于这些材料回答。LangChain4j 里做 RAG 特别顺手它有个Easy RAG模块几行代码就能把一堆文档灌进去。但“能跑”和“好用”之间差距巨大。我踩过的坑包括文档切块太大导致检索不准、切块太小导致语义断裂、没有做多路召回导致漏掉关键信息、没有重排序导致最相关的排在了后面。多路召回是个值得单独说的技巧。单一检索策略比如纯向量检索容易漏实践中常用“向量检索 关键词检索”双路并行再把两路结果合并去重、重排序。LangChain4j 支持配置多个检索器这个能力在生产环境几乎是必备的。3. 从零搭一个能用的 Agent完整实操3.1 环境准备与依赖引入先明确技术栈Spring Boot 3.x Spring AI 一个大模型服务。我以 Spring AI 为例因为它和现有 Java 项目集成最顺。dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version1.0.0/version /dependency如果你用的是国内的大模型服务比如百炼上的通义系列Spring AI Alibaba 提供了对应的 starter配置方式类似只是 base-url 和 model 名称不同。这里要注意版本兼容性——Spring AI 迭代很快不同版本 API 有差异建议锁定一个稳定版本再动手。配置文件里填上模型服务的地址、密钥、模型名称spring: ai: openai: base-url: https://your-model-service/v1 api-key: ${API_KEY} chat: options: model: qwen-plus temperature: 0.7temperature这个参数值得说一下。它控制输出的随机性0 到 2 之间。做 Agent 决策时建议调低0.1-0.3因为你需要它稳定地选择正确的工具做创意生成时可以调高。很多人忽略这个参数结果 Agent 行为飘忽不定排查半天才发现是温度太高。3.2 定义第一个 Tool 并让 Agent 调用假设我们要做一个“订单查询助手”用户用自然语言问订单状态Agent 自己去查数据库。Component public class OrderTool { Autowired private OrderService orderService; Tool(根据订单号查询订单的当前状态和物流信息。当用户询问订单进度、发货情况时使用此工具。) public String queryOrder( ToolParam(description 订单号通常是10到20位的数字字符串) String orderId) { Order order orderService.getByOrderId(orderId); if (order null) { return 未找到订单号为 orderId 的订单请确认订单号是否正确。; } return String.format(订单 %s 当前状态%s物流信息%s, orderId, order.getStatus(), order.getLogistics()); } }注意几个细节。第一工具方法的返回值是 String因为最终要拼进提示词给模型看返回结构化对象反而增加复杂度。第二找不到订单时返回的是友好提示而不是抛异常因为异常会中断 Agent 循环而友好提示能让模型继续和用户交互。第三参数描述里写了格式要求这能显著降低模型传错参数的概率。注册工具并创建 AgentConfiguration public class AgentConfig { Bean public ChatClient chatClient(ChatClient.Builder builder, OrderTool orderTool) { return builder .defaultSystem(你是一个专业的订单查询助手帮助用户查询订单状态。) .defaultTools(orderTool) .build(); } }调用的时候String response chatClient.prompt() .user(帮我查下订单 1234567890 到哪了) .call() .content();模型会自动识别出需要调用queryOrder工具提取出订单号执行后把结果组织成自然语言返回。整个过程你不需要写任何解析逻辑这就是 Tool Calling 的威力。3.3 用 ReAct 模式处理多步任务单工具调用只是入门真正的 Agent 要能处理需要多步、多工具协作的任务。这时候 ReAct 模式就派上用场了。假设用户问“帮我看看订单 1234567890 的状态如果还没发货就帮我取消掉。”这个任务需要两步先查状态再根据状态决定是否取消。用 ReAct 模式Agent 会先调用查询工具观察到“未发货”再调用取消工具。Tool(取消指定订单。仅在订单状态为未发货时才能取消。) public String cancelOrder(ToolParam(description 要取消的订单号) String orderId) { boolean success orderService.cancel(orderId); return success ? 订单 orderId 已成功取消。 : 取消失败订单可能已发货。; }把两个工具都注册进去Agent 就能自主编排这个流程。这里的关键是系统提示词要写清楚业务规则比如“取消订单前必须先查询状态”否则模型可能直接跳过查询去取消导致业务逻辑出错。ReAct 循环有个maxIterations参数控制最多循环几轮防止 Agent 陷入死循环。默认值通常够用但复杂任务要适当调大。我一般设成 10超过这个轮次还没完成基本说明任务描述有问题或者工具设计有缺陷。3.4 接入 RAG 让 Agent 回答私有知识订单查询是结构化数据还有大量非结构化的知识需要处理比如产品手册、FAQ、内部规范。这时候上 RAG。Configuration public class RagConfig { Bean public EmbeddingStoreTextSegment embeddingStore() { return new InMemoryEmbeddingStore(); } Bean public EmbeddingModel embeddingModel() { return new OpenAiEmbeddingModel(...); } Bean public ContentRetriever contentRetriever( EmbeddingStoreTextSegment store, EmbeddingModel model) { return EmbeddingStoreContentRetriever.builder() .embeddingStore(store) .embeddingModel(model) .maxResults(5) .minScore(0.7) .build(); } }灌数据的时候文档切块策略是重中之重。我的经验是中文文档按 300-500 字切块块之间保留 50-100 字重叠。重叠是为了防止关键信息正好被切在边界上导致语义丢失。maxResults设 5 是经验值太少可能漏太多会稀释相关性。minScore是相似度阈值低于这个分数的结果直接丢弃避免用不相关的内容污染回答。把 ContentRetriever 注册到 Agent 里它就会在回答前自动检索相关知识。LangChain4j 里这个集成更简单AiServices直接支持contentRetriever参数。4. 生产环境必须解决的问题4.1 并发Agent 怎么扛住高并发这是 Java 工程师最关心的问题也是面试高频考点。Agent 的并发瓶颈通常不在你的 Java 代码而在两个地方大模型 API 的调用速率限制和向量检索的响应时间。大模型 API 一般都有 QPS 限制你并发再高超过限制照样被拒。解决方案是加一层信号量或令牌桶限流把并发请求排队处理。Spring AI 本身不提供限流但你可以用 Resilience4j 或 Sentinel 包一层。Bean public ChatClient rateLimitedChatClient(ChatClient.Builder builder) { RateLimiter limiter RateLimiter.of(llm, RateLimiterConfig.custom() .limitForPeriod(10) .limitRefreshPeriod(Duration.ofSeconds(1)) .timeoutDuration(Duration.ofSeconds(5)) .build()); // 在调用处用 limiter 包裹 return builder.build(); }另一个思路是异步化。Agent 的调用链路长同步阻塞会浪费线程。用CompletableFuture或 Spring 的Async把模型调用异步化配合流式响应Streaming用户体验和吞吐量都能提升。流式响应还有个好处用户能实时看到 Agent 的思考过程等待焦虑大大降低。向量检索的并发优化核心是选对向量库。内存版InMemoryEmbeddingStore只适合开发生产必须上专业向量库比如 Milvus、Qdrant、PgVector。PgVector 对 Java 团队最友好因为你本来就在用 PostgreSQL加个扩展就行运维成本最低。4.2 可观测性Agent 出问题怎么排查Agent 最让人头疼的地方是“黑盒”——它为什么这么回答、为什么调了这个工具、为什么没调那个工具你光看结果根本不知道。所以日志和追踪是生产环境的生命线。至少要记录这几样每次请求的完整提示词、模型的原始响应、工具调用的入参和出参、整个链路的耗时。LangChain4j 和 Spring AI 都支持注册监听器Listener可以在关键节点打点。Bean public ChatModelListener loggingListener() { return new ChatModelListener() { Override public void onRequest(ChatModelRequestContext ctx) { log.info(LLM请求: {}, ctx.chatRequest().messages()); } Override public void onResponse(ChatModelResponseContext ctx) { log.info(LLM响应: {}, ctx.chatResponse().aiMessage()); } }; }有了这些日志排查问题就有据可依了。模型没调工具看提示词里工具描述是不是不清楚。调错了工具看是不是两个工具的描述太相似。回答跑偏看检索出来的内容是不是不相关。Agent 调试的本质就是通过日志还原它的“思考过程”然后针对性优化提示词或工具设计。4.3 成本控制别让 token 账单吓到你Agent 比普通对话费 token因为它要循环、要带历史、要带检索结果。一个复杂任务跑下来token 消耗可能是普通问答的十倍。控制成本有几个实用手段精简系统提示词别写又臭又长的角色设定把规则写清楚就行。限制历史长度只保留最近 N 轮对话或者用摘要压缩历史。控制检索数量maxResults别设太大5 个通常够用。缓存高频问题相同或相似的问题直接返回缓存结果别每次都调模型。选对模型简单任务用小模型复杂任务才上大模型分级处理。我做过一个统计加上缓存和分级模型后同样的业务量 token 成本降了将近 60%。这些优化不需要多高深的技术就是工程上的精打细算恰恰是 Java 工程师的强项。5. 常见问题与避坑指南5.1 工具调用不稳定的排查思路这是新手遇到最多的坑明明定义了工具模型就是不调或者时调时不调。排查顺序如下。先看工具描述。描述太笼统是头号原因。“查询数据”这种描述模型根本不知道什么时候该用。改成“根据用户提供的订单号查询订单状态当用户询问订单进度时使用”命中率立刻提升。再看系统提示词。如果系统提示词里说“你只能回答订单相关问题”而用户问的是别的模型可能就拒绝调工具了。提示词和工具描述要相互配合不能打架。然后看模型能力。小模型在工具调用上的表现确实不如大模型。如果业务对准确性要求高别在模型上省钱。最后看参数格式。模型传参格式不对会导致调用失败但失败信息往往被吞掉。打开详细日志看看模型到底传了什么。5.2 常见问题速查表问题现象可能原因解决方向模型不调用工具工具描述不清、提示词冲突优化描述、检查系统提示词调用工具报参数错误参数描述缺失、格式未约束补充参数说明和格式示例Agent 陷入死循环任务无法完成、工具返回不明确设 maxIterations、优化工具返回值回答与知识库不符检索不准、切块不合理调整切块策略、加多路召回响应特别慢同步阻塞、检索慢、模型慢异步化、换向量库、分级模型token 消耗过高历史太长、检索太多压缩历史、限制检索数量、加缓存并发上不去API 限流、线程阻塞加限流、异步化、连接池调优5.3 几个我踩过的坑坑一把业务逻辑写进提示词。有人喜欢在系统提示词里写一大堆 if-else 规则比如“如果用户是 VIP 就打九折如果是普通用户就不打折”。这种逻辑应该放在工具方法里用 Java 代码实现提示词只负责告诉模型“什么时候调用这个工具”。把业务规则塞进提示词既难维护又容易出错。坑二忽略工具的幂等性。Agent 可能因为重试机制重复调用同一个工具。如果你的工具是“扣款”“下单”这类有副作用的操作必须做幂等设计否则会重复执行。这个坑我在一个支付场景里踩过教训深刻。坑三不做超时控制。模型调用、工具调用都可能卡住。每个环节都要设超时否则一个卡住的请求会拖垮整个线程池。超时时间根据业务定模型调用一般 30 秒工具调用看具体操作。坑四直接用模型返回的 JSON。有些场景需要模型返回结构化数据但模型输出的 JSON 经常有格式问题多逗号、少引号、带 markdown 标记。一定要做解析容错解析失败要有降级方案别直接抛异常。6. 学习路线与进阶方向6.1 分阶段的学习路径如果你是完全的新手我建议按这个顺序来别一上来就啃框架源码。第一阶段先把大模型 API 调通。用最朴素的方式发一个 HTTP 请求理解请求和响应的结构。这一步不需要任何框架就是让你对“模型调用”这件事有体感。第二阶段用 LangChain4j 或 Spring AI 跑通一个带工具的 Agent。重点理解 Tool Calling 的机制多改改工具描述观察模型行为的变化。第三阶段加上 Memory 和 RAG。做一个能记住上下文、能回答私有知识的助手。这一步会接触到向量库、Embedding、检索这些概念。第四阶段上生产。解决并发、可观测性、成本控制这些问题。这一步才是 Java 工程师真正发挥价值的地方。6.2 值得深入的方向跑通基础 Agent 之后有几个方向值得深挖。多 Agent 协作是当前的热点让多个各有所长的 Agent 分工合作完成复杂任务LangChain4j 和 Spring AI 都在往这个方向演进。工作流编排也很实用把 Agent 嵌入到已有的业务流程里比如用 Agent 处理客服工单、自动生成报表。Agent 的可观测和评测是生产落地的刚需怎么量化 Agent 的回答质量、怎么发现回归问题这些都需要工程手段。我个人最看好的方向是把 Agent 能力沉淀成企业内部的平台。单个 Agent 好做但让全公司都能快速搭建自己的 Agent、共享工具和知识库、统一监控和计费这才是大厂和中小团队都需要的。而这套平台的建设恰恰需要 Java 工程师的架构能力和工程经验。转型这件事最怕的是被“AI”两个字吓住觉得自己不懂算法就没戏。实际上 Agent 开发里算法占比很小工程占比很大。你已有的 Java 功底、你对并发和分布式的理解、你处理复杂业务系统的经验都是这个新领域里稀缺的能力。缺的只是几个新概念和一套新工具花两三周认真实践就能补上。真正拉开差距的还是那些老生常谈的东西把问题拆清楚、把边界处理好、把异常兜住。这些你本来就会。