一、先搞懂:AI Agent 到底是什么?
1.1 一句话定义
Java AI Agent = 大模型的“脑子” + Java 工程的“手和脚”。
普通的 ChatGPT 聊天窗口,本质是“你问一句,它答一句”,它没有手脚、不能真正帮你做事。而 Agent(智能体)不一样,它在一个目标驱动下,可以:
- 理解用户意图:你说“帮我查一下北京明天要不要带伞”,它知道这是“查天气”。
- 规划下一步动作:它会想“我需要先调天气 API,再根据降水概率给建议”。
- 调用外部工具 / 系统:真的去调 HTTP 接口、查数据库、发消息。
- 利用上下文记忆持续工作:记得你上一句说的是“北京”,这一句“那后天呢?”也能接上。
- 必要时结合知识库检索(RAG):遇到公司内部制度、产品文档这种模型不知道的东西,它会先查资料再回答。
- 最终返回一个“助手”,而不是“聊天机器人”。
1.2 用一个生活类比
把 Agent 想象成公司里的 “新来的实习生助理”:
| 能力 | 对应 Agent 的组件 |
|---|---|
| 听懂你在说啥 | 大模型(LLM) |
| 会查资料(翻手册、翻 wiki) | RAG(知识库检索) |
| 会用公司的各种系统(OA、CRM、工单) | Tools(工具调用) |
| 记得你昨天交代过啥 | Memory(记忆) |
| 做事有流程、不乱来 | Workflow(编排) |
| 干活有留痕、能追溯 | 可观测性 + 审计 |
二、Java 生态目前主流的技术选型
在 Java 里做 Agent,当前比较常见的两个框架:
| 框架 | 特点 | 适合场景 |
|---|---|---|
| Spring AI | 跟 Spring Boot 深度绑定,配置中心、依赖注入、AOP、Actuator 都能复用 | 企业级后端系统集成 |
| LangChain4j | 轻量直接,API 更像 Python 的 LangChain,Tool Calling / Memory / RAG 一把梭 | 快速做原型、独立 Agent 服务 |
还有一个新东西值得关注:
- MCP(Model Context Protocol):Anthropic 提出的“工具接入标准协议”。未来工具越接越多时,用 MCP 做标准化接入,能避免每个工具都写一遍对接代码。
🧭 选型建议:
- 想快速跑起来看效果 → LangChain4j
- 要接入现有 Spring Boot 项目 → Spring AI
- 工具很多、要长期维护 → 在上面两个之外再引入 MCP
三、Java Agent 开发的 8 个步骤
下面是一条比较实用的推进顺序。建议严格按顺序来,别一上来就搞多 Agent 协作——那是第 9 步以后的事。
1. 定义目标 → 2. 选框架 → 3. 接大模型 → 4. 定义工具
↓
5. 加记忆 → 6. 接 RAG → 7. 编排流程 → 8. 可观测 + 安全
第 1 步:先定义 Agent 的目标(最重要!)
很多人一上来就写代码,结果做到一半发现“我到底要做个啥?”
先用一句话回答三个问题:
- 谁用?(客服、员工、运维、开发者…)
- 解决什么问题?(退款查询、报销咨询、故障排查…)
- 边界在哪?(不处理投诉、不做支付、不回答 xx…)
常见的 Agent 类型:
- 智能客服:回答产品问题、查订单、开工单
- 工单助手:自动分类工单、补全字段、推荐处理人
- 旅行助手:查天气、订酒店、规划路线
- 知识库问答:读内部文档回答问题
- 运维排障:读监控、查日志、给修复建议
⚠️ 小白最容易踩的坑:想做一个“什么都能干”的 Agent。不要。目标越聚焦,效果越好。
第 2 步:选择基础框架
假设我们要做一个 “天气小助手 Agent”,用 LangChain4j 起步。
先在 Maven 里引依赖:
<dependencies>
<!-- LangChain4j 核心 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>0.35.0</version>
</dependency>
<!-- OpenAI 兼容接口(国内模型大多兼容这个协议) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>0.35.0</version>
</dependency>
</dependencies>
💡 国内大多数模型平台(DeepSeek、通义千问、豆包、Kimi 等)都提供“OpenAI 兼容模式”,换一个
baseUrl和apiKey就能用。
第 3 步:接入大模型(Agent 的脑子)
最基础的调用长这样:
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.chat.ChatLanguageModel;
public class HelloAgent {
public static void main(String[] args) {
// 1. 构建一个模型客户端(相当于“找到脑子”)
ChatLanguageModel model = OpenAiChatModel.builder()
.baseUrl("https://api.deepseek.com/v1") // 也可以换成其他兼容平台
.apiKey(System.getenv("LLM_API_KEY"))
.modelName("deepseek-chat")
.temperature(0.3) // 温度越低越稳重
.timeout(java.time.Duration.ofSeconds(30))
.build();
// 2. 让模型说句话
String answer = model.generate("用一句话解释什么是 AI Agent");
System.out.println(answer);
}
}
跑通这一步,你已经拥有一个“会聊天的 Java 程序”了。但它还不是 Agent——它没有工具、没有记忆。
第 4 步:定义工具(Tools)—— Agent 和聊天机器人的分水岭
没有工具的 Agent 只是一个会聊天的玩具。
工具是什么? 就是 Agent 可以调用的“外部能力”,本质上是一个 Java 方法。在 LangChain4j 里,你只要给方法加两个注解,模型就能自动决定“要不要调”“怎么调”。
4.1 定义两个工具:查天气 + 查订单
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.P;
public class MyTools {
@Tool("查询指定城市未来一天的天气,返回文字描述")
public String getWeather(@P("城市中文名,例如:北京") String city) {
// 真实场景:调用高德/和风天气 API
// 这里先 mock
return city + "明天多云转晴,20~28℃,降水概率 20%";
}
@Tool("根据订单号查询订单状态")
public String getOrderStatus(@P("订单号,例如:O20260101001") String orderId) {
// 真实场景:调 DB 或内部 HTTP API
return "订单 " + orderId + " 状态:已发货,预计明天送达";
}
}
4.2 把工具交给 Agent 使用
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
// 定义 Agent 的对外接口(就是一个普通 Java 接口)
interface WeatherAgent {
@SystemMessage("你是一个贴心的生活助手,必要时请调用工具获取真实数据,不要编造。")
String chat(String userMessage);
}
public class AgentDemo {
public static void main(String[] args) {
ChatLanguageModel model = /* 同第 3 步 */;
WeatherAgent agent = AiServices.builder(WeatherAgent.class)
.chatLanguageModel(model)
.tools(new MyTools()) // 把工具塞进去
.build();
System.out.println(agent.chat("帮我查一下北京明天的天气,顺便看看订单 O20260101001 到哪了"));
}
}
背后发生了什么?(重要,小白一定要理解)
- 模型看到用户问题,自己决定:这里要调
getWeather("北京")和getOrderStatus("O20260101001")。 - LangChain4j 真的执行这两个 Java 方法,把结果喂回模型。
- 模型再把结果组织成自然语言返回给用户。
这就是所谓的 Function Calling / Tool Calling,是现代 Agent 的核心机制。
🎯 口诀:能让程序去做的事,都写成 Tool;让模型只负责“想”,别让它“算”“查”“执行”。
第 5 步:加入 Memory(记忆)
没有记忆的 Agent,每轮对话都是“初次见面”。加上记忆才像个助手。
常见的三种记忆策略:
| 类型 | 做法 | 适用 |
|---|---|---|
| 窗口记忆 | 只保留最近 N 轮对话 | 绝大多数对话场景 |
| 持久化记忆 | 存到 Redis / DB,按 userId 隔离 | 多会话、多用户 |
| 用户画像记忆 | 抽取长期偏好(如“不吃辣”)单独存 | 个性化强的场景 |
5.1 窗口记忆(入门用这个)
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
WeatherAgent agent = AiServices.builder(WeatherAgent.class)
.chatLanguageModel(model)
.tools(new MyTools())
.chatMemory(MessageWindowChatMemory.withMaxMessages(20)) // 只记最近 20 条
.build();
5.2 持久化记忆(多用户场景)
import dev.langchain4j.memory.chat.ChatMemoryProvider;
// 改成按 userId 维护各自的记忆
AiServices.builder(WeatherAgent.class)
.chatLanguageModel(model)
.tools(new MyTools())
.chatMemoryProvider(userId ->
MessageWindowChatMemory.builder()
.id(userId)
.maxMessages(30)
.chatMemoryStore(new RedisChatMemoryStore(/*…*/)) // 自己实现存 Redis
.build())
.build();
⚠️ 小白坑点:Memory 不是越长越好!每一条消息都会变成 token 扔给模型,太长 = 又贵又慢,还容易“跑题”。
第 6 步:接入 RAG(检索增强生成)
什么时候需要 RAG?
当你要让 Agent 回答“模型自己不可能知道”的事,比如:
- 公司内部 HR 制度
- 自研产品的 API 文档
- 最新的销售手册、FAQ
- 项目的设计文档
RAG 的核心逻辑(一句话):
把你的资料切片 → 转成向量 → 用户提问时,先去向量库找最相关的几段 → 连同问题一起喂给模型。
6.1 RAG 的最小流程图
[你的 PDF / MD / Wiki]
↓ 切块(chunk)
↓ 向量化(embedding)
[向量数据库]
↑
用户问题 → 向量化 → 相似度检索 → Top-K 片段 + 问题 → 大模型 → 答案
6.2 LangChain4j 里的简化写法
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
// 1. 加载文档
Document doc = FileSystemDocumentLoader.loadDocument("docs/handbook.md");
// 2. 切块 + 向量化 + 存入向量库(入门先用内存版)
EmbeddingModel embeddingModel = /* 省略:通常用 OpenAI embedding 或本地 bge-m3 */;
InMemoryEmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor.ingest(doc, store);
// 3. 构造一个 “带资料检索能力” 的 Agent
WeatherAgent agent = AiServices.builder(WeatherAgent.class)
.chatLanguageModel(model)
.contentRetriever(EmbeddingStoreContentRetriever.builder()
.embeddingStore(store)
.embeddingModel(embeddingModel)
.maxResults(3) // 取最相关的 3 段
.minScore(0.6) // 相似度低于 0.6 就不要了
.build())
.build();
📌 小白误区:
- RAG 不是万能的,资料质量决定一切。垃圾进,垃圾出。
- 切块大小很关键:一般 300~800 字一块,按语义(段落、标题)切,别按字数硬切。
- 生产环境建议用 Milvus / Qdrant / PGVector,内存版只适合 demo。
第 7 步:加入工作流与约束(Workflow / Orchestration)
一个真实的 Agent,很少是“来一句答一句”,而是一条流水线:
用户输入
↓
意图识别(是闲聊?查资料?办业务?)
↓
分支:
├─ 闲聊 → 直接 LLM 回复
├─ 知识问答 → 走 RAG
└─ 办业务 → Tool 调用(可能多步)
↓
结果整理 + 异常兜底
↓
返回
7.1 用代码表达这个流程(伪代码 + Java)
public class RouterAgent {
public String handle(String userInput, String userId) {
// 1. 意图识别(也可以用模型做,也可以用正则 + 关键词)
Intent intent = intentClassifier.classify(userInput);
// 2. 分发
switch (intent) {
case CHITCHAT:
return chatAgent.chat(userInput);
case KNOWLEDGE_QA:
return ragAgent.ask(userInput);
case BUSINESS:
try {
return toolAgent.chat(userInput);
} catch (ToolException e) {
// 3. 异常兜底:降级到“告诉用户我没查到,要不要转人工”
return "抱歉我暂时没查到结果,是否转人工?";
}
default:
return "这个问题我暂时不擅长回答~";
}
}
}
7.2 更复杂的场景:多步规划(ReAct / Plan-and-Execute)
对于“帮我订明天去上海的机票并发消息给同事”这种任务,Agent 要能自己拆步骤:
- 查机票
- 选一个合适的
- 下单
- 发消息
LangChain4j 的 AiServices + Tools 默认就支持 多轮 Tool Calling,模型会自己循环调用工具直到完成。但当步骤多、需要人工介入时,建议显式编排,不要完全放养模型——那样不可控。
💡 经验法则:
- 简单任务(1~3 步)→ 让模型自己 Tool Calling
- 复杂任务(多分支、多系统)→ 自己写 Workflow,把模型当“判断器”用
第 8 步:可观测性与安全(上线前必须)
原型跑通 ≠ 可以上线。生产环境最容易爆雷的几件事:
8.1 必须监控的指标
| 指标 | 为什么重要 |
|---|---|
| Token 消耗 | 直接等于钱,要能按用户 / 接口 / 场景拆分 |
| 响应时延(P95/P99) | 模型调用慢是常态,要能定位瓶颈 |
| 工具调用成功率 | 一个工具挂了可能拖垮整个 Agent |
| 重试次数 / 超时率 | 网络、限流、模型故障都会触发 |
| 命中率(RAG) | 检索结果没命中,模型会乱编 |
Spring Boot 项目直接用 Micrometer + Prometheus + Grafana 就能搞定。
8.2 必须做的安全措施
// 示例:工具调用前的权限校验
@Tool("删除指定订单(危险操作)")
public String deleteOrder(@P("订单号") String orderId) {
// 1. 校验当前用户权限
if (!SecurityContext.current().hasRole("ADMIN")) {
throw new SecurityException("无权限执行该操作");
}
// 2. 敏感操作要二次确认(可以通过 workflow 控制)
// 3. 写审计日志
auditLog.record("deleteOrder", orderId, SecurityContext.current().getUserId());
return orderService.delete(orderId);
}
安全红线清单:
- ✅ 工具调用前做权限校验,不要相信模型的判断
- ✅ 敏感操作(删除、支付、外发消息)二次确认
- ✅ 对模型输入做 Prompt 注入防护(过滤“忘记之前的指令…”这类攻击)
- ✅ 对模型输出做脱敏(手机号、身份证、密钥)
- ✅ 所有工具调用写审计日志,可追溯
- ✅ 限流:防止用户恶意刷 token
四、一个最小可运行的完整例子
把前面的步骤串起来,做一个**“带记忆 + 会调工具 + 会查资料”的天气助手**:
public class MiniAgentApp {
public static void main(String[] args) {
// 1. 模型
ChatLanguageModel model = OpenAiChatModel.builder()
.baseUrl("https://api.deepseek.com/v1")
.apiKey(System.getenv("LLM_API_KEY"))
.modelName("deepseek-chat")
.temperature(0.3)
.build();
// 2. 向量库 + RAG
EmbeddingModel embeddingModel = /* … */;
InMemoryEmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor.ingest(
FileSystemDocumentLoader.loadDocument("docs/travel-tips.md"),
store);
// 3. 组装 Agent
WeatherAgent agent = AiServices.builder(WeatherAgent.class)
.chatLanguageModel(model)
.tools(new MyTools())
.chatMemory(MessageWindowChatMemory.withMaxMessages(20))
.contentRetriever(EmbeddingStoreContentRetriever.builder()
.embeddingStore(store)
.embeddingModel(embeddingModel)
.maxResults(3)
.build())
.build();
// 4. 跑起来
System.out.println(agent.chat("北京明天天气怎么样?要带伞吗?"));
System.out.println(agent.chat("那出差需要准备啥?")); // 会命中 travel-tips.md
}
}
短短几十行,你就拥有了一个真实可用的 Java Agent。
五、小白最容易踩的 10 个坑(血泪总结)
- 目标不聚焦:想做“全能助手”,最后啥都做不好。
- 上来就搞多 Agent:单 Agent 都没跑通就想 Multi-Agent,纯属自找麻烦。
- Memory 无限增长:token 爆炸 + 回答跑偏。务必加窗口或摘要。
- Tool 描述写得敷衍:模型靠
@Tool的描述决定要不要调,描述越清楚调用越准。 - 把所有逻辑塞给模型:该用 if-else 写死的判断,不要交给模型“猜”。
- RAG 切块乱切:按固定字数硬切,上下文断裂,检索全是残片。
- 没有超时和重试:一次模型接口卡住,整个请求挂住。
- 不监控 token:等月底账单来了才发现烧了好几万。
- 不做权限校验:模型被 Prompt 注入后直接删库,责任你背。
- 不做评估(Eval):没有评估集,迭代全凭感觉,改着改着越改越差。
六、进阶方向(下一步学什么)
当你跑通上面所有步骤后,可以继续探索:
- Function Calling vs MCP:标准化工具协议,解决工具爆炸问题。
- Agent 评估(Eval):搭建一个“考题库”自动打分。
- 多 Agent 协作:比如一个 Planner Agent + 多个 Worker Agent。
- 流式输出(SSE / WebFlux):让回答像打字一样实时出来。
- 工作流引擎:LangGraph / Spring AI 的 Advisor 机制 / 自研 DAG。
- Agent + 微服务治理:熔断、限流、灰度,让 Agent 成为真正的生产级服务。
七、总结
做一个 Java AI Agent,本质上是把大模型这个“新同事”融入到你现有的 Java 工程体系里。记住这条主线:
目标 → 框架 → 模型 → 工具 → 记忆 → 知识 → 编排 → 可观测 + 安全
一步一步来,不要跳步。跑通第一个最小可用版本,比追求“架构先进”重要 100 倍。
祝你早日把那个“只会聊天的玩具”,变成一个真正帮你干活的数字同事。🚀
如果这篇文章对你有帮助,欢迎收藏、转发;有问题也欢迎在评论区交流。