关于 Convex Agents Rate Limiting
面向 Convex Agents 的限流技能,介绍如何限制消息频率和 Token 用量,并涵盖用户及全局配额、突发容量和用量记录。
主要功能
- 01固定窗口消息限流
- 02令牌桶突发容量
- 03用户及全局配额
- 04估算并记录 Token 用量
- 05客户端限流状态检查
适用场景
- 01限制聊天消息频率
- 02设置用户 Token 配额
- 03控制在 API 配额内
- 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