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 从信息问答扩展为本地代码辅助工具,主要完成:

  1. 根据指定位置检查代码是否存在错误。
  2. 将代码内容发送给大模型分析。
  3. 向用户展示错误提示。
  4. 询问用户是否需要 AI 修改代码。
  5. 获取模型生成的修改后代码。

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() 方法。
Logo

更多推荐