Function Calling 中间件模式:鉴权、日志、限流统一拦截 Function Calling 中间件模式鉴权、日志、限流统一拦截一、100 个 Function 各自实现了鉴权逻辑的维护灾难Agent 平台的工具函数从 5 个增长到 50 个。每个函数里有 20 行重复的校验权限 → 记录日志 → 限流检查代码。修改一个日志格式要改 50 个文件。更糟的是有两个函数忘了加鉴权——导致任何用户都能调用退款接口。这和 HTTP 中间件解决的问题一模一样。HTTP Handler 用中间件做鉴权、日志、限流Function Calling 的 Handler 为什么不能把横切关注点Cross-cutting Concerns从业务逻辑中抽离出来这就是中间件模式的价值。二、Function Calling 中间件的架构设计中间件链的执行流程请求入 → 中间件1鉴权 → 中间件2限流 → 中间件3日志 → 实际函数 → 响应出。flowchart LR A[Function Call 请求] -- M1[鉴权中间件] M1 --|通过| M2[限流中间件] M1 --|拒绝| E1[返回 403] M2 --|通过| M3[参数校验中间件] M2 --|限流| E2[返回 429] M3 --|通过| M4[日志中间件] M3 --|失败| E3[返回 400] M4 -- F[业务函数] F -- M4R[日志中间件: 记录响应] M4R -- RES[返回结果]中间件的设计原则每个中间件只做一件事、中间件可以按需组合不是所有函数都需要限流、中间件执行顺序很重要鉴权必须在限流前面——否则限流器会受到未认证请求的攻击。三、Go 实现可组合的 Function Calling 中间件链package funccall import ( context fmt log sync time ) // 核心类型定义 // FuncCall 函数调用请求 type FuncCall struct { FuncName string Params map[string]interface{} UserID string TenantID string TraceID string Timestamp time.Time } // FuncResult 函数调用结果 type FuncResult struct { Data interface{} Error error Duration time.Duration TokenUsed int64 } // FuncHandler 函数处理器类似 http.Handler type FuncHandler func(ctx context.Context, call *FuncCall) (*FuncResult, error) // Middleware 中间件函数类似 http 中间件 type Middleware func(next FuncHandler) FuncHandler // 中间件实现 // 1. 鉴权中间件 func AuthMiddleware(next FuncHandler) FuncHandler { return func(ctx context.Context, call *FuncCall) (*FuncResult, error) { // 校验 UserID 是否存在 if call.UserID { return nil, fmt.Errorf([Auth] 未认证的调用: func%s, call.FuncName) } // 校验该用户是否有权限调用此函数 if !hasPermission(call.UserID, call.FuncName) { return nil, fmt.Errorf([Auth] 用户 %s 无权调用 %s, call.UserID, call.FuncName) } return next(ctx, call) } } // 2. 限流中间件 func RateLimitMiddleware(limiter *TokenBucketLimiter) Middleware { return func(next FuncHandler) FuncHandler { return func(ctx context.Context, call *FuncCall) (*FuncResult, error) { // 限流 key按用户函数做更精细控制 key : fmt.Sprintf(%s:%s, call.UserID, call.FuncName) if !limiter.Allow(key) { return nil, fmt.Errorf([RateLimit] 用户 %s 调用 %s 被限流, call.UserID, call.FuncName) } return next(ctx, call) } } } // 3. 参数校验中间件 func ValidationMiddleware(validators map[string]ParamValidator) Middleware { return func(next FuncHandler) FuncHandler { return func(ctx context.Context, call *FuncCall) (*FuncResult, error) { validator, ok : validators[call.FuncName] if ok { if err : validator.Validate(call.Params); err ! nil { return nil, fmt.Errorf([Validation] 参数校验失败: %w, err) } } return next(ctx, call) } } } // 4. 日志中间件 func LoggingMiddleware(next FuncHandler) FuncHandler { return func(ctx context.Context, call *FuncCall) (*FuncResult, error) { start : time.Now() // 入参日志敏感信息脱敏后记录 log.Printf([FuncCall] IN: trace%s func%s user%s params%v, call.TraceID, call.FuncName, call.UserID, sanitizeParams(call.Params)) result, err : next(ctx, call) elapsed : time.Since(start) if err ! nil { log.Printf([FuncCall] ERR: trace%s func%s err%v duration%v, call.TraceID, call.FuncName, err, elapsed) } else { log.Printf([FuncCall] OUT: trace%s func%s duration%v tokens%d, call.TraceID, call.FuncName, elapsed, result.TokenUsed) } return result, err } } // 5. 超时控制中间件 func TimeoutMiddleware(timeout time.Duration) Middleware { return func(next FuncHandler) FuncHandler { return func(ctx context.Context, call *FuncCall) (*FuncResult, error) { ctx, cancel : context.WithTimeout(ctx, timeout) defer cancel() return next(ctx, call) } } } // 6. Panic 恢复中间件 func RecoveryMiddleware(next FuncHandler) FuncHandler { return func(ctx context.Context, call *FuncCall) (result *FuncResult, err error) { defer func() { if r : recover(); r ! nil { err fmt.Errorf([Recovery] func%s panic: %v, call.FuncName, r) log.Printf([PANIC] func%s trace%s panic%v, call.FuncName, call.TraceID, r) } }() return next(ctx, call) } } // 7. 重试中间件 func RetryMiddleware(maxRetries int, backoff time.Duration) Middleware { return func(next FuncHandler) FuncHandler { return func(ctx context.Context, call *FuncCall) (*FuncResult, error) { var lastErr error for attempt : 0; attempt maxRetries; attempt { if attempt 0 { time.Sleep(backoff * time.Duration(attempt)) } // 每次重试前检查 context 是否取消 select { case -ctx.Done(): return nil, ctx.Err() default: } result, err : next(ctx, call) if err nil { return result, nil } // 不可重试错误直接返回 if !isRetryable(err) { return nil, err } lastErr err } return nil, fmt.Errorf(重试 %d 次后仍失败: %w, maxRetries, lastErr) } } } // 中间件链组合器 // Chain 将多个中间件串联成一个 func Chain(handler FuncHandler, middlewares ...Middleware) FuncHandler { // 从后往前包装最外层中间件先执行 for i : len(middlewares) - 1; i 0; i-- { handler middlewares[i](handler) } return handler } // 函数注册中心 // FuncRegistry 函数注册中心带中间件 type FuncRegistry struct { handlers map[string]FuncHandler // 业务函数 middlewares map[string][]Middleware // 每个函数的中间件链 defaultMW []Middleware // 默认中间件 mu sync.RWMutex } func NewFuncRegistry() *FuncRegistry { return FuncRegistry{ handlers: make(map[string]FuncHandler), middlewares: make(map[string][]Middleware), defaultMW: []Middleware{ RecoveryMiddleware, LoggingMiddleware, AuthMiddleware, }, } } // Register 注册函数带默认中间件 func (r *FuncRegistry) Register(name string, handler FuncHandler, mws ...Middleware) { r.mu.Lock() defer r.mu.Unlock() r.handlers[name] handler r.middlewares[name] mws } // Call 执行函数调用经过中间件链 func (r *FuncRegistry) Call(ctx context.Context, call *FuncCall) (*FuncResult, error) { r.mu.RLock() handler, ok : r.handlers[call.FuncName] r.mu.RUnlock() if !ok { return nil, fmt.Errorf(未注册的函数: %s, call.FuncName) } // 组装中间件链默认中间件 函数特定中间件 业务函数 mws : append([]Middleware{}, r.defaultMW...) r.mu.RLock() mws append(mws, r.middlewares[call.FuncName]...) r.mu.RUnlock() wrapped : Chain(handler, mws...) return wrapped(ctx, call) } // 辅助类型 type ParamValidator interface { Validate(params map[string]interface{}) error } type TokenBucketLimiter struct { buckets map[string]*rateLimiterEntry mu sync.Mutex } type rateLimiterEntry struct { tokens float64 lastCheck time.Time rate float64 // 每秒生成令牌数 burst int } func NewTokenBucketLimiter() *TokenBucketLimiter { return TokenBucketLimiter{ buckets: make(map[string]*rateLimiterEntry), } } func (l *TokenBucketLimiter) Allow(key string) bool { l.mu.Lock() defer l.mu.Unlock() entry, ok : l.buckets[key] if !ok { entry rateLimiterEntry{ tokens: 10, rate: 10, burst: 10, lastCheck: time.Now(), } l.buckets[key] entry return true } now : time.Now() elapsed : now.Sub(entry.lastCheck).Seconds() entry.tokens elapsed * entry.rate if entry.tokens float64(entry.burst) { entry.tokens float64(entry.burst) } entry.lastCheck now if entry.tokens 1 { entry.tokens-- return true } return false } func hasPermission(userID, funcName string) bool { return true } func sanitizeParams(params map[string]interface{}) map[string]interface{} { // 实际实现中脱敏手机号、Token 等 return params } func isRetryable(err error) bool { errStr : err.Error() return errStr ! permission denied errStr ! invalid params }四、中间件的边界与性能影响中间件链不要太长。每个中间件都是一个函数调用层10 层中间件意味着每次调用多 10 次函数调用开销。虽然对 AI 类函数调用耗时 200ms可以忽略但对高频轻量函数耗时 1ms中间件开销会占显著比例。中间件的顺序影响正确性。如果在限流中间件之前执行日志中间件被限流的请求也会产生日志可能造成日志风暴。正确顺序是恢复 → 鉴权 → 限流 → 日志 → 校验 → 业务。有些中间件不应该全局应用。比如重试中间件——如果业务函数不是幂等的如创建订单重试会创建重复订单。重试只应该用于幂等操作或带了幂等性的操作。中间件的错误处理要一致。不同中间件返回的错误格式应该统一方便上游解析。定义一个统一的 FungCallError 类型包含错误码、错误信息和是否可以重试。五、总结Function Calling 中间件模式的核心价值是分离关注点——把横切的鉴权、限流、日志、校验从每个业务函数中抽出来。设计要点中间件单一职责、按重要性排序、默认中间件覆盖所有函数、特定中间件按需添加。如果你已经为 HTTP API 设计过中间件链这套模式几乎是 1:1 迁移——思维模型不变只是换了个应用场景。