跳转到内容

中间件系统:before/after 双阶段执行管线

中间件系统:before/after 双阶段执行管线

学习目标

理解 CopilotKit 的中间件架构:

  • before/after 两个生命周期阶段
  • 函数内联 vs Webhook 两种执行模式
  • SSE 响应解析(从流中提取 messages、threadId、runId)
  • Agent 作用域中间件(限定特定 Agent)

前置知识

本章涉及 API 网关与编排层的通用原理,建议先阅读:

项目实践

中间件生命周期

中间件类型定义

BeforeRequestMiddleware

参数:
{
runtime: CopilotRuntimeLike,
request: Request,
path: string
}
返回值:Request | void // 可修改请求或仅做副作用

AfterRequestMiddleware

参数:
{
runtime: CopilotRuntimeLike,
response: Response,
path: string,
messages?: Message[], // 从 SSE 流解析的消息
threadId?: string, // 从 RUN_STARTED 提取
runId?: string // 从 RUN_STARTED 提取
}
返回值:void

SSE 响应解析

parseSSEResponse 从 SSE 流中提取结构化数据:

解析流程(伪代码):
1. 读取 response.body(ReadableStream)
2. 逐块解析 SSE 事件
3. 提取 RUN_STARTED 事件中的 threadId 和 runId
4. 提取 TEXT_MESSAGE_* 事件组装为 Message[]
5. 返回 { messages, threadId, runId }

关键挑战

  • SSE 流是逐步到达的,需要增量解析
  • 消息边界需要通过事件类型识别(TEXT_MESSAGE_START → CONTENT → END)
  • 非 SSE 响应(如 JSON 错误)需要跳过解析

中间件执行

callBeforeRequestMiddleware(源码验证):
1. 若 middleware 是函数 → 直接调用
2. 若 middleware 是其他值 → 记录警告并跳过
callAfterRequestMiddleware(源码验证):
1. 先调用 parseSSEResponse 提取 messages/threadId/runId
2. 若 middleware 是函数 → 调用并传入解析结果

Agent 作用域

中间件可以通过配置限定只对特定 Agent 生效:

// 在 CopilotRuntime 中配置
beforeRequestMiddleware: authMiddleware // 对所有 Agent 生效
// OpenGenerativeUI 等自动中间件支持作用域
openGenerativeUI: {
agents: ["my-agent"] // 仅对 my-agent 生效
}

中间件链

多个中间件按注册顺序执行:

执行链(伪代码):
request → mw1(request) → mw2(modifiedRequest) → mw3(furtherModifiedRequest) → handler

before 中间件可以修改 Request 并传递给下一个;after 中间件只能观察 Response(不可修改)。

问题与规避

问题规避策略
After 中间件必须等待 SSE 流结束长时间运行的 Agent 会延迟审计日志;考虑异步日志队列
中间件修改 body 后未正确序列化使用 request.clone() 保留原始 body;修改后重新构建 Request
中间件遗漏单端点路径确保中间件同时覆盖 REST 和 single-endpoint 路径
SSE 解析失败导致 after 中间件拿不到数据非 SSE 响应(如错误)的 messages 为空;中间件需要处理空数组

设计取舍

函数 vs Webhook

函数中间件(当前支持)

  • 优势:类型安全、同步执行、无网络延迟
  • 代价:必须在运行时进程中执行

Webhook 中间件(协议支持,未广泛使用)

  • 优势:可在独立服务中执行,支持跨语言
  • 代价:网络延迟、需要处理超时和重试

Before vs After

Before

  • 适合:认证、日志、请求转换、速率限制
  • 可修改请求内容

After

  • 适合:审计日志、响应分析、指标上报
  • 只能观察,不可修改响应(SSE 流已经发送给客户端)

参考来源

  • packages/runtime/src/v2/runtime/core/middleware.ts
  • packages/runtime/src/v2/runtime/core/middleware-sse-parser.ts