mcpField
C
Convex Agents Rate Limiting

Convex Agents Rate Limiting

github.com/sstobo开发24197 次安装40 周前更新
代码仓库

关于 Convex Agents Rate Limiting

面向 Convex Agents 的限流技能,介绍如何限制消息频率和 Token 用量,并涵盖用户及全局配额、突发容量和用量记录。

主要功能

  1. 01固定窗口消息限流
  2. 02令牌桶突发容量
  3. 03用户及全局配额
  4. 04估算并记录 Token 用量
  5. 05客户端限流状态检查

适用场景

  1. 01限制聊天消息频率
  2. 02设置用户 Token 配额
  3. 03控制在 API 配额内
  4. 04记录 Token 用量以计费

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sstobo/convex-skills --skill convex-agents-rate-limiting

安装后在 Claude Code 中输入/convex-agents-rate-limiting确认已加载。

详细介绍

用途

限流可防止滥用、管理大模型成本并确保公平的资源分配。涵盖消息频率限制和 token 用量配额。

何时使用此技能

  • 防止快速连续的消息垃圾信息
  • 限制每个用户的总 token 数
  • 实现突发容量
  • 全局 API 限制以保持在提供商配额之内
  • 多用户系统中的公平资源分配
  • 基于 token 用量的计费

配置限流器

import { RateLimiter, MINUTE, SECOND } from "@convex-dev/rate-limiter";

export const rateLimiter = new RateLimiter(components.rateLimiter, {
  sendMessage: {
    kind: "fixed window",
    period: 5 * SECOND,
    rate: 1,
    capacity: 2,
  },
  globalSendMessage: {
    kind: "token bucket",
    period: MINUTE,
    rate: 1_000,
  },
  tokenUsagePerUser: {
    kind: "token bucket",
    period: MINUTE,
    rate: 2000,
    capacity: 10000,
  },
  globalTokenUsage: {
    kind: "token bucket",
    period: MINUTE,
    rate: 100_000,
  },
});

检查消息速率限制

export const sendMessage = mutation({
  args: { threadId: v.string(), message: v.string(), userId: v.string() },
  handler: async (ctx, { threadId, message, userId }) => {
    try {
      await rateLimiter.limit(ctx, "sendMessage", {
        key: userId,
        throws: true,
      });

      await rateLimiter.limit(ctx, "globalSendMessage", { throws: true });

      const { messageId } = await saveMessage(ctx, components.agent, {
        threadId,
        prompt: message,
      });

      return { success: true, messageId };
    } catch (error) {
      if (isRateLimitError(error)) {
        return {
          success: false,
          error: "Rate limit exceeded",
          retryAfter: error.data.retryAfter,
        };
      }
      throw error;
    }
  },
});

检查 token 用量

export const checkTokenUsage = action({
  args: { threadId: v.string(), question: v.string(), userId: v.string() },
  handler: async (ctx, { threadId, question, userId }) => {
    const estimatedTokens = await estimateTokens(ctx, threadId, question);

    try {
      await rateLimiter.check(ctx, "tokenUsagePerUser", {
        key: userId,
        count: estimatedTokens,
        throws: true,
      });

      // Proceed with generation
      const { thread } = await myAgent.continueThread(ctx, { threadId });
      const result = await thread.generateText({ prompt: question });

      return { success: true, response: result.text };
    } catch (error) {
      if (isRateLimitError(error)) {
        return {
          success: false,
          error: "Token limit exceeded",
          retryAfter: error.data.retryAfter,
        };
      }
      throw error;
    }
  },
});

async function estimateTokens(
  ctx: QueryCtx,
  threadId: string,
  question: string
): Promise<number> {
  const questionTokens = Math.ceil(question.length / 4);
  const responseTokens = Math.ceil(questionTokens * 3);
  return questionTokens + responseTokens;
}

跟踪实际用量

const myAgent = new Agent(components.agent, {
  name: "My Agent",
  languageModel: openai.chat("gpt-4o-mini"),
  usageHandler: async (ctx, { usage, userId }) => {
    if (!userId) return;

    await rateLimiter.limit(ctx, "tokenUsagePerUser", {
      key: userId,
      count: usage.totalTokens,
      reserve: true,
    });
  },
});

客户端限流检查

import { useRateLimit } from "@convex-dev/rate-limiter/react";
import { isRateLimitError } from "@convex-dev/rate-limiter";

function ChatInput() {
  const { status } = useRateLimit(api.rateLimiting.getRateLimit);

  if (status && !status.ok) {
    return (
      <div className="text-red-500">
        Rate limit exceeded. Retry after{" "}
        {new Date(status.retryAt).toLocaleTimeString()}
      </div>
    );
  }

  return <input type="text" placeholder="Send a message..." />;
}

关键原则

  • 频率使用固定窗口:适用于简单的每周期 X 次
  • 容量使用令牌桶:适用于对突发友好的限制
  • 事前估算,事后跟踪:提前预防,记录实际用量
  • 全局 + 每用户限制:在公平访问与资源上限之间取得平衡
  • 可重试错误:客户端可通过退避重试

后续步骤

  • 关于基于 token 用量的计费,请参阅 usage-tracking
  • 关于 Agent 设置,请参阅 fundamentals
  • 关于故障排除,请参阅 debugging

评论

支持 Markdown