java编写 agent 入门案例
·
LangChain4j Agent 完整示例
基于 Java 17 + Spring Boot 2.6.13 + LangChain4j 0.36.2 的 AI Agent 实现,使用 DeepSeek API 进行流式对话和工具调用。
依赖配置
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version>
<relativePath/>
</parent>
<properties>
<java.version>17</java.version>
<hutool.version>5.8.16</hutool.version>
<ip2region.version>2.6.6</ip2region.version>
<langchain4j.version>0.36.2</langchain4j.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- LangChain4j Core -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- LangChain4j OpenAI (兼容DeepSeek) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- LangChain4j Spring Boot Starter -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- OkHttp (解决版本冲突) -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- OkHttp SSE (流式响应支持) -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-sse</artifactId>
<version>4.12.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
核心代码
WeatherAgentDemo.java
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.TokenStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* LangChain4j Agent Demo
* 使用 DeepSeek 模型,支持流式交互和工具调用
*/
public class WeatherAgentDemo {
/**
* 定义 AI Assistant 接口
*/
interface WeatherAssistant {
TokenStream chat(String userMessage);
}
/**
* 天气工具类 - 包含多个工具方法
*/
static class WeatherTools {
@Tool("查询指定城市的天气情况")
public String getWeather(String location) {
// 规范命名
return location + " 今天是晴天,温度25°C";
}
@Tool("查询指定城市的空气质量")
public String tool2(@P("城市名称,例如:北京、上海") String a) {
// 不规范的命名,通过 @P 注解说明参数含义
return a + " 今天空气质量良好";
}
@Tool("查询指定城市未来三天的天气情况,不包括今天")
public String tool3(@P("城市名称,例如:北京、上海") String a) {
// 不规范的命名,通过 @P 注解说明参数含义
return a + "明天阴天,后天是雨天,大后天可能会下雪";
}
}
public static void main(String[] args) {
// 1. 创建 StreamingChatLanguageModel (使用 DeepSeek API)
StreamingChatLanguageModel model = OpenAiStreamingChatModel.builder()
.baseUrl("https://api.deepseek.com")
.apiKey("xxxxxxx")
.modelName("deepseek-chat") // 使用 deepseek-chat 模型(非推理模式)
.temperature(0.7)
.build();
// 2. 创建工具实例
WeatherTools weatherTools = new WeatherTools();
// 3. 创建带记忆功能的 Assistant (使用内存 checkpointer)
WeatherAssistant assistant = AiServices.builder(WeatherAssistant.class)
.streamingChatLanguageModel(model)
.tools(weatherTools)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10)) // 内存中保存最近10条消息
.build();
// 使用 CountDownLatch 等待异步响应完成
CountDownLatch latch = new CountDownLatch(1);
// 测试问题: 询问天气、空气质量和未来预报
String question1 = "北京今天的天气质量如何?空气质量如何?未来三天的天气又怎么样";
System.out.println("用户: " + question1);
assistant.chat(question1)
.onNext(token -> System.out.print(token)) // 流式输出每个 token
.onComplete(response -> {
System.out.println("\n"); // 完成后换行
latch.countDown(); // 通知主线程继续
})
.onError(error -> {
error.printStackTrace();
latch.countDown(); // 即使出错也要释放锁
})
.start();
// 等待流式响应完成(最多等待30秒)
try {
latch.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("等待被中断");
}
System.out.println("=== Demo 结束 ===");
}
}
运行步骤
1. 环境要求
- JDK: 17 或更高版本
- Maven: 3.6+
- IDE: IntelliJ IDEA / Eclipse(推荐)
4. 预期输出
用户: 北京今天的天气质量如何?空气质量如何?未来三天的天气又怎么样
北京今天的天气是晴天,温度25°C。空气质量良好。未来三天:明天阴天,后天是雨天,大后天可能会下雪。
=== Demo 结束 ===
核心概念解析
1. 工具定义
使用 @Tool 注解标记方法为 AI 可调用的工具:
@Tool("查询指定城市的天气情况")
public String getWeather(String location) {
return location + " 今天是晴天,温度25°C";
}
关键点:
@Tool中的字符串是工具的功能描述,AI 会根据这个描述决定是否调用- 方法名、参数名、返回类型都会被 langchain4j 自动提取并发送给 AI
- 一个类可以有多个
@Tool方法,都会被注册
2. 参数说明
当参数命名不规范时,使用 @P 注解显式说明:
@Tool("查询指定城市的空气质量")
public String tool2(@P("城市名称,例如:北京、上海") String a) {
return a + " 今天空气质量良好";
}
作用:
- 告诉 AI 参数的具体含义和用法
- 即使参数名是
a、x等无意义名称,AI 也能理解
3. 流式响应
使用 TokenStream 实现逐字输出的流式效果:
assistant.chat(question)
.onNext(token -> System.out.print(token)) // 逐字输出
.onComplete(response -> latch.countDown()) // 完成后通知
.onError(error -> { error.printStackTrace(); latch.countDown(); })
.start();
4. 异步等待
由于流式响应是异步的,需要使用 CountDownLatch 等待完成:
CountDownLatch latch = new CountDownLatch(1);
// ... 发起请求 ...
latch.await(30, TimeUnit.SECONDS); // 最多等待30秒
5. 对话记忆
使用 MessageWindowChatMemory 保存对话历史:
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
- 内存中保存最近 10 条消息
- AI 可以基于上下文进行多轮对话
工作流程
工具元数据
langchain4j 会将工具信息转换为 JSON Schema 发送给 AI:
{
"name": "getWeather",
"description": "查询指定城市的天气情况",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称"
}
},
"required": ["location"]
}
}
AI 根据这些信息决定:
- 是否需要调用工具
- 调用哪个工具
- 传入什么参数
常见问题
Q1: 为什么选择 deepseek-chat 而不是 deepseek-v4-pro?
A: deepseek-v4-pro 是推理模型,启用了 thinking mode,要求客户端回传 reasoning_content 字段,但 langchain4j 0.36.2 还不支持这个特性。使用 deepseek-chat 可以避免这个问题。
Q2: 一个工具类只能有一个 @Tool 方法吗?
A: 不是!一个类可以有多个 @Tool 方法,.tools() 会自动扫描并注册所有带注解的方法。AI 会根据用户问题智能选择最合适的工具。
Q3: 如何注册多个工具类?
A:
WeatherTools weatherTools = new WeatherTools();
SearchTools searchTools = new SearchTools();
WeatherAssistant assistant = AiServices.builder(WeatherAssistant.class)
.streamingChatLanguageModel(model)
.tools(weatherTools, searchTools) // 传入多个工具实例
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
Q4: 为什么需要 CountDownLatch?
A: 流式响应是异步的,.start() 会立即返回,如果不等待,main 方法会直接退出导致线程终止。CountDownLatch 让主线程等待异步响应完成。
Q5: 如何隐藏工具执行的日志?
A: 在 application.properties 中添加:
logging.level.dev.langchain4j.service.tool=OFF
logging.level.dev.ai4j.openai4j=OFF
技术栈总结
| 组件 | 版本 | 说明 |
|---|---|---|
| Java | 17 | 运行环境 |
| Spring Boot | 2.6.13 | Web 框架 |
| LangChain4j | 0.36.2 | AI Agent 框架 |
| DeepSeek API | - | AI 模型服务 |
| OkHttp | 4.12.0 | HTTP 客户端 |
| Maven | 3.6+ | 构建工具 |
最后, 贴出完整的代码
package com.yuhou.utils;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.TokenStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* LangChain4j Agent Demo
* 使用 DeepSeek v4-flash 模型,支持流式交互和工具调用
*/
public class WeatherAgentDemo {
/**
* 定义 AI Assistant 接口
*/
interface WeatherAssistant {
TokenStream chat(String userMessage);
}
/**
* 天气工具类
*/
static class WeatherTools {
@Tool("查询指定城市的天气情况")
public String getWeather(String location) {
//规范命名
return location + " 今天是晴天,温度25°C";
}
@Tool("查询指定城市的空气质量")
public String tool2(@P("城市名称,例如:北京、上海") String a) {
//不规范的命名
return a + " 今天空气质量良好";
}
@Tool("查询指定城市未来三天的天气情况,不包括今天")
public String tool3(@P("城市名称,例如:北京、上海") String a) {
//不规范的命名
return a + "明天阴天,后天是雨天,大后天可能会下雪";
}
}
//发言检测类
static class CheckUserSpeech {
@Tool("我会告诉你用户在我的平台说了什么,你来帮助我检测用户的发言是否违规," +
"例如涉及到:黄赌毒、流血暴力等。" +
"如果你觉得用户的发言不应该被展示出来,调用此方法")
public Boolean checkViolation(@P("用户发言的数据ID,例如:1546") Integer id) {
System.out.println("deepseek 认为发言id:" + id + "不合规,系统将对此发言进行删除操作");
这里你就可以调用你的自己xxxService.remove(id)来操作了
return true;
}
}
public static void main(String[] args) {
// 1. 创建 StreamingChatLanguageModel (使用 DeepSeek API)
StreamingChatLanguageModel model = OpenAiStreamingChatModel.builder()
.baseUrl("https://api.deepseek.com")
.apiKey("xxxxxxxxxxx")
.modelName("deepseek-chat") // 使用 deepseek-chat 模型(非推理模式)
.temperature(0.7)
.build();
// 2. 创建工具实例
WeatherTools weatherTools = new WeatherTools();
CheckUserSpeech checkUserSpeech = new CheckUserSpeech();
// 3. 创建带记忆功能的 Assistant (使用内存 checkpointer)
WeatherAssistant assistant = AiServices.builder(WeatherAssistant.class)
.streamingChatLanguageModel(model)
.tools(weatherTools, checkUserSpeech)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10)) // 内存中保存最近10条消息
.build();
// 使用 CountDownLatch 等待异步响应完成
CountDownLatch latch = new CountDownLatch(1);
// 测试问题1: 询问天气
String question1 = "用户的发言是:<" + "小明你真该死啊,CNMD" + ">该数据的id是 1233";
System.out.println("用户: " + question1);
assistant.chat(question1)
.onNext(token -> System.out.print(token)) // 流式输出每个 token
.onComplete(response -> {
System.out.println("\n"); // 完成后换行
latch.countDown(); // 通知主线程继续
})
.onError(error -> {
error.printStackTrace();
latch.countDown(); // 即使出错也要释放锁
})
.start();
// 等待流式响应完成(最多等待30秒)
try {
latch.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("等待被中断");
}
System.out.println("=== Demo 结束 ===");
}
}
更多推荐
所有评论(0)