24h AIと書かれた時計とエージェントループのフローチャートを背景に、タブレットを持つ人物が描かれた画像。スケジューラー、ツール使用、状態永続化などの要素で構成される24時間稼働AIエージェントのアーキテクチャを示している。

はじめに

Googleが社内テスト中の「Remy」は、ユーザーのニーズを予測しながら、バックグラウンドで継続的にタスクを管理するAIエージェントです。

ユーザーが何も指示しなくても、必要な情報を収集して事前にアクションを起こす——そんな「24時間稼働型の自律AI」という概念は、今後の開発現場で重要な設計パターンになりつつあります。

本記事では、Remyのような「バックグラウンドAIエージェント」を自分のプロジェクトに組み込む際のアーキテクチャと実装パターンを、コード例を交えて紹介します。
チャットボットの枠を超えた、次世代のAI活用を模索しているエンジニアの参考になれば幸いです。

こんな人におすすめ

  • LLM(大規模言語モデル)のAPIを使って自社サービスにAI機能を追加したいエンジニア
  • メールチェック・通知・データ収集などの定型業務をAIで自動化したい方
  • Anthropic / OpenAI などのAPIでツール使用(Function Calling)を初めて試す方
  • 「チャットボット以上のAI活用」を模索しているバックエンドエンジニア
  • 自律型AIエージェントの設計パターンを体系的に理解したい方

バックグラウンドAIエージェントのアーキテクチャ

従来のAIアシスタントは「ユーザーが入力する → AIが応答する」というリアクティブ(反応型)なモデルでした。
バックグラウンドAIエージェントはこれを大きく超えています。

flowchart TD
    A[スケジューラー] -->|定期実行| B[エージェントループ]
    B --> C{LLM判断}
    C -->|ツール呼び出し| D[ツール実行]
    D -->|結果返却| C
    C -->|タスク完了| E[状態保存]
    E --> F[通知・ログ記録]
    F --> A

    G[(状態ストア\nRedis/DB)] <-.->|読み書き| B
    H[外部API\nメール/カレンダー等] <-.->|実呼び出し| D

このループ構造の中で、LLMが自律的に判断しながらツールを組み合わせてタスクをこなします。

主な特徴は次の通りです。

  • プロアクティブ(先回り型): ユーザーの指示を待たず、パターンを学習して自律的に行動します
  • 継続的: スケジューラーが定期的にエージェントを起動し、24時間稼働を実現します
  • 状態保持: 過去の実行履歴や好みを記憶し、次の行動に活かします
  • ツール駆動: 外部APIやデータベースを呼び出して実際のアクションを起こします

エージェントループの実装

エージェントの核心は「ループ処理」です。
LLMにツールリストを渡し、LLMが必要なツールを呼び出しながら自律的にタスクをこなす仕組みです。

import anthropic
import json

client = anthropic.Anthropic()

# エージェントが使えるツールを定義する
tools = [
    {
        "name": "get_unread_emails",
        "description": "未読メールを取得し、件名と送信者を返す",
        "input_schema": {
            "type": "object",
            "properties": {
                "limit": {
                    "type": "integer",
                    "description": "取得する最大件数"
                }
            },
            "required": []
        }
    },
    {
        "name": "create_task",
        "description": "タスク管理システムに新規タスクを作成する",
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "due_date": {"type": "string", "description": "YYYY-MM-DD形式"},
                "priority": {
                    "type": "string",
                    "enum": ["high", "medium", "low"]
                }
            },
            "required": ["title", "due_date", "priority"]
        }
    }
]


def run_agent(context: str, max_steps: int = 10) -> str:
    """エージェントループを実行する"""
    messages = [{"role": "user", "content": context}]

    for _ in range(max_steps):
        response = client.messages.create(
            model="claude-opus-4-7",
            max_tokens=4096,
            tools=tools,
            messages=messages
        )

        # ツール呼び出しがなければ完了
        if response.stop_reason == "end_turn":
            return response.content[0].text

        # ツールを実行して結果を返す
        if response.stop_reason == "tool_use":
            messages.append({"role": "assistant", "content": response.content})
            tool_results = []

            for block in response.content:
                if block.type == "tool_use":
                    result = _execute_tool(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": json.dumps(result, ensure_ascii=False)
                    })

            messages.append({"role": "user", "content": tool_results})

    return "最大ステップ数に達しました"


def _execute_tool(name: str, inputs: dict) -> dict:
    """ツールを実際に呼び出す(実装はサービスに合わせて差し替える)"""
    if name == "get_unread_emails":
        # Gmail / Exchange API を呼ぶ箇所
        return {"emails": [{"subject": "明日の会議について", "from": "boss@example.com"}]}
    elif name == "create_task":
        # Linear / Jira API を呼ぶ箇所
        return {"status": "success", "task_id": "TASK-042"}
    return {"error": f"未定義のツール: {name}"}

stop_reason == "tool_use" を検出してツールを実行し、その結果を再度LLMに渡す——このループがAI自律動作の本質です。
観察された傾向として、5〜15ステップで多くの日常タスクは完了します。

24時間稼働のスケジューリング設計

バックグラウンドで動き続けるには、定期実行の仕組みが必要です。
PythonではAPSchedulerが手軽で、既存のWebサーバーと同一プロセスに組み込めます。

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from datetime import datetime

scheduler = AsyncIOScheduler()


async def morning_briefing():
    """毎朝9時に実行:当日のタスクを自動整理する"""
    today = datetime.now().strftime("%Y年%m月%d日")
    prompt = f"""
    今日は{today}です。以下を順番に確認して、
    優先度の高いタスクを最大3つ作成してください:
    1. 未読メールをチェックし、返信が必要なものを特定する
    2. 今日が期限のタスクを確認する
    3. 昨日完了できなかったタスクがあれば繰り越しタスクを作成する

    各タスクには必ず優先度(high/medium/low)と期限を設定してください。
    """
    result = run_agent(prompt)
    print(f"[朝のブリーフィング完了] {result[:100]}...")


async def hourly_check():
    """1時間ごとに実行:緊急メールを監視する"""
    prompt = (
        "未読メールを確認し、「緊急」または「至急」が含まれるものがあれば"
        "highの優先度でタスクを作成してください。なければ何もしなくて構いません。"
    )
    run_agent(prompt, max_steps=5)


# スケジューラーにジョブを登録する
scheduler.add_job(morning_briefing, "cron", hour=9, minute=0)
scheduler.add_job(hourly_check, "interval", hours=1)
scheduler.start()

本番環境ではCelery + Redisを使ったワーカー構成にすることで、スケール対応や障害復旧が容易になります。

状態管理と文脈の永続化

Remyが「ニーズを予測する」動作を実現するには、過去の行動パターンや好みを記憶させる必要があります。
会話履歴と設定をRedisに保存することで、エージェントが再起動しても文脈を引き継げます。

// TypeScript + Redis による状態の永続化
import Redis from "ioredis";

const redis = new Redis(process.env.REDIS_URL!);

interface AgentMemory {
  userId: string;
  history: Array<{ role: string; content: string; ts: number }>;
  preferences: Record<string, string>;
}

async function loadMemory(userId: string): Promise<AgentMemory> {
  const raw = await redis.get(`agent:memory:${userId}`);
  if (raw) return JSON.parse(raw);
  return { userId, history: [], preferences: {} };
}

async function saveMemory(memory: AgentMemory): Promise<void> {
  // 直近48時間分の履歴のみ保持(トークンコスト削減)
  const cutoff = Date.now() - 48 * 60 * 60 * 1000;
  memory.history = memory.history.filter((h) => h.ts > cutoff);

  await redis.setex(
    `agent:memory:${memory.userId}`,
    30 * 24 * 60 * 60, // 30日間保持
    JSON.stringify(memory)
  );
}

履歴のトリミングはコスト管理の観点でも重要です。
直近24〜48時間分のみを保持する設計が、目安として現実的なバランスです。

つまづきやすいポイント

  • ループの無限化: max_stepsを必ず設定してください。
    ツール呼び出しがエラーを繰り返すと際限なくAPIを呼び続けます。
    観察された傾向として、5〜15ステップで多くのタスクは完了するため、上限は15〜20が目安として適切です

  • 副作用ツールの誤動作: メール送信・タスク作成・Slack通知など外部に作用するツールは、必ずテスト用のdry_runフラグを用意してください。
    開発中に本番データを誤って更新するリスクを避けるためです

  • トークンコストの積み上がり: バックグラウンドで高頻度実行するとトークン消費が想定外に膨らみます。
    会話履歴を定期的に要約・圧縮するロジックは早めに実装しておくことをおすすめします

  • エラーの無通知化: バックグラウンド処理は失敗に気づきにくいです。
    SentryやSlack Webhookで異常を即座に検知できる監視を、最初から組み込んでおくことが重要です

まとめ

バックグラウンドAIエージェントの実装は、「エージェントループ(LLMとツール呼び出しの反復)」「スケジューラー」「状態永続化」の3つを組み合わせることで実現できます。

GoogleのRemyが目指す「ユーザーのニーズを予測する自律AI」は、こうした設計パターンの延長線上にあります。

最初は副作用の小さいツール(メールチェックや通知作成など)だけに絞ってスコープを小さく始め、安全性を確認しながら段階的に機能を拡張していくアプローチが現実的です。

LLMの活用が「チャットボット」から「自律型エージェント」へと進化しつつある今、こうしたアーキテクチャパターンを押さえておくことは、今後の開発現場でますます価値を持つことになるでしょう。

💡 CTA 指示: ここに D1「開発壁打ち(¥12,000)」 の CTA を WordPress 側で設定してください。