Java学习进程16
Java Agent 开发
Agent 入门
1.Agent 与基座模型
Agent(智能体)是能够根据用户需求进行判断,并结合工具完成任务的智能程序。
基座模型 是 Agent 的核心语言模型,负责理解用户输入、分析问题并生成回答。可看作百科全书或人的大脑。
- 基座模型:负责理解、分析和生成内容。
- Agent:在基座模型的基础上,进一步组织任务、调用工具。
- 外部工具:为 Agent 提供模型本身不直接具备的操作能力,例如获取当前日期、读取本地文件和检查代码。
2.使用 Java 调用大模型 API
使用 DeepSeek API,通过 HTTP 请求向大模型发送消息,再获取模型返回的文本。
主要涉及:
技术 作用
HashMap 保存模型请求参数
List 保存消息列表
ObjectMapper 将 Java 对象转换为 JSON,并解析 JSON
HttpClient 创建 HTTP 客户端
HttpRequest 构造 API 请求
HttpResponse 接收服务器响应
JsonNode 读取 JSON 响应中的字段
核心代码流程:
package com.agent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
/**
* @author YAN
* @date 2026/10/24 19:08
* @description
* Agent:豆包,workBuddy,chatGPT
* 基座模型:一本百科全书,人的大脑(deepseek,千问)
*/
public class MAgent {
public static void main(String[] args){
try {
chat();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static String API_KEY = "sk-1412e45194d440168e28c96484c692fe";
static String URL = "https://api.deepseek.com/chat/completions";
//链接deepseek模型,实现文本聊天
public static void chat() throws Exception {
//利用哈希表保存数据,创建请求参数
HashMap<String, Object> hm = new HashMap<>();
hm.put("model", "deepseek-flash");
//创建消息
List<HashMap<String, String>> hashMapList = new ArrayList<>();
HashMap<String, String> msgHm = new HashMap<>();
msgHm.put("role", "user");
msgHm.put("content", "风铃玉怎么养护会更出状态?");
hashMapList.add(msgHm);
hm.put("messages", hashMapList);
//把hm对象转成json格式的字符串数据
//ObjectMapper
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(hm);
//给大模型服务器发送请求
//创建 HTTP 客户端对象
HttpClient client = HttpClient.newHttpClient();
//请求对象:绑定数据
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(URL))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + API_KEY)
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
//发送请求,并且得到响应对象
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
//通过响应对象,提取大模型返回数据
JsonNode root = mapper.readTree(response.body());
System.out.println("root:" + root);
String msg = root.path("choices").get(0).path("message").path("content").asText();
System.out.println("AI:" + msg);
}
}
其中,API_KEY 是访问模型服务的密钥,URL 是模型接口地址。实际使用时应将密钥妥善保管,不要直接提交到公开代码仓库。
3.实现连续对话
通过控制台输入语句,实现与大模型的连续对话。
public static void main(String[] args){
try {
Scanner scan = new Scanner(System.in);
while(true){
System.out.println("用户:");
String prompt = scan.nextLine();
chat(prompt);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
这里通过 Scanner 获取用户输入,通过循环持续调用 chat() 方法。
Agent 功能拓展
1.从普通聊天到工具调用
第一阶段的 Agent 主要负责向大模型发送问题并获取回答。
第二阶段开始让 Agent 结合外部工具获取实时信息,例如:
- 获取当前日期。
- 获取当前时间。
- 获取天气信息。
- 读取本地代码并检查、修复错误。
核心思想是:大模型负责判断用户意图,Java 程序负责执行具体操作。
例如,用户询问当前日期时,程序可以先让模型判断用户是否在询问日期。如果模型返回约定的标识,Java 程序再调用本地日期方法。
2.使用自定义方法获取日期
使用 LocalDate 获取系统当前日期
import java.time.LocalDate;
public static String getTime(){
LocalDate date = LocalDate.now();
return date.toString();
}
LocalDate.now() 返回当前系统日期,toString() 将日期转换为字符串。
另:该方法获取的是日期,并不包含具体时分秒。若要获取时间,需要使用相应的时间 API;天气信息则需要接入相应的外部服务。
3.通过提示词约定模型输出
使用提示词要求模型在特定情况下返回指定格式:
String input = """
用户问题:%s
严格根据规制回答问题:
1.如果用户问今天日期,不要直接输出日期
输出内容:日期
回答问题简洁,不啰嗦
""".formatted(prompt);
之后,Java 程序根据模型返回的内容进行判断:
if(resp.startsWith("日期")){
String time = getTime();
String timePrompt = """
用户问题:%s
时间信息:%s
只需要回答日期或时间,不啰嗦
""".formatted(prompt,time);
String tm = send(timePrompt);
System.out.println("AI:" + tm);
}else{
System.out.println("AI:" + resp);
stringBuilder.append(resp);
}
Agent 代码检查功能
1.功能目标
将 Agent 从信息问答扩展为本地代码辅助工具,主要完成:
- 根据指定位置检查代码是否存在错误。
- 将代码内容发送给大模型分析。
- 向用户展示错误提示。
- 询问用户是否需要 AI 修改代码。
- 获取模型生成的修改后代码。
2.读取本地代码文件
使用 File、FileReader 和 BufferedReader 读取文件。
String filePath = "链接地址" + filename;
//创建字符输入流,读取代码文件
FileReader fir = new FileReader(new File(filePath));
BufferedReader br = new BufferedReader(fir);
//保存读取到的源代码
StringBuilder codeStr = new StringBuilder();
String line = "";
//读取一行代码,不空就一直读
while((line = br.readLine()) != null){
codeStr.append(line + "\r\n");
}
System.out.println("codeStr" + codeStr);
实现过程:
- File:表示指定路径的文件。
- FileReader:读取文本文件内容。
- BufferedReader:按行读取文本。
- StringBuilder:将读取到的多行代码拼接起来。
3.将源代码交给大模型检查
读取文件后,通过提示词要求模型检查代码。
//设置提示词
String codePrompt = """
你是一个专业程序员,帮我检查下面这段代码是否存在错误
源代码:%s
如果有错误。返回错误信息
如果没有错误,就直接说代码没有明显错误
""".formatted(codeStr);
String codeResp = send(codePrompt);
System.out.println("错误提示:" + codeResp);
这里的 send() 是之前封装的大模型 API 请求方法,这样可以复用已有的模型调用逻辑,而不必在代码检查功能中重复编写 HTTP 请求和 JSON 解析代码。
4.由用户决定是否修改代码
通过控制台询问用户:
System.out.println("是否需要AI修改代码:(y/n)");
Scanner scan = new Scanner(System.in);
String type = scan.nextLine();
//equalsIgnoreCase() 该判断方法可忽略大小写
if("y".equalsIgnoreCase(type)){
String prompt = """
修改源代码中的错误,只保留源代码
不要显示``````
源代码:%s
""".formatted(codeStr);
String code = send(prompt);
System.out.println("code:" + code);
equalsIgnoreCase() 用于忽略大小写进行字符串比较,因此输入 y 或 Y 都可以进入修改分支。
Agent 代码重构
由于前面几个版本将对话、模型调用、日期判断和代码检查等逻辑集中在 MAgent 中。随着工具数量增加,若继续把所有功能写在同一个类中,会导致类的职责越来越多;新增工具时需要不断修改原有代码;工具逻辑和对话逻辑耦合;代码维护和功能扩展更加困难。因此将工具抽象为统一接口,通过工具注册中心管理工具,并由 Agent 根据模型返回的结果调用相应工具。
1.设计 Tool 接口
将每一种外部工具设计为统一的接口
package com.agent1102;
/**
* @author YAN
* @date 2026/11/2 11:43
* @description 接口起到的作用:规范工具类的设计
*/
public interface Tool {
//工具名
public String name();
//工具的描述
public String desc();
//工具的实现
public String execute(String msg) throws Exception;
}
接口中定义了三个方法,通过接口统一规范,不同工具可以采用不同的内部实现方式,但对外提供相同的方法结构。
2.实现具体工具类
CodeCheck 类实现了 Tool 接口
package com.agent1102;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Scanner;
/**
* @author YAN
* @date 2026/11/2 11:49
* @description 修复代码文件的工具类
*/
public class CodeCheck implements Tool {
@Override
public String name() {
return "代码检查";
}
@Override
public String desc() {
return "如果桌面代码文件,检查错误并修改";
}
@Override
public String execute(String msg) throws Exception {
String filePath = "链接地址" + msg;
//后续实现读取文件、检查错误和修改代码的功能
//字符流读取代码文件
FileReader fir = new FileReader(new File(filePath));
BufferedReader br = new BufferedReader(fir);
StringBuilder codeStr = new StringBuilder();
String line = "";
//读取一行代码,不空就一直读
while ((line = br.readLine()) != null) {
codeStr.append(line + "\r\n");
}
System.out.println("codeStr" + codeStr);
//设置提示词
String codePrompt = """
你是一个专业程序员,帮我检查下面这段代码是否存在错误
源代码:%s
如果有错误。返回错误信息
如果没有错误,就直接说代码没有明显错误
""".formatted(codeStr);
String codeResp = MAgent.send(codePrompt);
System.out.println("是否需要AI修改代码:(y/n)");
Scanner scan = new Scanner(System.in);
String type = scan.nextLine();
//equalsIgnoreCase() 该判断方法可忽略大小写
if ("y".equalsIgnoreCase(type)) {
String prompt = """
修改源代码中的错误,只保留源代码
不要显示``````
源代码:%s
""".formatted(codeStr);
String code = MAgent.send(prompt);
System.out.println("code:" + code);
//把修改后的代码再写入文件
}
return "";
}
}
代码中 msg 用于传递文件名; execute() 负责执行具体工具逻辑,而 Agent 不需要了解文件读取的全部细节。
3.设计 ToolRegister 工具注册中心
使用 ToolRegister 统一管理工具对象。
package com.agent1102;
import java.util.ArrayList;
import java.util.List;
/**
* @author YAN
* @date 2026/11/2 11:55
* @description 工具的注册中心
*/
public class ToolRegister {
//获取所有工具的方法
public static List<Tool> getAllTool(){
List<Tool> tools = new ArrayList<>();
tools.add(new CodeCheck());
return tools;
}
}
后续如果新增功能,可以在相应的工具类实现 Tool 接口,再将工具加入注册中心。Agent 就可以统一获取可用工具,而不需要在对话逻辑中逐一创建各种工具对象。
4.Agent 如何选择并调用工具
在重构后的 MAgent 中,首先获取全部工具,再将工具名称和描述提供给大模型。
- 获取所有工具并整理工具信息
public static void chat(String prompt) throws Exception {
StringBuilder toolMsg = new StringBuilder();
//遍历出所有的工具类,发给大模型,让它知道可以调用哪些工具
for(Tool tool:ToolRegister.getAllTool()){
toolMsg.append("[工具名:").append(tool.name()).append("]").append(" 工具描述:").append(tool.desc()).append("\n");
}
System.out.println("toolMsg: " + toolMsg);
- 通过提示词让大模型判断是否调用工具
String input = """
你是一个智能助手,能使用一下工具
&s
用户问题:%s
规则:
1.如果需要使用工具 输出: 工具#参数
2.如果不要使用工具就直接回答
回答问题简洁,不啰嗦
""".formatted(toolMsg,prompt);
将工具名称、工具描述和用户问题一起发送给大模型,让模型根据用户需求判断是否需要使用工具。
- 根据模型返回结果调用对应工具
//更高效的拼接字符串方法
stringBuilder.append(input);
//响应结果
String resp = send(stringBuilder.toString());
System.out.println("resp:" + resp);
//根据响应结果,调用对应的工具
String[] toolArr = resp.split(":");
//根据响应结果遍历所有的工具
for(Tool tool:ToolRegister.getAllTool()){
if(toolArr[0].equals(tool.name())){
System.out.println("toolArr[0]:" + toolArr[1]);
tool.execute(toolArr[1]);
return;
}
}
System.out.println("AI:" + resp);
stringBuilder.append(resp);
}
程序通过split(“#”) 将返回内容拆分:
- toolArr[0]:工具名称,即“代码检查”
- toolArr[1]:工具参数
然后遍历注册中心中的工具,找到名称匹配的工具,调用其 execute() 方法。
更多推荐


所有评论(0)