【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 和行为可能在后续版本中变化。生产使用前评估稳定性。

相关推荐
程序员梅雨2 小时前
Linux & Shell 实用干货
linux·运维·服务器·后端·面试·php
William一直在路上2 小时前
GPT-6 Astra 研究报告——模型能力、API 演进与 Agent/MCP 生态影响分析
人工智能·gpt·llm·openai·astra
tntxia2 小时前
AI 应用开发生产落地实践指南
人工智能
林墨聊AIGC2 小时前
AI视频怎么做跳舞的动作效果:从入门到精通的舞蹈动画制作指南
大数据·人工智能·自动化·aigc·音视频
大牧师2 小时前
TypeORM 入门教程
后端·sql·mysql·orm·nest·typeorm
IT_陈寒2 小时前
Vite静态资源路径这个大坑害我调了一下午
前端·人工智能·后端
武哥聊编程2 小时前
【AI实战项目】基于SpringAI+Springboot+Vue的AI面试刷题训练平台
vue.js·人工智能·spring boot·springai
摆烂工程师2 小时前
别只拿 GPT-6 Astra 聊天,它真正恐怖的是开始会“干活”了
人工智能·程序员·vibecoding
LearnYard2 小时前
技术博主实测:2026年大语言模型辅助学习工具横向对比
人工智能·学习·语言模型
oliver_sys_log2 小时前
绕过 Yearning 查询体验限制:我做了一个 DataGrip 只读 SQL 代理
后端·mysql