Agent 内で Chat SDK ↗ を動かすときは agents/chat-sdk を使います。最初の統合ヘルパーは、Agents のサブエージェントに状態を保存する Chat SDK の StateAdapter です。
アダプターは、Chat SDK のサブスクリプション、ロック、キュー、重複排除キー、スレッド状態、チャネル状態、コールバックのメタデータ、トランスクリプト一覧、スレッド履歴を Durable Object の SQLite に保存します。各状態シャードは、入口となる Agent 配下の ChatSdkStateAgent サブエージェントです。
メッセンジャー入口をホストする Worker に、両方のパッケージをインストールします。
npm i agents chatyarn add agents chatpnpm add agents chatbun add agents chatagents/chat-sdk は Chat SDK 向けの耐久性のある状態を提供します。Telegram、Slack、Discord、Teams、Google Chat など、任意の Chat SDK アダプターと組み合わせて使えます。
Chat SDK ランタイムを所有する親 Agent を作成します。Chat SDK の state オプションに createChatSdkState() を渡します。
import { Agent } from "agents";
import { createChatSdkState } from "agents/chat-sdk";
import { Chat } from "chat";
import { createTelegramAdapter } from "@chat-adapter/telegram";
export { ChatSdkStateAgent } from "agents/chat-sdk";
export class MessengerAgent extends Agent {
chat;
onStart() {
const telegram = createTelegramAdapter({
botToken: this.env.TELEGRAM_BOT_TOKEN,
mode: "webhook",
userName: "my_bot",
});
this.chat = new Chat({
adapters: { telegram },
userName: "my_bot",
state: createChatSdkState(),
concurrency: { strategy: "burst", debounceMs: 600 },
});
}
}import { Agent } from "agents";
import { createChatSdkState } from "agents/chat-sdk";
import { Chat } from "chat";
import { createTelegramAdapter } from "@chat-adapter/telegram";
export { ChatSdkStateAgent } from "agents/chat-sdk";
export class MessengerAgent extends Agent<Env> {
private chat!: Chat;
onStart() {
const telegram = createTelegramAdapter({
botToken: this.env.TELEGRAM_BOT_TOKEN,
mode: "webhook",
userName: "my_bot",
});
this.chat = new Chat({
adapters: { telegram },
userName: "my_bot",
state: createChatSdkState(),
concurrency: { strategy: "burst", debounceMs: 600 },
});
}
}Durable Object のマイグレーションに、親 Agent を追加します。
{
"$schema": "./node_modules/wrangler/config-schema.json",
// Set this to today's date
"compatibility_date": "2026-09-20",
"compatibility_flags": [
"nodejs_compat"
],
"durable_objects": {
"bindings": [
{
"class_name": "MessengerAgent",
"name": "MessengerAgent"
}
]
},
"migrations": [
{
"new_sqlite_classes": [
"MessengerAgent"
],
"tag": "v1"
}
]
}# Set this to today's date
compatibility_date = "2026-09-20"
compatibility_flags = ["nodejs_compat"]
[[durable_objects.bindings]]
class_name = "MessengerAgent"
name = "MessengerAgent"
[[migrations]]
new_sqlite_classes = ["MessengerAgent"]
tag = "v1"サブエージェントのルーティングが解決できるよう、Worker のエントリポイントから ChatSdkStateAgent をエクスポートします。createChatSdkState() を Agent のライフサイクルメソッドまたはリクエストハンドラー内で呼ぶと、現在の Agent を親として使い、this.subAgent() で状態シャードを作成します。
デフォルトでは、Chat SDK の状態は、スレッド風キーのコロン区切り先頭 2 セグメントでシャードされます。
たとえば telegram:-100123:456 と telegram:-100123:789 は、同じ状態シャード telegram:-100123 を共有します。
デフォルトのキーシャーダーは、次の Chat SDK キー接頭辞を認識します。
thread-state:channel-state:msg-history:transcripts:user:
未知のキーは、アダプターのデフォルトシャード名 default を使います。
スレッド ID を状態サブエージェント名へどう対応させるかは、shardKey で制御します。
const state = createChatSdkState({
shardKey(threadId) {
return threadId.split(":").slice(0, 2).join(":");
},
});const state = createChatSdkState({
shardKey(threadId) {
return threadId.split(":").slice(0, 2).join(":");
},
});アダプターがスレッド形ではないキーを保存していても、プロバイダー固有のシャードへ振り分けたいときは keyShard を使います。
const state = createChatSdkState({
keyShard(key) {
if (!key.startsWith("dedupe:telegram:")) {
return undefined;
}
const chatId = key.slice("dedupe:telegram:".length).split(":")[0];
return chatId ? `telegram:${chatId}` : undefined;
},
});const state = createChatSdkState({
keyShard(key) {
if (!key.startsWith("dedupe:telegram:")) {
return undefined;
}
const chatId = key.slice("dedupe:telegram:".length).split(":")[0];
return chatId ? `telegram:${chatId}` : undefined;
},
});undefined を返すと、組み込みのキーシャーダーへ戻り、その後デフォルトシャードへフォールバックします。
ChatSdkStateAgent サブエージェントをバックエンドにした、Chat SDK の StateAdapter を作成します。
import { createChatSdkState } from "agents/chat-sdk";
export { ChatSdkStateAgent } from "agents/chat-sdk";
const state = createChatSdkState({
// parent: this // Optional. Defaults to the current Agent from getCurrentAgent().
});import { createChatSdkState } from "agents/chat-sdk";
export { ChatSdkStateAgent } from "agents/chat-sdk";
const state = createChatSdkState({
// parent: this // Optional. Defaults to the current Agent from getCurrentAgent().
});オプション:
| オプション | 説明 |
|---|---|
agent |
任意。ChatSdkStateAgent のカスタムサブクラス。デフォルトは ChatSdkStateAgent です。 |
parent |
任意。subAgent() を呼んで状態シャードを作る親 Agent。デフォルトは getCurrentAgent() が返す現在の Agent です。 |
name |
対応付けできないキー向けのデフォルトシャード名。デフォルトは default です。 |
shardKey |
Chat SDK のスレッド ID とロックキーをシャード名へ対応付けます。 |
keyShard |
汎用の Chat SDK キャッシュキーまたはリストキーをシャード名へ対応付けます。 |
SQLite に状態を保存するサブエージェントクラスです。ランタイムが作成できるよう、Worker のエントリポイントからエクスポートします。
export { ChatSdkStateAgent } from "agents/chat-sdk";export { ChatSdkStateAgent } from "agents/chat-sdk";createChatSdkState() が返す具体的な StateAdapter 実装です。ほとんどのアプリケーションでは、直接インスタンス化する必要はありません。
アダプターは Chat SDK の StateAdapter インターフェース全体を実装します。
thread.subscribe()とthread.unsubscribe()のサブスクリプション。- スレッド単位またはチャネル単位の同時実行用ロック。
queue、debounce、burstの同時実行戦略向けの保留メッセージキュー。- 任意の TTL 付き汎用キー値キャッシュ。
- 最大長トリミングとリスト単位 TTL 更新を備えた追記専用リスト。
これらのプリミティブの上に構築される Chat SDK 機能には、次があります。
- メッセージの重複排除。
- スレッドとチャネルの状態。
persistThreadHistoryをオプトインしたアダプター向けの永続スレッド履歴。- コールバック URL トークンの保存。
- モーダルコンテキストの保存。
- クロスプラットフォームのトランスクリプト。
TTL の読み取りは厳密です。期限切れのロック、キャッシュ値、キュー項目、リスト項目は、返す前に無視または削除されます。
物理的なクリーンアップは遅延実行です。ChatSdkStateAgent は既知の最短有効期限に対してクリーンアップコールバックを 1 つスケジュールし、実行後に再スケジュールします。アイドルなシャードは静かにしたまま、期限切れ行が無限に溜まるのを防ぎます。