【LangChain4j系列08】Agentic AI 多智能体协作

langchain4j-agentic 是 LangChain4j 的实验性模块,提供构建多智能体系统的完整框架。本章解析 @Agent 架构、五种工作流模式、SupervisorAgent 自主规划、以及 Planner 接口的扩展机制。


8.1 核心理念:从单一 AI Service 到多智能体系统

ini 复制代码
单一 AI Service:
  User → [AI Service] → Answer

多智能体系统:
  User → [路由器] → [医疗专家] → [验证器] → Answer
              ↘ [法律专家] ↗
              ↘ [技术专家] ↗

LangChain4j 参考 Anthropic 的分类:Workflows (确定性编排)和 Pure Agents(LLM 自主规划)。


8.2 定义 Agent

Agent 本质上就是带有 @Agent 注解的 AI Service 接口:

java 复制代码
public interface CreativeWriter {
    @UserMessage("""
        You are a creative writer.
        Generate a draft of a story no more than
        3 sentences long around the given topic.
        Return only the story and nothing else.
        The topic is {{topic}}.
        """)
    @Agent(
        value = "Generates a creative story based on the given topic",
        outputKey = "story"    // 输出存入 AgenticScope 的 "story" 变量
    )
    String generateStory(@V("topic") String topic);
}

@Agent 注解

java 复制代码
public @interface Agent {
    String value() default "";          // 描述(其他 Agent / Supervisor 依赖这个做决策)
    String name() default "";           // 唯一标识(默认取方法名)
    String outputKey() default "";      // 输出在共享状态中的键名
    boolean optional() default false;   // 缺少输入参数时跳过而非抛异常
    boolean async() default false;      // 异步执行
}

构建 Agent

java 复制代码
CreativeWriter writer = AgenticServices
    .agentBuilder(CreativeWriter.class)
    .chatModel(model)
    .outputKey("story")
    .name("creativeWriter")
    .build();

Untyped Agent(无定义接口,完全动态):

java 复制代码
UntypedAgent agent = AgenticServices.agentBuilder()
    .description("Generates a story")
    .userMessage("Write a story about {{topic}}")
    .inputKey(String.class, "topic")    // 从 scope 读 "topic"
    .returnType(String.class)
    .outputKey("story")                 // 写到 scope 的 "story"
    .chatModel(model)
    .build();

Map<String, Object> result = agent.invoke(Map.of("topic", "dragons"));

8.3 AgenticScope:共享状态

AgenticScope 是所有 Agent 之间的共享数据容器:

java 复制代码
// Agent A 产出 → scope.writeState("story", storyText)
// Agent B 消费 → scope.readState("story")

// 类型安全的访问(TypedKey)
scope.readState(Category.class);           // 编译期就知道类型
scope.writeState(StoryKey.class, story);   // 类型检查

// 弱类型访问
scope.readState("story", "default");       // 需要手动类型转换

TypedKey:告别字符串 Key

java 复制代码
// 定义强类型 Key
public static class UserRequest implements TypedKey<String> {}
public static class Category implements TypedKey<RequestCategory> {
    @Override
    public RequestCategory defaultValue() {
        return RequestCategory.UNKNOWN;
    }
}

// 在 Agent 中使用
@Agent(typedOutputKey = Category.class)
RequestCategory classify(@K(UserRequest.class) String request);

// 在条件逻辑中使用(无需类型转换)
.subAgents(
    scope -> scope.readState(Category.class) == RequestCategory.MEDICAL,
    medicalExpert
)

8.4 五种工作流模式

1. Sequential(顺序执行)

java 复制代码
UntypedAgent pipeline = AgenticServices.sequenceBuilder()
    .subAgents(creativeWriter, audienceEditor, styleEditor)  // A → B → C
    .outputKey("story")
    .build();

Map<String, Object> result = pipeline.invoke(Map.of(
    "topic", "dragons and wizards",
    "style", "fantasy",
    "audience", "young adults"
));

2. Loop(循环执行)

java 复制代码
UntypedAgent reviewLoop = AgenticServices.loopBuilder()
    .subAgents(styleScorer, styleEditor)  // 评分 → 润色 → 评分 → ...
    .maxIterations(5)
    .exitCondition((scope, loopCounter) -> {
        double score = scope.readState("score", 0.0);
        if (loopCounter <= 3) return score >= 0.8;  // 前3轮要求严格
        return score >= 0.6;                         // 之后放宽
    })
    .testExitAtLoopEnd(true)  // 在每轮结束时检查(而非开始)
    .build();

// 循环可以作为任何工作流的子 Agent
UntypedAgent full = AgenticServices.sequenceBuilder()
    .subAgents(creativeWriter, reviewLoop)
    .outputKey("story")
    .build();

3. Parallel(并行执行)

java 复制代码
UntypedAgent planner = AgenticServices.parallelBuilder()
    .subAgents(foodExpert, movieExpert)
    .executor(Executors.newFixedThreadPool(2))
    .outputKey("plans")
    .output(scope -> {
        List<String> movies = scope.readState("movies", List.of());
        List<String> meals = scope.readState("meals", List.of());
        // 合并两个并行 Agent 的输出
        return IntStream.range(0, Math.min(movies.size(), meals.size()))
            .mapToObj(i -> new EveningPlan(movies.get(i), meals.get(i)))
            .toList();
    })
    .build();

4. Parallel Mapper(批量并行)

java 复制代码
UntypedAgent batchProcessor = AgenticServices.parallelMapperBuilder()
    .subAgent(horoscopeAgent)    // 同一个 Agent
    .itemsProvider("persons")    // 遍历 persons 列表,每个元素并发执行
    .build();

// 输入 {"persons": [Person("张三"), Person("李四"), Person("王五")]}
// 输出: ["张三的运势...", "李四的运势...", "王五的运势..."]

⚠️ 子 Agent 不能使用 ChatMemory(同一个 Agent 实例会被多次并发调用)。

5. Conditional(条件分支)

java 复制代码
// Step 1: 分类器(也是一个 Agent)
UntypedAgent router = AgenticServices.agentBuilder()
    .userMessage("Categorize this request: {{request}}")
    .returnType(RequestCategory.class)
    .outputKey("category")
    .chatModel(model)
    .build();

// Step 2: 条件路由
UntypedAgent experts = AgenticServices.conditionalBuilder()
    .subAgents(
        scope -> scope.readState("category", RequestCategory.UNKNOWN) == RequestCategory.MEDICAL,
        medicalExpert
    )
    .subAgents(
        scope -> scope.readState("category", RequestCategory.UNKNOWN) == RequestCategory.LEGAL,
        legalExpert
    )
    .subAgents(
        scope -> scope.readState("category", RequestCategory.UNKNOWN) == RequestCategory.TECHNICAL,
        technicalExpert
    )
    .build();

// Step 3: 组合
UntypedAgent fullSystem = AgenticServices.sequenceBuilder()
    .subAgents(router, experts)
    .outputKey("response")
    .build();

8.5 工作流组合

所有工作流模式都可以互相嵌套:

java 复制代码
// 顺序 { 创意写作 → 循环{ 评分 → 润色 } → 并行{ 审核A, 审核B } }
UntypedAgent novelCreator = AgenticServices.sequenceBuilder()
    .subAgents(creativeWriter, reviewLoop, reviewPanel)
    .outputKey("novel")
    .build();

8.6 Pure Agentic AI:SupervisorAgent

SupervisorAgent 让 LLM 自己决定调用哪些 Agent、以什么顺序、何时结束:

java 复制代码
SupervisorAgent supervisor = AgenticServices.supervisorBuilder()
    .chatModel(plannerModel)   // 需要一个强推理能力的模型做规划
    .subAgents(withdrawAgent, creditAgent, exchangeAgent)
    .responseStrategy(SupervisorResponseStrategy.SUMMARY)
    .contextGenerationStrategy(SupervisorContextStrategy.CHAT_MEMORY)
    .supervisorContext("""
        Policies:
        - Prefer internal tools
        - Currency in USD
        - No external APIs without explicit approval
        """)
    .build();

String result = supervisor.chat("Transfer 100 EUR from Mario's account to Georgios");

Supervisor 的规划过程

ini 复制代码
User: "Transfer 100 EUR from Mario to Georgios"
  ↓
Supervisor 生成 Plan:
  1. AgentInvocation(agentName="exchange", arguments={"amount":"100","from":"EUR","to":"USD"})
  2. AgentInvocation(agentName="withdraw", arguments={"name":"Mario","amount":"92"})
  3. AgentInvocation(agentName="credit", arguments={"name":"Georgios","amount":"92"})
  4. AgentInvocation(agentName="done", arguments={"summary":"Transferred 100 EUR..."})
  ↓
按 Plan 顺序执行

三种响应策略

java 复制代码
public enum SupervisorResponseStrategy {
    LAST,      // 返回最后一个 Agent 的输出(默认)
    SUMMARY,   // 返回 Supervisor 生成的摘要(适合事务场景)
    SCORED     // 通知 Scorer Agent 对比 LAST vs SUMMARY,选更好的
}

8.7 跨切面特性

可选 Agent

java 复制代码
@Agent(optional = true)
String generateStory(@V("topic") String topic);
// 如果 topic 不在 scope 中,跳过而非抛异常

异步 Agent

java 复制代码
@Agent(async = true)
String fetchExternalData(@V("query") String query);
// 在独立线程中执行,不阻塞后续 Agent(前提是后续 Agent 不依赖它的输出)

动态模型选择

java 复制代码
StoryEditor editor = AgenticServices.agentBuilder(StoryEditor.class)
    .chatModel(scope -> {
        // 根据上一轮的评分动态切换模型
        double score = scope.readState("score", 0.0);
        return score > 7.5 ? cheapModel : expensiveModel;
    })
    .outputKey("story")
    .build();

错误处理

java 复制代码
UntypedAgent agent = AgenticServices.sequenceBuilder()
    .subAgents(writer, editor, formatter)
    .errorHandler(errorContext -> {
        if (errorContext.agentName().equals("generateStory")
                && errorContext.exception() instanceof MissingArgumentException ex) {
            // 写入默认值并重试
            errorContext.agenticScope().writeState("topic", "dragons and wizards");
            return ErrorRecoveryResult.retry();
        }
        if (errorContext.agentName().equals("formatStory")) {
            // 跳过非关键 Agent 的错误,返回已有结果
            return ErrorRecoveryResult.result(errorContext.agenticScope().readState("story"));
        }
        return ErrorRecoveryResult.throwException();
    })
    .outputKey("story")
    .build();

跨 Agent 补偿

java 复制代码
UntypedAgent transferWorkflow = AgenticServices.sequenceBuilder()
    .subAgents(creditAgent, debitAgent, notificationAgent)
    .compensateOnError(true)   // 任一 Agent 失败,前面的全部回滚
    .outputKey("result")
    .build();
// 与 @Tool 的 @CompensateFor 机制复用同一套注解

8.8 可观测性

AgentListener

java 复制代码
AgentListener listener = new AgentListener() {
    @Override
    public void beforeAgentInvocation(AgentRequest request) {
        log.info("Agent {} starting", request.agentName());
    }

    @Override
    public void afterAgentInvocation(AgentResponse response) {
        log.info("Agent {} done in {}ms, tokens: {}",
            response.agentName(),
            response.duration().toMillis(),
            response.tokenUsage().totalTokenCount());
    }

    @Override
    public boolean inheritedBySubagents() {
        return true;  // 传播到所有子 Agent
    }
};

// 注册
AgenticServices.agentBuilder(CreativeWriter.class)
    .chatModel(model)
    .listener(listener)
    .build();

AgentMonitor(内置监控)

java 复制代码
// 方式1:手动注册
AgentMonitor monitor = new AgentMonitor();
// 注册到 agentBuilder: .listener(monitor)

// 方式2:通过接口扩展自动创建
public interface StyledWriter extends MonitoredAgent {
    @Agent("Write a creative story")
    String generateStory(@V("topic") String topic, @V("style") String style);
}

StyledWriter writer = AgenticServices.agentBuilder(StyledWriter.class)
    .chatModel(model)
    .outputKey("story")
    .build();

AgentMonitor monitor = writer.agentMonitor();

// 查看执行记录
MonitoredExecution execution = monitor.successfulExecutions().get(0);
System.out.println(execution);  // 树形嵌套输出

// 生成 HTML 报告
HtmlReportGenerator.generateReport(monitor, Path.of("agent-execution.html"));
HtmlReportGenerator.generateTopology(writer, Path.of("agent-topology.html"));

8.9 声明式 API

除了编程式 Builder,也可以用注解声明工作流:

java 复制代码
@ParallelAgent(
    outputKey = "plans",
    subAgents = { FoodExpert.class, MovieExpert.class }
)
public interface EveningPlannerAgent {
    List<EveningPlan> plan(@V("mood") String mood);

    @ParallelExecutor
    static Executor executor() {
        return Executors.newFixedThreadPool(2);
    }

    @Output
    static List<EveningPlan> createPlans(
        @V("movies") List<String> movies,
        @V("meals") List<String> meals
    ) {
        // 合并并行结果
        return IntStream.range(0, Math.min(movies.size(), meals.size()))
            .mapToObj(i -> new EveningPlan(movies.get(i), meals.get(i)))
            .toList();
    }
}

// 一键实例化
EveningPlannerAgent planner = AgenticServices.createAgenticSystem(EveningPlannerAgent.class, model);

注解速查

注解 适用范围 用途
@Output 所有模式 从 scope 组装最终输出
@ActivationCondition Conditional 子 Agent 激活条件
@ExitCondition Loop 循环退出条件
@ParallelExecutor Parallel / ParallelMapper 线程池
@ErrorHandler 所有模式 错误处理逻辑
@ChatModelSupplier 所有 LLM Agent ChatModel 提供者
@ToolsSupplier 所有 LLM Agent 工具对象提供者
@SupervisorRequest Supervisor Supervisor 指令

8.10 自定义 Agentic 模式:Planner 接口

所有内置模式都基于 Planner 接口。你也可以实现自己的:

java 复制代码
public interface Planner {
    default void init(InitPlanningContext ctx) {}
    default Action firstAction(PlanningContext ctx) { return nextAction(ctx); }
    Action nextAction(PlanningContext ctx);
}

Action 返回值:

  • call(agent1, agent2, ...) --- 调用(并行执行多个)
  • done() --- 完成

参考实现:ParallelPlanner

java 复制代码
public class ParallelPlanner implements Planner {
    private List<AgentInstance> agents;

    @Override
    public void init(InitPlanningContext ctx) {
        this.agents = ctx.subagents();
    }

    @Override
    public Action firstAction(PlanningContext ctx) {
        return call(agents);  // 所有 Agent 并行执行一次
    }

    @Override
    public Action nextAction(PlanningContext ctx) {
        return done();        // 然后结束
    }
}

参考实现:GOAP(Goal-Oriented Action Planning)

一种算法化的规划器------计算从当前状态到目标的最短路径:

java 复制代码
// 每个 Agent 声明的 inputKey = 前置条件(需要什么)
// outputKey = 后置条件(产出什么)
// 目标:AgenticScope 的 outputKey

// 调用时构造依赖图,Dijkstra 求最短路径
// 例如: inputs {name, sign} → path [extractPerson, extractSign, horoscope, findStory, write]

参考实现:P2P(Peer-to-Peer)

去中心化模式------所有 Agent 平等,由 scope 状态驱动:

erlang 复制代码
AgenticScope 状态变化 → 检查每个 Agent 的激活条件 → 激活满足条件的 Agent → 更新状态 → ...

8.11 实验性模块说明

langchain4j-agentic 当前标记为实验性,API 和行为可能在后续版本中变化。生产使用前评估稳定性。

相关推荐
烟雨江南7851 小时前
离线语音识别为什么一到本地部署,准确率反而会变?
人工智能·语音识别
凤山老林1 小时前
零停机数据库演进:Spring Boot 集成 Flyway 与平滑DDL变更策略
数据库·spring boot·后端
步行cgn1 小时前
MyBatis <sql> 标签详解:SQL 片段的定义与复用
后端
想要成为糕糕手1 小时前
🏭 设计模式之工厂模式:从蜜雪冰城到 NestJS,把「new」外包出去
后端·nestjs
科技云报道1 小时前
【重磅】瑞数信息发布《2026Bots & Agents自动化威胁报告》,重构AI Agent时代安全认知
人工智能·重构·自动化
抓哇小菜鸡1 小时前
Spring Boot + 本地大模型(Ollama/DeepSeek) + MyBatis-Plus 企业级智能体数据分析系统从零到一源码全解析
spring boot·后端·mybatis
星火10241 小时前
【LangChain4j系列07】结构化输出与类型安全
人工智能·后端
用户298698530141 小时前
3 种方法,轻松将 PowerPoint 转换为 PDF 格式
人工智能·后端·c#
安逸sgr1 小时前
视觉 Token 是什么?图片是怎么送进大模型的?
人工智能·ai·大模型·agent·智能体