本文详细介绍了如何使用Spring AI框架构建智能体(Agent)的全过程,包括环境配置、依赖引入、智能体代码编写和架构设计。通过BaseAgent、ReActAgent、ToolCallAgent和ManusAgent四个层次的类设计,实现了基于模板方法、责任链、策略等多种设计模式的智能体系统。文章提供了具体的代码示例,展示了如何创建可调用工具的智能体,并详细解析了架构设计原则,为开发者提供了完整的Spring AI智能体开发指南。

介绍:通过Spring AI自由调度工具,完成Agent的搭建

智能体:基于对话的AI项目,它通过对话方式接收用户的输入,由大模型自动调用工具执行用户指定的业务流程,并生成回复。如Dify、Coze等。

JDK版本:17
SpringBoot版本:3.4.5
Spring AI版本:1.0.0

步骤一:引入依赖

版本管理

<spring-ai.version>1.0.0</spring-ai.version>
<spring-boot.version>3.4.5</spring-boot.version>
<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
 </dependencyManagement>

具体依赖

	 <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-ollama</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-openai</artifactId>
        </dependency>

步骤二:配置信息

application.yml

server:
  port: 端口
spring:
  application:
    name: Agent-Demo
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: qwen2.5:3b
    openai:
      base-url: https://dashscope.aliyuncs.com/compatible-mode
      api-key: 个人key
      chat:
        options:
          model: qwen-plus

这里使用的ollama模型,如已安装,请使用cmd命令查看已有模型

ollama list

模型可自定义,聊天模型即可,同时需要申请阿里云百炼账号

阿里云百炼地址:https://bailian.console.aliyun.com/console?tab=model#/model-market

步骤三:智能体代码

[1]BaseAgent


@Component
@Data
@Slf4j
public abstract class BaseAgent {
    // 名称
    protected String name;
    protected String description;
    protected String systemPrompt;
    protected String nextStepPrompt;

    // llm 客户端
    @Resource(name = "agentAssist")
    protected ChatClient chatClient;

    // 智能体状态
    protected AgentState state = AgentState.IDLE;

    // 消息列表
    protected List<Message> msg = new ArrayList<>();

    // 最大步数
    protected int maxSteps = 10;

    // 初始步数
    protected int currentStep = 0;

    /**
     * 执行
     *
     * @param message
     * @param emitter
     */
    public void run(String message, SseEmitter emitter) {
        log.info("智能体状态: {}", state.getState());

        addMessage("user", message);
        setState(AgentState.RUNNING);

        log.info("current step: {}", currentStep);
        try {
            while (currentStep < maxSteps && state != AgentState.FINISHED) {
                log.info("test info..");
                currentStep++;
                String stepResult = step();
                String resMessage = String.format("{\"step\": %d, \"result\": %s}", currentStep, stepResult);
                try {
                    emitter.send(SseEmitter.event()
                            .name("step" + currentStep)
                            .data(resMessage)
                            .id(String.valueOf(currentStep))
                    );
                    emitter.send("\n\n");
                } catch (IOException e) {
                    setState(AgentState.ERROR);
                    emitter.send(SseEmitter.event()
                            .name("error")
                            .data("{\"error\": \"发送数据失败: " + e.getMessage() + "\"}")
                            .id("error")
                    );
                    emitter.completeWithError(e);
                    return;
                }
            }
            emitter.complete();
        } catch (Exception e) {
            setState(AgentState.ERROR);
            try {
                emitter.send(SseEmitter.event()
                        .name("error")
                        .data("{\"error\": \"发送数据失败: " + e.getMessage() + "\"}")
                        .id("error")
                );
                emitter.completeWithError(e);
            } catch (IOException ex) {
                log.info("执行异常: {}", ex.getMessage());
            }
        } finally {
            cleanup();
        }
    }

    protected abstract String step();

    protected abstract void cleanup();

}

通过基底抽象类,定义核心执行流程,方便继承并使用

[2]ReActAgent

@Data
public abstract class ReActAgent extends BaseAgent {
    @Override
    protected String step() {
        boolean shouldAct = think();
        System.out.println("shouldAct --> " + shouldAct);
        if (!shouldAct) {
            return "执行完成,无需再调用方法";
        }
        return act();
    }

    protected abstract boolean think();

    protected abstract String act();
}

定义执行抽象类,通过继承基底类,使其自定义功能和方法,实现功能扩展

[3]ToolCallAgent

@Component
@Slf4j
public class ToolCallAgent extends ReActAgent {
    @Autowired
    protected ToolCallbackProvider toolCallbackProvider;
    protected List<AssistantMessage.ToolCall> toolCalls = new ArrayList<>();

    @Override
    protected boolean think() {
        ChatOptions options = ToolCallingChatOptions.builder()
                .toolCallbacks(toolCallbackProvider.getToolCallbacks())
                .internalToolExecutionEnabled(false)
                .build();
        ChatResponse chatResponse = chatClient.prompt(systemPrompt)
                .system(nextStepPrompt)
                .messages(msg)
                .options(options)
                .call()
                .chatResponse();

        AssistantMessage output = chatResponse.getResult().getOutput();
        toolCalls.addAll(output.getToolCalls());
        log.info("---> 本轮挑选的工具:{}", toolCalls);
        String content = output.getText();
        addMessage("assistant", content);

        // 检查是否为终止请求
        if (toolCalls.stream().anyMatch(toolCall -> "terminate".equals(toolCall.name()))) {
            System.out.println("检测到终止请求,结束交互");
            setState(AgentState.FINISHED);
            toolCalls.clear();
            return false;
        }

        System.out.println("测试是否返回..");
        return true;
    }

    @Override
    protected String act() {
        // 技术群获取
    }

    @Override
    protected void cleanup() {
       // 技术群获取
    }
}

通过工具类继承执行抽象类,实现具体的业务方法

[4]ManusAgent

@Component
public class ManusAgent extends ToolCallAgent {
    @Resource(name = "agentAssist")
    private ChatClient chatClient;

    public ManusAgent() {
        this.name = "Java Manus";
        this.description = "解决多个任务的多功能智能体";

        this.setSystemPrompt("""
                您必须对所有用户交互以中文进行响应.你是Java Manus,一个先进的人工智能助手,旨在通过利用各种工具有效地解决任何用户任务.您的功能包括运行计算、处理文本、与远程服务交互以及在必要时终止任务.遵循这些步骤来处理任务:

                1.任务分析与分解
                2.工具选择
                3.参数格式化
                4.异常处理
                5.任务完成与终止

                例子:
                - 用户任务:“查找最近的新闻并进行简单的计算。”
                    1. 任务1:检索最近的新闻
                     - 思考:这需要从网上获取信息
                     - 执行:使用网页浏览工具,输入“最近的新闻”之类的查询关键字
                     - 输出:总结出一些文章摘要
                    2. 任务2:执行一个简单的计算
                     - 思考:这需要执行一个数学表达式
                     - 执行:使用代码执行工具,如使用“2+2”
                     - 输出:结果为4
                    3. 合并结果:用中文回复新闻摘要和计算结果
                    4. 终止:如果所有子任务都已完成,则调用终止工具

                即使内部推理是用英语,也要用中文回答.如果对任务不确定,请与用户澄清或使用终止工具以避免无限循环.
            """);
        this.setNextStepPrompt("""
                根据用户的请求和当前上下文,确定下一个操作.遵循以下步骤:

                1. 分析上下文
                 - 查看用户的原始请求和最近的对话历史.
                 - 标识当前子任务或任务计划中的下一步.
                 - 检查之前的工具输出以获取相关信息.
                2. 计划并行动
                 - 确定当前子任务是否需要工具、进一步思考或终止.
                 - 如果需要工具,请根据其功能选择最合适的工具.
                 - 指定准确的参数,确保它们符合工具的需求(例如,字符串,JSON).
                3. 执行或终止
                 - 如果调用工具,请提供清晰的参数并预测输出.
                 - 如果所有子任务都完成了(例如:检索到的信息、保存的文件),调用终止工具来完成任务.
                 - 如果卡住了(例如:重复的错误,重复的操作,比如保存相同的文件),调用终止工具并用中文解释,例如:“任务已完成,无需重复操作”.
                4. 日志推理
                 - 简要解释你的思考过程(内部可以用英语,但用中文回答).
                 - 注意在计划期间所做的任何挑战或假设.

                例子:
                 - 上下文:用户询问最近的新闻和计算;检索到的新闻,正在进行计算.
                    1.分析:下一个子任务是执行计算.
                    2.计划:用一个数学表达式作为字符串来使用代码执行工具.
                    3.执行:使用“2 + 2”之类的参数调用工具.
                    4.推理:计算简单明了,期望得到数值结果.
                    5.请用中文回答:"正在执行计算...".
                    6.终止检查:如果所有子任务都已完成,则调用终止工具.

                如果调用工具失败,请分析错误,调整参数或工具后重试.如果多次尝试仍无进展或任务已完成,则调用终止工具并用中文进行解释.
            """);
        this.setMaxSteps(10);
        this.setChatClient(chatClient);
    }
}

通过继承工具类并编写提示词,满足业务需求,通过@Component注解,方便Spring容器管理

步骤四:核心代码

[1]ManusController

@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai/agent/manus")
public class ManusController {
    @Autowired
    private ManusAgent manusAgent;

    /**
     * 执行
     *
     * @param message
     * @return
     */
    @GetMapping(value = "/execute", produces = "text/html;charset=UTF-8")
    public SseEmitter execute(@RequestParam(name = "message") String message) {
        SseEmitter sseEmitter = new SseEmitter(-1L);
        manusAgent.run(message, sseEmitter);
        return sseEmitter;
    }
}

通过注入ManusAgent,实现智能体run方法调用,根据用户请求自动调用工具完成任务

[2]MathTool

@Service
public class MathTool {
    @Tool(name = "add", description = "加法运算")
    public String add(@ToolParam(description = "第一个数字") int a, @ToolParam(description = "第二个数字") int b) {
        System.out.println("加法运算");
        return String.valueOf((a + b));
    }

    @Tool(name = "substract", description = "减法运算")
    public String substract(@ToolParam(description = "第一个数字") int a, @ToolParam(description = "第二个数字") int b) {
        System.out.println("减法运算");
        return String.valueOf((a - b));
    }

    @Tool(name = "multiply", description = "乘法运算")
    public String multiply(@ToolParam(description = "第一个数字") int a, @ToolParam(description = "第二个数字") int b) {
        System.out.println("乘法运算");
        return String.valueOf((a * b));
    }

    @Tool(name = "divide", description = "除法运算")
    public String divide(@ToolParam(description = "第一个数字") int a, @ToolParam(description = "第二个数字") int b) {
        System.out.println("除法运算");
        return String.valueOf((a / b));
    }
}

业务工具,可以根据需求自定义,通过@Service交给Spring容器管理

[3]AgentConfig

@Configuration
public class AgentConfig {
    @Bean("toolCallbackProvider")
    public ToolCallbackProvider toolCallbackProvider(MathTool mathTool) {
        return MethodToolCallbackProvider
                .builder()
                .toolObjects(mathTool)
                .build();
    }
}

将工具类告诉Spring AI,方便调用,如有多个工具,也是支持的

源码解析

步骤五:测试

启动项目

接口测试

日志打印

步骤六:架构解析

设计模式应用

  1. 模板方法模式(Template Method Pattern)
public abstract class BaseAgent {

    public void run(String message, SseEmitter emitter) {}

    protected abstract String step(); // 模板方法

    protected abstract void cleanup();
}

并且每个层级Agent都能扩展模板

# 模板方法模式概念
定义一个操作中算法的骨架,而将一些步骤延迟到子类中,
模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤.
  1. 责任链模式(Chain of Responsibility Pattern)

通过继承链形成处理责任传递:

BaseAgent -> ReActAgent -> ToolCallAgent -> ManusAgent

每个层级都增强了智能体的能力,上级不知道下级的具体表现,但共同完成智能体的功能

# 责任链模式概念
如果有多个对象有机会处理请求,责任链可使请求的发送者和接受者解耦,
请求沿着责任链传递,直到有一个对象处理了它为止.
  1. 策略模式(Strategy Pattern)

体现在不同层级的智能体可以有不同的行为策略:

protected abstract boolean think();

protected abstract String act();

不同的Agent子类可以提供不同的think()和act()实现策略

# 策略模式概念
策略模式定义了一系列算法,并将每个算法封装起来,
使他们可以相互替换,且算法的变化不会影响到使用算法的客户.

4.工厂方法模式(Factory Method Pattern)

通过Spring的@Component注解,这些智能体可以被Spring容器作为bean创建和管理

@Component
public class ManusAgent extends ToolCallAgent {
// Spring容器会负责创建这个bean
}

设计原则应用

  1. 开闭原则(Open Close Principle)
  2. 单一职责原则(Single Responsibility Principle)
  3. 依赖倒置原则(Dependence Inversion Principle)
# 开闭原则:对扩展开放,对修改关闭,抽象思维搭建结构,具体实现扩展细节.
- 对扩展开放:新增智能体类型只需扩展基类
- 对修改关闭:无需修改现有代码即可添加新功能
# 单一职责原则:要求一个接口或类只有一个原因引起变化,也就是说一个接口或一个类只有一个原则,它就只负责一件事.
每个类都有明确的职责
- BaseAgent:基础流程控制
- ReActAgent:ReAct模式实现
- ToolCallAgent:工具调用功能
- ManusAgent:具体业务逻辑
# 依赖倒置原则:上层模块不应该依赖底层模块,它们都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象;面向接口编程;
- 高层模块(BaseAgent)不依赖低层模块,都依赖抽象
- 通过抽象方法(step(),think(),act())定义契约

至此,基于Spring AI搭建智能体Demo版结束啦!


如何系统学习掌握AI大模型?

AI大模型作为人工智能领域的重要技术突破,正成为推动各行各业创新和转型的关键力量。抓住AI大模型的风口,掌握AI大模型的知识和技能将变得越来越重要。

学习AI大模型是一个系统的过程,需要从基础开始,逐步深入到更高级的技术。

这里给大家精心整理了一份全面的AI大模型学习资源,包括:AI大模型全套学习路线图(从入门到实战)、精品AI大模型学习书籍手册、视频教程、实战学习、面试题等,资料免费分享

1. 成长路线图&学习规划

要学习一门新的技术,作为新手一定要先学习成长路线图方向不对,努力白费

这里,我们为新手和想要进一步提升的专业人士准备了一份详细的学习成长路线图和规划。可以说是最科学最系统的学习成长路线。

在这里插入图片描述

2. 大模型经典PDF书籍

书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础(书籍含电子版PDF)

在这里插入图片描述

3. 大模型视频教程

对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识

在这里插入图片描述

4. 大模型行业报告

行业分析主要包括对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。

在这里插入图片描述

5. 大模型项目实战

学以致用 ,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。

在这里插入图片描述

6. 大模型面试题

面试不仅是技术的较量,更需要充分的准备。

在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。

在这里插入图片描述

全套的AI大模型学习资源已经整理打包,有需要的小伙伴可以微信扫描下方CSDN官方认证二维码,免费领取【保证100%免费

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐