Ohhnews

分类导航

$ cd ..
foojay原文

构建智能体驱动的仓库管理系统——第2部分:Java和Spring AI

#java#spring ai#人工智能#仓库管理#智能体

构建智能体 WMS —— 第二部分:Java 与 Spring AI

如果你没有阅读第一部分就直接来到这里,我建议你先从 智能体 WMS —— 第一部分:AI 智能体在哪些地方创造价值 开始。在第一部分中,我们介绍了 WMS 场景,探讨了补货问题,并讨论了 AI 智能体在哪些地方能够增加价值,而又不取代确定性的应用逻辑。

在第二部分中,我们将从设计进入实现的第一步。

我们将了解如何使用 Java 与 Spring AI 构建智能体,如何定义它的能力与边界,WMS 如何触发它,以及规划器如何将智能体的目标变成执行计划。

第 3 部分 中,我们将从该计划出发,了解智能体如何使用受控工具来执行任务、收集所需上下文、做出补货决策,并在必要时采取行动。

智能体 WMS 的在线版本可以在这里访问,完整的源代码可以在这里获取。

设计智能体

从现在开始,我们将重点介绍使用 Java 与 Spring AI 实现该智能体的主要部分。我们不会从零开始介绍 Spring AI,而是聚焦与本部分智能体直接相关的组件:提示词、能力、触发和规划。关于 Spring AI 的更全面介绍,请参阅我同事 Tim Kelly 撰写的这篇文章

第 1 部分中,我们设定了一条重要边界:WMS 定义智能体被允许做什么,而智能体在这些边界内决定做什么。现在,我们可以把这个设计转化为代码。

在深入实现之前,下面的图总结了第 2 部分和第 3 部分的完整智能体流程。本部分将重点介绍 定义智能体、触发执行和创建计划

[LOADING...]

定义智能体

智能体在出库操作完成后被触发,目标是判断该操作是否产生了补货需求。

第一步是定义智能体应该如何表现,以及允许它做什么。

在我们的实现中,智能体围绕三个主要组件组织:

  1. planner(规划器)
  2. executor(执行器)
  3. reporter(汇报器)

planner(规划器)接收目标,并确定为达成目标所需的任务:

$ java
private static final String PLANNER_PROMPT = """
        - You are the planning agent for a WMS.
        - Create a short execution plan for the provided goal using the available capabilities.
        - Do not execute tasks, call tools or make business decisions.
        - Assign exactly one capability to each task.
        """;

executor(执行器)每次只接收一个任务,并使用该任务可用的工具来执行任务:

$ java
private static final String EXECUTOR_PROMPT = """
        - You are an AI agent specialized in Warehouse Management Systems.
        - Analyze inventory after outbound operations and anticipate replenishment needs.
        - Execute only the task you receive, using its available tools.
        - Apply the depositor policies and avoid unnecessary replenishments.
        """;

最后,reporter(汇报器)对执行期间发生的情况进行总结:

$ java
private static final String REPORTER_PROMPT = """
        - Summarize the outcome of the agent execution.
        - Explain what was decided and why.
        - Do not repeat the task list.
        """;

这些提示词是完整应用中使用提示词的简化版本,但它们展示了每个组件的职责。这些组件共同构成了我们的智能体定义:

$ java
public record AgentDefinition(
        String trigger,
        String goal,
        ChatClient planner,
        ChatClient executor,
        ChatClient reporter,
        List<AgentCapability> capabilities
) {}

该定义中还包含智能体可用的 能力。在我们的补货工作流中,共有五种能力:

  • 分析(Analysis):访问发票、库存和库存移动记录。
  • 策略(Policy):获取货主策略。
  • 决策(Decision):判断是否需要进行补货。
  • 补货(Replenishment):创建补货请求。
  • 通知(Notification):起草相关邮件。

每种能力都定义了操作类型,以及执行该操作时可以使用的工具:

$ java
public record AgentCapability(
        String name,
        String description,
        List<Object> tools
) {}

这样就为每个任务建立了一个清晰的边界。例如,ANALYSIS 任务可以访问库存和库存移动工具,但不能创建补货请求;DECISION 任务不暴露任何工具,只使用前面任务收集到的结果。

最后,当定义补货智能体时,我们可以把这些部分组合在一起:

$ java
return new AgentDefinition(
        "OUTBOUND_INVOICE_COMPLETED",
        "Outbound invoice %s has just been completed.",
        planner,
        executor,
        reporter,
        List.of(
                new AgentCapability(
                        "ANALYSIS",
                        "Read inventory, stock movements and invoice movements.",
                        List.of(inventoryAnalysisTool)
                ),
                new AgentCapability(
                        "DECISION",
                        "Decide whether replenishment is required.",
                        List.of()
                ),
                new AgentCapability(
                        "REPLENISHMENT",
                        "Create a replenishment request.",
                        List.of(replenishmentTool)
                )
                // Other capabilities: POLICY and NOTIFICATION
        )
);

至此,我们已经定义了 智能体可以做什么,以及这些操作周围的边界。在查看计划是如何创建之前,先来看看智能体执行是如何启动的。

触发智能体

智能体会在出库操作成功完成后启动。一旦事务提交,应用会发布 OUTBOUND_INVOICE_COMPLETED 事件:

$ java
public void execute(String invoiceNumber) {
    // registerStock()
    // saveOutboundInvoice()
    eventPublisher.publishEvent(new OutboundInvoiceCompleted(number));
}

监听器收到该事件后,以出库发票号作为引用启动智能体:

$ java
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOutboundInvoiceCompleted(OutboundInvoiceCompleted event) {
    agentRunner.run(
        replenishmentAgentDefinition,
        event.invoiceNumber()
    );
}

使用 AFTER_COMMIT 可以确保只有在出库操作已经持久化,并且库存已经反映最新状态时,智能体才会启动。之后,智能体收到目标,并开始规划如何完成它。

计划

智能体被触发后,AgentRunner 的首要职责是创建执行计划。

应用不会预先定义任务列表。相反,规划器会收到 目标 以及 智能体可用的能力,并确定执行该目标所需的任务。

每个计划任务包含两条信息:需要做什么,以及该任务应由哪种能力负责:

$ java
public record PlannedTask(String description, String capability) {}

基于这些信息,规划器会生成一个有序的任务列表:

$ java
private List<AgentRun.AgentTask> plan(
        AgentDefinition definition,
        String goal
) {
    List<PlannedTask> planned = definition.planner()
            .prompt()
            .user("""
                    %s
                    Available capabilities:
                    %s
                    Create the execution plan required to accomplish this goal.
                    Assign exactly one capability to each task.
                    """.formatted(goal, capabilityCatalog(definition)))
            .call()
            .entity(new ParameterizedTypeReference<>() {});

    return planned.stream()
            .map(task -> new AgentRun.AgentTask(
                    task.description(),
                    AgentRun.TaskStatus.PENDING,
                    task.capability(),
                    null,
                    null,
                    null
            ))
            .toList();
}

在这里,Spring AI 通过结构化输出将模型响应直接映射到 PlannedTask 对象,使应用获得类型化的执行计划,而无需手动解析生成的文本。

一旦规划器返回任务,我们就会把执行的初始状态以 AgentRun 形式持久化到 MongoDB。

此时,执行状态被标记为 RUNNING,规划器生成的每个任务都以 PENDING 状态开始:

$ java
String goal = definition.goal().formatted(reference);

List<AgentRun.AgentTask> tasks =
        new ArrayList<>(plan(definition, goal));

AgentRun agentRun = agentRunService.save(new AgentRun(
        null,
        definition.trigger(),
        reference,
        AgentRun.Status.RUNNING,
        null,
        LocalDateTime.now(),
        null,
        List.copyOf(tasks)
));

这样,我们从一开始就拥有了执行的持久化表示,包括已经规划好的任务。此时,文档可能如下所示:

{
  _id: ObjectId('6a8f5973315f4506564a6f5b'),
  trigger: 'OUTBOUND_INVOICE_COMPLETED',
  reference: 'NF-OUT-01',
  status: 'RUNNING',
  startedAt: ISODate('2026-08-27T17:04:03.350Z'),
  tasks: [
    {
      description: 'Identify the products affected by the outbound invoice.',
      status: 'PENDING',
      capability: 'ANALYSIS'
    },
    {
      description: 'Check the current inventory of the affected products.',
      status: 'PENDING',
      capability: 'ANALYSIS'
    },
    {
      description: 'Analyze recent stock movements and consumption.',
      status: 'PENDING',
      capability: 'ANALYSIS'
    },
    {
      description: 'Read the replenishment policies for the depositor.',
      status: 'PENDING',
      capability: 'POLICY'
    },
    {
      description: 'Determine whether replenishment is required.',
      status: 'PENDING',
      capability: 'DECISION'
    },
    {
      description: 'Create a replenishment request if required.',
      status: 'PENDING',
      capability: 'REPLENISHMENT'
    },
    {
      description: 'Draft the notification if a request was created.',
      status: 'PENDING',
      capability: 'NOTIFICATION'
    }
  ]
}

计划现已就绪,执行也已完成登记。

结论

在第二部分中,我们将智能体设计落实为实现的最初几个部分。我们定义了它的职责与能力,将其接入 WMS 工作流,并使用规划器把智能体目标转化成了可持久化的执行计划。

此时,智能体已经知道需要做什么,也知道了每个任务由哪种能力负责。在第 3 部分中,我们将关注如何执行该计划:使用受控工具、收集运营与业务上下文、做出补货决策,并在必要时采取行动。

这篇构建智能体 WMS —— 第二部分:Java 与 Spring AI 的文章最初发布在 foojay 上。