このケーススタディでは、既存の AI ストーリー SaaS である StoryShelf を AgentChat に接続する。ユーザーは会話を通じてストーリーを作成し、エージェントは StoryShelf API を介して構造化されたストーリーおよび章のレコードを書き込み、イラスト作成ジョブを開始し、返された画像 URL を確認して、ストーリーを更新する。その後、StoryShelf は自身のデータベースを読み込み、通常のライブラリとエディターに結果を表示する。
重要な点は、AgentChatがストーリーデータベースになるわけではなく、StoryShelfが長いアシスタントの回答をレコードに解析するわけでもないということです。エージェントは、StoryShelfアプリケーションがすでに理解しているものと同じ、制御された操作を実行します。
ステップ1:StoryShelfの機能パックを公開する
POST /v1/stories
GET /v1/stories/{story_id}
POST /v1/stories/{story_id}/chapters
PUT /v1/stories/{story_id}/chapters/{chapter_id}
POST /v1/image-jobs
GET /v1/image-jobs/{job_id}
PUT /v1/stories/{story_id}/cover 各作成レスポンスは安定したIDを返します。ストーリーとチャプターの書き込みはclient_request_idを使って冪等になるため、再試行しても重複は作成されません。画像ジョブは非同期で、poll_after_secondsを返します。完了した画像レスポンスにはasset_idとimage_urlが含まれ、base64データは決して含まれません。
ステップ2:StoryShelfドキュメントをプロビジョニングする
POST /api/api-documents
Authorization: Bearer ac_live_AGENT_CHAT_KEY
Content-Type: application/json
{
"title": "StoryShelf Story and Illustration API",
"description": "Create stories and chapters, generate illustrations, and attach image assets.",
"content": "# StoryShelf API\nBase URL: https://stories.example.com/v1\n\nCreate the story record before chapters and preserve every returned ID. Write chapters one at a time.\n\nPOST /stories body: client_request_id, title, audience, style, premise, outline[]. Response: story_id, status.\n\nPOST /stories/{story_id}/chapters body: client_request_id, position, title, content. Response: chapter_id, revision.\n\nPOST /image-jobs body: client_request_id, story_id, chapter_id optional, prompt, aspect_ratio. Response: job_id, status, poll_after_seconds. Start the job in one turn; sleep and poll in a later turn.\n\nGET /image-jobs/{job_id} returns status and, when succeeded, asset_id and image_url. Call view_image when visual inspection is useful.\n\nPUT /stories/{story_id}/cover body: asset_id.\n\nNever publish a draft unless the user explicitly asks."
} ドキュメントには、OpenAPIのパス一覧からは確実に推測できないワークフロールールが含まれています。IDを保持すること、非同期のターンを分離すること、画像URLを検査すること、暗黙的に公開しないことです。
ステップ3:ユーザーのストーリーセッションを作成する
POST /api/agent/sessions
Authorization: Bearer ac_live_AGENT_CHAT_KEY
Content-Type: application/json
{
"title": "Story creator for user_88",
"llm_config_id": "creative_model_uuid",
"api_document_ids": ["story_api_document_uuid"],
"system_prompt": "Help the user plan and create stories. Save approved creative work through StoryShelf APIs. Keep the user informed when image jobs are running.",
"max_turns": 25,
"tool_timeout_seconds": 180,
"tool_result_max_chars": 12000,
"host_headers": [
{ "host": "stories.example.com", "header_key": "Authorization", "header_value": "Bearer short_lived_story_user_token" }
]
} StoryShelf はバックエンドからこのセッションを作成し、返されたセッション UUID をユーザーの会話の横に保存します。短期間有効なヘッダーにより、すべてのストーリー API の読み取りと書き込みが user_88 に限定されます。これはリクエスト時に注入され、LLM コンテキストや AgentChat API のレスポンスには含まれません。
ステップ 4:ユーザーがチャットで作成できるようにする
POST /api/agent/sessions/session_uuid/chat
Authorization: Bearer ac_live_AGENT_CHAT_KEY
Content-Type: application/json
{
"message": "Create a three-chapter story for ages 8-10 about a child who repairs a lighthouse for lost spaceships. Use a gentle watercolor style and make an illustrated cover."
} AgentChat はユーザーメッセージを保存して processing を返します。StoryShelf UI はチャットリクエストを開いたままにせず、直ちにメッセージのポーリングを開始します。
具体的なツール呼び出しシーケンス
- エージェントはタイトル、前提、対象読者、スタイル、3 部構成のアウトラインを計画します。
- POST /stories を呼び出し、story_id story_123 を受け取ります。
- 第 1 章、第 2 章、第 3 章を個別の API 呼び出しで書き込み、それぞれの chapter_id を保持します。
- 保存されたストーリースタイルと一致する画像プロンプトを作成し、POST /image-jobs を呼び出します。
- ジョブは非同期で完了する必要があるため、次のモデルターンでは poll_after_seconds を使って sleep を呼び出し、その後のリクエストで GET /image-jobs/job_7 をポーリングします。
- レスポンスに image_url が含まれている場合、エージェントは view_image を呼び出し、イラストをネイティブなマルチモーダル入力としてコンテキストに取り込みます。
- 画像がストーリーに合っていれば、asset_id を指定して PUT /stories/story_123/cover を呼び出します。合っていなければ、修正したプロンプトを新しいジョブとして送信します。
- 保存されたストーリーID、章の概要、カバーのステータスを示して終了します。
ツールの結果は完了したものから永続化されますが、次のモデルリクエストには、並行実行されたツールの結果が元のツール呼び出し順で渡されます。APIドキュメントでは、ある呼び出しが別の呼び出しから返されたIDを必要とする場合、同じ並列バッチ内で依存する書き込みを行わないよう促すべきです。
ステップ5:AgentChatメッセージを正しくマージする
GET /api/agent/sessions/session_uuid/messages?after_id=msg_uuid&after_revision=6
Authorization: Bearer ac_live_AGENT_CHAT_KEY StoryShelfクライアントは、revision が増加した場合にのみメッセージを置き換えます。アシスタントのテキスト、簡潔なイラスト生成ジョブのアクティビティ、最終画像のプレビューを表示できます。失敗した部分的なアシスタントメッセージは表示されたままですが、AgentChatによって今後のLLMコンテキストから除外されます。
画像生成によって残りのターン予算が使われた場合、AgentChat は next_action が continue である状態を返します。StoryShelf は「続行」ボタンを表示し、POST /api/agent/sessions/session_uuid/continue を呼び出します。新しい実行には新たなターン予算が与えられ、永続化されたメッセージを引き継いで続行します。
ステップ 6:StoryShelf データベースからストーリーをレンダリングする
POST /stories、POST /chapters、PUT /cover によって、検証済みのレコードがすでに StoryShelf に書き込まれています。ライブラリページは StoryShelf のテーブルをクエリし、新しいタイトルとカバーを表示します。エディターは story_id に基づいてチャプターを読み込みます。チャットのトランスクリプトを解析してストーリーを再構築することはありません。
この設計により、部分的な完了を安全に扱えます。カバーのジョブが失敗しても、3 つのチャプターは残ります。ユーザーは同じ AgentChat セッションを続行し、ストーリーを再生成せずに新しいカバーを依頼できます。
ローンチ前にテストすべきこと
- 同じ client_request_id を使ってストーリーとチャプターの作成を再試行し、重複レコードが作成されないことを確認する。
- 画像ジョブの失敗を返し、エージェントがストーリーを保持し、復旧可能な手順を説明することを確認する。
- 画像のポーリング中に停止し、その後セッションを続行して表紙を完成させる。
- 注入されたトークンを使って別ユーザーのストーリーIDを読み取ろうとし、StoryShelfが403または「見つかりません」を返すことを確認する。
- 無効な画像URLを返し、エージェントがそれをテキストとして埋め込むのではなく、ツールエラーを報告することを確認する。
- 完了した各ミューテーションの後、StoryShelfライブラリを独自のデータベースから更新する。