目录:
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 Filter Unix 管道 / 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 之前 动态注入时间/角色/业务上下文、技能描述
onReasoning LLM 推理阶段 审计、敏感词检测、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) onReasoning Onion
记忆注入(MEMORY.md) onSystemPrompt Transformer
技能加载(Skills) onSystemPrompt Transformer
权限管控(Permission) onActing Onion
沙箱隔离(Sandbox) onActing Onion
Session 持久化 onAgent Onion
优雅关闭 onAgent Onion

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 Hook 2.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

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐