从 1 个 Agent 到 5 个 Agent:Spring AI 多智能体协作实战

用一个完整的自动报销审批流,讲清楚多 Agent 协作的三种主流模式。

一、单 Agent 的天花板:为什么必须拆?

单体 Agent 走到一定规模,必然撞到三堵墙:

第一堵:工具地狱。工具超过 20 个后,模型选错工具的概率显著上升。OpenAI 官方建议单次 Function Calling 工具数控制在 20 以内,超过就要分组。

第二堵:Prompt 膨胀。你需要给模型解释"什么时候用工具 A、什么时候用工具 B、A 和 B 同时满足时怎么办"……规则越多,幻觉越多。

第三堵:职责不清。一个 Agent 同时管 OCR、管规则、管财务计算、管审批路由,等于一个员工兼任五个岗位——出问题谁都说不清。

工程上的答案早就有了:分而治之。把一个胖 Agent 拆成若干个瘦 Agent,每个只懂自己那点事,再由一个调度者协调。

下面用一个真实场景看它怎么工作。

二、场景:一张发票的自动审批之旅

员工上传一张 ¥3,860 的差旅发票,附言:“上海出差,4 月 10–12 日”。

传统流程:员工提交 → 部门主管审 → 财务核 → 出纳付款。耗时 3–5 天。

多 Agent 流程跑起来是这样:

用户输入:[发票图片] + "上海出差4月10-12日"
   │
   ▼
[OcrAgent]      → 识别金额 ¥3,860、商户、日期、发票号
   │
   ▼
[PolicyAgent]   → 校验差旅标准:上海三日,住宿+餐补上限 ¥4,500,通过
   │
   ▼
[FinanceAgent]  → 拆分科目:交通 ¥1,200 / 住宿 ¥2,000 / 餐补 ¥660
   │
   ▼
[RoutingAgent]  → 金额 < ¥5,000,直接到部门主管;> ¥5,000 加一级总监
   │
   ▼
[BookingAgent]  → 写入 ERP,生成凭证号 PV-2026-04829
   │
   ▼
返回:「已自动核算并提交至王经理审批,预计 1 个工作日内到账。」

整条链路 8–12 秒跑完。员工再也不用填那张 14 个字段的表单。

三、多 Agent 协作的三种模式

在写代码之前,先把脑子里的模型定清楚。业界 95% 的多 Agent 方案都是这三种或它们的组合:

3.1 Supervisor 模式(主管 - 下属)

用户

SupervisorAgent

OcrAgent

PolicyAgent

FinanceAgent

主管 Agent 看用户意图,动态决定调哪个下属、调几次。最灵活,最贵(每次决策都要过一遍大模型)。适合开放式任务,比如"帮我安排下周差旅"。

3.2 Pipeline 模式(流水线)

用户

OcrAgent

PolicyAgent

FinanceAgent

BookingAgent

顺序固定,每个 Agent 干完交给下一个。最便宜,最稳定。适合流程类业务——报销、审批、工单、KYC 全部是这类

3.3 Group Chat 模式(圆桌讨论)

ProductAgent

EngineerAgent

QaAgent

多个 Agent 围绕一个话题轮流发言,直到达成共识或达到轮次上限。适合需要"多视角辩论"的场景,比如方案评估、代码 Review。

怎么选? 默认 Pipeline。需要分支时上 Supervisor。需要多视角时才上 Group Chat。别上来就 Group Chat,那是炫技不是工程

报销审批是典型 Pipeline + 局部 Supervisor 的组合,下面我们就这么实现。

四、动手:用 Spring AI 1.0 实现报销审批流

4.1 整体结构

expense-agents/
├── agent/
│   ├── OcrAgent.java
│   ├── PolicyAgent.java
│   ├── FinanceAgent.java
│   ├── RoutingAgent.java
│   ├── BookingAgent.java
│   └── ExpenseSupervisor.java   ← 编排者
├── model/
│   └── ExpenseContext.java      ← 共享上下文(黑板)
└── controller/
    └── ExpenseController.java

核心思路:每个 Agent 是一个 ChatClient Bean,编排器按顺序调用,共享一份 ExpenseContext

4.2 共享上下文:黑板模式

多 Agent 协作最容易翻车的点是状态传递。一个 Agent 算出来的数据,下一个 Agent 拿不到。

最干净的做法是定义一个不可变的"黑板"对象,每个 Agent 输出一份增量:

public record ExpenseContext(
        String requestId,
        String userId,
        String rawText,
        InvoiceInfo invoice,         // OcrAgent 填
        PolicyCheck policyCheck,     // PolicyAgent 填
        List<AccountEntry> entries,  // FinanceAgent 填
        ApprovalRoute route,         // RoutingAgent 填
        String voucherNo             // BookingAgent 填
) {
    public ExpenseContext withInvoice(InvoiceInfo info) {
        return new ExpenseContext(requestId, userId, rawText,
                info, policyCheck, entries, route, voucherNo);
    }
    // 其它 withXxx 同理
}

Record + with 方法,保持不可变。这比塞一个 Map<String,Object> 强一百倍——后者半年后没人看得懂。

4.3 每个子 Agent

每个 Agent 都是一个轻量 ChatClient,只配自己需要的 System Prompt 和工具。

OcrAgent(识别发票字段):

@Component
public class OcrAgent {

    private final ChatClient client;

    public OcrAgent(ChatClient.Builder builder, OcrTools tools) {
        this.client = builder
                .defaultSystem("""
                    你负责从发票图片或文本中提取结构化信息。
                    必须调用 ocrInvoice 工具完成识别,禁止猜测。
                    输出 JSON:{amount, merchant, date, invoiceNo}
                """)
                .defaultTools(tools)
                .build();
    }

    public InvoiceInfo extract(String rawText) {
        return client.prompt()
                .user(rawText)
                .call()
                .entity(InvoiceInfo.class);   // Spring AI 1.0 直接反序列化
    }
}

.entity(Class) 是 Spring AI 1.0 的一个甜点——自动把模型输出 JSON 映射成对象,省掉手写 ObjectMapper。

PolicyAgent(规则校验):

@Component
public class PolicyAgent {

    private final ChatClient client;

    public PolicyAgent(ChatClient.Builder builder, PolicyTools tools) {
        this.client = builder
                .defaultSystem("""
                    你负责对照公司差旅制度,判断报销是否合规。
                    调用 getPolicy 工具读取制度,调用 checkBudget 工具核实预算。
                    输出 JSON:{passed: boolean, reason: string, breachItems: []}
                """)
                .defaultTools(tools)
                .build();
    }

    public PolicyCheck check(InvoiceInfo info, String userId) {
        String prompt = """
                员工:%s
                发票金额:%.2f 元
                商户:%s
                日期:%s
                请核对差旅标准并给出结论。
                """.formatted(userId, info.amount(), info.merchant(), info.date());

        return client.prompt().user(prompt).call().entity(PolicyCheck.class);
    }
}

剩下三个 Agent 套路完全一样:定 System Prompt、注入工具、.entity(...) 拿结构化结果。这就是 Spring AI 的好处——每个 Agent 都是 30 行以内的小类,看着就让人安心

4.4 编排者:ExpenseSupervisor

Pipeline 模式下的编排器其实不需要 LLM,纯 Java 就够:

@Service
@RequiredArgsConstructor
public class ExpenseSupervisor {

    private final OcrAgent ocrAgent;
    private final PolicyAgent policyAgent;
    private final FinanceAgent financeAgent;
    private final RoutingAgent routingAgent;
    private final BookingAgent bookingAgent;

    public ExpenseContext process(String userId, String rawText) {
        var ctx = new ExpenseContext(UUID.randomUUID().toString(),
                userId, rawText, null, null, null, null, null);

        ctx = ctx.withInvoice(ocrAgent.extract(rawText));

        var check = policyAgent.check(ctx.invoice(), userId);
        ctx = ctx.withPolicyCheck(check);
        if (!check.passed()) {
            return ctx;   // 不合规直接退回
        }

        ctx = ctx.withEntries(financeAgent.split(ctx.invoice()));
        ctx = ctx.withRoute(routingAgent.decide(ctx.invoice(), userId));
        ctx = ctx.withVoucherNo(bookingAgent.book(ctx));
        return ctx;
    }
}

一句话总结:Pipeline 模式下,“编排” 就是 Java 方法的顺序调用。没有花架子。

4.5 什么时候才该用 LLM 当编排者?

当流程有分支判断且分支条件难以用代码穷举时。比如:

用户上传了一张图,可能是发票、可能是行程单、也可能就是张照片。

这种情况下让一个 SupervisorAgent 看一眼决定走哪条 Pipeline 才合适:

@Tool(description = "处理标准报销发票流程")
public String runExpensePipeline(String rawText, String userId) {
    return expenseSupervisor.process(userId, rawText).voucherNo();
}

@Tool(description = "处理出差行程单匹配流程")
public String runTripMatch(String rawText, String userId) { /* ... */ }

@Tool(description = "标记为非财务文件,转人工")
public String escalateToHuman(String reason) { /* ... */ }

把这三个工具挂给一个 RouterAgent,它会按用户输入选合适的 Pipeline。LLM 只做决策,不做执行——这是性价比最高的多 Agent 用法

五、少走弯路的生产化经验

5.1 并行能并的,不要傻等

报销流程里,PolicyAgent 和 FinanceAgent 其实没有依赖关系——前者校验合规、后者拆分科目,可以并发跑:

CompletableFuture<PolicyCheck> policyFut =
        CompletableFuture.supplyAsync(() -> policyAgent.check(ctx.invoice(), userId));
CompletableFuture<List<AccountEntry>> financeFut =
        CompletableFuture.supplyAsync(() -> financeAgent.split(ctx.invoice()));

ctx = ctx.withPolicyCheck(policyFut.join())
         .withEntries(financeFut.join());

8 秒的链路压到 5 秒,用户体验显著上升。LLM 调用都是 IO 密集型,并行收益巨大

5.2 每个 Agent 必须能"独立失败"

子 Agent 出错不能让整条链路崩。我习惯每个 Agent 包一层降级:

public InvoiceInfo extract(String rawText) {
    try {
        return client.prompt().user(rawText).call().entity(InvoiceInfo.class);
    } catch (Exception e) {
        log.warn("OcrAgent failed, fallback to manual", e);
        return InvoiceInfo.manualRequired(rawText);
    }
}

下游 Agent 看到 manualRequired 就知道要触发人工补录。这比抛异常优雅得多

5.3 留好"人在回路"(Human-in-the-loop)的口子

任何涉及钱的流程,都不要让 Agent 全自动走到底。在 BookingAgent 之前加一个确认节点:

if (ctx.invoice().amount() > 10_000) {
    return ctx.withRoute(ApprovalRoute.toHuman("金额超过 1 万,需人工复核"));
}

监管查起来你才说得清楚——关键节点保留人工卡点不是软弱,是合规

5.4 全链路 trace 必须有

5 个 Agent 串起来,出问题排查比单 Agent 难 5 倍。最少要做到:

  • 每个 Agent 入参出参打日志,关联同一个 requestId
  • LLM 调用走 Micrometer,能看 token 数和延迟
  • 失败的 Agent 自动重试一次(注意幂等)

Spring AI 1.0 的 ChatClientObservation 已经把 Hook 留好了,配一下 SkyWalking 或 OpenTelemetry 就能用。

六、Java 多 Agent 的现状与 Python 对比

绕不开这个话题。截至 2026 年初,主流多 Agent 框架长这样:

框架 语言 协作模式 成熟度 适用场景
Spring AI + 自编排 Java 全部 稳定 企业 Spring 项目
LangChain4j Agentic Java Supervisor + Pipeline 增长中 Java 项目偏 LangChain 生态
LangGraph Python 图编排 成熟 复杂状态机
AutoGen Python Group Chat 见长 成熟 多角色辩论
CrewAI Python 角色驱动 增长快 模拟团队协作

国内 Java 团队的现实是:业务系统是 Java 的,没人愿意单独起个 Python 服务。Spring AI + 手写编排(像本文这样)已经能覆盖 90% 企业场景。剩下 10% 真的需要图状态机的,再考虑接入 Python LangGraph 微服务。

不要为了用 LangGraph 而 LangGraph。一个 30 行的 Java Supervisor,可读性和稳定性都比花哨的 DAG 配置强。

七、后话

下一步是什么?是把这些 Agent 接入企业现有的工作流引擎(Activiti、Camunda、Flowable),让 Agent 在合适的节点接管,不合适的节点交还给人。

那才是企业 AI 真正落地的样子——不是 AI 取代流程,而是 AI 嵌入流程

现在已经不是在"用大模型",而是在"用大模型重做业务系统"。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐