Spring AI 2.0 让 Java 应用更容易构建 Agent,Spring Modulith 2.1 则提醒我们:Agent 的动作最终还是业务动作。业务动作需要事务、领域事件、Outbox、补偿、审计、模块测试和观测。

今天的核心结论很简单:不要让 Agent Tool 成为绕过业务系统的后门。让它调用受控命令,让命令发布领域事件,让事件发布注册表和 Outbox 管住后续副作用。这样 Agent 才能从“会调用工具”走向“能进入生产业务系统”。

分享日期:2026-06-17
主题:Java / Spring Modulith 2.1.0 / Spring Boot 4.1 / Spring AI 2.0 / Agent 动作治理 / Outbox / 事件发布注册表 / 可观测性
版本背景:截至 2026-06-17,Spring 官方参考文档显示 Spring Modulith 2.1.0、Spring Boot 4.1.0、Spring AI 2.0.0 均为 Stable 入口

1. 为什么今天值得关注

昨天我们看了 Spring AI 2.0 GA 的 Agent 治理边界:模型负责推理,应用负责执行,平台负责治理。今天更值得往下一层看:当 Agent 真的触发业务动作时,这个动作应该落在哪里?

一个成熟的 Java Agent 应用,不应该只把工具调用写成一段普通方法,然后在日志里留一句“模型调用成功”。只要动作会影响订单、库存、审批、支付、合同、工单、消息通知或外部 SaaS,它就必须进入业务系统熟悉的事务、事件、补偿、审计和观测链路。

Spring Modulith 2.1.0 正好把这个话题推到台前。官方文档显示,2.1.0 已经是稳定文档入口,并且在应用事件、事件发布注册表、事件外部化、Outbox、模块测试、Actuator 和 Micrometer 观测上形成了一条很完整的路线。

一句话:Spring AI 让 Agent 能调用工具,Spring Modulith 让这些工具调用变成可追踪、可恢复、可测试的业务事件。

2. 版本坐标:Agent 不能绕过领域模型

今天的技术组合可以这样理解:

  • Spring AI 2.0.0:提供 ChatClient、Tool Calling、MCP、RAG、记忆和模型观测。
  • Spring Modulith 2.1.0:提供应用模块边界、领域事件、事件发布注册表、事件外部化、Outbox、模块测试和模块观测。
  • Spring Boot 4.1.0:提供自动配置、Actuator、Micrometer、事务、数据访问、消息、配置和运行底座。
  • Spring Cloud:在多服务场景里负责 Gateway、Config、Vault、CircuitBreaker、Stream、Kubernetes 等分布式治理。

如果把 Agent 看成一个更聪明的 Controller,很容易写出这种结构:

用户问题 -> LLM -> Tool -> 直接改订单 / 调库存 / 发审批 / 发消息

这条链路短,但问题很明显:

  • 工具调用和普通业务命令没有统一入口。
  • 成功和失败只存在于一次模型交互里,缺少可恢复的业务状态。
  • 外部消息发送失败时,很难知道是否应该重试。
  • Agent 误判、重复调用或超时重试时,幂等边界不清楚。
  • 审计只能看到“模型说了什么”,看不到“业务发生了什么”。

更稳的结构应该是:

用户问题 -> LLM -> Tool -> 业务命令 -> 领域事件 -> 监听器 / Outbox / 外部消息

Agent 的工具层只负责把模型意图翻译成受控业务命令。真正的业务状态变化仍然发生在领域服务里,并通过领域事件向其他模块或外部系统扩散。

3. Spring Modulith 2.1 这次最适合 Agent 的点

从 Agent 工程角度看,Spring Modulith 2.1.0 有五个点最值得关注:

  1. 模块边界验证:用 ApplicationModules.of(...).verify() 检查模块之间是否存在循环依赖、非法内部包访问和未允许的模块依赖。
  2. 应用事件协作:模块之间优先通过领域事件交互,减少直接 Bean 调用造成的耦合。
  3. 事件发布注册表:事件发布会写入持久化记录,监听器成功后再标记完成,失败时可以保留待处理记录。
  4. Outbox 外部化:2.1 文档新增了通过 Namastack Outbox 和 JobRunr 支持更高级 Outbox 能力的说明。
  5. 生产级观测spring-modulith-starter-insight 可以暴露模块结构 Actuator 端点,并对模块调用和事件发布注册 Micrometer 指标与 trace。

这套能力对 Agent 很关键。Agent 不稳定的地方不是“能不能调用 Java 方法”,而是“模型驱动的动作能不能在普通业务系统里被治理”。Spring Modulith 的价值,就是让 Agent 动作回到模块化单体和微服务都能理解的事件模型里。

4. 推荐架构:Agent 工具只发业务意图

flowchart LR
    User[用户 / 业务系统] --> Gateway[Spring Cloud Gateway]
    Gateway --> AgentApi[Spring Boot Agent API]
    AgentApi --> ChatClient[Spring AI ChatClient]
    ChatClient --> Tool[受控 Tool]
    Tool --> Command[业务命令服务]
    Command --> Aggregate[领域聚合]
    Aggregate --> Event[领域事件]
    Event --> Registry[(Event Publication Registry)]
    Registry --> Listener[@ApplicationModuleListener]
    Registry --> Outbox[Outbox]
    Outbox --> Broker[Kafka / RabbitMQ / JMS]
    Listener --> LocalModule[本地业务模块]
    Broker --> External[外部服务 / 数据平台 / 通知系统]
    Registry --> Metrics[Actuator / Micrometer / Trace]

这张图的核心原则是:

  • Tool 不直接拼 SQL、不直接调用外部系统、不直接绕过权限模型。
  • Tool 把模型意图转成业务命令,例如“申请退款”“创建审批草稿”“同步库存校验任务”。
  • 业务命令服务负责鉴权、租户隔离、参数校验、幂等和事务。
  • 领域事件表达已经发生或已经受理的业务事实。
  • 事件监听器处理模块内或模块间协作。
  • Outbox 负责把需要外部化的事件可靠发送到消息系统。
  • 事件发布注册表负责记录哪些事件待处理、处理中、已完成或失败。

这比“Tool 里直接调用三个外部 API”慢一点,但可运维性完全不是一个级别。

5. 最小依赖组合

如果项目已经是 Spring Boot 应用,可以先引入 Spring Modulith BOM:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.modulith</groupId>
            <artifactId>spring-modulith-bom</artifactId>
            <version>2.1.0</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

按能力选择 starter:

<!-- 核心模块模型、事件注册表和 JDBC 持久化 -->
<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-starter-jdbc</artifactId>
</dependency>

<!-- 生产观测:Actuator + 模块结构 + Micrometer 观测 -->
<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-starter-insight</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- 模块级集成测试 -->
<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-starter-test</artifactId>
    <scope>test</scope>
</dependency>

如果要把事件发到消息中间件,再加对应外部化模块:

<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-events-kafka</artifactId>
</dependency>

如果要启用 2.1 文档里提到的高级 Outbox 外部化,可以评估:

<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-starter-namastack</artifactId>
</dependency>

或:

<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-starter-jobrunr</artifactId>
</dependency>

普通项目不需要一开始全量引入。建议从 starter-jdbc + starter-insight + starter-test 开始,把事件记录、模块观测和模块测试先跑起来。

6. 建模:把 Agent 动作拆成命令和事件

先定义一个业务命令。注意,这不是模型输出的原始 JSON,而是应用内部已经校验过的命令对象:

import java.math.BigDecimal;
import java.util.UUID;

public record RefundApprovalCommand(
        String tenantId,
        String operatorId,
        String orderId,
        BigDecimal amount,
        String reason,
        UUID requestId) {
}

再定义一个领域事件。事件应该表达业务事实,而不是表达技术动作:

import java.math.BigDecimal;
import java.time.Instant;
import java.util.UUID;

import org.springframework.modulith.events.Externalized;

@Externalized("refund-approval-requested::#{#this.tenantId()}")
public record RefundApprovalRequested(
        String tenantId,
        String orderId,
        BigDecimal amount,
        String reason,
        String requestedBy,
        UUID requestId,
        Instant occurredAt) {
}

这里的 @Externalized 表示这个事件可以被外部化到消息基础设施。refund-approval-requested 是逻辑目标,tenantId 可以作为路由键,方便下游按租户分区或过滤。

关键点:

  • 命令可以失败,事件必须表达已经被系统接受或已经发生的事实。
  • requestId 用于幂等,不要依赖模型“不会重复调用”。
  • tenantId 和 requestedBy 必须来自认证上下文,不要直接信任模型参数。
  • 事件里不要放完整提示词、敏感字段或超长文本。

7. Tool 层:不要直接执行高风险副作用

下面是一个故意克制的 Tool 示例。模型可以请求“申请退款”,但工具只创建受控业务命令,不直接退款:

import java.math.BigDecimal;
import java.util.UUID;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

@Component
class RefundAgentTools {

    private final RefundApplicationService refunds;
    private final CurrentUser currentUser;

    RefundAgentTools(RefundApplicationService refunds, CurrentUser currentUser) {
        this.refunds = refunds;
        this.currentUser = currentUser;
    }

    @Tool(description = "Create a refund approval request for the current authenticated tenant. Do not use it for direct refund execution.")
    RefundToolResult requestRefundApproval(
            @ToolParam(description = "Business order id") String orderId,
            @ToolParam(description = "Refund amount requested by the user") BigDecimal amount,
            @ToolParam(description = "Short business reason") String reason) {

        var command = new RefundApprovalCommand(
                currentUser.tenantId(),
                currentUser.userId(),
                orderId,
                amount,
                reason,
                UUID.randomUUID());

        var request = refunds.requestApproval(command);

        return new RefundToolResult(
                request.requestId(),
                "REFUND_APPROVAL_REQUESTED",
                "Refund approval request has been created and is waiting for workflow confirmation.");
    }
}

record RefundToolResult(UUID requestId, String status, String message) {
}

生产环境里,Tool 层应该遵守几条底线:

  • 工具描述只说明适用场景,不替代权限校验。
  • 高风险动作默认创建申请、草稿或待确认任务。
  • 当前用户、租户、角色从安全上下文读取,不从模型参数读取。
  • 每次工具调用都生成或接收幂等键。
  • Tool 的返回值应该是业务状态,不应该泄露内部异常和敏感字段。

8. 应用服务:在事务里发布领域事件

业务应用服务负责真正的校验、持久化和事件发布:

import java.time.Clock;
import java.time.Instant;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
class RefundApplicationService {

    private final RefundApprovalRepository approvals;
    private final OrderPolicy orderPolicy;
    private final ApplicationEventPublisher events;
    private final Clock clock;

    RefundApplicationService(
            RefundApprovalRepository approvals,
            OrderPolicy orderPolicy,
            ApplicationEventPublisher events,
            Clock clock) {

        this.approvals = approvals;
        this.orderPolicy = orderPolicy;
        this.events = events;
        this.clock = clock;
    }

    @Transactional
    RefundApproval requestApproval(RefundApprovalCommand command) {
        orderPolicy.checkRefundAllowed(command.tenantId(), command.operatorId(), command.orderId(), command.amount());

        var approval = RefundApproval.requested(
                command.tenantId(),
                command.orderId(),
                command.amount(),
                command.reason(),
                command.operatorId(),
                command.requestId());

        approvals.saveIfAbsent(approval);

        events.publishEvent(new RefundApprovalRequested(
                command.tenantId(),
                command.orderId(),
                command.amount(),
                command.reason(),
                command.operatorId(),
                command.requestId(),
                Instant.now(clock)));

        return approval;
    }
}

这个服务有几个重要边界:

  • @Transactional 覆盖业务状态写入和事件发布注册。
  • saveIfAbsent 或唯一约束保证重复请求不会创建多条审批。
  • 事件发布发生在业务校验和状态变更之后。
  • 下游监听器失败,不应该回滚这个“审批已创建”的事实。

Spring Modulith 的事件发布注册表会把监听器执行纳入可恢复记录。也就是说,不是“发了一个 Spring 事件就算完”,而是有持久化的事件发布状态可以管理。

9. 模块监听器:处理内部协作

模块内或模块间协作可以用 @ApplicationModuleListener

import org.springframework.modulith.events.ApplicationModuleListener;
import org.springframework.stereotype.Component;

@Component
class RefundWorkflowListener {

    private final WorkflowClient workflowClient;

    RefundWorkflowListener(WorkflowClient workflowClient) {
        this.workflowClient = workflowClient;
    }

    @ApplicationModuleListener
    void on(RefundApprovalRequested event) {
        workflowClient.createApprovalTask(
                event.tenantId(),
                event.requestId(),
                event.orderId(),
                event.amount(),
                event.reason());
    }
}

@ApplicationModuleListener 可以理解为 Spring Modulith 对常见异步事务监听模式的简化封装。它适合处理“主业务已经完成,后续动作可以异步执行”的场景,例如:

  • 创建审批任务。
  • 通知风控模块。
  • 写入审计摘要。
  • 推送站内信。
  • 触发异步补数或缓存刷新。

但要注意,监听器仍然是业务代码。它需要幂等、超时、错误分类和可观测性,不应该因为是异步就放松治理。

10. Outbox:外部消息不能靠一次内存回调

Agent 动作经常需要通知外部系统,例如数据平台、消息中心、CRM、工单系统或 BI 流水。如果只在监听器里直接调用 Kafka、RabbitMQ 或外部 HTTP,一旦进程崩溃、网络抖动或 broker 短暂不可用,就会出现业务状态已提交但外部消息丢失的问题。

Spring Modulith 的事件外部化机制正是为这个问题准备的。官方文档说明,事件外部化会选择需要发布的事件、准备消息、确定路由目标,然后交给对应 broker 基础设施。事件发布注册表会保护外部化过程,失败后可以通过 API 重提。

基础配置示例:

spring:
  modulith:
    events:
      externalization:
        enabled: true
        serialize-externalization: true
      completion-mode: archive
      staleness:
        processing: 2m
        published: 2m
        resubmitted: 5m
        check-interval: 30s

如果引入 2.1 文档里的 Namastack 或 JobRunr Outbox 支持,可以把外部化模式切到 outbox

spring:
  modulith:
    events:
      externalization:
        mode: outbox

建议这样分层选择:

场景 推荐方式 原因
模块内异步协作 @ApplicationModuleListener 简单、贴近 Spring 事件模型
普通 broker 外部化 Spring Modulith events Kafka/AMQP/JMS 够用、接入成本低
高可靠外部消息 Outbox 模式 更适合强恢复、批量重试和运维治理
高风险业务动作 审批流 + Outbox + 人工确认 模型不能直接完成最终副作用

不要把 Outbox 理解成“消息发送更稳一点”。它真正解决的是事务边界:业务状态和待发送消息必须先在同一侧形成可恢复记录,然后再异步投递。

11. 事件发布生命周期:Agent 场景要重点看失败和重提

Spring Modulith 2.0 之后,事件发布有更明确的生命周期状态,例如待处理、处理中、完成、失败、重提。2.1 继续沿用这套机制。对 Agent 场景来说,最重要的不是成功路径,而是以下几类异常路径:

  • 模型重复触发同一个工具。
  • Tool 成功写入业务状态,但监听器执行失败。
  • 外部 broker 不可用。
  • 应用在监听器处理中崩溃。
  • 某个事件卡在处理中状态。
  • 下游系统恢复后需要批量重投。

建议至少配置三类策略:

  1. 幂等策略:业务命令有 requestId,核心表有唯一约束。
  2. 失效检测:设置 staleness,让卡住的发布记录可以被标记为失败。
  3. 重提策略:使用 FailedEventPublications 或 IncompleteEventPublications 做受控重提,不要无限重试。

示例方向:

import java.time.Duration;

import org.springframework.modulith.events.core.FailedEventPublications;
import org.springframework.modulith.events.core.ResubmissionOptions;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
class FailedPublicationResubmitter {

    private final FailedEventPublications failed;

    FailedPublicationResubmitter(FailedEventPublications failed) {
        this.failed = failed;
    }

    @Scheduled(fixedDelayString = "PT1M")
    void resubmitOldFailures() {
        var options = ResubmissionOptions.defaults()
                .withBatchSize(50)
                .withMinAge(Duration.ofMinutes(2))
                .withFilter(publication -> publication.getCompletionAttempts() < 5);

        failed.resubmit(options);
    }
}

这类任务要加运维开关。线上出现下游系统大面积故障时,盲目自动重提可能把故障放大。

12. 可观测性:看见模块、事件和 Agent 工具链路

spring-modulith-starter-insight 会带来两个很实用的能力:

  • 通过 Actuator 暴露应用模块结构。
  • 通过 Micrometer 对模块调用和事件发布建立指标与 trace。

对于 Agent 应用,建议把三类观测串起来:

AI 调用观测:model、promptHash、toolName、tokenUsage、duration
业务事件观测:eventType、module、requestId、tenantId、status、attempts
外部化观测:broker、topic、routingKey、publicationStatus、resubmissionCount

日志字段示例:

{
  "requestId": "63ef5a7d-0bb9-40cf-bf0c-6bb2b3f5c938",
  "tenantId": "t001",
  "agentId": "support-agent",
  "toolName": "requestRefundApproval",
  "module": "refund",
  "eventType": "RefundApprovalRequested",
  "publicationStatus": "PUBLISHED",
  "completionAttempts": 1,
  "externalizedTarget": "refund-approval-requested",
  "routingKey": "t001"
}

注意不要在日志里写完整 prompt、完整用户输入、身份证号、手机号、银行卡号、合同正文或工具原始参数。建议写 hash、业务 ID、状态和低基数字段。

13. 模块边界验证:把架构约束放进测试

Agent 应用容易在快速迭代中变成“大一统工具箱”。今天一个 Tool 调订单,明天另一个 Tool 调库存,后天为了省事直接注入审批模块内部 Bean。几轮之后,模块边界会被模型工具层悄悄打穿。

Spring Modulith 的结构验证应该放进常规测试:

import org.junit.jupiter.api.Test;
import org.springframework.modulith.core.ApplicationModules;

class ModulithStructureTests {

    @Test
    void verifiesModuleBoundaries() {
        ApplicationModules.of(Application.class).verify();
    }
}

它会检查典型问题:

  • 模块之间不能形成循环依赖。
  • 其他模块不能访问当前模块的内部包。
  • 如果声明了允许依赖,未允许的依赖会被拒绝。

对 Agent 项目,建议新增一条团队规则:Tool 所在模块只能依赖领域模块公开 API,不能依赖内部实现类。

14. 模块测试:用 Scenario 测异步事件链路

Agent 动作的测试不能只 mock 模型返回。真正要测的是:工具调用之后,业务命令是否落库,领域事件是否发布,监听器是否完成,下游状态是否符合预期。

Spring Modulith 的 @ApplicationModuleTest 可以只启动当前模块或依赖模块,避免每个测试都全量跑一个大应用:

import org.junit.jupiter.api.Test;
import org.springframework.modulith.test.ApplicationModuleTest;
import org.springframework.modulith.test.Scenario;

@ApplicationModuleTest
class RefundModuleTests {

    @Test
    void createsWorkflowTaskAfterRefundApprovalRequested(Scenario scenario) {
        var event = new RefundApprovalRequested(
                "t001",
                "ORD-1001",
                new BigDecimal("99.00"),
                "duplicate payment",
                "u001",
                UUID.randomUUID(),
                Instant.now());

        scenario.publish(event)
                .andWaitForStateChange(() -> workflowTasks.findByRequestId(event.requestId()))
                .andVerify(task -> assertThat(task.status()).isEqualTo("WAITING_APPROVAL"));
    }
}

这类测试对 Agent 特别有价值,因为它不依赖真实 LLM。你可以把模型输出固定成工具调用输入,然后验证业务事件链路是否正确。

建议覆盖这些用例:

  • 同一个 requestId 重复调用只创建一条业务记录。
  • 监听器失败后事件发布记录保留为未完成或失败。
  • 下游恢复后可以重提。
  • 高风险动作只创建审批,不直接执行最终副作用。
  • 非授权用户即使让模型调用工具,也不能越权创建业务命令。

15. 和 Spring Cloud 的关系:本地事件不等于架构退回单体

Spring Modulith 很适合模块化单体,但它不和 Spring Cloud 冲突。更准确地说,它可以帮助团队先把服务内部边界理清,再决定哪些能力真正需要拆成微服务。

在 Agent 平台里,可以这样分工:

  • Spring Cloud Gateway:统一入口、鉴权、租户、限流和审计。
  • Agent Orchestrator:承载 Spring AI ChatClient、工具注册和工具选择。
  • 业务 Modulith 应用:承载领域命令、领域事件、Outbox 和模块测试。
  • Spring Cloud Stream / Kafka:接收外部化后的业务事件。
  • Config / Vault:管理模型开关、工具开关、外部化目标、密钥和降级策略。
  • CircuitBreaker:保护外部 SaaS、MCP Server、broker 和工作流系统。

不要因为用了 Agent 就绕开微服务体系,也不要因为用了 Spring Cloud 就把所有内部协作都拆成远程调用。一个更稳的原则是:

进程内强一致:领域命令 + 本地事务 + 应用事件
跨系统最终一致:事件发布注册表 + Outbox + 消息中间件
平台级治理:Gateway + Config + Vault + Observability

16. 迁移建议:从高价值低风险动作开始

如果团队已经有 Spring AI Tool Calling,可以按这个顺序改造:

  1. 盘点工具:把工具分成只读、低风险写入、高风险写入、外部网络访问。
  2. 给写入工具补命令对象:不要让 Tool 直接改状态。
  3. 给命令补幂等键:至少使用 requestId 或业务自然键。
  4. 发布领域事件:把后续动作从主事务里拆出来。
  5. 启用事件发布注册表:使用 JDBC/JPA/MongoDB 等持久化方案。
  6. 外部消息走事件外部化:Kafka、AMQP、JMS 或 Outbox。
  7. 补模块边界测试:防止工具层打穿模块内部包。
  8. 补模块事件测试:用 Scenario 覆盖异步链路。
  9. 补观测字段:把 toolName、eventType、requestId、publicationStatus 串起来。
  10. 配置降级开关:能按工具、事件类型、外部化目标关闭。

不建议第一步就改所有 Tool。优先选择“高价值、低风险、需要审计”的动作,例如:

  • 创建审批草稿。
  • 创建工单。
  • 发起库存校验任务。
  • 创建客户跟进计划。
  • 生成待确认报价。
  • 创建知识库更新申请。

这些动作即使模型误判,也不会直接造成不可逆副作用,但能验证 Agent 到业务事件的整条链路。

17. 常见误区

误区一:Tool 调用成功就等于业务成功。
Tool 成功只说明模型到应用的入口成功。业务是否成功,要看命令是否提交、事件是否发布、监听器是否完成、外部化是否成功。

误区二:事件发布就是消息发送。
领域事件首先是进程内业务事实。需要跨系统传播时,再通过事件外部化或 Outbox 发送到 broker。

误区三:Outbox 可以替代幂等。
Outbox 提高投递可靠性,但下游仍然可能收到重复消息。业务 ID、事件 ID 和消费幂等仍然必须做。

误区四:模块化单体不需要观测。
Agent 动作的复杂度不一定来自服务数量,而是来自模型、工具、事件、监听器和外部系统的组合。进程内也需要 trace 和指标。

误区五:让模型决定事件类型。
模型可以选择工具,但事件类型应该由应用代码决定。不要把模型生成的字符串直接当业务事件或消息路由。

18. 今天可以直接落地的实践建议

如果你正在做第一个 Java Agent:

  • 先只开放只读工具和“创建草稿/申请”的低风险工具。
  • 每个写入 Tool 都必须调用应用服务,不直接操作 Repository。
  • 每个写入动作都生成 requestId
  • 每个业务动作都发布领域事件。

如果你已经有 Spring AI 应用:

  • 找出所有有副作用的 Tool,检查是否绕过了业务服务。
  • 把高风险 Tool 改成“生成待确认任务”。
  • 给工具调用链路补 toolName + requestId + tenantId + eventType
  • 把外部消息发送从 Tool 或普通 listener 里移到事件外部化。

如果你在做企业级 Agent 平台:

  • 把工具注册、权限、审计、事件类型和 Outbox 目标都纳入平台配置。
  • 用 Spring Modulith 测试业务模块边界,用 Spring Cloud 管跨服务治理。
  • 建立失败事件看板,按事件类型、租户、监听器和失败原因聚合。
  • 给重提任务增加批量大小、最小失败时间、最大尝试次数和人工暂停开关。

19. 参考资料

  • Spring Modulith Reference:https://docs.spring.io/spring-modulith/reference/
  • Spring Modulith - Working with Application Events:https://docs.spring.io/spring-modulith/reference/events.html
  • Spring Modulith - Production-ready Features:https://docs.spring.io/spring-modulith/reference/production-ready.html
  • Spring Modulith - Verifying Application Module Structure:https://docs.spring.io/spring-modulith/reference/verification.html
  • Spring Modulith - Integration Testing Application Modules:https://docs.spring.io/spring-modulith/reference/testing.html
  • Spring Boot Reference:https://docs.spring.io/spring-boot/index.html
  • Spring AI Reference:https://docs.spring.io/spring-ai/reference/
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐