LangChain4j + Spring Boot 多智能体协调架构原理深度解析

56 阅读8分钟

前言

LangChain4j 是 Java 生态中比较成熟的 LLM 应用开发框架,提供了丰富的多智能体协调能力。本文以一个《部落冲突》玩家分析系统为载体,深入剖析 LangChain4j 中 @SupervisorAgentAgenticServices、工具绑定机制、内存管理、消息编排等核心组件的工作原理与实现细节。以下是具体的实现。

💡 前情回顾 如果你想先了解框架Spring AI的落地应用,欢迎阅读我之前的文章:

image.png

一、核心组件全景图

image.png

二、Supervisor 的并行协调机制

2.1 注解定义与元数据

@SupervisorAgent 是 LangChain4j 提供的核心协调注解,用于声明一个接口为监督者智能体:

@SupervisorAgent(
    subAgents = {
        BattleTacticsAgent.class,
        DataSummaryAgent.class,
        DefenseTacticsAgent.class,
        UpgradePlannerAgent.class
    }
)
@SystemMessage("""
    # Role:部落冲突(CoC)高级参谋长

    ## 核心职责
    1. 分析用户的请求意图,找出需要执行哪些分析模块。
    2. 并行调用对应的子 Agent 来获取数据。
    3. 收集所有子 Agent 的结果,严格按照 JSON 格式输出最终报告。
    """)
String answer(@V("openId") String openId, @UserMessage String request);

注解参数解析

参数类型作用
subAgentsClass<?>[]声明该Supervisor管辖的子Agent类型数组
supervisorResponseStrategySupervisorResponseStrategy结果聚合策略,默认实现为JSON拼接
supervisorResponseStrategySupervisorResponseStrategy结果聚合策略,为可选的 enum 值,默认 LAST
maxAgentsInvocationsint单次执行中最大子Agent调用次数,默认 10

注意:LangChain4j 1.12 版本中,@SupervisorAgent 的推荐用法是用 @SubAgent 注解嵌套声明子Agent及其 outputKey。上面的数组写法当前可用,但后续可能会变为 Deprecated,建议关注官方文档的更新。

2.2 Supervisor 的工作原理

当调用 supervisor.answer(openId, request) 时,LangChain4j 内部经历以下流程:

Gemini_Generated_Image_shsr0wshsr0wshsr.png

2.3 SupervisorResponseStrategy 聚合策略

LangChain4j 提供了三种内置聚合策略:

public enum SupervisorResponseStrategy {
    LAST,        // 默认:仅返回最后调用的子Agent的最终响应
    SUMMARY,     // 返回Supervisor与其子Agent交互的摘要
    SCORED       // 使用内部LLM对最后响应和摘要分别打分,返回得分高者
}

在业务代码中,可以这样配置:

@Bean
public SuperAgent cocSupervisor(ChatModel chatModel,
                                 DataSummaryAgent dataSummaryAgent,
                                 UpgradePlannerAgent upgradePlannerAgent,
                                 BattleTacticsAgent battleTacticsAgent,
                                 DefenseTacticsAgent defenseTacticsAgent) {

    return AgenticServices.supervisorBuilder(SuperAgent.class)
            .chatModel(chatModel)
            .subAgents(dataSummaryAgent, upgradePlannerAgent,
                       battleTacticsAgent, defenseTacticsAgent)
            // 可选:指定聚合策略(默认为 LAST)
            .supervisorResponseStrategy(SupervisorResponseStrategy.SUMMARY)
            .build();
}

3.3 子Agent并行调用

当 Supervisor 分析出需要多个子Agent并行执行时:

// Supervisor内部的并行执行逻辑(伪代码)
public String answer(String openId, String request) {
    // 1. 分析意图,确定需要调用的子Agent
    List<SubAgent> targetAgents = analyzeIntent(request);

    // 2. 并行执行所有子Agent
    List<Future<String>> futures = new ArrayList<>();
    for (SubAgent agent : targetAgents) {
        futures.add(executor.submit(() ->
            agent.answer(buildSubPrompt(openId, agent))
        ));
    }

    // 3. 等待所有结果
    List<String> results = new ArrayList<>();
    for (Future<String> future : futures) {
        results.add(future.get(timeout, TimeUnit.SECONDS));
    }

    // 4. 聚合结果
    return mergeStrategy.merge(request, results);
}

3.4 变量传递机制

@V 注解实现 Supervisor 到子Agent的变量传递:

@SupervisorAgent(subAgents = {...})
@SystemMessage("""
    当前请求的玩家 openId 是:{{openId}}。
    当你在调用任何子 Agent 时,必须将 "{{openId}}" 作为参数传给他们。
    """)
String answer(@V("openId") String openId, @UserMessage String request);

内部实现中,Supervisor 会将 @V("openId") 绑定的参数自动注入到子Agent调用中:

// Supervisor构建子Agent调用时的参数绑定
public String callSubAgent(SubAgent agent, String openId) {
    // 查找子Agent接口中接受openId的方法
    Method method = findMethodWithOpenIdParam(agent.getClass());

    // 将Supervisor接收的openId传递给子Agent
    return agent.answer(openId, buildSubTaskPrompt(openId, agent));
}

3.5 串行依赖处理

某些场景下子Agent之间存在依赖关系,可以通过 Supervisor 的系统消息描述:

@SystemMessage("""
    ## 执行规则
    1. 首先调用 DataSummaryAgent 获取玩家基础数据
    2. 基于基础数据,调用 UpgradePlannerAgent 生成规划
    3. 最后调用 BattleTacticsAgent 推荐进攻流派

    ## 依赖关系说明
    UpgradePlannerAgent 必须等待 DataSummaryAgent 完成!
    """)

虽然默认是并行执行,但通过系统消息的约束引导,LLM会选择串行执行流程。

三、AgenticServices 工厂解析

3.1 整体架构

AgenticServices 是 LangChain4j 提供的能力工厂类,通过 Builder 模式创建各类 AI 服务:

public class AgenticServices {

    // 通用AI服务构建器
    public static <T> AiServicesBuilder<T> builder(Class<T> interfaceClass) {
        return new AiServicesBuilder<>(interfaceClass);
    }

    // 工具型Agent构建器(用于带@Tool注解的Agent)
    public static <T> AgentBuilder<T, ?> agentBuilder(Class<T> agentServiceClass) {
        return new AgentBuilder<>(agentServiceClass);
    }

    // Supervisor构建器
    public static <T> SupervisorAgentService<T> supervisorBuilder(Class<T> agentServiceClass) {
        return new SupervisorAgentService<>(agentServiceClass);
    }
}

3.2 AgentBuilder 内部机制

public class AgentBuilder<T, SELF extends AgentBuilder<T, SELF>> {

    private final Class<T> agentServiceClass;
    private ChatModel chatModel;
    private SystemMessageProvider systemMessageProvider;
    private List<Object> tools = new ArrayList<>();
    private ChatMemory chatMemory;
    private ChatMemoryProvider chatMemoryProvider;
    private ContentRetriever contentRetriever;

    // 关键方法:注册可调用工具
    public SELF tools(Object... tools) {
        this.tools.addAll(Arrays.asList(tools));
        return self();
    }

    // 关键方法:注册系统消息提供器
    public SELF systemMessageProvider(
            SystemMessageProvider provider) {
        this.systemMessageProvider = provider;
        return self();
    }

    // 最终build()方法
    public T build() {
        // 1. 通过动态代理创建接口实现
        // 2. 绑定chatModel、tools、memory等组件
        // 3. 注册到Spring容器(如在@Configuration中使用@Bean)
        return (T) Proxy.newProxyInstance(
            agentServiceClass.getClassLoader(),
            new Class[]{agentServiceClass},
            new AgentInvocationHandler(this)
        );
    }
}

说明:LangChain4j 内部的具体类名和包路径可能随版本变化(如 AgenticServicesBuilder 可能更名为 AgentBuilder),但 Builder 模式的核心逻辑保持一致。上面代码是基于 1.12.2-beta22 版本行为的抽象描述。

3.3 动态代理机制

LangChain4j 使用 Java 动态代理实现接口,当调用 agent.answer() 时:

public class AgentInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 1. 提取@SystemMessage和@UserMessage注解
        SystemMessage sysMsg = method.getAnnotation(SystemMessage.class);
        UserMessage userMsg = method.getAnnotation(UserMessage.class);

        // 2. 从@V注解提取变量绑定
        Map<String, Object> variables = extractVariables(method, args);

        // 3. 获取系统消息(可能通过provider动态获取)
        String systemMessage = systemMessageProvider.provide(memoryId);

        // 4. 构建最终Prompt
        String prompt = buildPrompt(systemMessage, userMsgTemplate, variables);

        // 5. 调用ChatModel
        AiMessage response = chatModel.sendUserMessage(prompt);

        // 6. 检查是否需要执行工具
        if (response.hasToolCalls()) {
            return executeToolCalls(response.toolCalls(), tools);
        }

        // 7. 直接返回文本结果
        return response.text();
    }
}

四、工具绑定机制(@Tool)

4.1 注解定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Tool {
    String name() default "";
    String description() default "";
}

4.2 工具注册与调用流程

// 1. 定义工具类
public class PlayerDataTool {

    @Tool(name = "queryPlayerBuildInfo", 
          description = "根据openid查询玩家所有建筑升级数据")
    public String queryPlayerBuildInfo(String openid) {
        // 实现查询逻辑
        return buildInfoDao.selectByOpenid(openid);
    }

    @Tool(name = "queryBuildLevel",
          description = "根据openid和建筑名称查询玩家该建筑当前等级")
    public String queryBuildLevel(String openid, String buildName) {
        // 实现查询逻辑
        return buildLevelDao.selectLevel(openid, buildName);
    }
}
// 注:@Tool注解的name和description属性均可省略。
// 省略name时默认使用方法名;省略description时使用name作为tool description。

// 2. 注册到Agent
@Bean
public DataSummaryAgent dataSummaryAgent(ChatModel chatModel) {
    return AgenticServices.agentBuilder(DataSummaryAgent.class)
            .chatModel(chatModel)
            .tools(playerDataTool)  // ← 工具绑定
            .build();
}

4.3 工具调用的内部实现

当 LLM 返回 AiMessage 包含工具调用时:

// AgentInvocationHandler.invoke() 中的工具执行逻辑
if (response.hasToolCalls()) {
    List<ToolExecution> executions = new ArrayList<>();
    for (ToolCall toolCall : response.toolCalls()) {
        // 1. 根据toolCall.name()查找对应的@Tool方法
        Method toolMethod = findToolMethod(toolCall.name());

        // 2. 反序列化参数
        Object[] params = deserializeArguments(toolCall.arguments(), toolMethod);

        // 3. 反射调用
        Object result = toolMethod.invoke(toolObject, params);

        // 4. 包装结果
        executions.add(new ToolExecution(toolCall.id(), result));
    }

    // 5. 将工具结果反馈给LLM生成最终回复
    return chatModel.sendToolResults(executions);
}

4.4 工具选择的LLM引导

LangChain4j 会自动将 @Tool 方法的签名和描述注入到 System Prompt 中:

你是一个AI助手。你可以使用以下工具:

- queryPlayerBuildInfo(openid: String): String
  根据openid查询玩家所有建筑升级数据

- queryBuildLevel(openid: String, buildName: String): String
  根据openid和建筑名称查询玩家该建筑当前等级

当你需要查询玩家数据时,请调用合适的工具。

五、系统消息与内存管理

5.1 SystemMessage 注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemMessage {
    String value() default "";
}

在接口定义时使用:

public interface BattleTacticsAgent {

    @SystemMessage("""
        # Role:部落冲突进攻战术专家

        # 任务
        分析当前版本主流进攻流派,给出配兵建议和打法思路。

        # 输出格式
        ### ⚔️ 流派名称
        * **兵种与数量**:...
        """)
    String recommendTactics(String openid);
}

5.2 动态 SystemMessageProvider

最强大的特性是可以动态提供 System Message:

@Bean
public DataSummaryAgent dataSummaryAgent(ChatModel chatModel) {
    return AgenticServices.agentBuilder(DataSummaryAgent.class)
            .chatModel(chatModel)
            .systemMessageProvider(memoryId -> {
                // memoryId 可以用于区分不同会话
                // 此处从Redis动态加载Prompt
                return stringRedisTemplate.opsForValue()
                    .get("ai:prompt:module1:" + memoryId);
            })
            .tools(playerDataTool)
            .build();
}

应用场景

  1. 多租户Prompt隔离:不同用户加载不同Prompt模板
  2. AB测试:对不同用户群使用不同策略
  3. Prompt热更新:无需重启即可更新Prompt内容
  4. 会话历史注入:根据memoryId加载历史上下文

5.3 Memory 组件

LangChain4j 的 Memory 用于维护多轮对话状态:

public interface ChatMemory {
    // 获取对话历史
    List<ChatMessage> messages();

    // 添加消息到历史
    void add(ChatMessage message);

    // 清除历史
    void clear();
}

默认实现:MessageWindowChatMemory

public class MessageWindowChatMemory implements ChatMemory {

    private final int maxMessages;

    public MessageWindowChatMemory(int maxMessages) {
        this.maxMessages = maxMessages;
    }

    @Override
    public List<ChatMessage> messages() {
        List<ChatMessage> all = store.getMessages();
        // 只返回最近N条消息(滑动窗口)
        return all.subList(Math.max(0, all.size() - maxMessages), all.size());
    }
}

在Agent中使用Memory:

// 带Memory的Agent配置
@Bean
public DataSummaryAgent dataSummaryAgent(ChatModel chatModel,
                                          ChatMemory chatMemory) {
    return AgenticServices.agentBuilder(DataSummaryAgent.class)
            .chatModel(chatModel)
            .chatMemory(chatMemory)  // ← Memory绑定
            .tools(playerDataTool)
            .build();
}

// Agent 接口中通过 @MemoryId 标记会话标识参数
public interface DataSummaryAgent {
    String answer(@MemoryId String memoryId, @V("openId") String openId);
}

// 调用时传入 memoryId
String result = agent.answer("user123", openId);

5.4 消息编排流程

Gemini_Generated_Image_3sbwum3sbwum3sbw.png

六、Spring Boot集成

6.1 配置类结构

@Configuration
public class AiConfig {

    // 1. 注入ChatModel(由Spring Boot自动配置或自定义)
    @Autowired
    private ChatModel chatModel;

    // 2. 注入工具Bean
    @Resource
    private PlayerDataTool playerDataTool;

    @Resource
    private TavilyTools tavilyTools;

    // 3. 按需注入Memory
    @Autowired
    private ChatMemory chatMemory;

    // 4. 创建各子Agent
    @Bean
    public DataSummaryAgent dataSummaryAgent(ChatModel chatModel) {
        return AgenticServices.agentBuilder(DataSummaryAgent.class)
                .chatModel(chatModel)
                .systemMessageProvider(memoryId ->
                    stringRedisTemplate.opsForValue().get("ai:prompt:module1"))
                .tools(playerDataTool)
                .build();
    }

    // 5. 创建Supervisor(注入所有子Agent)
    @Bean
    public SuperAgent cocSupervisor(ChatModel chatModel,
                                     DataSummaryAgent dataSummaryAgent,
                                     UpgradePlannerAgent upgradePlannerAgent,
                                     BattleTacticsAgent battleTacticsAgent,
                                     DefenseTacticsAgent defenseTacticsAgent) {

        return AgenticServices.supervisorBuilder(SuperAgent.class)
                .chatModel(chatModel)
                .subAgents(dataSummaryAgent, upgradePlannerAgent,
                           battleTacticsAgent, defenseTacticsAgent)
                .build();
    }
}

6.2 ChatModel配置

LangChain4j支持多种ChatModel实现:

langchain4j:
  open-ai:
    chat-model:
      base-url: https://api.minimaxi.com/v1
      api-key: ${COC_API_KEY}
      model-name: MiniMax-M2.7
      temperature: 0.2
      timeout: PT3M
      log-requests: true
      log-responses: true

七、流程图:完整请求处理链路

Gemini_Generated_Image_yintdfyintdfyint.png

八、总结与展望:LangChain4j vs LangGraph vs Spring AI

8.1 LangChain4j 的多智能体能力总结

通过上述各组件的有机组合,LangChain4j 提供了一条从简单单 Agent 到复杂多 Agent 协调的完整演进路径:

能力层级核心组件适用场景
简单问答AiServices + @SystemMessage单轮问答、文本生成
工具调用AgenticServices.agentBuilder() + @Tool需要查询数据库、调用 API 等工具的场景
多 Agent 协调@SupervisorAgent + AgenticServices.supervisorBuilder()意图分发、并行子任务执行、结果聚合
动态提示词SystemMessageProvider多租户、AB 测试、Prompt 热更新
多轮对话ChatMemory + @MemoryId需要上下文记忆的连续对话

在 Java 生态中,LangChain4j 是目前集成度最高、对 Spring Boot 支持最完善的选择之一,它让 Java 开发者可以用纯接口定义 + 注解驱动的方式快速构建 AI 应用,无需深究底层协议。但与之并肩的还有官方出品的 Spring AI,以及多智能体领域事实标准 LangGraph,三者在技术定位和能力边界上有显著差异。

8.2 三者核心定位对比:为什么多智能体流程编排选 LangGraph?

维度LangChain4jSpring AILangGraph
开发语言Java / Spring Boot ⭐Java / Spring Boot ⭐Python / TypeScript
上手门槛低(注解驱动,社区成熟)低(Spring 原生风格,自动配置)中(需理解图和状态概念)
多 Agent 能力中等(Supervisor 模式,隐式路由)弱(无内置的多 Agent 编排,需手动组合)强(显式有向图、条件边、循环、Map-Reduce)  ⭐
流程编排灵活性一般(依赖 LLM 自主决策)一般(需自行封装)极强(图级控制、状态持久化、断点续跑)  ⭐
工具/函数调用丰富(@Tool 注解,自动生成 Schema)丰富(@Tool 注解,与函数式 Bean 结合紧密)灵活(自定义 Node 函数,可集成任意库)
状态管理与可观测性较弱(调用链隐式,依赖 LLM 日志)较弱(无内置图状态,需借助 Micrometer)极强(State 对象流转、图可视化、回放与回溯)  ⭐
高级 Multi-Agent 模式有限(Supervisor 为主)需手动编码实现丰富(分层、辩论、Reflexion、ReAct 循环)  ⭐
Spring 整合深度较好(Starter、自动配置)极佳(官方出品,极致原生体验)  ⭐无(需自行封装 HTTP/gRPC 服务)
多模型支持广泛(OpenAI、MiniMax、Ollama 等)广泛(与 Spring 生态无缝对接)广泛(LangChain 生态模型全部可用)
生产就绪程度简单场景可快速落地企业级 Java 环境首选,生态协同强复杂场景长期迭代首选,学术与工业验证充分

8.3 如何做出技术选型?

① 什么时候选 Spring AI?

  • 当你的团队以 Spring Boot 为核心技术栈,追求极致的一体化开发体验时,Spring AI 是最自然的选择。
  • 它提供了与 Spring 生态的无缝整合(如 @Tool 直接对标 Service Bean、与 WebClientRetryObservability 深度结合),尤其适合在已有 Spring 微服务体系上扩展 AI 能力。
  • 但它的多智能体能力目前较弱,如果你需要复杂的 Agent 协作,不建议用 Spring AI 从零造轮子,而应将其作为调用下游 Agent 服务的“门面”。

② 什么时候选 LangChain4j?

  • 当你的业务场景需要显式的多 Agent 协调(如 Supervisor 模式),但又不想脱离 Java 技术栈时,LangChain4j 是最佳折中。
  • 它比 Spring AI 提供了更成熟的 Agent 抽象(@SupervisorAgentAgenticServices),又比 LangGraph 更容易落地到 Spring Boot 中。
  • 适合:智能客服、报告生成、数据汇总与分析等流程相对线性的多 Agent 场景

③ 什么时候必须选 LangGraph?

  • 当你面对高度复杂的多 Agent 协同:如多步规划、条件分支、持续循环(ReAct)、人工审批介入、多代理辩论等。
  • 当你需要图级别的可观测性:步骤追踪、状态可视化、断点续跑、回放。
  • 当你需要前沿的 Multi-Agent 模式:如 Reflexion、Self-Refine、Multi-Agent Collaboration 等,这些论文实现的第一语言通常是 Python。
  • 推荐架构:用 Spring AI / LangChain4j 作为 Java 侧的 AI 服务入口,将核心 Agent 流程编排交给 LangGraph(Python 微服务) ,通过 HTTP 或 gRPC 通信。这种“混合架构”兼顾了 Java 生态的稳定性和 Python 生态的灵活性,是目前企业级应用的最佳实践。

8.4 最后的话

LangChain4j 与 Spring AI 让 Java 开发者迈入了 AI 应用开发的大门,而 LangGraph 则打开了多智能体协同的复杂世界。在真实业务中,技术选型从来不是“非此即彼”,而是根据场景灵活组合。当前阶段,如果你想构建真正复杂的、可进化的多智能体系统,LangGraph 依然是流程编排的底座,而 Java 侧的工具(LangChain4j/Spring AI)负责将其包装为业务可用的服务。随着 Spring AI 的 Roadmap 中已开始出现 StateGraph 等概念,未来 Java 生态的 Multi-Agent 能力也值得期待。

完整的pom文件如下:

xml

<properties>
    <docker.repostory>registry.cn-hangzhou.aliyuncs.com/coccounter/coc</docker.repostory>
    <java.version>17</java.version>
    <spring-cloud.version>2025.0.1</spring-cloud.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.31</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        <version>3.5.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>32.1.2-jre</version>
    </dependency>

    <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.21.2</version>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.21</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>2.0.39</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- 阿里云 OSS 依赖 -->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>3.15.1</version>
    </dependency>

    <!-- Knife4j Swagger UI 依赖 -->
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
        <version>4.4.0</version>
    </dependency>

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
        <version>1.12.2-beta22</version>
    </dependency>

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-spring-boot-starter</artifactId>
        <version>1.12.2-beta22</version>
    </dependency>

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-agentic</artifactId>
        <version>1.12.2-beta22</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>build-info</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>com.spotify</groupId>
            <artifactId>docker-maven-plugin</artifactId>
            <version>1.1.0</version>
        </plugin>
    </plugins>
</build>

参考