ai-agent について
対話ごとに永続メモリーを持つ AI エージェントバックエンドを構築するための RivetKit スキルです。キュー処理やストリーミング応答、サンドボックス型コーディングエージェントの例を含みます。
主な機能
- 01対話ごとに1つの Rivet Actor
- 02永続化される対話状態
- 03メッセージキューの逐次処理
- 04ストリーミング応答の配信
- 05サンドボックス型コーディングエージェント
活用シーン
- 01対話ごとのメモリーを持つチャットエージェントの構築
- 02対話内のメッセージを順番に処理
- 03隔離サンドボックスでのコーディングエージェント作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rivet-dev/ skills --skill ai-agentインストール後、Claude Code で/ai-agentを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
BASE_SKILL.mdindex.jsonopenapi.jsonreference/actors/access-control.mdactions.mdactor-runtime-socket.mdappearance.mdauthentication.mdcommunicating-between-actors.mdconnections.mddebugging.mddesign-patterns.mddestroy.mderrors.mdevents.mdfetch-and-websocket-handler.mdhelper-types.mdhttp-api.mdinput.mdinspector-tabs.mdkeys.mdkv.mdlifecycle.mdlimits.mdmetadata.mdqueues.mdreference/actors/quickstart/backend.mdcloudflare.mdeffect.mdnext-js.md詳細
AI エージェント
重要:作業を始める前に、このスキルのディレクトリにある BASE_SKILL.md を必ず読んでください。デバッグ、エラー処理、状態管理、デプロイ、プロジェクトのセットアップに関する重要なガイダンスが記載されています。これらのルールとパターンは、すべての RivetKit の作業に適用されます。以下は、すでに読んで内容を理解していることを前提としています。
実装例
参考となる実装が必要な場合は、以下のテンプレートにある動作する実装例のコードを直接読んでください。
RivetKit で AI エージェントのバックエンドを構築するためのパターンです。各会話は、メモリ、メッセージキュー、ストリーミング出力を管理する 1 つの Rivet Actor になります。
スターターコード
GitHub にある動作する実装例のいずれかを使い、必要に応じて変更してください。以下のセクションでは、特に別のバリエーションが指定されていない限り、主要な ai-agent の実装例について説明します。
| バリエーション | スターターコード | 使用する場面 |
|---|---|---|
| キュー駆動の AI SDK エージェント | GitHub | 各会話が独自の永続メモリを保持し、メッセージを一度に 1 つずつ処理する、ストリーミング対応のチャットエージェントが必要な場合。 |
| サンドボックス型コーディングエージェント | GitHub | Docker、Daytona、または E2B を使い、隔離されたサンドボックス内でコーディングエージェント(デフォルトでは Codex)を実行する場合。 |
| 耐久性のあるストリームを使うエージェント(実験的) | GitHub | アクターの状態やイベントではなく、耐久性のあるストリームを使って、プロンプトと応答を再生可能かつ再起動に耐える形で配信したい場合。 |
会話メモリ
会話またはエージェントの ID をキーとして、会話ごとに 1 つのアクターを使います(アクターキーを参照)。エージェントアクターの永続的な状態が会話メモリになります。ai-agent の実装例では、messages と status は JSON アクター状態に保存され、外部データベースなしでスリープや再起動後も保持されます。モデルを呼び出すたびに、c.state.messages とシステムプロンプトからプロンプトが再構築されるため、メモリと推論への入力は同じデータになります。
| バリエーション | メモリの保存場所 | 永続化される状態フィールド |
|---|---|---|
ai-agent | JSON アクター状態 | messages、status |
sandbox-coding-agent | JSON アクター状態とサンドボックスの ACP セッション | messages、status、sessionId |
experimental-durable-streams-ai-agent | 耐久性のあるストリーム。アクターには会話 ID と読み取りカーソルのみを保存 | conversationId、promptStreamOffset |
メッセージ処理
ai-agent の実装例では、クライアントは agent.connection.send("message", { text, sender }) を使って、ユーザー入力をエージェントの message キューに追加します。これはキューへの追加であり、アクションの呼び出しではありません。エージェントの run フック(ライフサイクルを参照)は、for await (const queued of c.queue.iter()) を使ってキューを順番に消費します。
キューを順番に消費することで、会話ごとの並行処理が保証されます。アクターごとにモデル呼び出しが実行中になるのは最大 1 つであり、追加のロックは必要ありません。status フィールド(モデル呼び出し中の thinking)は UI 向けのシグナルにすぎず、実際のロックは実行ループが担います。また、トークンストリーム内で c.aborted も確認するため、シャットダウン時には正常に終了します。
| バリエーション | メッセージの入力方法 | 直列化の保証 |
|---|---|---|
ai-agent | connection.send 経由で message キューに追加 | run フックが c.queue.iter() を使い、キューからメッセージを一度に 1 つずつ取り出します。 |
sandbox-coding-agent | キューを使わず、sendMessage アクションを呼び出す | 各呼び出しは、結果をブロードキャストする前にサンドボックスとの往復処理を待ちます。 |
experimental-durable-streams-ai-agent | onWake から耐久性のあるプロンプトストリームをロングポーリング | promptStreamOffset はチャンクごとに永続化されるため、再起動後もプロンプトを再処理せずに再開できます。 |
ストリーミング応答
ai-agent アクターは、モデルのテキスト差分ごとに response イベントをブロードキャストします。ペイロードには messageId、トークンごとの delta、累積された content、done フラグ(失敗時には error も)が含まれるため、クライアントは差分を追加することも、content を使って messageId ごとにメッセージを冪等に置き換えることもできます。サンプルのフロントエンドでは messageId ごとに置き換えるため、イベントが失われても問題ありません。最後のブロードキャストでは、空の delta、完全な content、done: true が含まれます。
アシスタントのメッセージオブジェクトは c.state.messages にあり、ストリーミング中にその場で変更されるため、ストリームの途中でアクターが再起動しても部分的なコンテンツは保持されます。サンプルではスロットリングなしで AI SDK の差分ごとにブロードキャストします。高トラフィックのデプロイで推奨される拡張機能は差分のバッチ処理またはスロットリングであり、サンプルでは実装されていません。
バリアントによる違い:sandbox-coding-agent は、サンドボックスの処理完了後に done: true の response ブロードキャストを 1 回送信します(段階的なストリーミングはありません)。experimental-durable-streams-ai-agent は、トークンごとのチャンクを永続的な応答ストリームに追加してから、responseComplete または responseError をブロードキャストします。
アーキテクチャ
| トピック | 概要 |
|---|---|
| トポロジー | agentManager["primary"] シングルトンディレクトリと、会話ごとに 1 つの agent[agentId] アクター。 |
| 入力 | クライアントは connection.send を使って、AgentQueueMessage ペイロードをエージェントの message キューにプッシュします。 |
| ストリーミング | モデルの差分ごとに response を 1 回ブロードキャストし、最後に done: true を含むブロードキャストを送信します。 |
| メモリ | 会話履歴全体とステータスは JSON アクター状態に保持されます。外部データベースは使用しません。 |
マネージャーは AgentInfo レコードを作成し、アクター間通信を通じて各エージェントを起動します。具体的には、createAgent が c.client<typeof registry>() を呼び出し、続いて client.agent.getOrCreate([info.id]) を呼び出して getStatus() を待機するため、クライアントの接続前に会話アクターが作成されます。サンドボックスのバリアントでは、エージェントと同じキーを共有する codingSandbox アクター(codingSandbox.getOrCreate([c.key[0]]))を加えてこのトポロジーを拡張するため、エージェントとサンドボックスの対応関係はキー空間に暗黙的に含まれます。
アクター
-
キー:
agentManager["primary"] -
役割:ディレクトリアクター。
AgentInfoレコードを作成し、エージェントを一覧表示し、c.client()を通じて各エージェントアクターを起動します。 -
アクション
createAgentlistAgents
-
キュー
- なし
-
状態
- JSON
agents
-
キー:
agent[agentId] -
役割:会話ごとに 1 つのアクター。メッセージ履歴全体とステータスを保持し、
runループでキューに入ったユーザーメッセージを処理し、AI SDK を通じてモデルを呼び出し、ストリーミング差分をブロードキャストします。 -
アクション
getHistorygetStatus
-
キュー
message
-
イベント
messageAddedstatusresponse
-
状態
- JSON
messagesstatus
ライフサイクル
sequenceDiagram
participant C as Client
participant AM as agentManager
participant A as agent
participant LLM as Model API
C->>AM: createAgent(name)
AM->>A: getOrCreate([info.id]) + getStatus()
AM-->>C: AgentInfo
C->>A: connection.send("message", {text, sender})
Note over A: run loop pops queue via c.queue.iter()
A-->>C: messageAdded (user message)
A-->>C: messageAdded (assistant placeholder)
A-->>C: status (thinking)
A->>LLM: streamText(system prompt + history)
loop each text delta
LLM-->>A: delta
A-->>C: response {messageId, delta, content, done: false}
end
A-->>C: response {delta: "", content, done: true}
A-->>C: status (idle)
セキュリティチェックリスト
最小限の構成を保つため、サンプルには認証機能が含まれていません。エージェントのバックエンドを公開する前に、以下の基本対策を適用してください。
- API キーはサーバー側に保持する:
OPENAI_API_KEY(またはANTHROPIC_API_KEY)は、アクタープロセス内の AI SDK によって読み込まれます。キーがブラウザーに渡ることはありません。クライアントは RivetKit 経由でのみアクターと通信します。サンドボックス版ではキーをサンドボックスの環境変数に渡し、クライアントには渡しません。 - 認証を追加する:サンプルには認証機能がないため、サーバーにアクセスできる人なら誰でもエージェントを作成、一覧表示でき、キーを推測できたエージェントにメッセージを送信できます。推奨される拡張として、スコープ付きトークンを使用する
onBeforeConnectまたはcreateConnStateのチェックを追加してください。認証を参照してください。 - キューペイロードを検証し、レート制限を設定する:サンプルでは、
textが文字列ではない本文を除外するだけです。推奨される拡張として、ペイロードサイズの上限、スキーマ検証、接続ごとのレート制限を適用してください。 - 送信者の ID はサーバー側で決定する:サンプルでは、クライアントから渡された
senderフィールドをそのまま信頼します。代わりに、送信者の ID を認証済みの接続に紐付けてください。 - メッセージ履歴に上限を設けるか、短縮する:サンプルでは上限を設けず、モデル呼び出しのたびに会話履歴全体を送信します。プロンプトと状態が肥大化しないよう、推奨される拡張として古いメッセージを短縮または要約してください。
- 会話ごとにコストの上限を設定する:推奨される拡張として、エージェントごとのトークン予算とクォータを追加してください。サンドボックス版では実際の計算処理が実行されるため、ユーザーごとのサンドボックスクォータも適用し、サンドボックスからのネットワーク送信を制限してください。
リファレンスマップ
アクター
- アクセス制御
- アクション
- アクターキー
- アクターランタイムソケット
- アクターのステータス
- 認証
- Cloudflare Workers クイックスタート
- アクター間の通信
- 接続
- カスタムインスペクタータブ
- デバッグ
- 設計パターン
- アクターの破棄
- Effect.ts クイックスタート(ベータ版)
- エラー
- Fetch および WebSocket ハンドラー
- ヘルパー型
- アイコンと名前
- インメモリ状態
- 入力パラメーター
- ライフサイクル
- 制限
- 低レベル HTTP リクエストハンドラー
- 低レベル KV ストレージ
- 低レベル WebSocket ハンドラー
- メタデータ
- Next.js クイックスタート
- Node.js と Bun のクイックスタート
- キューと実行ループ
- React クイックスタート
- リアルタイム
- Rust クイックスタート(ベータ版)
- スケーリングと並行処理
- スケジュールと Cron
- 状態の共有と参加
- SQLite
- SQLite と Drizzle
- Supabase Functions クイックスタート
- テスト
- トラブルシューティング
- 型
- 標準 HTTP API
- バージョンとアップグレード
- ワークフロー
CLI
クライアント
クックブック
- AI エージェント
- チャットルーム
- 共同テキストエディター
- Cron ジョブとスケジュールされたタスク
- テナントごとのデータベース
- VPC またはエアギャップネットワークへの Rivet のデプロイ
- ライブカーソルとプレゼンス
- マルチプレイヤーゲーム
デプロイ
- コンテナランナー
- Amazon Web Services Lambda へのデプロイ
- AWS ECS へのデプロイ
- Cloudflare Workers へのデプロイ
- Freestyle へのデプロイ
- Google Cloud Run へのデプロイ
- Hetzner へのデプロイ
- Kubernetes へのデプロイ
- Railway へのデプロイ
- Rivet Compute へのデプロイ
- Supabase Functions へのデプロイ
- Vercel へのデプロイ
- VM とベアメタルへのデプロイ
全般
- アクターの設定
- クロスオリジンリソース共有
- LLM と AI 向けのドキュメント
- エッジネットワーキング
- エンドポイント
- 環境変数
- HTTP サーバー
- ロギング
- プールの設定
- 本番環境チェックリスト
- レジストリの設定
- ランタイムモード
- WASM とネイティブ SDK の比較