「Java AI实战」LangChain4J - 多 Agent 协作实践

系列文章目录

第一章 「Java AI实战」LangChain4J - 接入Xinference本地大模型
第二章 「Java AI实战」LangChain4J - ChatAPI 及常用配置
第三章 「Java AI实战」LangChain4J - 向量数据库接入与语义检索
第四章 「Java AI实战」LangChain4J - Agent智能体开发
第五章 「Java AI实战」LangChain4J -记忆缓存
第六章 「Java AI实战」LangChain4J -文本分类器实践



前言:为什么需要「多 Agent 协作」?

在大部分 Java 项目里,“接入大模型” 往往长这样:

  • Controller 收到一个 question
  • 调用大模型接口拿到 answer
  • 把结果原样丢回前端

这种玩法可以叫「高配搜索框模式」——能用,但很快就会遇到瓶颈:

  • 一个 Agent 要写 Prompt、要查数据库、还要改写文案,太忙了
  • 不同角色(创作者、编辑、审稿人)的职责混在一起,难以维护
  • 想复用其中一段能力(比如只要润色)也不方便

更工程化的玩法是:

把大任务拆成多个 “角色清晰的 Agent”,再编排它们像流水线一样协同完成任务。

这一篇,我们就用一个简单的场景——故事创作 + 面向不同读者的改写——来落地一套:

  • CreativeWriter:负责创作故事
  • AudienceEditor:负责针对目标读者进行润色

一、基于 LangChain4J 的多 Agent 能力

在 LangChain4J 的 Agentic 模块中,有几个核心概念:

  • @Agent:把一个 Java 接口方法标记为 Agent 能力
  • @UserMessage:定义调用模型时的 Prompt 模板
  • @V:把上下文里的变量注入到 Prompt 中
  • AgenticServices:
    • agentBuilder():把接口实现为一个可调用的 Agent Bean
    • sequenceBuilder():把多个 Agent 组装成顺序执行的工作流

在这套 Demo 里,我们做的事情可以抽象成一句话:

CreativeWriter 生成 story → 用 AudienceEditor 根据 audience 对 story进行二次创作 → 返回 finalStory。

二、代码实践:从接口到 HTTP 接口的完整闭环

2.1 整体设计思路

整个 Demo 的结构可以概括为:

  1. 两个 Agent 接口

    • CreativeWriter:按主题生成故事初稿
    • AudienceEditor:根据读者群体对故事进行改写
  2. 一个 LLM 配置

    • 使用 OpenAiChatModel 协议,对接 Xinference + 本地 Qwen2.5 模型
  3. 一个 AgentConfig

    • 通过 AgenticServices.agentBuilder 把接口变成 Bean
    • 通过 AgenticServices.sequenceBuilder 串成一个顺序执行的工作流
  4. 一个 REST Controller

    • 暴露 /story GET 接口,对外提供服务

2.2 定义两个 Agent:创作 & 润色

2.2.1 创作故事的 Agent:CreativeWriter

package org.example.agents;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface CreativeWriter {

    @UserMessage("你是一名富有创造力的故事作者。请围绕给定的主题创作一个不超过 3 句话的故事草稿。" +
            "只返回故事内容本身,不要包含任何解释或其他文字。主题是:{{topic}}。")
    @Agent(description = "根据给定主题生成一个故事")
    String generateStory(@V("topic") String topic);
}

这里有几个关键点:

  1. @UserMessage 里写的是系统 Prompt 模板
    • 限制为「不超过 3 句话」
    • 要求「只返回故事内容本身」
  2. @V(“topic”):把上下文中的 topic 变量注入到 {{topic}} 占位符中
  3. @Agent:告诉 LangChain4J,generateStory 是一个 Agent 方法

2.2.2 润色故事的 Agent:AudienceEditor

package org.example.agents;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface AudienceEditor {

    @UserMessage(" 你是一名专业的文字编辑。请将下面的故事改写得更适合目标读者:{{audience}}。" +
        "故事长度请控制在 3 句话以内,要生动、有吸引力,并且通俗易懂。" +
        "只返回改写后的故事内容,不要包含任何解释或多余文字。原始故事如下:{{story}}")
    @Agent(description = "根据目标读者对故事进行润色与改写")
    String editStory(@V("story") String story,
                     @V("audience") String audience);

}

设计要点:

  1. 接收两个变量:
    • story:上一阶段的创作结果
    • audience:目标读者(例如“小学生”“开发者”“职场新人”等)
  2. Prompt 中明确约束:
    • 输出长度(≤ 3 句)
    • 风格要求(生动、有吸引力、通俗易懂)
    • 不要解释,只给结果文本

💡 小结:到这里,我们只是定义了「能力接口」,真正的 Agent 实例将在配置类里通过AgenticServices 生成。

2.3 配置大模型:对接 Xinference + Qwen2.5

package org.example.config;

import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;

@Configuration
public class LlmConfig {

    @Bean
    public ChatModel xinferenceChatModel(
            @Value("${xinference.base-url}") String baseUrl,
            @Value("${xinference.api-key}") String apiKey,
            @Value("${xinference.model-name}") String modelName) {
        return OpenAiChatModel.builder()
                .baseUrl(baseUrl)
                .apiKey(apiKey)
                .modelName(modelName)
                .temperature(0.8)
                .timeout(Duration.ofSeconds(60))
                .maxRetries(2)
                .build();
    }
}

说明:

  1. 使用 OpenAiChatModel 兼容 OpenAI 协议,对接 Xinference
  2. 关键参数:
    • baseUrl:Xinference 的 HTTP 地址
    • apiKey:本地可写占位,如 not-used
    • modelName:例如 qwen2.5-instruct
    • temperature:0.8,稍微有点“会写故事”的发挥空间

2.4 组装多 Agent 工作流:AgentConfig

package org.example.config;

import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.agentic.UntypedAgent;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.service.tool.ToolProvider;
import org.example.agents.AudienceEditor;
import org.example.agents.CreativeWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
1. 使用 AgenticServices 把接口变成真正的 Agent Bean,并组装成一个顺序工作流
*/
@Configuration
public class AgentConfig {

   /**
    * CreativeWriter Agent:输出写到 agentic scope 的 "story" key
    */
   @Bean
   public CreativeWriter creativeWriter(ChatModel xinferenceChatModel, ToolProvider mcpToolProvider) {
       return AgenticServices.agentBuilder(CreativeWriter.class)
               .chatModel(xinferenceChatModel)
               .outputKey("story")
               .build();
   }

   /**
    * AudienceEditor Agent:最终结果写到 "finalStory"
    */
   @Bean
   public AudienceEditor audienceEditor(ChatModel xinferenceChatModel, ToolProvider mcpToolProvider) {
       return AgenticServices.agentBuilder(AudienceEditor.class)
               .chatModel(xinferenceChatModel)
               .outputKey("finalStory")
               .build();
   }

   /**
    * 多 Agent 顺序协同:
    *  1)CreativeWriter:根据 topic 生成初稿(story)
    *  2)AudienceEditor:根据 audience + story 做润色,产出 finalStory
    */
   @Bean
   public UntypedAgent storyPipeline(CreativeWriter creativeWriter,
                                     AudienceEditor audienceEditor) {
       return AgenticServices.sequenceBuilder()
               .subAgents(creativeWriter, audienceEditor)
               .outputKey("finalStory")
               .build();
   }
}

这里是整个 Demo 的关键:

  1. AgenticServices.agentBuilder:

    • 把接口转为可调用的 Agent Bean
    • 绑定使用哪个 ChatModel
    • 可选绑定 ToolProvider(例如 MCP 工具)
    • 通过 outputKey 把输出写入「Agentic 上下文」
  2. AgenticServices.sequenceBuilder():

    • 用 subAgents(creativeWriter, audienceEditor) 定义执行顺序
    • 整个 pipeline 的最终输出使用 outputKey(“finalStory”)

✅ 你可以把这个 pipeline 理解成一个「小型 AI 工作室」: 前半段是「创作部」,后半段是「编辑部」。

2.5 暴露 HTTP 接口:StoryController

package org.example.controller;

import dev.langchain4j.agentic.UntypedAgent;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

@RestController
public class StoryController {

   @Resource
   private UntypedAgent storyPipeline;

   @GetMapping("/story")
   public String createStory(@RequestParam("topic") String topic,
                             @RequestParam(name = "audience", defaultValue = "开发") String audience) {
      return (String) storyPipeline.invoke(Map.of("topic", topic, "audience", audience));
   }
}

说明:

  1. 注入的是前面组装好的 UntypedAgent storyPipeline

  2. 接口入参:

    • topic:故事主题(必填)
    • audience:读者人群(可选,默认 “开发”)
  3. 核心调用:

    • storyPipeline.invoke(input),其中 input 是一个 Map:
      • topic 会被 CreativeWriter 的 @V(“topic”) 使用
      • audience 会被 AudienceEditor 的 @V(“audience”) 使用
  4. 返回值直接就是 finalStory,一个已经针对目标读者优化过的故事

2.6 配置文件 application.properties

server.port=8081
xinference.base-url=http://本地ip:9997/v1
xinference.api-key=not-used
xinference.model-name=qwen2.5-instruct

2.7 调用示例

GET http://localhost:8081/story?topic=今天的天气&audience=幼儿园老师

可能得到类似这样的结果(示意):

今天早上,天空像被轻轻洗过一样干净,太阳笑眯眯地从云朵后面探出头来。 小朋友们穿着彩色的小外套,在操场上开心地跑来跑去。
这样的好天气,最适合做游戏、晒太阳和和好朋友一起分享快乐啦!


总结

这一篇,我们用一个非常轻量的 Demo,走通了:

在 Java / Spring Boot 中如何:

  • 定义多个职责清晰的 Agent(创作 / 润色)
  • 使用 LangChain4J 的 AgenticServices 把它们串成一个顺序工作流
  • 对接 Xinference + 本地大模型,提供 HTTP 服务

可以看到,多 Agent 协作带来的好处包括:

  • 职责解耦:每个 Agent 做一件事,Prompt 更干净、维护更轻松
  • 组合灵活:可以很容易新增一个「审稿 Agent」「敏感内容检查 Agent」接到 pipeline 末尾
  • 更工程化:业务层可以像调用普通 Service 一样使用 Agent 工作流

接下来,完全可以基于这个 Demo 做更多扩展,比如:

  1. AudienceEditor 中挂上 MCP 工具
    让它能先查知识库 / 文档,再结合上下文做更精准的润色。

  2. 新增一个「风格转换 Agent」
    用来控制整体输出风格:是「技术博客风」「科普文风」还是「营销文案风」。

  3. storyPipeline 升级为「可配置的多 Agent 流水线」
    做成一个简单的「AI 工作流编排中心」,按业务场景自由组合、调整 Agent 顺序。

更多完整实战案例: https://wx.zsxq.com/group/48885482144828

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值