作为深耕 AI API 集成领域多年的技术顾问,我见过太多因网络抖动、限流、临时服务不可用导致的调用失败问题。今天给出一个明确的工程结论:用 TypeScript 装饰器实现自动重试机制,是目前最优雅、最可维护的方案

本文将从实战角度出发,完整讲解如何用装饰器模式封装一个生产级的重试逻辑,同时对比 HolySheep API 与官方 API 的接入差异,帮助你在成本降低 85% 的同时获得更稳定的调用体验。

一、为什么选择装饰器实现重试

传统的重试逻辑往往是直接在调用处写 while 循环或 if 判断,代码耦合严重且难以统一配置。而装饰器模式将重试逻辑与业务逻辑分离,带来三个核心优势:

二、方案对比:HolySheep API vs 官方 API vs 其他平台

对比维度HolySheep AIOpenAI 官方Anthropic 官方
汇率优势¥1=$1(无损)¥7.3=$1¥7.3=$1
支付方式微信/支付宝直充国际信用卡国际信用卡
国内延迟<50ms 直连150-300ms200-400ms
GPT-4.1 输出价$8/MTok$15/MTok
Claude Sonnet 4.5$15/MTok$15/MTok
Gemini 2.5 Flash$2.50/MTok
DeepSeek V3.2$0.42/MTok
注册优惠送免费额度
适合人群国内开发者/企业海外用户海外用户

对于国内开发者而言,选择 立即注册 HolySheep AI 不仅能省去跨境支付的麻烦,更能享受显著的成本优势和极低的调用延迟。

三、TypeScript 装饰器重试实现

3.1 基础版本:同步装饰器

import "reflect-metadata";

// 定义可重试错误的配置
const RETRYABLE_STATUS_CODES = [408, 429, 500, 502, 503, 504];

// 装饰器工厂函数
function Retry(maxAttempts: number = 3, delayMs: number = 1000) {
    return function <T extends (...args: any[]) => Promise<any>(
        target: T,
        propertyKey: string,
        descriptor: TypedPropertyDescriptor<T>
    ) {
        const originalMethod = descriptor.value!;

        descriptor.value = async function (...args: Parameters<T>) {
            let lastError: Error | null = null;

            for (let attempt = 1; attempt <= maxAttempts; attempt++) {
                try {
                    console.log([Retry] 第 ${attempt} 次尝试调用 ${propertyKey});
                    const result = await originalMethod.apply(this, args);
                    return result;
                } catch (error: any) {
                    lastError = error;
                    const isRetryable = RETRYABLE_STATUS_CODES.includes(error?.response?.status);

                    if (attempt === maxAttempts || !isRetryable) {
                        console.error([Retry] 已达最大重试次数,最终失败:, error.message);
                        throw error;
                    }

                    // 指数退避策略:delayMs * 2^(attempt-1)
                    const backoffDelay = delayMs * Math.pow(2, attempt - 1);
                    console.log([Retry] 等待 ${backoffDelay}ms 后重试...);
                    await new Promise(resolve => setTimeout(resolve, backoffDelay));
                }
            }

            throw lastError;
        } as T;

        return descriptor;
    };
}

// 使用示例
class AIClient {
    private baseURL = "https://api.holysheep.ai/v1";
    private apiKey = "YOUR_HOLYSHEEP_API_KEY";

    @Retry(3, 1000)
    async chat(messages: any[]) {
        const response = await fetch(${this.baseURL}/chat/completions, {
            method: "POST",
            headers: {
                "Authorization": Bearer ${this.apiKey},
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                model: "gpt-4.1",
                messages
            })
        });

        if (!response.ok) {
            const error = new Error(API 调用失败: ${response.status});
            (error as any).response = response;
            throw error;
        }

        return response.json();
    }
}

3.2 生产版本:支持自定义判定逻辑

// 扩展的错误判定接口
interface RetryCondition {
    (error: any, attempt: number): boolean;
}

// 装饰器配置选项
interface RetryOptions {
    maxAttempts?: number;
    baseDelay?: number;
    maxDelay?: number;
    onRetry?: (error: any, attempt: number, nextDelay: number) => void;
    shouldRetry?: RetryCondition;
}

// 默认错误判定:网络错误或 5xx/429 状态码
const defaultShouldRetry: RetryCondition = (error, attempt) => {
    // 网络层错误
    if (error.name === "TypeError" && error.message.includes("fetch")) {
        return true;
    }
    // HTTP 状态码判定
    const status = error?.response?.status;
    return status === 429 || (status >= 500 && status < 600);
};

function SmartRetry(options: RetryOptions = {}) {
    const {
        maxAttempts = 3,
        baseDelay = 1000,
        maxDelay = 30000,
        onRetry,
        shouldRetry = defaultShouldRetry
    } = options;

    return function <T extends (...args: any[]) => Promise<any>(
        target: T,
        propertyKey: string,
        descriptor: TypedPropertyDescriptor<T>
    ) {
        const originalMethod = descriptor.value!;

        descriptor.value = async function (...args: Parameters<T>) {
            let attempt = 0;

            while (true) {
                attempt++;
                try {
                    return await originalMethod.apply(this, args);
                } catch (error: any) {
                    const shouldRetryNow = shouldRetry(error, attempt);

                    if (attempt >= maxAttempts || !shouldRetryNow) {
                        throw error;
                    }

                    // 计算带抖动的延迟
                    const exponentialDelay = Math.min(
                        baseDelay * Math.pow(2, attempt - 1),
                        maxDelay
                    );
                    const jitter = Math.random() * 0.3 * exponentialDelay;
                    const nextDelay = Math.floor(exponentialDelay + jitter);

                    if (onRetry) {
                        onRetry(error, attempt, nextDelay);
                    }

                    await new Promise(r => setTimeout(r, nextDelay));
                }
            }
        } as T;

        return descriptor;
    };
}

// 使用示例:对接 HolySheep API
class HolySheepClient {
    private baseURL = "https://api.holysheep.ai/v1";
    private apiKey: string;

    constructor(apiKey: string) {
        this.apiKey = apiKey;
    }

    @SmartRetry({
        maxAttempts: 5,
        baseDelay: 1000,
        maxDelay: 15000,
        shouldRetry: (error, attempt) => {
            // HolySheep API 特定错误码处理
            const status = error?.response?.status;
            const isRateLimit = status === 429;
            const isServerError = status >= 500;
            const isTimeout = status === 408 || error.code === "ETIMEDOUT";
            return isRateLimit || isServerError || isTimeout;
        },
        onRetry: (error, attempt, delay) => {
            console.log([HolySheep] 重试 #${attempt},等待 ${delay}ms,错误: ${error.message});
        }
    })
    async complete(prompt: string, model: string = "deepseek-v3.2") {
        const response = await fetch(${this.baseURL}/chat/completions, {
            method: "POST",
            headers: {
                "Authorization": Bearer ${this.apiKey},
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                model,
                messages: [{ role: "user", content: prompt }]
            })
        });

        if (!response.ok) {
            const error = new Error(HolySheep API 错误: ${response.status} ${response.statusText});
            (error as any).response = response;
            throw error;
        }

        return response.json();
    }
}

// 实例化使用
const client = new HolySheepClient("YOUR_HOLYSHEEP_API_KEY");
client.complete("你好,请介绍一下 TypeScript 装饰器").then(console.log);

3.3 高级版本:支持泛型和上下文传递

// 完整的重试上下文
interface RetryContext {
    attempt: number;
    totalDelay: number;
    startTime: number;
    lastError: any;
}

// 装饰器元数据键
const RETRY_CONTEXT_KEY = Symbol("retry_context");

function RetryWithContext(options: RetryOptions = {}) {
    const {
        maxAttempts = 3,
        baseDelay = 1000,
        maxDelay = 60000,
        onRetry
    } = options;

    return function <T extends (...args: any[]) => Promise<any>(
        target: T,
        propertyKey: string,
        descriptor: TypedPropertyDescriptor<T>
    ) {
        const originalMethod = descriptor.value!;

        descriptor.value = async function (...args: Parameters<T>) {
            const context: RetryContext = {
                attempt: 0,
                totalDelay: 0,
                startTime: Date.now(),
                lastError: null
            };

            // 将上下文附加到 this 上,便于业务代码访问
            (this as any)[RETRY_CONTEXT_KEY] = context;

            while (context.attempt < maxAttempts) {
                context.attempt++;
                try {
                    const result = await originalMethod.apply(this, args);
                    return result;
                } catch (error: any) {
                    context.lastError = error;

                    if (context.attempt >= maxAttempts) {
                        throw error;
                    }

                    // 指数退避 + 抖动
                    const delay = Math.min(
                        baseDelay * Math.pow(2, context.attempt - 1),
                        maxDelay
                    );
                    const jitter = Math.random() * delay * 0.2;
                    const finalDelay = Math.floor(delay + jitter);

                    context.totalDelay += finalDelay;

                    if (onRetry) {
                        onRetry(error, context.attempt, finalDelay);
                    }

                    await new Promise(r => setTimeout(r, finalDelay));
                }
            }

            throw context.lastError;
        } as T;

        return descriptor;
    };
}

// 业务中使用上下文
class AdvancedAIClient {
    private baseURL = "https://api.holysheep.ai/v1";
    private apiKey: string;

    constructor(apiKey: string) {
        this.apiKey = apiKey;
    }

    @RetryWithContext({
        maxAttempts: 4,
        baseDelay: 500,
        onRetry: (error, attempt, delay) => {
            console.log([重试] 第 ${attempt} 次,延迟 ${delay}ms);
        }
    })
    async generateEmbedding(text: string): Promise<number[]> {
        const context: RetryContext = (this as any)[RETRY_CONTEXT_KEY];
        
        // 可根据重试次数调整模型或参数
        if (context.attempt > 2) {
            console.log([Warning] 高频重试,当前尝试次数: ${context.attempt});
        }

        const response = await fetch(${this.baseURL}/embeddings, {
            method: "POST",
            headers: {
                "Authorization": Bearer ${this.apiKey},
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                model: "text-embedding-3-small",
                input: text
            })
        });

        if (!response.ok) {
            const error = new Error(Embedding API 失败: ${response.status});
            (error as any).response = response;
            throw error;
        }

        const data = await response.json();
        return data.data[0].embedding;
    }

    // 查询本次调用的统计信息
    getRetryStats(): { attempts: number; totalDelay: number; duration: number } | null {
        const context: RetryContext | undefined = (this as any)[RETRY_CONTEXT_KEY];
        if (!context) return null;

        return {
            attempts: context.attempt,
            totalDelay: context.totalDelay,
            duration: Date.now() - context.startTime
        };
    }
}

四、常见报错排查

4.1 TypeError: Cannot read property 'apply' of undefined

错误原因:装饰器使用了箭头函数作为 descriptor,导致 this 上下文丢失。

解决代码:确保 descriptor.value 使用普通函数而非箭头函数:

// ❌ 错误写法
descriptor.value = async (...args) => {
    return originalMethod(...args); // this 丢失
};

// ✅ 正确写法
descriptor.value = async function (...args: Parameters<T>) {
    return originalMethod.apply(this, args); // 保持 this 绑定
};

4.2 无限重试导致服务雪崩

错误原因:未设置 maxAttempts 上限,或 shouldRetry 判定逻辑过于宽泛。

解决代码:务必设置合理的重试上限和判定条件:

@SmartRetry({
    maxAttempts: 3, // 最多重试3次
    shouldRetry: (error, attempt) => {
        // 只对特定错误进行重试
        const status = error?.response?.status;
        const safeStatuses = [408, 429, 500, 502, 503, 504];
        return attempt < 3 && safeStatuses.includes(status);
    }
})
async callAPI() {
    // ...
}

// 添加熔断器:连续失败计数
class CircuitBreaker {
    private failures = 0;
    private lastFailure = 0;
    private threshold = 5;
    private timeout = 60000; // 1分钟

    shouldAllowRequest(): boolean {
        if (Date.now() - this.lastFailure > this.timeout) {
            this.failures = 0;
        }
        return this.failures < this.threshold;
    }

    recordFailure() {
        this.failures++;
        this.lastFailure = Date.now();
    }
}

4.3 重试时请求体被篡改或幂等性问题

错误原因:部分 API(如 POST 请求)在重试时可能产生副作用,重复执行导致数据不一致。

解决代码:实现幂等键机制,或对 GET 请求优先使用重试:

const NON_IDEMPOTENT_METHODS = ["POST", "PUT", "PATCH"];
const IDEMPOTENT_METHODS = ["GET", "DELETE", "HEAD"];

@SmartRetry({
    shouldRetry: (error, attempt) => {
        const method = error?.config?.method?.toUpperCase();
        const status = error?.response?.status;

        // GET 请求可以安全重试
        if (IDEMPOTENT_METHODS.includes(method)) {
            return true;
        }

        // POST 等非幂等请求,仅在网络错误时重试(且需业务侧支持幂等键)
        if (NON_IDEMPOTENT_METHODS.includes(method)) {
            if (error.name === "TypeError") return true;
            // 429 限流可重试,5xx 服务器错误可重试
            return status === 429 || (status >= 500 && status < 600);
        }

        return false;
    }
})
async createResource(data: any) {
    const response = await fetch(${this.baseURL}/resources, {
        method: "POST",
        headers: {
            "Authorization": Bearer ${this.apiKey},
            "Content-Type": "application/json",
            // HolySheep API 支持 Idempotency-Key
            "Idempotency-Key": crypto.randomUUID()
        },
        body: JSON.stringify(data)
    });

    if (!response.ok) {
        const error = new Error(创建失败: ${response.status});
        (error as any).response = response;
        throw error;
    }

    return response.json();
}

4.4 装饰器与依赖注入框架冲突

错误原因:在使用 NestJS、TypeDI 等 DI 框架时,装饰器可能与框架的拦截器机制冲突。

解决代码:使用框架内置拦截器或确保装饰器优先级:

// NestJS 环境下,推荐使用框架拦截器而非自定义装饰器
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
import { Observable, throwError, timer } from "rxjs";
import { mergeMap, retryWhen } from "rxjs/operators";

@Injectable()
export class RetryInterceptor implements NestInterceptor {
    intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        return next.handle().pipe(
            retryWhen(errors => 
                errors.pipe(
                    mergeMap((error, attempt) => {
                        if (attempt >= 3 || ![408, 429, 500, 502, 503, 504].includes(error?.response?.status)) {
                            return throwError(() => error);
                        }
                        const delay = 1000 * Math.pow(2, attempt);
                        return timer(delay);
                    })
                )
            )
        );
    }
}

// 非 NestJS 环境下,可混合使用
class StandaloneClient {
    @Retry(3, 500)
    async call() { /* ... */ }
}

五、实战经验总结

在我参与过的多个大型 AI 应用项目中,重试机制的设计直接决定了系统的可用性和用户体验。基于 HolySheep API 接入的实测数据,使用本文方案后:

关键经验是:重试策略必须与业务场景匹配。对于实时聊天场景,建议最多重试 2 次且延迟不超过 2 秒;对于后台批处理任务,可接受更长的重试间隔和更多的重试次数。

六、结语

通过 TypeScript 装饰器实现的自动重试机制,不仅代码优雅、配置灵活,还能与各种 AI API 无缝集成。如果你正在寻找高性价比、低延迟的 AI API 服务,强烈建议你尝试 立即注册 HolySheep AI,享受人民币直充、<50ms 延迟、以及主流模型全覆盖的优质体验。

👉 免费注册 HolySheep AI,获取首月赠额度