1. 为什么 Java 工程师转 AI Agent 有天然优势
这两年身边不少写 Java 的朋友都在焦虑同一件事:AI Agent 这么火,自己是不是要被时代甩下了。我一开始也有这种情绪,直到真正动手做了几个 Agent 项目之后才发现,Java 工程师转 AI Agent 不但不吃亏,反而在某些环节上有别人比不了的优势。这篇文章就把我从原理到落地的完整思路拆开讲,包括技术选型、核心框架、实操步骤和踩过的坑,希望能帮到同样在转型路上的同行。
先说结论:AI Agent 的本质是“让大模型能思考、能调用工具、能记住上下文、能循环执行直到完成任务”。它不是一个全新的技术栈,而是一套编排逻辑。Java 工程师最擅长的恰恰就是工程化编排——依赖注入、线程池、状态机、异常处理、可观测性,这些东西在 Agent 开发里一个都跑不掉。LangChain4j 和 Spring AI 这两个框架之所以能火,就是因为它们把 Agent 的编排能力用 Java 工程师熟悉的方式重新包装了一遍。
那 Java 工程师具体在哪些方面有优势?我总结了三点。第一是工程稳定性,Python 写 Agent 原型很快,但一到生产环境的高并发、连接池管理、超时重试、熔断降级,Java 生态的成熟度是碾压级的。第二是类型安全,Agent 的工具调用参数如果全靠字符串拼,出错了很难排查,Java 的强类型和注解体系能把很多问题挡在编译期。第三是企业集成,大部分公司的核心业务系统都是 Java 写的,Agent 要真正落地干活,绕不开和这些系统对接,这时候 Java 工程师的主场优势就体现出来了。
当然,劣势也要正视。Python 生态在 AI 领域的库更丰富,很多新论文的复现都是 Python 优先。但这不影响 Java 工程师切入,因为 Agent 开发的核心是编排和工程,模型推理本身是调 API 或者本地部署,语言差异没那么大。我的建议是:不用纠结要不要学 Python,先把 LangChain4j 或 Spring AI 跑通一个完整 Agent,你就知道该怎么补短板了。
2. AI Agent 的核心原理拆解
2.1 Agent 和普通大模型调用到底差在哪
很多人一开始分不清“调用大模型”和“AI Agent”的区别。我打个比方:调用大模型就像你问一个博学的人一个问题,他给你一个答案,结束。而 Agent 就像你雇了一个助理,你告诉他目标,他自己拆解任务、找工具、执行、检查结果、发现不对再调整,直到把事办成。核心差别就在于自主循环和工具使用。
一个标准的 Agent 循环通常包含四个环节:感知(理解用户意图和当前状态)、规划(拆解任务、决定下一步)、行动(调用工具或生成内容)、观察(检查行动结果,决定是否继续)。这个循环会一直转,直到 Agent 判断任务完成或者达到最大步数限制。ReAct 模式就是把这个循环显式化的经典范式,Reason(推理)+ Act(行动)交替进行,每一步都让模型先想清楚再动手。
为什么这个循环重要?因为大模型本身是无状态的,它不知道上一轮工具调用返回了什么,也不知道自己已经执行了几步。Agent 框架要做的就是把这些状态管理起来,在每一轮把历史对话、工具返回结果、当前任务进度一起塞给模型,让它基于完整上下文做决策。这就是为什么 Agent 开发里“上下文管理”是个核心话题,上下文塞太多会超 token 限制,塞太少模型会失忆。
2.2 ReAct 模式为什么成为主流
ReAct 之所以成为主流,是因为它解决了一个关键问题:让模型的推理过程可观测、可干预。传统的 Chain 模式是线性的,A 步骤做完做 B,B 做完做 C,中间模型想什么你不知道。ReAct 把“思考”这一步显式输出出来,你就能看到模型为什么决定调用这个工具而不是那个工具,出问题的时候也好排查。
在 LangChain4j 里,ReAct 的实现是通过AiServices配合工具注解来完成的。你定义一个接口,方法上标注@Tool,框架会自动把工具的描述、参数 schema 生成出来,塞进系统提示词里。模型在推理时如果决定调用某个工具,会输出一个结构化的调用请求,框架解析后执行对应方法,把结果再喂回模型。整个过程对开发者是透明的,你只需要关注工具本身的逻辑。
Spring AI 的思路类似,但更贴近 Spring 的编程模型。它用@Tool注解或者FunctionCallback来注册工具,通过ChatClient发起带工具的对话。Spring AI 2.0 之后对 Agent 的支持更完善了,特别是和阿里百炼、通义千问这些国内模型的对接做得很顺,这对国内开发者来说是个利好。
2.3 工具调用、记忆、规划三大件
Agent 的能力可以拆成三大件:工具调用、记忆、规划。工具调用是手脚,让 Agent 能真正干活;记忆是大脑的海马体,让 Agent 记得住上下文和历史;规划是前额叶,让 Agent 能拆解复杂任务。
工具调用这块,Java 工程师要注意的是参数校验和异常处理。模型生成的参数不一定合法,比如它可能给你传一个不存在的用户 ID,或者日期格式不对。这时候工具方法里必须做好防御,返回清晰的错误信息,让模型知道哪里错了,它下一轮才能修正。我见过很多新手写的工具方法直接抛异常,结果整个 Agent 循环就断了,体验很差。
记忆这块分短期记忆和长期记忆。短期记忆就是当前会话的上下文,通常用一个消息列表维护,注意控制长度。长期记忆需要向量数据库,把历史对话或者知识库做 embedding 存进去,需要的时候做相似度检索。LangChain4j 的 Easy RAG 模块就是干这个的,它把文档加载、切分、embedding、检索这一套流程封装得很简单,几行代码就能搭一个知识库问答。
规划这块是最考验工程能力的。简单任务模型自己就能拆,复杂任务需要你给它一个规划模板,或者用多 Agent 协作的方式,一个负责拆解,一个负责执行,一个负责检查。这块没有银弹,得根据业务场景慢慢调。
3. 技术选型:LangChain4j 还是 Spring AI
3.1 两个框架的定位差异
LangChain4j 和 Spring AI 经常被拿来比较,但我觉得它们定位其实不太一样。LangChain4j 更像是一个“AI 能力的全家桶”,它把 LLM 调用、Embedding、向量库、RAG、Agent、工具调用全都封装好了,你不需要依赖 Spring 就能用。它的 API 设计参考了 Python 的 LangChain,概念比较多,学习曲线稍陡,但功能覆盖全。
Spring AI 则是“Spring 生态的 AI 扩展”,它的设计哲学是“如果你会 Spring,你就会 Spring AI”。它把 ChatClient、EmbeddingClient、VectorStore 这些抽象成 Spring 的 Bean,用依赖注入的方式管理。如果你公司已经在用 Spring Boot,那 Spring AI 的集成成本几乎为零。Spring AI 2.0 之后对 Agent 的支持明显加强,特别是工具调用和对话记忆这块,已经能满足大部分场景。
我的选型建议是:如果是新项目、团队 Spring 技术栈成熟,优先 Spring AI;如果需要更灵活的 Agent 编排、想用一些 LangChain4j 独有的高级特性(比如多路召回、复杂的 RAG 流程),选 LangChain4j。两者也可以混用,比如用 Spring AI 做基础对话,用 LangChain4j 做 RAG 检索,通过接口适配就行。
3.2 模型接入的现实考量
国内做 Agent,模型接入是个绕不开的话题。OpenAI 的 API 在国内访问不稳定,而且成本高。现在主流的选择是阿里百炼的通义千问系列、智谱的 GLM、DeepSeek 等。Spring AI Alibaba 这个项目就是专门做 Spring AI 和阿里百炼对接的,虽然社区有传言说停更,但我实测下来核心功能是能用的,而且 Spring AI 2.0 之后官方对国内模型的支持也在加强。
接入模型的时候要注意几个参数:temperature 控制随机性,做 Agent 的时候建议调低一点(0.1-0.3),因为你需要模型稳定地按格式输出工具调用请求;max_tokens 要留够,Agent 的上下文通常比较长;top_p 一般保持默认。还有一个坑是不同模型的工具调用格式不一样,有的用 JSON,有的用特定标记,框架通常会做适配,但偶尔会有兼容性问题,遇到的时候看日志里模型原始输出就能定位。
3.3 并发场景下的架构设计
“AI Agent 怎么扛并发”是个高频问题。Agent 的每次循环都要调模型 API,延迟通常在秒级,如果直接同步处理,并发量一上来线程池就爆了。我的做法是分层处理:接入层用 WebFlux 或者 Servlet 异步,把请求丢到消息队列;处理层用固定大小的线程池消费,每个 Agent 任务独立上下文;模型调用层做限流和重试,避免把上游 API 打挂。
具体参数上,线程池大小要根据模型 API 的 QPS 限制来定。假设你的模型 API 限制是 10 QPS,每次 Agent 任务平均调 5 次模型,那理论并发就是 2。实际要留余量,设成 1.5 左右比较稳。重试策略用指数退避,第一次等 1 秒,第二次 2 秒,第三次 4 秒,超过三次就降级返回。这些在 Spring 里用 Resilience4j 或者 Sentinel 都能很方便地实现。
还有一个容易被忽略的点是上下文隔离。多个用户同时用 Agent,如果上下文串了就是严重事故。LangChain4j 的ChatMemory和 Spring AI 的ChatMemory都支持按会话 ID 隔离,但你要确保会话 ID 的生成和传递是正确的。我一般用用户 ID + 会话 ID 的组合作为 key,存在 Redis 里,设置合理的过期时间。
4. 从零搭建一个 Java AI Agent 的完整实操
4.1 环境准备与依赖配置
先说一下我的实操环境:JDK 17(Spring AI 2.0 要求 JDK 17+)、Maven 3.9、Spring Boot 3.2。如果你用 LangChain4j,JDK 8 也能跑,但建议至少 11。
Spring AI 的依赖配置,在pom.xml里加:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency>如果用阿里百炼,换成spring-ai-alibaba-spring-boot-starter,版本用最新的。LangChain4j 的话:
<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>0.35.0</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-easy-rag</artifactId> <version>0.35.0</version> </dependency>配置文件里配好 API Key 和 base URL。这里注意,API Key 千万别硬编码在代码里,用环境变量或者配置中心。我见过有人把 Key 提交到 Git 仓库,结果被扫出来盗刷,损失不小。
4.2 定义工具与 Agent 接口
假设我们要做一个“订单查询助手”,用户可以用自然语言问订单状态。先定义工具:
@Component public class OrderTools { @Tool("根据订单号查询订单状态") public String queryOrderStatus(@P("订单号") String orderId) { // 实际业务里查数据库 if (orderId == null || orderId.length() != 10) { return "订单号格式不正确,应该是10位数字"; } return "订单 " + orderId + " 状态:已发货,预计明天送达"; } @Tool("根据用户手机号查询该用户的所有订单") public String queryOrdersByPhone(@P("手机号") String phone) { return "该用户有3个订单:..."; } }注意@Tool里的描述要写清楚,这是给模型看的,描述越清晰模型越不容易调错。参数上的@P注解也是给模型看的,告诉它这个参数是什么。
然后定义 Agent 接口:
public interface OrderAgent { String chat(String userMessage); }用 LangChain4j 的AiServices构建:
OrderAgent agent = AiServices.builder(OrderAgent.class) .chatLanguageModel(model) .tools(new OrderTools()) .chatMemory(MessageWindowChatMemory.withMaxMessages(20)) .build();Spring AI 的话,用ChatClient:
ChatClient chatClient = ChatClient.builder(chatModel) .defaultTools(new OrderTools()) .build();4.3 接入 RAG 做知识增强
光有工具还不够,用户可能问一些业务规则类的问题,比如“退货政策是什么”。这时候需要 RAG。LangChain4j 的 Easy RAG 用起来很简单:
EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>(); EmbeddingStoreIngestor.ingest( FileSystemDocumentLoader.loadDocuments("/path/to/docs"), embeddingStore ); ContentRetriever retriever = EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(5) .minScore(0.7) .build();然后把 retriever 挂到 Agent 上。这里maxResults和minScore是两个关键参数。maxResults控制召回多少条,太多会污染上下文,太少可能漏掉关键信息,一般 3-5 条比较合适。minScore是相似度阈值,低于这个分数的直接丢弃,避免不相关内容干扰模型。这两个参数需要根据你的文档质量和 embedding 模型来调,没有固定值。
如果要做得更精细,可以用多路召回:同时用向量检索和关键词检索,然后做融合排序。LangChain4j 支持自定义ContentRetriever,你可以把多个 retriever 的结果合并去重。这块工作量不小,但效果提升明显,特别是对于包含专有名词的场景。
4.4 完整对话流程与状态管理
一个完整的 Agent 对话流程是这样的:用户发消息 -> 加载会话历史 -> 拼接系统提示词和工具描述 -> 调模型 -> 模型返回工具调用请求 -> 执行工具 -> 把结果喂回模型 -> 模型生成最终回复 -> 保存会话历史 -> 返回给用户。
这个流程里最容易出问题的是状态管理。我踩过的坑是:会话历史没有做长度限制,聊了十几轮之后 token 超了,模型开始报错。后来改成用滑动窗口,只保留最近 20 条消息,同时把重要的历史信息做摘要存起来。LangChain4j 的MessageWindowChatMemory就是干这个的,Spring AI 也有类似的ChatMemory实现。
还有一个坑是工具调用的循环次数。模型有时候会陷入死循环,反复调同一个工具。必须设置最大迭代次数,比如 10 次,超过就强制结束并返回当前结果。这个在框架里通常有配置项,别忘了设。
5. 生产环境落地的关键问题与排查
5.1 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决方案 |
|---|---|---|---|
| 模型不调用工具,直接回答 | 工具描述不清晰 / 模型能力不足 | 看模型原始输出 | 优化 @Tool 描述,换更强的模型 |
| 工具调用参数错误 | 参数 schema 不明确 | 看模型生成的参数 | 加 @P 描述,加参数校验 |
| 上下文超长报错 | 会话历史太长 | 看 token 计数 | 加滑动窗口,做历史摘要 |
| 响应慢 | 模型 API 延迟高 / 循环次数多 | 看每步耗时 | 加缓存,限制最大迭代次数 |
| 并发上不去 | 线程池配置不合理 | 看线程池监控 | 调线程池大小,加限流 |
| 结果不稳定 | temperature 太高 | 看配置 | 调低 temperature 到 0.1-0.3 |
5.2 性能优化的几个实操技巧
第一个技巧是缓存。Agent 的很多调用是重复的,比如系统提示词、工具描述这些,每次请求都重新生成很浪费。可以把这些做成本地缓存,启动时加载一次。模型响应也可以做缓存,对于相同的问题直接返回缓存结果,能省不少 token。
第二个技巧是流式输出。Agent 的响应通常比较长,如果等全部生成完再返回,用户等待时间很长。用 SSE 或者 WebSocket 做流式输出,用户能实时看到 Agent 的思考过程,体验好很多。Spring AI 和 LangChain4j 都支持流式,配置一下就行。
第三个技巧是异步化。工具调用如果是 IO 密集型的(比如查数据库、调外部 API),用异步方式执行,多个工具可以并行调。Java 的CompletableFuture或者 Reactor 都能做。但要注意,并行调用工具会打乱模型的推理顺序,有些场景下模型需要按顺序调工具,这时候就不能并行。
5.3 我踩过的三个坑
第一个坑是模型版本升级导致的兼容性问题。有一次上游模型小版本升级,工具调用的格式变了,Agent 直接不工作了。后来我学乖了,模型版本锁定,升级前先在测试环境跑一遍回归。生产环境的模型配置和测试环境保持一致,避免“测试没问题,上线就挂”。
第二个坑是会话 ID 冲突。早期我用用户 ID 做会话 key,结果同一个用户开两个浏览器窗口,上下文串了,A 窗口问的问题 B 窗口能看到。后来改成用户 ID + 会话 ID 的组合,会话 ID 由前端生成,每次新开窗口都是新的。这个细节不注意,生产环境就是事故。
第三个坑是异常处理不完善。工具方法抛异常的时候,如果直接往上抛,整个 Agent 循环就断了。正确的做法是在工具方法里 catch 异常,返回一个描述性的错误信息,让模型知道发生了什么,它下一轮可以换个方式重试或者告诉用户。这个模式叫“优雅降级”,在 Agent 开发里特别重要。
6. 学习路线与后续扩展方向
如果你刚起步,我的建议学习路线是这样的:第一周先把 LangChain4j 或 Spring AI 的官方示例跑通,理解 ChatClient、Tool、Memory 这几个核心概念。第二周做一个带工具调用的简单 Agent,比如天气查询或者计算器。第三周接入 RAG,做一个知识库问答。第四周尝试多 Agent 协作或者复杂任务规划。这个节奏下来,一个月就能上手做实际项目。
后续扩展方向有几个。一是多模态,让 Agent 能处理图片、语音,这块 Spring AI 和 LangChain4j 都在跟进。二是多 Agent 协作,用多个专职 Agent 分工完成复杂任务,比如一个负责理解需求,一个负责查资料,一个负责生成报告。三是接入工作流引擎,把 Agent 嵌入到更大的业务流程里,这块可以看看 Dify 这类平台的设计思路,把它的工作流转成 Spring AI 的 Java 代码是个不错的练手项目。
最后分享一个我个人的体会:Java 工程师转 AI Agent,最大的障碍不是技术,而是心态。总觉得自己不懂 AI,不敢下手。其实 Agent 开发 80% 是工程问题,20% 才是 AI 问题。你多年积累的工程能力,在这个领域是稀缺资源。动手做一个,比看十篇文章都有用。