連載の現在地:Phase 5「自動化」(1 / 3本目) — 人が呼ばなくても裏で働く仕組みにする
1. 公開 → 2. 費用と安定 → 3. 安全 → 4. 品質 → 5. 自動化

こんな悩みの人へ

手元のパソコンで動かしているAIエージェントを、いつでも動く状態にしたい。定期的に情報を集めて、変化があれば知らせてくれるエージェントを作りたい。ユーザーごとに、会話の続きや作業の途中経過を覚えさせたい。そんな方に向けた記事です。

結論

CloudflareのAgents SDKがおすすめです。 エージェント1体ごとに、状態(覚えていること)を保存する仕組みと、決まった時刻に自分を起こす仕組みが用意されています。サーバーを常に起動しておく必要はなく、リクエストや予定の時刻が来たときだけ動きます。

一方、ずっと処理を回し続ける重い計算や、Python前提のエージェントフレームワークをそのまま使いたい場合は、コンテナやVPSのほうが向いています。

なぜ「常時稼働」が難しいのか

「常に動いている」といっても、その中身はほとんどが待ち時間です。実際のエージェントは、次の指示や予定の時刻、外部APIの応答を待っている時間が大半を占めます。そのためにサーバーを起動し続けると、費用も保守も無駄が多くなります。

状態をどこに置くかでも、設計が変わります。会話の履歴、作業の途中経過、ユーザーの設定。メモリに置けば再起動で消え、データベースに置けば読み書きの設計が必要になります。

複数のユーザーの状態を扱うアプリでは、利用者ごとの分離も必要です。認証した利用者と保存先の対応付けや、同時更新の設計が欠かせません。とはいえ、1つのプロセスを使うだけで必ず混ざる、という意味ではありません。

Agents SDKでは、エージェントのインスタンスをDurable Object上で動かします。インスタンスごとに保存領域があり、アプリがインスタンス名を割り当てて、ユーザーごと・用途ごとに分けられます。認証した利用者と割り当て先の照合は、アプリ側で行います。

この記事で作る構成は、次のとおりです。

[クライアント] --WebSocket / HTTP--> [Worker: routeAgentRequest()]
                                            |  /agents/watch-agent/{インスタンス名}
                                            v
                         [WatchAgent インスタンス(名前ごとに1つ)]
                           state: keywords / lastCheckedAt  (自動で保存)
                           schedule(): 予定の時刻に、自分を起こす

解決策の比較

方法

費用

手間

向いている状況

Cloudflare Agents SDK

無料枠あり。本格運用はWorkers有料プラン

中くらい

ユーザーごとに状態を持つ。待ち時間が多い

VPSで常駐プロセス

月数百円〜

多い

Python製のフレームワークをそのまま使う。重い処理を回し続ける

Cloud Runなど + スケジューラー

使った分だけ

中くらい

定期実行が中心で、状態は外部のデータベースに置ける

GitHub Actionsの定期実行

無料枠あり

少ない

1日数回の情報収集など、軽い定期作業だけ

どれを選ぶか

定期的に1つの作業をするだけなら、GitHub Actionsや、Workersの定期実行(Cron Triggers)で足ります。ユーザーごとに会話や作業の状態を持たせたいならAgents SDKを、PythonのエージェントライブラリやGPUが必要ならVPSやコンテナを選びます。

手順

前提:Cloudflareのアカウントと、Node.js 22.19以降が入ったパソコン。設定例は現在のWranglerを前提にしています。

  1. TypeScriptのWorkerプロジェクトを作り、Agents SDKを追加します。以下は画面を含まない、サーバー側の最小構成です。
npm create cloudflare@latest -- watch-agent --type=hello-world --lang=ts
cd watch-agent
npm install agents@latest
npm install --save-dev wrangler@latest
  1. src/index.ts にエージェントの中身を書きます。状態は this.state で読み、this.setState() で更新します。更新した状態は自動で保存されます。接続する画面を作れば、状態を同期することもできます。
import { Agent, routeAgentRequest, callable } from "agents";

interface Env {
  WatchAgent: DurableObjectNamespace<WatchAgent>;
}

type WatchState = { keywords: string[]; lastCheckedAt: string | null };

export class WatchAgent extends Agent<Env, WatchState> {
  initialState: WatchState = { keywords: [], lastCheckedAt: null };

  @callable()
  addKeyword(keyword: string) {
    this.setState({ ...this.state, keywords: [...this.state.keywords, keyword] });
    return this.state.keywords;
  }
}

export default {
  async fetch(request: Request, env: Env) {
    return (
      (await routeAgentRequest(request, env)) ??
      new Response("Not found", { status: 404 })
    );
  },
} satisfies ExportedHandler<Env>;

this.state を直接書き換えると保存されません。必ず setState() を使います。

  1. 設定ファイル(wrangler.jsonc)に、エージェントのクラスを登録します。exports にクラスと storage: "sqlite" を指定すると、SQLiteを使うDurable Objectとして宣言できます。
{
  "name": "watch-agent",
  "main": "src/index.ts",
  "compatibility_date": "2026-11-03",
  "compatibility_flags": ["nodejs_compat"],
  "durable_objects": {
    "bindings": [{ "name": "WatchAgent", "class_name": "WatchAgent" }]
  },
  "exports": {
    "WatchAgent": { "type": "durable-object", "storage": "sqlite" }
  }
}
  1. WatchAgent クラスの中に、次のメソッドを追加します。クライアントから scheduleCheck() を呼ぶと、60秒後に checkKeywords() が実行されます。
@callable()
async scheduleCheck() {
  return this.schedule(60, "checkKeywords");
}

async checkKeywords() {
  console.log("確認するキーワード:", this.state.keywords);
  this.setState({ ...this.state, lastCheckedAt: new Date().toISOString() });
}

この例は予約の実行と状態の保存を示すものです。情報の収集や通知は checkKeywords() に組み込みます。予約時刻は秒数・日時・cron形式で指定できます。予約APIの仕様も参照してください。

  1. npx wrangler types でWorkersの型を生成し、npx wrangler dev で起動します。クライアントの接続先は /agents/watch-agent/demo のような形式です。demo は1つの共有インスタンス名なので、利用者ごとに分ける場合は認証したユーザーIDと対応付け、他人のインスタンスへ接続できないようにします。公開時は認証・認可を設定します。

起動したら、別のターミナルからエージェントを呼んで試せます。次は、Node.jsのスクリプトから @callable() のメソッドを呼ぶ動作確認用の例です。

// try-agent.ts(動作確認用)
import { AgentClient } from "agents/client";

async function main() {
  const client = new AgentClient({ agent: "watch-agent", name: "demo", host: "localhost:8787" });
  console.log(await client.call("addKeyword", ["Cloudflare"])); // 追加後のキーワード一覧
  await client.call("scheduleCheck"); // 60秒後に checkKeywords() を予約
  client.close();
}
main();

npx tsx try-agent.ts で実行すると、追加後のキーワード一覧が表示されます。もう一度実行して前回のキーワードが残っていれば、状態が保存されています。60秒ほど待つと、wrangler dev のターミナルに checkKeywords() のログが出ます。画面から使う場合は、Reactの useAgent() フックでも同じように呼び出せます。書き方はSDKのバージョンで変わることがあるため、Agentクラスの仕様もあわせて確認してください。

動作を確認できたら、公開します。

npx wrangler deploy
更新日:2026-11-03

つまずきやすい点

設定ファイルの class_name は、コードのクラス名と完全に一致させます。違っていると、デプロイの時点か実行時にエラーになります。

routeAgentRequest が返したレスポンスは、加工せずにそのまま返してください。作り直すと、WebSocketの接続が切れてしまいます。

TypeScriptの設定で、experimentalDecorators を有効にしないことも大切です。これを有効にすると、@callable() が実行時に動かなくなります。

なお、LLMの利用料は別にかかります。エージェントが自動で動くほど、APIの呼び出しも増えます。費用の上限は、「LLM APIの請求額が読めない」の方法で設定してください。

この記事のまとめ:AIエージェントを常時動かしたい。悩み:PCを閉じても使いたい、予定の時刻に動かしたい、利用者ごとに覚えたい。解決の流れ:イベントを受ける→状態を読み書き→次の実行を予約。進め方:プロジェクトを作る→状態とメソッドを作る→クラスと予約を設定→保存と実行を確認。つまずきやすい点:クラス名を一致させる、WebSocketを維持、LLM費用にも上限この記事のまとめ:AIエージェントを常時動かしたい。悩み:PCを閉じても使いたい、予定の時刻に動かしたい、利用者ごとに覚えたい。解決の流れ:イベントを受ける→状態を読み書き→次の実行を予約。進め方:プロジェクトを作る→状態とメソッドを作る→クラスと予約を設定→保存と実行を確認。つまずきやすい点:クラス名を一致させる、WebSocketを維持、LLM費用にも上限

次に出やすい悩み

状態を持ったエージェントが動いたら、次は、重い処理をどう分けるか、何を覚えさせるかを設計します。1回では終わらない大量の処理や、決まった時刻の処理を組み込みたいなら「定期実行や時間のかかる処理が必要になった」を、会話の続きや利用者の好みを覚えさせたいなら、Phase 4の「AIに会話の続きを覚えさせたい」を読んでみてください。checkKeywords() で実際にWebの情報を集めたい場合は、「Webの情報収集や画面操作を自動化したい」で方法を紹介しています。


「手元のPCで動いているエージェントを、そのまま常時動かせるか」の見極めからご相談いただけます。エージェントの開発と、動かす基盤の構築はCloudflareインフラ構築をご覧ください。

よくある質問

Q. 常時使えるエージェントには、起動し続けるサーバーが必要ですか?

この記事のように、指示や予約の到着を待ち、そのときだけ処理する構成なら、常駐サーバーを自分で管理する必要はありません。会話や作業の状態を保存し、イベントを受けて処理を再開します。CPUを使い続ける計算や、GPUが必要な処理は分けて設計します。

Q. 複数の利用者の会話や作業が混ざるのを防ぐには?

認証した利用者とエージェントのインスタンスをサーバー側で対応付けます。利用者が送ってきたインスタンス名をそのまま信用せず、その利用者に接続権限があるかも確認してください。状態の保存領域を分けることと、他人の保存領域へのアクセスを防ぐことの両方が必要です。

Q. Pythonで作ったエージェントを、そのまま移せますか?

使っているライブラリや実行環境の確認が必要です。本文はTypeScriptのAgents SDKを使う構成なので、Python前提のフレームワークやネイティブ拡張をそのまま使えるとは限りません。既存のPython環境を維持したい場合は、コンテナやVPSで動かす方法も比較します。

Q. 自動実行でLLMの利用料が増えないようにするには?

実行頻度、1回あたりの呼び出し数、再試行の回数に上限を設け、利用者や処理の種類ごとに利用量を記録します。エージェントを動かす基盤の費用と、LLM APIの料金は分けて管理してください。予約方法はAgentsの予約APIを、予算管理はLLM APIの費用対策を参照できます。

執筆者情報

パブロフ株式会社

IT顧問、AI・業務アプリ開発、SESを通じて、企業のIT活用を支援しています。代表の藤澤 昌隆は、2007年からシステム開発・運用に携わり、アクセンチュアでの業務ITコンサルティングを経て、2019年にパブロフ株式会社を設立しました。

AI活用の支援事例として、営業工程を支える7つの業務別AIアプリの構築を公開しています。アプリの開発に加え、入力・出力の形式、評価基準、テストケースの整備にも取り組んでいます。

記事の内容に関するご質問や誤りのご指摘は、お問い合わせ窓口へお寄せください。

参考資料