TalorysでステートフルなAIエージェントをサーバーレス運用
ステートフルなAIエージェントをサーバーレスで動かす方法。Talorysは Cloudflare Workers・Durable Objects・SQLiteを使い、すべて無料枠で運用できます。

ここで私の逆張りの主張をしておきます。個人向けAIエージェントは、デスクの下のRaspberry Piより、サーバーレス基盤のほうに向いている、というものです。Talorysプロジェクト(オープンソースのパーソナルアシスタントで、npx create-talorys@latest ひとつで自分のCloudflareアカウントにデプロイできます)は、「ステートレスなサーバーレス上でステートフルなAIエージェントを動かす」問題が、解決可能なだけでなく自然に合う問題だということを示す、これまでで最良の証拠です。「セルフホストと言えるのか」をHacker Newsで議論している人たちは、そもそも間違った議論をしていると思います。
私は長年インフラを運用し、その後始末もしてきました。セルフホストの個人向けソフトを駄目にするのは、デプロイではありません。3か月目です。ディスクがログで埋まり、TLS証明書が切れ、スケジューリングを担当しているsystemdユニットがどれだったか思い出せなくなる。Talorysはサーバーをまったく持たないことで、この失敗パターンを丸ごと避けています。チャット、メモリ、タスク、定期リマインダーなど必要なものはすべて、Cloudflareの無料枠で動きます。フロントエンドはPages、APIはプライベートなWorker、全状態はSQLiteを使うDurable Object、モデルはWorkers AIです。個人ユーザー1人あたりの月額費用はゼロです。
本当の問題:エージェントはステートフルで、サーバーレスはそうではない
AIエージェントは、長期間保持される状態を、リクエスト/レスポンス型アプリに見せかけたものです。会話履歴、永続的なメモリ、タスクリスト、誰もログインしていなくても朝7時に動くスケジュールジョブ、そして複数ステップのチェーン実行中にツール出力を置いておく場所が必要です。どれも、古典的なサーバーレスモデルとは相性が悪いものです。そのモデルでは関数は金魚のようなもので、起動し、1リクエストを処理し、すべてを忘れます。
業界の標準的な答えは、状態をエッジで組み直すことです。セッションデータにRedis、永続レコードにPostgres、cron用にキューとスケジューラ、意味検索にベクターデータベース。これで動きはしますが、マネージドサービスでサーバー群を組み直しただけであり、コストも運用の負担もそれ相応に増えます。エージェントのために5つのステートフルなサービスを繋ぎこむことに、エージェントのロジックを書く以上のエンジニアリング時間を費やしているチームを何度も見てきました。以前にも書いたとおり、LLMエージェントは分散システムそのものであり、分散システムは善意が死ぬ場所です。
Talorysは逆の賭けをしています。状態をすべて1か所に置くのです。personal-agentという名前のDurable Objectが1つ、会話、メモリ、タスク、ノート、プロジェクト、自動化、セッション、設定、利用量の記録といった世界全体を、組み込みのSQLiteデータベースに保持します。KV、D1、R2、Vectorizeは使いません。READMEにもこれらはプロビジョニングされないと明記されています。これは偶然ではなく、設計そのものです。
Durable Objectsは裏技だ
Durable Objectsは、今のクラウドコンピューティングで最も静かに革命的なプリミティブだと思います。これは誇張ではありません。Durable Objectは、強い一貫性を持つトランザクショナルなストレージが物理的に同じ場所に置かれた、単一インスタンスのコードです。personal-agent宛てのリクエストが届くと、Cloudflareはそのオブジェクトをコールドスタートからミリ秒単位で起動し、ローカルのSQLiteに対してコードを実行します。アイドルになると再び凍結されます。長時間稼働するサーバープロセスの使い勝手を、Lambdaの課金モデルで手に入れられるわけです。
単一ユーザー向けのパーソナルエージェントでは、このモデルの悪名高い制約が、むしろ長所になります。
- 単一ライターは機能です。ユーザーが1人なら書き込み元も1つです。マルチテナントの状態管理につきまとう並行性の悩みは消え、すべてに対して直列化されたトランザクショナルなアクセスが無料で得られます。
- 同一配置でレイテンシが消えます。エージェントのメモリ検索、タスク参照、会話履歴はローカルディスク上のSQLite読み取りであり、別のアベイラビリティゾーンにあるデータベースへのネットワーク往復ではありません。
- アラームがcronの代わりになります。Durable Objectのアラームで、オブジェクト自身がウェイクアップの予定を立てられます。リマインダー、定期ルーチン、デイリーダイジェストは、どのマシンもオンラインでなくても発火します。オブジェクトはスリープし、アラームで起き、仕事をして、また眠ります。
- 休止は無料です。アイドル時間に料金はかかりません。1日23時間アイドルのパーソナルアシスタントは、まさにサーバーレスの料金体系が想定しているワークロードです。
このプロジェクトから、もっと多くの人に持ち帰ってほしいと思う知見がこれです。アプリケーションが本質的に単一テナントなら(個人ツール、顧客ごとのワークスペース、デバイスごとのコーディネーターなど)、「テナントごとに1つのDurable Object」というパターンで、かつてはVPSが必要だったものが手に入ります。概念的には常に動いている小さなコンピューターで、アイドル時は費用がかからず、システム管理者も不要です。スケジューリングの仕組みだけでも導入の価値があります。個人用のcronサーバーを運用したことがある人なら失敗パターンを知っているはずです。マシンが再起動し、cronデーモンが戻ってこず、リマインダーが2週間ぶりに黙って止まっていたと気づく。アラームはインフラ側が管理するスケジューリングであり、面倒を見る対象が1つ減ります。
ネットワークモデルは、多くの本番構成より優れている
セキュリティの経歴がある私が身を乗り出したのはここです。TalorysのエージェントWorkerはworkers_dev: falseとpreview_urls: falseでデプロイされます。つまり公開URLが一切ありません。ブラウザが話すのは*.pages.devのサイトだけです。/api/*にあるPages Functionが、サービスバインディング経由でWorkerにリクエストを転送します。サービスバインディングとは、同一アカウント内のコンピュートどうしをつなぐCloudflareの内部的なプライベート配線です。認証と認可はフロントエンドではなくWorkerで行われます。
これで何がなくなるか考えてみてください。スキャンされる公開APIエンドポイントがない。設定を誤るファイアウォールルールがない。パッチを忘れるリバースプロキシがない。エージェントのバックエンドの攻撃対象領域は、実質的に「フロントエンドの認証フローを通らなければならない」だけです。予算もセキュリティチームもある実在の企業の本番環境をレビューしたことがありますが、この個人プロジェクトより緩いネットワーク構成のものも多くありました。私がクラウド環境で最もよく見た事故パターンは、「内部からしか到達できない」はずだった内部サービスが、ある日ロードバランサーの設定ミスで到達可能になっていた、というものです。サービスバインディングを誤って公開状態にすることはできません。URLがそもそも存在しないからです。
インストーラーにも触れておきます。オーナーのパスワードをPBKDF2-SHA256でローカルでハッシュ化し、ハッシュだけをCloudflareのシークレットとして保存します。256ビットのセッションシークレットを生成し、リソース名を一意にし、そのうえで実際にデプロイされたものを検証します。認証されていないリクエストが実際に拒否されるかどうかまで確認し、しかもAI推論のクォータは消費しません。デプロイ後に、認証がきちんと未認証のアクセスを拒否しているかを確認するこのチェックは、私がインシデントの事後レビューに書き込んできたタイプのものです。それがワンコマンドのインストーラーに入っているのを見て、嬉しい驚きでした。

自分を騙さずに無料枠の中で暮らす
無料枠は本物ですが、これは食べ放題ではなく予算です。Cloudflareは無料アカウントに対し、「Neurons」単位で測るWorkers AIの1日あたりの割り当て(1日10,000)を与え、さらにリクエストとDurable Objectの利用量の上限があります。これらの数値はCloudflareが決めるもので、変わることもあります。Talorysは、私が見てきた多くの「無料枠」プロジェクトより正直にこれを扱っています。AIの割り当てを使い切ると、チャットはそのことを率直に伝え、日次リセット後に再開します。一方で、タスク、ノート、メモリ、リマインダーはモデルを必要としないため、引き続き動きます。
自分のプロジェクトに取り入れる価値のあるガードレールです。Talorysには、最大出力トークン数、最大コンテキストトークン数(古い履歴は黙って切り捨てられるのではなく要約されます)、リクエストごとの最大ツール呼び出し数と推論ステップ数、1日の最大AIリクエスト数、1日の最大スケジュールAI実行数など、設定可能な上限があります。これは成熟した姿勢です。上限のないエージェントループは、請求額の急増やクォータ切れの障害につながります。「エージェントがツールを40回呼び出した」は、本番環境で実際に金を燃やした失敗パターンとして私も見たことがあります。関連して、単純なリマインダーやダイジェストをAIを一切使わないようにしている点も正しい判断です。コードパスが決定論的なら、確率的なモデルを経由させる必要はありません。これはモデルの出力を構造化された判断に制約することと同じ規律で、判断が本当に必要な場所でだけモデルを使うべきです。
コミュニティからの実体験に基づく注意点です。複数の人が、Workers AIを有料のWorkersプランと組み合わせた際に、不透明な請求の驚きがあったと報告しています。Neuronの計算が文書化された上限と一致せず、サポートチケットも進展しなかったそうです。詳細は私には確認できませんが、従量課金型のAI請求は至るところで分かりにくいという、私がよく知っているパターンと一致します。「無料枠に優しい」は「請求されることがあり得ない」とは違います。有料プランでデプロイするなら、初日にCloudflareダッシュボードで支出アラートを設定してください。アプリ内のローカル推定ではなく、プロバイダーの利用状況画面を真実の源として扱ってください。
「セルフホスト」論争は要点を外している
さて、冒頭の主張が求める譲歩をします。このプロジェクトへのトップコメントは「セルフホスト」という言葉をめぐる炎上になっていて、細かいことを言う人たちにも実際の一理があります。これはCloudflareに完全に依存しています。データはCloudflareのデータセンターにあり、推論はCloudflareのGPUで動き、明日無料枠を変えられたら、アシスタントもそれに合わせて変わります。これをセルフホストと呼ぶのは、言葉の意味を破綻するほど引き伸ばしています。好意的に読めば(既に管理しているCloudflareアカウント以外にサードパーティがなく、開発者へのテレメトリもなく、間に入る運用者もいない)、「自前で所有している」「自己管理(セルフカストディ)」と表現するほうが適切です。言葉は重要で、より良い言葉を選べばプロジェクトはこれほど批判されずに済んだでしょう。
ただ、細かいことを言う人たちの主張には穴があります。個人向けソフトウェアの実際の脅威モデルは「企業の利用規約が変わるかもしれない」ではありません。データの持ち出し、テレメトリ、そして開発者が破産してホスト版をシャットダウンすることです。この3つすべてに対して、Talorysは実際に強いです。分析やトラッキングのコードはなく、作者に何かを送信する仕組みもなく、シャットダウンされるホスト版も存在しません。さらに、コードはMITライセンスで、あるコメントの指摘どおり、AIの呼び出し先をローカルのモデルサーバーに向け直すのは小さなパッチで済みます。スタック全体もwrangler devの下でモックのAIプロバイダーを使えばローカルで動きます。もしCloudflareが許容できなくなっても、出口は短いのです。ライセンス確認のために開発元へ通信する、誰かのレジストリからDocker Composeファイルを引いてくるだけの「セルフホスト」アプリの多くと比べてみてください。
スレッドに埋もれている、もっとまともな批判もあります。このアシスタントが実際に優れているという証拠はないのです。チャットUI、メモリのCRUD、埋め込み、cronはどれも簡単に作れます。ハーネス、プロンプト設計、メモリ検索のポリシー、ツールスキーマこそ、パーソナルアシスタントの成否を分ける部分で、きれいなアーキテクチャ図からはそれが一切示されていません。このジャンルのほぼすべてのプロジェクトに当てはまることで、疑ってかかるべき正しい対象です。Talorysは、実証された副操縦士ではなく、インフラ(よく設計されたシャーシ)として評価してください。こうしたものにモデルを選定しようとしているなら、LLMを自分のハードウェアで動かすについての記事で、主権のスペクトラムの反対側を扱っています。

この設計から取り入れるべきこと
Talorysをデプロイしなくても、このアーキテクチャは身につける価値のある参考になります。転用できるアイデアは次のとおりです。
- テナントごとに1つのオブジェクト。アプリが単一ユーザー向け、または綺麗に分割可能なら、コードと状態を1つのDurable Objectにまとめ、キャッシュ層、メッセージキュー、コネクションプーラーを削除しましょう。
- 公開のバックエンドURLを持たない。
workers_dev: falseを使ったサービスバインディングは、サーバーレスにおいて最も安価なセキュリティ上の勝ち筋です。到達できないサービスは攻撃もされません。 - cronボックスよりアラーム。インフラと共に存在するスケジューリングは、再起動、再デプロイ、そして自分の忘れっぽさを生き延びます。
- クォータを意識した機能縮退。高コストな依存先(モデル)が失敗したり枯渇したりしても、コア製品は動き続けるようにアプリを設計しましょう。縮退は機能であり、障害であってはなりません。
- 追記型のマイグレーション。Durable Objectの名前空間は更新時に再作成されず、スキーママイグレーションは最初のリクエストでトランザクション内に実行されます。これが、データ喪失のルーレットを回さずにステートフルなサーバーレスを更新する方法です。
より大きな教訓は、個人向けソフトウェアがどこへ向かっているかについてです。過去20年、選択肢は二択でした。SaaS企業にデータを預けるか、自分が非常勤のシステム管理者になるか。Durable Objects(そして他のクラウドが必ず出すであろう模倣的なプリミティブ)は第三の道を開きます。自分だけが管理するソフトウェアを、借りているインフラで運用し、個人規模ならほぼ無料で済む道です。これはセルフホストではありません。むしろ、より良いかもしれません。


