Convex Agents Rate Limiting について
Convex Agents 向けに、メッセージ頻度とトークン使用量の制限方法を解説するスキルです。ユーザー単位・全体のクォータ、バースト容量、使用量の記録を扱います。
主な機能
- 01固定ウィンドウのメッセージ制限
- 02トークンバケットによるバースト対応
- 03ユーザー単位・全体のクォータ
- 04トークン使用量の推定と記録
- 05クライアント側の制限状況確認
活用シーン
- 01チャットメッセージ頻度の制限
- 02ユーザーごとのトークン枠設定
- 03API クォータ内での利用
- 04課金用トークン使用量の記録
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ sstobo/ convex-skills --skill convex-agents-rate-limitingインストール後、Claude Code で/convex-agents-rate-limitingを入力して読み込まれたか確認します。
詳細
目的
レート制限は、不正利用を防ぎ、LLM のコストを管理し、公平なリソース配分を実現します。メッセージ頻度の制限とトークン使用量のクォータを扱います。
このスキルを使用する場面
- 短時間に大量のメッセージが送信されるのを防ぐ
- ユーザーごとの合計トークン数を制限する
- バースト容量を実装する
- プロバイダーのクォータを超えないよう、API 全体の制限を設定する
- マルチユーザーシステムでリソースを公平に配分する
- トークン使用量に基づいて課金する
レートリミッターの設定
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;
}
},
});
トークン使用量を確認する
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..." />;
}
主な原則
- 頻度には固定ウィンドウ:一定期間あたりの回数を単純に制限する場合に使用します
- 容量にはトークンバケット:バーストに対応した制限に使用します
- 事前に推定し、事後に追跡する:早期に防止し、実際の使用量を記録します
- 全体制限とユーザーごとの制限:公平なアクセスとリソース上限のバランスを取ります
- 再試行可能なエラー:クライアントはバックオフして再試行できます
次のステップ
- トークン使用量に基づく課金については、usage-tracking を参照してください
- エージェントのセットアップについては、fundamentals を参照してください
- トラブルシューティングについては、debugging を参照してください
コメント
Markdown 対応