☰
Java工程师转型AI Agent开发实战:LangChain4j与Spring AI核心指南
2026/10/5 14:15:58 网站建设 项目流程

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“帮我安排明天去北京的行程”,它会自己拆解成:查天气、查航班、查酒店、比对时间、生成方案,中间发现航班取消了还会重新查。

这个“自己想办法”的能力,业界最经典的实现范式就是ReAct(Reasoning + 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 Calling:Agent 的手和脚

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> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>1.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.7

temperature这个参数值得说一下。它控制输出的随机性,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 循环,而友好提示能让模型继续和用户交互。第三,参数描述里写了格式要求,这能显著降低模型传错参数的概率。

注册工具并创建 Agent:

@Configuration 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 EmbeddingStore<TextSegment> embeddingStore() { return new InMemoryEmbeddingStore<>(); } @Bean public EmbeddingModel embeddingModel() { return new OpenAiEmbeddingModel(...); } @Bean public ContentRetriever contentRetriever( EmbeddingStore<TextSegment> 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 功底、你对并发和分布式的理解、你处理复杂业务系统的经验,都是这个新领域里稀缺的能力。缺的只是几个新概念和一套新工具,花两三周认真实践就能补上。真正拉开差距的,还是那些老生常谈的东西:把问题拆清楚、把边界处理好、把异常兜住。这些,你本来就会。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询