AIアシスタントにおけるポーリングエージェント:11の実装パターン
AIエージェント向けの信頼性の高いポーリングパターン
ポーリングエージェントは、AIアシスタントのアーキテクチャにおいて最も華やかさを感じない部分の一つですが、同時に最も実用的な部分の一つでもあります。
一般的なチャットアシスタントは、ユーザーが質問をするのを待ちます。一方、ポーリングエージェントは常に監視し続けます。ソースをチェックし、変化に気づき、それが重要かどうかを判断し、そして行動を起こします。その行動は、通知、要約、ドラフトの作成、ツール呼び出し、あるいはフルワークフローの実行である可能性があります。
これにより、アシスタントは「質問に答える」モードから「これを監視して」というモードへと移行します。リアクティブ(受動的)な存在ではなく、ユーザーの代わりに物事を察知し、条件が満たされた際に行動を起こすバックグラウンドプロセスへと変化します。

重要な設計のポイントはシンプルです。言語モデルに時間、状態、リトライ、ロックの責任を負わせすぎないことです。そのための通常のバックエンドインフラストラクチャを使用します。モデルは、その価値がある場面で使用します。つまり、複雑な文脈の解釈、意味的な判断、有用な言語の生成を行うためです。
ポーリングエージェントとは何か?
ポーリングエージェントとは、ソースを繰り返しチェックし、特定の条件が満たされたときにアシスタントのアクションをトリガーするバックグラウンドプロセスです。アシスタントがLLM(大規模言語モデル)、メモリ、ツール、ルーティング、可視化を組み合わせた広範な AIシステム スタックにおいて、ポーリングレイヤーはアシスタントを単なるリアクティブなものから、プロアクティブ(能動的)なものへと変える役割を果たします。5層構造の全体像については、AIアシスタントアーキテクチャ:LLM、メモリ、ツール、ルーティング、可視化 を参照してください。
例:
- 毎朝受信ボックスをチェックし、重要なメッセージを要約する。
- Notionのタスクリストを監視し、次のtodoアイテムを実行する。
- GitHubのIssueがステータスが変わるまで監視する。
- 長時間実行されるAIジョブの結果が準備できるまでポーリングする。
- 予約枠が空くまでチェックする。
- 供給ポータルでドキュメントが表示されるまで監視する。
- 週に1回新しい研究論文をスキャンし、関連するものを要約する。
実用的なポーリングエージェントには、5つの責任があります。
- 適切な時間に起動する。
- ソースから読み取る。
- すでに確認済みのものを記憶する。
- 新しい状態が重要かどうかを判断する。
- 自身を繰り返さずに、一度だけ安全に行動する。
典型的な本番環境のフローは以下のようになります。
scheduler
-> polling worker
-> source system
-> state store
-> deterministic filters
-> optional LLM evaluation
-> assistant action
この構造は、最も良い意味で「地味」です。地味なシステムは、午前2時のデバッグが容易です。
全てのポーリングエージェントに必要な状態
ポーリングエージェントは、耐久性のある状態(durable state)を必要とします。会話履歴だけでは不十分です。アシスタントは会話を記憶できるかもしれませんが、システムには信頼性の高い運用記録が必要です。
良いポーリング状態の記録には、通常以下が含まれます。
{
"poll_id": "poll_123",
"user_id": "user_456",
"source_type": "notion",
"source_ref": "database_tasks",
"condition": "Todo状態のタスクを1つ取得して実行する",
"interval_seconds": 600,
"last_run_at": "2026-06-19T01:00:00Z",
"next_run_at": "2026-06-19T01:10:00Z",
"last_seen_cursor": "cursor_or_timestamp",
"last_result_hash": "b64e8a...",
"failure_count": 0,
"status": "active"
}
正確なスキーマはソースに依存しますが、ほとんどのシステムではこれらの概念が必要です。
ポールの定義
これは、エージェントが何を監視し、なぜ監視するかを記述します。
poll_id
user_id
workspace_id
source_type
source_ref
condition_text
priority
status
例えば:
source_type: notion
source_ref: Tasks database
condition_text: Todoのタスクを1つ探し、それを主張(claim)し、実行し、Completeにマークする。
スケジュール
これは、エージェントがいつ実行されるべきかを記述します。
interval_seconds
cron_expression
timezone
last_run_at
next_run_at
jitter
10分ごとにNotionをチェックするHermesエージェントの場合:
interval_seconds: 600
timezone: Australia/Melbourne
カーソルまたはスナップショット
これは、エージェントが同じデータを再処理することを避けるのを助けます。
ソースに応じて、以下のようなものかもしれません。
last_seen_id
last_seen_timestamp
api_cursor
etag
version
content_hash
Notionのタスクキューの場合、カーソルよりもタスクのステータスとclaimフィールドの方が重要かもしれません。Gmail、GitHub、または同期APIの場合、カーソルは通常重要です。
クレーム(Claim)またはリース(Lease)
これは、2つのワーカーが同じジョブを取得することを防ぎます。
claimed_by
claimed_at
claim_expires_at
run_id
例えば、Notionのタスクは以下のように変更できます。
Status: Todo
から:
Status: InProgress
ClaimedBy: hermes
ClaimedAt: 2026-06-19T01:00:00Z
ClaimExpiresAt: 2026-06-19T01:30:00Z
RunId: run_789
これは、「1つのワーカーだけがそれを取得するのを祈る」ことと、「システムにクレームプロトコルがある」ことの違いです。
実行記録
これは、実行中に何が起こったかを記録します。
run_id
poll_id
source_object_id
started_at
finished_at
status
items_checked
items_changed
decision_summary
error
実行記録は、Notionや他の外部ツールだけでなく、アシスタントのバックエンドに保存されるべきです。Notionは人間の可視性には優れていますが、唯一の実行ログとしては理想的ではありません。
重複排除記録(Dedupe Record)
これは、重複した通知や反復的なアクションを防ぎます。
dedupe_key
poll_id
source_object_id
condition_version
action_type
delivered_at
例えば:
user_456:poll_123:notion_page_999:execute:v1
同じアクションが再度試みられた場合、システムはそれを抑制できます。
方法1:スケジュールされたポーリングワーカー
これは最もシンプルで信頼性の高いパターンです。
スケジューラは一定の間隔で起動し、ワーカーを呼び出します。ワーカーはソースを読み取り、状態を更新し、必要に応じてアシスタントのアクションをトリガーします。
scheduler
-> worker
-> source API
-> database
-> assistant action
実行方法
スケジューラは時間を担当します。それはcron、クラウドスケジューラ、Kubernetes CronJob、または内部の小さなスケジューラかもしれません。
各間隔で、ワーカーの実行を開始します。ワーカーは設定を読み込み、ターゲットソースをクエリし、結果を保存された状態と比較し、必要に応じてアクションを起こします。
シンプルなアシスタントの場合、これだけで十分です。単一のスケジューラと軽量なワーカープロセスは、キュー、リース、分散調整を必要とせずに、数十の日常的なチェックを処理できます。
状態モデル
スケジューラはほとんど何も保存しません。通常、ジョブをトリガーするタイミングしか知りません。
アプリケーションデータベースは重要な状態を保存します。
poll definition
schedule
cursor or snapshot
last run time
failure count
status
ワーカーはステートレスであるべきです。実行中に一時的なデータを保持できますが、耐久性のある真実はデータベースにあるべきです。
例のフロー
10分ごと:
Hermesポーリングワーカーをトリガー
ワーカー:
アクティブなポーリング設定を読み込む
ソースをクエリ
以前の状態と比較
決定論的なチェックを実行
必要に応じてLLMを呼び出す
状態を更新
アシスタントイベントを発行
最も適した用途
スケジュールされたポーリングワーカーは以下に使用します。
- 日次要約。
- 時間ごとのチェック。
- 小さな内部自動化。
- シンプルな「これを監視する」タスク。
- 低〜中規模のアシスタントジョブ。
弱点
スケジュールされたポーリングは理解しやすいですが、スケーリング時に脆くなる可能性があります。多くのポーリングが同時に実行されると、ワーカーに過負荷をかけたり、プロバイダーのレート制限に達したりする可能性があります。スケジューラが直接作業を開始する場合、リトライも複雑になりがちです。
方法2:キューベースのポーリングワーカー
キューベースのポーリングは、本番環境のAIアシスタントにおいて、通常、最も良いデフォルトです。
スケジューラはポーリングを直接実行しません。キューにジョブを投入します。ワーカープロセスはキューからジョブを消費します。
scheduler
-> queue
-> worker pool
-> source API
-> state store
-> assistant action
実行方法
スケジューラは期限を迎えたポーリングをスキャンし、ジョブをキューに投入します。ワーカーは容量があるときにジョブを引き取ります。
これにより、バックプレッシャーが得られます。システムが混雑している場合、ジョブはソースAPIやLLMプロバイダーを圧倒するのではなく、キューで待機します。
状態モデル
データベースはポーリング状態を保存します。
poll_id
user_id
source_ref
condition_text
next_run_at
cursor
status
failure_count
キューメッセージは小さく保つべきです。
{
"poll_id": "poll_123",
"scheduled_for": "2026-06-19T01:10:00Z",
"attempt": 1
}
ワーカーは開始時にデータベースから完全な状態を読み込みます。
例のフロー
1分ごと:
スケジューラは next_run_at <= now となるポーリングを見つけ
スケジューラはジョブをキューに投入
ワーカー:
キューからジョブを引き取る
ポーリングをロックまたはリースする
ソースをクエリ
状態を更新
必要に応じてアシスタントアクションを発行
next_run_at を設定
最も適した用途
キューベースのポーリングは以下に使用します。
- マルチユーザーのAIアシスタント。
- 多数の同時ポーリング。
- レート制限のある統合。
- リトライ可能なバックグラウンド作業。
- 時間がかかるジョブ。
- 信頼性が重要なSaaSプロダクト。
弱点
キューはインフラストラクチャを追加します。デッドレター処理、冪等性、可視性タイムアウト、リトライポリシーが必要です。本番システムには価値がありますが、小さなプロトタイプには過剰かもしれません。
方法3:外部ツールをタスクキューとして使用
これは、NotionとHermesの例におけるパターンです。
外部ツールは単なるデータソースではありません。それは人間向けのタスクキューになります。エージェントは定期的にツールをチェックし、1つのタスクをクレームし、実行し、タスクのステータスを更新します。
scheduler
-> Hermes worker
-> Notion database
-> claim one task
-> execute task
-> update Notion status
実行方法
10分ごとに、HermesはNotionデータベースをクエリし、Todo ステータスの1つのタスクを取得します。次のタスクを選択し、通常は優先度と作成時間に基づいて選択します。その後、InProgress に設定することでタスクをクレームします。
その後、Hermesはタスクを実行します。実行が成功した場合、タスクを Complete とマークします。実行が失敗した場合、タスクを Failed とマークするか、リトライカウントとともに Todo に戻します。
状態モデル
Notionは人間向けのタスク状態を保存します。
Title
Description
Status: Todo | InProgress | Complete | Failed
Priority
CreatedAt
ClaimedBy
ClaimedAt
ClaimExpiresAt
RunId
RetryCount
LastError
CompletedAt
Hermesバックエンドは運用実行状態を保存します。
run_id
notion_page_id
started_at
finished_at
execution_status
tool_calls
LLM trace
error details
idempotency_key
この分離は重要です。Notionは可視性と手動編集に優れています。Hermesバックエンドはログ、リトライ、重複排除、監査履歴には適しています。
例のフロー
10分ごと:
Hermesが起動
Hermes:
Status = Todo の1つのタスクのためにNotionをクエリ
Priority, CreatedAt でソート
選択されたタスクを InProgress に更新
ClaimedBy, ClaimedAt, ClaimExpiresAt, RunId を設定
タスクを実行
実行ログを書き込む
タスクを Complete または Failed に設定
最も適した用途
このパターンは以下で使用します。
- 人間がすでにNotion、Jira、Linear、Trello、または他のツールで作業を管理している場合。
- アシスタントに可視的なタスクを処理させたい場合。
- タスクボードがユーザーインターフェースである場合。
- シンプルなヒューマンインザループ自動化モデルが必要な場合。
弱点
外部ツールは完璧なキューではありません。アトミックなクレームが制限されているかもしれません。クエリの整合性が遅れる可能性があります。レート制限が適用されるかもしれません。エージェントが複数のインスタンスで実行できる場合、慎重なクレームまたはリース戦略が必要です。
実用的な推奨事項は、Notionを人間向けのタスクインボックスとして使用し、すべての実行ログ、リトライ記録、トレース、冪等性キーをHermesに保持することです。Notionはユーザーに可視性を提供し、Hermesはシステムの信頼性を保ちます。このパターンの背後にあるHermesのディスパッチャーと並行性メカニクスについては、Hermesエージェントでのセルフホスト型LLMワークフローのためのKanban を参照してください。
方法4:長時間実行ワーカーループ
長時間実行ループは、最もシンプルな実装です。
while True:
due_polls = db.find_due_polls()
for poll in due_polls:
run_poll(poll)
sleep(30)
このパターンは、スケジューリングと実行を1つのサービスに結合し、バックグラウンドエージェント作業の最もシンプルな開始点にします。
実行方法
ワーカープロセスは継続的に実行されます。数秒または数分ごとに、データベースをチェックし、期限を迎えたポーリングを実行します。構築が容易で、論理的に理解しやすく、開発中の反復が高速です。
状態モデル
データベースは依然として耐久性のある状態を保存します。
poll configuration
next_run_at
cursor
last result
failure count
status
プロセスメモリは一時的な状態のみを包含すべきです。
current batch
short-lived cache
in-flight run
重要な進行状況はメモリにのみ保存しないでください。プロセスがクラッシュした場合、耐久性のあるストレージに書き込まれていない状態は失われ、次の実行は処理がどこで中断されたかを把握する方法がありません。
最も適した用途
長時間実行ループは以下に使用します。
- プロトタイプ。
- ローカル開発。
- 内部ツール。
- シングルテナントシステム。
- 低容量エージェント。
弱点
このパターンは、複数のレプリカではリスクが高くなります。リースなしでは、2つのワーカーが同じポーリングを実行する可能性があります。また、本物のキューやワークフローエンジンの運用機能も不足しています。
長時間実行ループは開始点として正しくありませんが、分散スケジューラではなく、それとして扱われるべきではありません。複数のレプリカやより強い信頼性保証が必要になった時点で、上記のより構造化されたパターンのいずれかに移行する必要があります。
方法5:Webhookファーストとポーリングフォールバック
ソースがWebhookをサポートしている場合は、それを使用してください。ポーリングは、主メカニズムではなく、バックアップであるべきです。
external system
-> webhook endpoint
-> event store
-> assistant action
reconciliation poll
-> source API
-> compare with event store
-> repair missed events
実行方法
外部システムは、何かが変更されたときにWebhookエンドポイントにイベントを送信します。システムはイベントを保存し、非同期的に処理します。
より遅い再整合性ポーリングは、数時間ごとまたは1日1回実行されます。イベントが欠落していないかチェックします。
状態モデル
イベントストアは受信したWebhookを記録します。
event_id
source_type
source_object_id
event_type
received_at
payload_hash
processed_at
signature_valid
再整合性ポーリングは以下を保存します。
last_reconciliation_at
last_seen_cursor
last_seen_version
ソースオブジェクトテーブルは、最新の状態を保存します。
external_id
current_status
external_updated_at
last_processed_event_id
最も適した用途
Webhookファーストアーキテクチャは以下に使用します。
- GitHubイベント。
- Stripeイベント。
- Slackイベント。
- CRM更新。
- デプロイメント通知。
- チケッティングシステム。
弱点
Webhookはパブリックエンドポイント、署名検証、リプレイ保護、イベントの重複排除を必要とします。一部のプロバイダーは不完全なイベントも送信するため、完全なオブジェクトを取得する必要がある場合もあります。
それでも、良いWebhookが存在する場合、1分ごとにポーリングするのは通常無駄です。
方法6:プロバイダー側のバックグラウンドジョブポーリング
ポーリングされるものがAIジョブそのものであることがあります。
アプリケーションは長時間実行されるプロバイダージョブを開始し、ジョブIDを保存し、後で完了したかどうかをチェックします。
app
-> start AI background job
-> store provider job id
-> poll status
-> fetch result
-> notify user
実行方法
アシスタントはプロバイダーでジョブを開始します。プロバイダーはIDを返します。バックエンドはそのIDを保存し、ジョブが成功、失敗、期限切れ、またはタイムアウトするまでステータスを確認します。
状態モデル
バックエンドは以下を保存します。
assistant_task_id
provider_job_id
user_id
status
created_at
last_checked_at
expires_at
result_ref
プロバイダーは一時的なジョブ状態と出力を保存します。
出力が重要である場合、ジョブが完了した直後に、独自の耐久性のあるストレージにコピーしてください。プロバイダー側の結果ストレージは保持期間が短く、システム内の適切なアーカイブの代替にはなりません。
最も適した用途
プロバイダー側のバックグラウンドジョブポーリングは以下に使用します。
- 長時間のAI研究タスク。
- 大規模ドキュメント処理。
- コードベース解析。
- レポート生成。
- データ抽出ジョブ。
- 通常のHTTPリクエストタイムアウトを超えるタスク。
弱点
このパターンは1つの問題を解決します:長時間のプロバイダージョブを待つこと。ワークフローエンジン、スケジューラ、キュー、またはビジネス状態ストアの代替ではありません。
方法7:耐久性のあるワークフローエンジン
耐久性のあるワークフローエンジンは、長時間実行される実行、タイマー、リトライ、回復を管理します。TemporalsはGoおよびPythonベースのアシスタントバックエンドで最も一般的な選択肢です。完全な実装ガイドについては、GoでTemporalを使用したワークフローアプリケーションの実装 を参照してください。
各待機とリトライを手動で配線する代わりに、プロセスをワークフローとしてモデル化します。
workflow engine
-> activity: check source
-> timer: wait
-> activity: evaluate result
-> activity: notify user
実行方法
ワークフローは一度開始され、その後、独自の待機を制御します。数分、数日、または数週間スリープできます。ワーカープロセスがクラッシュしても、ワークフローエンジンは記録された状態から再開できます。
状態モデル
ワークフローエンジンは以下を保存します。
workflow_id
execution history
timer state
activity attempts
retry policy
current workflow state
アプリケーションデータベースは以下を保存します。
user-facing poll definition
authorization references
business records
notification records
ワークフローエンジンはプロセス状態(実行履歴、タイマー、リトライ、アクティビティ試行)を所有します。データベースはビジネス状態(ユーザー設定、承認記録、通知、監査ログ)を所有します。これらを分離することで、各レイヤーが両方の混同されたハイブリッドになるのを防ぎます。
最も適した用途
耐久性のあるワークフローは以下に使用します。
- 複数ステップのビジネスプロセス。
- 長時間実行される自動化。
- 人間の承認フロー。
- 信頼性の高いリトライ。
- 監査可能なバックグラウンド作業。
- 失敗後に再開する必要があるプロセス。
弱点
ワークフローエンジンは概念とインフラストラクチャを追加します。プロセスが重要な場合に優れていますが、単純な時間ごとのチェックには重すぎます。
方法8:永続的なエージェントランタイム
いくつかのエージェントフレームワークは、エージェント状態を永続化し、実行をチェックポイントし、後で再開できます。
これは、エージェント自体に複数ステップの推論プロセスがある場合に有用です。
scheduler or workflow
-> agent runtime
-> load checkpoint
-> call tools
-> save checkpoint
-> resume later
実行方法
外部スケジューラまたはワークフローがエージェントを開始します。エージェントランタイムは以前の状態を読み込み、次のステップを実行し、必要に応じてツールを呼び出し、チェックポイントを書き込みます。
エージェントランタイムは、唯一のスケジューラであるべきではありません。より大きなバックエンドアーキテクチャ内の推論レイヤーとして扱うのが良いでしょう。
状態モデル
エージェントチェックポイントストレージには以下が含まれます。
current node
messages
tool outputs
intermediate reasoning state
pending action
長期メモリには以下が含まれます。
stable user preferences
facts
project context
source references
運用状態は依然として他にあるべきです。
poll schedule
cursor
status
retry count
dedupe records
有用なルール:メモリはカーソルではなく、チェックポイントはキューではありません。エージェントメモリはモデルが知っていることを保存し、運用状態はプロセスがどこにあり、何を行ったかを追跡します。これら2つを混同すると、並行性や再起動後にのみ現れる微妙なバグにつながります。ワーキングメモリ、耐久性のある状態、検索レイヤーの完全な設計空間は、AIアシスタントのメモリシステム でカバーされています。
最も適した用途
永続的なエージェントランタイムは以下に使用します。
- 複数ステップの研究。
- 一時停止と再開を行うエージェント。
- ヒューマンインザループ作業。
- ツール多用の推論。
- コンテキストが時間とともに蓄積されるタスク。
弱点
エージェントの永続性は、運用信頼性と同じではありません。スケジューリング、ロック、リトライ、レート制限、監査ログを依然として必要とします。
方法9:データベース同期と変更評価
このパターンでは、ポーリングは外部データを独自のデータベースに同期するために使用されます。アシスタントは、各評価サイクルで外部APIを直接クエリする代わりに、ローカルデータベースの変更に応答します。
sync poller
-> external API
-> local database
-> change evaluator
-> assistant action
これは、データ同期とアシスタントの知性を分離します。同期ワーカーはローカルレコードを更新する責任があり、評価者は変更について何をすべきか決定する責任があります。各レイヤーは独立してテスト、監視、スケーリングできます。
実行方法
同期ワーカーは定期的に外部変更を取得し、正規化されたレコードをデータベースに書き込みます。2番目のワーカーまたは変更ストリームは、更新された行を検出し、アシスタントがアクションを起こすべきかどうかを決定します。
状態モデル
同期テーブルは以下を保存します。
external_id
source_type
raw_payload
normalized_fields
external_updated_at
synced_at
version
content_hash
同期状態は以下を保存します。
source_cursor
last_sync_at
rate_limit_status
failure_count
アシスタント評価テーブルは以下を保存します。
object_id
evaluation_status
last_evaluated_hash
decision
notification_id
最も適した用途
このパターンは以下に使用します。
- CRM同期。
- チケッティングシステム。
- 会計ドキュメント。
- 製品在庫。
- コンプライアンスレビュー。
- 検索インデックス。
- 内部ダッシュボード。
弱点
すべてを同期するのは高価で不要な場合があります。また、プライバシーと保持の義務を生み出す可能性があります。ローカルデータに単一のアシスタントアクション以上の価値がある場合にこのパターンを使用してください。
方法10:適応型ポーリング
適応型ポーリングは、状態、緊急性、または最近のアクティビティに基づいて頻度を変更します。
active object: poll every 1 minute
waiting object: poll every 1 hour
stale object: poll once per day
completed object: stop polling
実行方法
各実行後、ワーカーは次の実行がいつ行われるべきかを決定します。
オブジェクトが最近変更された場合、早めにポーリングします。長い間何も変更されていない場合、速度を落とします。タスクが完了した場合、停止します。
状態モデル
ポーリング状態には以下が含まれます。
current_interval
minimum_interval
maximum_interval
backoff_policy
last_activity_at
priority
stop_condition
ソーススナップショットには以下が含まれます。
status
updated_at
activity_level
expected_next_change
最も適した用途
適応型ポーリングは以下に使用します。
- デプロイメントステータス。
- 配送追跡。
- カレンダー枠の利用可能性。
- 価格監視。
- ビルドジョブ。
- 長時間実行されるプロバイダータスク。
- バースト更新のある任意のソース。
弱点
適応型ポーリングは論理的に理解しにくい場合があります。タスクが厳密な時間に実行される必要がある場合、厳密に保ってください。コンプライアンスジョブを賢くしないでください。
方法11:LLM評価者によるセマンティックポーリング
セマンティックポーリングは、条件が曖昧な場合に使用されます。
コードは以下に答えることができます。
ステータスはCompleteに等しいか?
価格は100未満か?
新しいメッセージがあるか?
LLMは以下に答えるのを手伝うことができます。
このメールは緊急性を帯びているか?
この顧客は不満を持っている可能性が高いか?
この研究論文は関連しているか?
この変更は私の注意が必要か?
実行方法
ワーカーはまず、安価な決定論的なフィルタを適用します。候補アイテムのみがLLMに送られます。
new item?
matches source filters?
not already processed?
not obviously irrelevant?
その後、LLMはより小さな候補セットを評価し、構造化された出力を返します。
{
"should_notify": true,
"urgency": "high",
"reason": "The customer reports a production outage."
}
状態モデル
ポーリング定義は以下を保存します。
semantic_condition
examples
negative_examples
user_preference_summary
model_config
評価ログは以下を保存します。
input_reference
model
prompt_version
structured_output
confidence
cost
latency
ポーリング状態は以下を保存します。
last_seen_ids
last_evaluated_hashes
last_decision
last_decision_reason
最も適した用途
セマンティックポーリングは以下に使用します。
- 重要なメールの検出。
- カスタマセンチメントモニタリング。
- 研究アラート。
- セールス機会の検出。
- セキュリティトリアージ。
- 経営陣向けブリーフィング。
弱点
LLM呼び出しはコストがかかり、レイテンシを追加します。また、プロンプトとスキーマが緩い場合、一貫性がないこともあります。まず決定論的なフィルタを使用してください。判断が実際に必要な場合にのみモデルに問いかけます。
意思決定テーブル:ポーリングエージェント方法の選択
| 方法 | 最も適したアプリケーション | 利点 | 欠点 |
|---|---|---|---|
| スケジュールされたポーリングワーカー | シンプルな定期的なアシスタントタスク | 構築が容易、デバッグが容易、最小限のインフラストラクチャ | スケーリングが限定的、基本的なリトライ、多くのポーリングが同時に発火するとワーカーに過負荷がかかる可能性があります |
| キューベースのポーリングワーカー | 多くのユーザーを持つ本番環境SaaSアシスタント | スケーラブル、回復力がある、リトライとバックプレッシャーをサポート | キューインフラストラクチャ、冪等性、デッドレター処理が必要 |
| 外部ツールをタスクキューとして | Notion、Jira、Linear、Trelloベースのタスク実行 | 人間に親しみやすい、検査が容易、既存のワークフローと連携 | 外部ツールは完璧なキューではない、アトミックなクレームが難しい可能性があります |
| 長時間実行ワーカーループ | プロトタイプと内部ツール | とてもシンプル、実装が高速、動く部品が少ない | 信頼性が弱く、マルチレプリカでの動作が悪い、運用制御が限定的 |
| Webhookファーストとポーリングフォールバック | イベント駆動型統合 | 反応が速い、API呼び出しが少なく、再整合性が欠落したイベントを捕捉 | パブリックエンドポイント、イベント検証、重複排除、プロバイダーWebhookサポートが必要 |
| プロバイダー側のバックグラウンドジョブポーリング | 長時間実行されるAIプロバイダージョブ | 低速なAIタスクを処理、シンプルなステータスモデル、非同期UXに適している | プロバイダージョブのステータスのみを管理し、完全なビジネスワークフローは管理しない |
| 耐久性のあるワークフローエンジン | 長時間実行される複数ステップのプロセス | 強力なリトライ、タイマー、監査履歴、クラッシュ後の回復 | より多くのインフラストラクチャと概念、単純なポーリングには重すぎる |
| 永続的なエージェントランタイム | 複数ステップの推論エージェント | エージェントコンテキストを保持、一時停止と再開をサポート、ツール多用タスクに適している | スケジューラやキューの代替ではない、依然として運用バックエンドが必要 |
| データベース同期と変更評価 | 外部データにローカル価値があるシステム | 明確な分離、ローカルレポート、繰り返し外部呼び出しが少ない | ストレージが多く、同期の複雑さが増し、プライバシーと保持の懸念の可能性 |
| 適応型ポーリング | バーストソースまたは可変緊急性タスク | コストを削減、レート制限を尊重、アクティビティが高い場合に素早く反応 | 論理的に理解しにくく、厳密なスケジュールには適さない |
| LLM評価者によるセマンティックポーリング | 判断が必要な曖昧な条件 | 自然言語の意図を処理、有用な要約、柔軟な決定 | コスト、レイテンシ、プロンプト品質のリスク、単純なコードチェックの代替にはならない |
推奨されるデフォルトアーキテクチャ
ほとんどの本番環境AIアシスタントの場合、以下から始めます。
polls table
-> scheduler
-> queue
-> stateless workers
-> deterministic filters
-> optional LLM evaluator
-> notification or assistant action
最小限のスキーマ:
CREATE TABLE polls (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
source_type TEXT NOT NULL,
source_ref TEXT NOT NULL,
condition_text TEXT NOT NULL,
schedule_type TEXT NOT NULL,
interval_seconds INTEGER,
timezone TEXT,
next_run_at TIMESTAMP NOT NULL,
last_run_at TIMESTAMP,
cursor_value TEXT,
last_hash TEXT,
status TEXT NOT NULL,
failure_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
CREATE TABLE poll_runs (
id TEXT PRIMARY KEY,
poll_id TEXT NOT NULL,
started_at TIMESTAMP NOT NULL,
finished_at TIMESTAMP,
status TEXT NOT NULL,
items_checked INTEGER,
items_matched INTEGER,
decision_summary TEXT,
error TEXT
);
CREATE TABLE notifications (
id TEXT PRIMARY KEY,
poll_id TEXT NOT NULL,
user_id TEXT NOT NULL,
dedupe_key TEXT NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
delivered_at TIMESTAMP,
UNIQUE (dedupe_key)
);
これにより、明確な分離が得られます。
scheduler owns time
queue owns buffering
worker owns execution
database owns state
LLM owns semantic judgment
assistant owns user interaction
その分離は、信頼性の高いポーリングエージェントの心臓部です。
例:HermesエージェントがNotionタスクを処理
ここで、アーキテクチャを具体的なケースに適用しましょう。
Notionデータベースにタスクが含まれていると仮定します。Hermesは10分ごとに実行し、Todo ステータスの1つのタスクを取得し、InProgress に設定し、実行し、その後 Complete とマークします。
これは以下として最もよく記述されます。
external tool as task queue
+
scheduled polling worker
+
claim or lease based execution
本番環境バージョンでは、以下になります。
queue-based polling with Notion as the human-facing task inbox
Notionタスクのプロパティ
Notionデータベースには以下のようなフィールドが含まれるべきです。
Name
Status: Todo | InProgress | Complete | Failed
Priority
CreatedAt
ClaimedBy
ClaimedAt
ClaimExpiresAt
RunId
RetryCount
LastError
CompletedAt
重要なフィールドは ClaimedAt、ClaimExpiresAt、RunId です。これらはタスクのクレームを可視的で回復可能にします。
Hermes実行状態
Hermesは独自の実行記録も保持すべきです。
run_id
notion_page_id
started_at
finished_at
status
input_snapshot
tool_calls
result_summary
error
idempotency_key
これにより、Notionが手動で編集された場合、API呼び出しが失敗した場合、またはHermesが実際に何を行ったかを監査する必要がある場合に保護されます。
実行フロー
10分ごと:
Hermesスケジューラが実行を作成
Hermesワーカー:
Status = Todo の1つのNotionタスクを見つける
Priority と CreatedAt でソート
Status = InProgress に設定してタスクをクレーム
ClaimedBy, ClaimedAt, ClaimExpiresAt, RunId を書き込む
タスクを実行
実行ログをHermesバックエンドに書き込む
成功時に Notion Status = Complete に設定
失敗時に Notion Status = Failed に設定
Hermesがタスクをクレームした後クラッシュした場合、リースは期限切れになります。
Status = InProgress
ClaimExpiresAt < now
将来の実行は、タスクを回復するか、失敗としてマークできます。
障害処理
成功時:
Status = Complete
CompletedAt = now
LastError = empty
回復可能な障害時:
Status = Todo
RetryCount = RetryCount + 1
LastError = short error message
回復不可能な障害時:
Status = Failed
LastError = clear explanation
安全性のために、Hermesは冪等性キーも使用するべきです。
notion_page_id + task_version + action_type
これにより、リトライが誤ったタイミングで発生した場合でも、同じタスクが2回実行されるのを防ぎます。
なぜこれは単なるポーリングではないのか
ポーリング部分は、起動メカニズムだけです。本当のアーキテクチャは、タスクのクレームと信頼性の高い実行です。
素朴な実装は以下と言います。
Every 10 minutes, find a Todo task and do it.
信頼性の高い実装は以下と言います。
Every 10 minutes, claim exactly one eligible task, record the run, execute idempotently, and move the task to a terminal state.
それが、デモと信頼できるエージェントの違いです。
一般的なポーリングエージェントのミス
ミス1:クレームプロトコルの欠如
2つのワーカーが同じタスクを見ることができる場合、両方が実行する可能性があります。
以下を使用してください。
ClaimedBy
ClaimedAt
ClaimExpiresAt
RunId
現在1つのワーカーを実行している場合でも、後で2番目のワーカーが出現するかもしれないとして設計してください。
ミス2:重複排除キーの欠如
外部アクションはすべて重複排除キーを持つべきです。
user_id + poll_id + source_object_id + action_type + condition_version
これにより、繰り返し通知、繰り返しメール、繰り返しタスク実行、および繰り返しツール呼び出しを防ぎます。これらのキーのスコーピング、保存、テストに関する広範な原則は、ここに等しく適用されます — 実際に機能する分散システムでの冪等性 を参照してください。
ミス3:LLMの早期呼び出し
モデルにデータベースフィルタリングを行わないでください。
悪い:
Send all tasks to the LLM and ask which one is Todo.
良い:
Use the Notion API filter to fetch Todo tasks.
Then use the LLM only if task interpretation is needed.
ミス4:Notionを唯一のバックエンドとして扱う
Notionは良い人間インターフェースです。それは完全な実行バックエンドではありません。
実行ログ、リトライ、トレース、冪等性記録をHermesに保持してください。
ミス5:無限ポーリング
ポーリングはすべて停止条件を持つべきです。
例:
stop after success
stop after date
stop after max retries
stop when user disables it
stop after repeated authorization failure
停止条件のないポーリングエージェントは、静かなコストの漏れです。
ミス6:可視性の欠如
以下に答えられるべきです。
What did the agent run?
Why did it run?
What did it read?
What did it change?
Why did it fail?
Did it notify the user?
Did it run twice?
これらの質問に答えられない場合、システムは重要な作業の準備ができていません。
可視性チェックリスト
以下のメトリクスを追跡してください。
polls_due
polls_started
polls_succeeded
polls_failed
tasks_claimed
tasks_completed
tasks_failed
claim_expired_count
duplicate_suppressed_count
llm_calls
llm_cost
rate_limit_count
average_run_duration
以下のログフィールドを記録してください。
poll_id
run_id
source_type
source_object_id
claim_id
cursor_before
cursor_after
decision
dedupe_key
error
以下の管理ビューを構築してください。
active polls
stuck InProgress tasks
recent failures
high retry tasks
dead letter jobs
expensive LLM evaluations
disabled integrations
ポーリングエージェントは、障害が静かで、問題が誰にも気づかれる前に累積するバックグラウンドで実行されます。バックグラウンドシステムには、問題が発生してから後から追加するのではなく、最初から組み込まれた可視性が必要です。AIおよびLLMベースのシステムのための完全な可視性スタック(メトリクス、トレース、構造化ログ、SLO)については、LLMシステムの可視性:本番環境でのメトリクス、トレース、ログ、およびテスト を参照してください。
最終推奨事項
本格的なAIアシスタントの場合、キューベースのポーリングワーカーと耐久性のある状態ストアから始めます。プロバイダーがサポートしている場所ではWebhookを追加します。レート制限が重要な場合は適応型ポーリングを使用します。プロセスが長時間実行され、複数ステップである場合は耐久性のあるワークフローエンジンを使用します。エージェントが時間とともに推論する必要がある場合は永続的なエージェントランタイムを使用します。
HermesとNotionの例の場合、正しいアーキテクチャは以下です。
Notion as the human-facing task inbox
Hermes scheduler every 10 minutes
Hermes worker with claim or lease logic
Hermes backend for execution logs and idempotency
Notion status updates for visibility
ポーリング間隔は難しい部分ではありません。難しい部分は、エージェントが1つのタスクをクレームし、1回だけ実行し、何が起こったかを記録し、システムを人間が理解できる状態に残すことを確実に行うことです。
それが、ポーリングスクリプトを信頼性の高いAIアシスタントに変えるものです。間隔でも、モデルでもなく、作業のクレーム、記録、そして人間と将来の実行の両方が理解できる状態にシステムを残すという規律です。