目录:
1. 面向生产环境的智能体工程平台
2. 快速上手 从零构建生产级智能体
3. Agent —— 智能体的核心抽象与工程化实践
4. Message & Event —— 消息模型与事件流深度解
5. Middleware —— 无侵入式智能体扩展机制深度解析
6. Model —— 统一模型接入层与容错机制深度解析
7. Permission System —— 权限控制系统深度解析
8. Tool —— 工具系统架构与生产级实践深度解析
9. Context —— 运行时上下文与状态管理深度解析

一、引言:为什么需要 Middleware?

在智能体生产化的过程中,开发者面临的典型需求是:

“我们想给 Agent 加上日志埋点、限流、Token 计费、敏感词过滤……这些需求有一个共同点:在 Agent 干活的特定时刻插一段自己的代码。”

AgentScope Java 1.x 用 Hook 实现这一需求,但扁平的 Hook 机制缺乏阶段划分、组合困难。2.0 用更通用的 Middleware(中间件) 全面取代 Hook,提供了结构化、可组合、无侵入的扩展机制。

Agent Middleware 是无侵入扩展机制:不改动 Agent、Model 源码,在执行链路关键节点插入横切逻辑。

典型场景包括:链路追踪、日志埋点、输入改写、权限拦截、限流、动态提示词、异常降级等。

二、核心设计:两类中间件模型

AgentScope Java 2.0 划分了两类中间件模型,覆盖不同的扩展需求:

2.1 Onion 洋葱模型

洋葱模型(Onion Model)是中间件执行的核心逻辑模式。请求先逐层穿过外层中间件到达核心逻辑,再反向逐层穿过外层中间件返回,形似洋葱的层级结构:

请求进入
  │
  ▼
┌─────────────────────────────────────────┐
│  Middleware A (进入)                     │
│  ┌───────────────────────────────────┐  │
│  │  Middleware B (进入)               │  │
│  │  ┌───────────────────────────┐     │  │
│  │  │    核心逻辑 (Core)         │     │  │
│  │  └───────────────────────────┘     │  │
│  │  Middleware B (离开)                │  │
│  └───────────────────────────────────┘  │
│  Middleware A (离开)                     │
└─────────────────────────────────────────┘
  │
  ▼
响应返回

**执行顺序:**进入 A → 进入 B → 执行 Core → 离开 B → 离开 A
适用场景:

  • 链路追踪(进入时开 Span,离开时关 Span)
  • 计时统计(进入时记录开始时间,离开时计算耗时)
  • 异常兜底(进入时 try,离开时 catch)
  • 优雅关闭(进入时注册,离开时清理)

2.2 Transformer 变换模型

变换模型(Transformer)关注的是数据流的改写:中间件接收输入数据,进行变换后传递给下一层:

Input → [Middleware A 变换] → [Middleware B 变换] → Core → Output

适用场景:

  • 动态注入上下文(在 System Prompt 中追加时间、角色信息)
  • 敏感词过滤(对输入/输出进行文本替换)
  • 参数校验与改写(对工具调用参数进行规范化)
  • 动态技能注入(根据用户身份注入不同的工具描述)

2.3 两类模型对比

维度Onion 洋葱型Transformer 变换型
核心思想包裹(Wrap)变换(Transform)
数据流穿透后原路返回单向流过,逐层修改
典型用途追踪、计时、异常处理输入改写、过滤、注入
类比Koa.js 中间件 / Servlet FilterUnix 管道 / Map 函数
能否中断可以(不调用 next)可以(返回空/异常)

三、五个 Hook 挂载点位

AgentScope Java 2.0 将 Agent 执行生命周期划分为五个关键节点,每个节点前后都可以插入 Middleware:

agent.call(msg, rt)
    │
    ① onAgent          ← 整轮调用的起点
    │
    ② onSystemPrompt   ← 系统提示词拼好之后、发给 LLM 之前
    │
    ③ onReasoning      ← LLM 做推理、吐文字
    │
    ④ onActing         ← 工具调用执行
    │
    ⑤ onModelCall      ← 底层模型 API 调用
    │
    ▼
返回结果

3.1 各挂载点详解

#挂载点位置说明典型用途
onAgent整轮调用的起点/终点设置日志上下文、绑定租户信息、初始化链路追踪、限流、计时
onSystemPrompt系统提示词拼好之后、发给 LLM 之前动态注入时间/角色/业务上下文、技能描述
onReasoningLLM 推理阶段审计、敏感词检测、Token 预算检查
onActing工具调用执行阶段权限检查、参数校验、沙箱策略、审批拦截
onModelCall底层模型 API 调用日志记录、Token 计费、重试策略、模型切换

3.2 完整执行流中的 Middleware 触发时序

用户请求到达
    │
    ▼
┌── onAgent (Onion: 进入) ──────────────────────────────────────┐
│                                                                │
│   ┌── onSystemPrompt (Transformer) ──────────────────────┐    │
│   │  动态注入当前时间、用户角色、工作区摘要                    │    │
│   └──────────────────────────────────────────────────────┘    │
│                                                                │
│   ┌── onReasoning (Onion: 进入) ─────────────────────────┐    │
│   │                                                        │    │
│   │   ┌── onModelCall (Onion) ─────────────────────┐      │    │
│   │   │  记录请求、检查限流、调用 LLM API            │      │    │
│   │   └────────────────────────────────────────────┘      │    │
│   │                                                        │    │
│   └── onReasoning (Onion: 离开) ─────────────────────────┘    │
│                                                                │
│   [如果模型决定调用工具]                                         │
│   ┌── onActing (Onion) ──────────────────────────────────┐    │
│   │  权限检查 → 参数校验 → 执行工具 → 结果审计             │    │
│   └──────────────────────────────────────────────────────┘    │
│                                                                │
│   [循环回到 onReasoning 直到模型给出最终回复]                     │
│                                                                │
└── onAgent (Onion: 离开) ──────────────────────────────────────┘
    │
    ▼
返回最终响应

四、MiddlewareBase 接口

4.1 核心接口定义

MiddlewareBase 是所有中间件的基类/接口,提供五个可覆写的钩子方法:

package io.agentscope.core.middleware;

public abstract class MiddlewareBase {

    /**
     * ① Agent 整轮调用包裹(Onion 模型)
     * 在 agent.call() 的最外层执行
     */
    public Mono<Void> onAgent(AgentContext ctx, MiddlewareChain chain) {
        return chain.next(ctx);  // 默认直接穿透
    }

    /**
     * ② 系统提示词变换(Transformer 模型)
     * 在 system prompt 组装完成后、发送给模型前调用
     */
    public Mono<String> onSystemPrompt(SystemPromptContext ctx) {
        return Mono.just(ctx.getPrompt());  // 默认不变换
    }

    /**
     * ③ 推理阶段包裹(Onion 模型)
     * 在每轮 LLM 推理前后执行
     */
    public Mono<Void> onReasoning(ReasoningContext ctx, MiddlewareChain chain) {
        return chain.next(ctx);
    }

    /**
     * ④ 工具执行包裹(Onion 模型)
     * 在每次工具调用前后执行
     */
    public Mono<Void> onActing(ActingContext ctx, MiddlewareChain chain) {
        return chain.next(ctx);
    }

    /**
     * ⑤ 模型 API 调用包裹(Onion 模型)
     * 在底层 HTTP 请求前后执行
     */
    public Mono<ModelCallResponse> onModelCall(
            ModelCallRequest request, MiddlewareChain chain) {
        return chain.next(request);
    }
}

4.2 设计要点

特性说明
响应式所有方法返回 Mono,基于 Project Reactor
链式调用通过 chain.next() 传递控制权,不调用即中断
默认穿透不覆写的方法默认直接放行,零开销
可选覆写只需覆写关心的钩子,其余保持默认

4.3 注册方式

通过 Builder 注册中间件链:

ReActAgent agent = ReActAgent.builder()
    .name("my-agent")
    .model("dashscope:qwen-plus")
    .sysPrompt("你是一个助手。")
    // 注册中间件(按顺序执行)
    .middleware(new OtelTracingMiddleware())
    .middleware(new RateLimitMiddleware(100))  // 每分钟 100 次
    .middleware(new SensitiveWordFilter())
    .middleware(new DynamicSkillMiddleware())
    .build();

五、内置 Middleware 实现详解

AgentScope Java 2.0 提供了多个开箱即用的内置中间件实现:

5.1 TaskReminderMiddleware —— 任务提醒

在 onSystemPrompt 阶段动态注入当前待办任务信息:

public class TaskReminderMiddleware extends MiddlewareBase {
    
    @Override
    public Mono<String> onSystemPrompt(SystemPromptContext ctx) {
        String reminder = taskService.getActiveReminders(ctx.getUserId());
        if (reminder != null && !reminder.isEmpty()) {
            return Mono.just(ctx.getPrompt() + "\n\n[当前待办]\n" + reminder);
        }
        return Mono.just(ctx.getPrompt());
    }
}

设计意图:让 Agent 在每次推理时自动感知用户的未完成任务,无需修改 Agent 核心逻辑。

5.2 OtelTracingMiddleware —— OpenTelemetry 链路追踪

在 onAgent 和 onModelCall 阶段创建/关闭 Span:

public class OtelTracingMiddleware extends MiddlewareBase {
    
    private final Tracer tracer = GlobalOpenTelemetry.getTracer("agentscope");
    
    @Override
    public Mono<Void> onAgent(AgentContext ctx, MiddlewareChain chain) {
        Span span = tracer.spanBuilder("agent.call")
            .setAttribute("agent.name", ctx.getAgentName())
            .setAttribute("user.id", ctx.getUserId())
            .setAttribute("session.id", ctx.getSessionId())
            .startSpan();
        
        return chain.next(ctx)
            .doFinally(signal -> span.end());  // Onion: 离开时关闭
    }
    
    @Override
    public Mono<ModelCallResponse> onModelCall(
            ModelCallRequest request, MiddlewareChain chain) {
        Span span = tracer.spanBuilder("model.call")
            .setAttribute("model.name", request.getModelName())
            .startSpan();
        
        return chain.next(request)
            .doOnNext(resp -> {
                span.setAttribute("tokens.prompt", resp.getPromptTokens());
                span.setAttribute("tokens.completion", resp.getCompletionTokens());
            })
            .doFinally(signal -> span.end());
    }
}

设计意图:生产环境必备的全链路可观测性,精确追踪每次模型调用的耗时与 Token 消耗。

5.3 GracefulShutdownMiddleware —— 优雅关闭

在 onAgent 阶段注册执行状态,支持滚动发布时等待在途请求完成:

public class GracefulShutdownMiddleware extends MiddlewareBase {
    
    private final AtomicInteger inFlight = new AtomicInteger(0);
    private volatile boolean shuttingDown = false;
    
    @Override
    public Mono<Void> onAgent(AgentContext ctx, MiddlewareChain chain) {
        if (shuttingDown) {
            return Mono.error(new ServiceUnavailableException("Agent is shutting down"));
        }
        
        inFlight.incrementAndGet();
        return chain.next(ctx)
            .doFinally(signal -> inFlight.decrementAndGet());
    }
    
    public void initiateShutdown() {
        this.shuttingDown = true;
        // 等待 inFlight 归零...
    }
}

设计意图:支撑零停机滚动发布,避免在途 Agent 任务被强制中断。

5.4 DynamicSkillMiddleware —— 动态技能注入

在 onSystemPrompt 阶段根据用户角色/组织动态注入可用技能:

public class DynamicSkillMiddleware extends MiddlewareBase {
    
    private final SkillRepository skillRepo;
    
    @Override
    public Mono<String> onSystemPrompt(SystemPromptContext ctx) {
        // 根据用户组织获取可用技能
        List<Skill> skills = skillRepo.getSkillsByOrg(ctx.getOrgId());
        
        if (skills.isEmpty()) {
            return Mono.just(ctx.getPrompt());
        }
        
        StringBuilder sb = new StringBuilder(ctx.getPrompt());
        sb.append("\n\n[可用技能]\n");
        for (Skill skill : skills) {
            sb.append("- ").append(skill.getName())
              .append(": ").append(skill.getDescription()).append("\n");
        }
        
        return Mono.just(sb.toString());
    }
}

设计意图:多租户场景下,不同组织/用户看到不同的 Agent 能力集,无需为每个租户创建独立 Agent。

六、实战:自定义 Middleware 开发

6.1 日志审计中间件

public class AuditMiddleware extends MiddlewareBase {
    
    private final AuditLogService auditService;
    
    @Override
    public Mono<Void> onAgent(AgentContext ctx, MiddlewareChain chain) {
        long startTime = System.currentTimeMillis();
        
        auditService.log(AuditEvent.builder()
            .type("AGENT_CALL_START")
            .userId(ctx.getUserId())
            .sessionId(ctx.getSessionId())
            .timestamp(Instant.now())
            .build());
        
        return chain.next(ctx)
            .doFinally(signal -> {
                long duration = System.currentTimeMillis() - startTime;
                auditService.log(AuditEvent.builder()
                    .type("AGENT_CALL_END")
                    .userId(ctx.getUserId())
                    .sessionId(ctx.getSessionId())
                    .duration(duration)
                    .status(signal.toString())
                    .timestamp(Instant.now())
                    .build());
            });
    }
}

6.2 限流中间件

public class RateLimitMiddleware extends MiddlewareBase {
    
    private final RateLimiter rateLimiter;
    
    public RateLimitMiddleware(int permitsPerMinute) {
        this.rateLimiter = RateLimiter.create(permitsPerMinute / 60.0);
    }
    
    @Override
    public Mono<Void> onAgent(AgentContext ctx, MiddlewareChain chain) {
        if (!rateLimiter.tryAcquire()) {
            return Mono.error(new RateLimitExceededException(
                "请求过于频繁,请稍后重试"));
        }
        return chain.next(ctx);
    }
}

6.3 敏感词过滤中间件(Transformer 模式)

public class SensitiveWordMiddleware extends MiddlewareBase {
    
    private final SensitiveWordDetector detector;
    
    @Override
    public Mono<String> onSystemPrompt(SystemPromptContext ctx) {
        // 对输入进行敏感词检测
        String prompt = ctx.getPrompt();
        if (detector.containsSensitive(prompt)) {
            return Mono.error(new ContentPolicyException("输入包含敏感内容"));
        }
        return Mono.just(prompt);
    }
    
    @Override
    public Mono<Void> onReasoning(ReasoningContext ctx, MiddlewareChain chain) {
        return chain.next(ctx)
            .doOnNext(result -> {
                // 对模型输出进行敏感词检测
                String output = result.getTextContent();
                if (detector.containsSensitive(output)) {
                    throw new ContentPolicyException("输出包含敏感内容");
                }
            });
    }
}

6.4 Token 计费中间件

public class TokenBillingMiddleware extends MiddlewareBase {
    
    private final BillingService billingService;
    
    @Override
    public Mono<ModelCallResponse> onModelCall(
            ModelCallRequest request, MiddlewareChain chain) {
        return chain.next(request)
            .doOnNext(response -> {
                billingService.charge(BillingRecord.builder()
                    .userId(request.getUserId())
                    .modelName(request.getModelName())
                    .promptTokens(response.getPromptTokens())
                    .completionTokens(response.getCompletionTokens())
                    .timestamp(Instant.now())
                    .build());
            });
    }
}

6.5 权限二次校验中间件

public class PermissionCheckMiddleware extends MiddlewareBase {
    
    private final PermissionService permissionService;
    
    @Override
    public Mono<Void> onActing(ActingContext ctx, MiddlewareChain chain) {
        String toolName = ctx.getToolCallName();
        String userId = ctx.getUserId();
        
        // 二次校验工具调用权限
        PermissionDecision decision = permissionService.check(userId, toolName);
        
        switch (decision) {
            case ALLOW:
                return chain.next(ctx);
            case APPROVE:
                // 触发人工审批流程
                return requestApproval(ctx).then(chain.next(ctx));
            case DENY:
                return Mono.error(new PermissionDeniedException(
                    "工具 " + toolName + " 调用被拒绝"));
            default:
                return chain.next(ctx);
        }
    }
}

七、Middleware 与 Harness 的协作

7.1 Harness 层本身就是 Middleware 的叠加

官方文档明确指出:

Harness 在不改写推理循环的前提下,把这些问题各自的解法以 Middleware 与 Toolkit 的形式叠加到关键时机上,只叠加,不替换。

这意味着 HarnessAgent 的以下能力,本质上都是通过 Middleware 机制实现的:

Harness 能力对应 Middleware 挂载点模式
上下文压缩(Compaction)onReasoningOnion
记忆注入(MEMORY.md)onSystemPromptTransformer
技能加载(Skills)onSystemPromptTransformer
权限管控(Permission)onActingOnion
沙箱隔离(Sandbox)onActingOnion
Session 持久化onAgentOnion
优雅关闭onAgentOnion

7.2 执行优先级

当多个 Middleware 注册在同一挂载点时,按注册顺序执行:

.middleware(new OtelTracingMiddleware())       // 最外层:追踪
.middleware(new GracefulShutdownMiddleware())   // 次外层:优雅关闭
.middleware(new RateLimitMiddleware(100))       // 限流
.middleware(new AuditMiddleware())              // 审计
.middleware(new PermissionCheckMiddleware())    // 权限

Onion 模型下,执行顺序为:

进入: Otel → Shutdown → RateLimit → Audit → Permission → Core
离开: Permission → Audit → RateLimit → Shutdown → Otel

八、从 1.x Hook 到 2.0 Middleware 的迁移

8.1 对比总结

维度1.x Hook2.0 Middleware
结构扁平回调,无阶段区分五个明确阶段
执行模型单一(仅通知)双模型(Onion + Transformer)
数据流控制无法修改数据可拦截、修改、中断
组合方式注册顺序不明确链式组合,顺序可控
响应式支持有限完整 Mono/Flux 支持
可测试性难以单独测试每个 Middleware 可独立单测
关注点分离混杂各居其层

8.2 迁移示例

1.x Hook 写法:

// 1.x: 扁平 hook,所有逻辑混在一起
agent.registerHook(HookType.PRE_REPLY, ctx -> {
    // 日志 + 限流 + 权限... 全部堆在这里
});

2.0 Middleware 写法:

// 2.0: 关注点分离,各居其层
agent.builder()
    .middleware(new TracingMiddleware())       // onAgent
    .middleware(new RateLimitMiddleware())     // onAgent
    .middleware(new PermissionMiddleware())    // onActing
    .build();

九、设计哲学与最佳实践

9.1 核心设计原则

原则说明
无侵入不修改 Agent/Model 源码,纯外挂式扩展
单一职责每个 Middleware 只做一件事
可组合通过链式注册自由组合
可替换每个 Middleware 可独立替换或移除
响应式原生基于 Project Reactor,天然支持异步
默认穿透不覆写即无开销

9.2 最佳实践

  1. 追踪类放最外层:确保所有内层异常都能被捕获
  2. 限流放权限前:先限流再鉴权,避免无效鉴权开销
  3. Transformer 类靠近 Core:数据变换越晚执行,越接近最终状态
  4. 避免在 Middleware 中做重 IO:会阻塞整个执行链
  5. 使用 doFinally 确保清理:Onion 模型的"离开"阶段必须执行清理逻辑

9.3 典型生产组合

// 生产环境推荐组合
HarnessAgent agent = HarnessAgent.builder()
    .name("production-agent")
    .model("dashscope:qwen-plus")
    // === 可观测性层 ===
    .middleware(new OtelTracingMiddleware())
    .middleware(new MetricsMiddleware())
    // === 治理层 ===
    .middleware(new GracefulShutdownMiddleware())
    .middleware(new RateLimitMiddleware(200))
    // === 安全层 ===
    .middleware(new SensitiveWordMiddleware())
    .middleware(new PermissionCheckMiddleware())
    // === 业务层 ===
    .middleware(new DynamicSkillMiddleware())
    .middleware(new TaskReminderMiddleware())
    // === 计费层 ===
    .middleware(new TokenBillingMiddleware())
    .build();

十、总结

AgentScope Java 2.0 的 Middleware 机制,用两类模型(Onion + Transformer)覆盖了智能体扩展的全部场景,用五个挂载点(onAgent / onSystemPrompt / onReasoning / onActing / onModelCall)精确覆盖了 ReAct 循环的每个关键时机。
它的核心价值在于:

将"横切关注点"从 Agent 核心逻辑中彻底解耦,让推理循环保持纯净,让工程能力按需叠加。

对于 Java 开发者而言,这套设计与 Servlet Filter、Spring Interceptor、AOP 切面的思想一脉相承——用熟悉的模式解决新问题。从 Demo 到生产,Middleware 就是那座桥梁。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐