ChatGPT 上で早期にワークフローが必要な場合や、独自のエージェントスタックに投資する前に ChatGPT 上でツールを試したい場合、Apps SDK は実用的な選択肢です。エージェントの動作をすべて自社で制御する必要がある場合は、通常、適していません。
ChatGPT を主な接点とし、完全なチャット製品を構築せずにツールと小規模な UI を提供したい場合は、Apps SDK を選びます。フロー、メモリ、プロンプト、書き込み処理を厳密に制御する必要がある場合は、独自のエージェントスタックを選びます。
Apps SDK は、チャットと短い UI ステップを組み合わせる製品に適しています。迅速にリリースできる一方、一部の制御を手放すことになります。
私たちの場合、明確なツール、明確なウィジェット動作、明確な次のステップが有効でした。フローの決定には LLM ではなく、これらを活用しました。モデルが最も役立ったのは、システムがすでに選択した結果を説明するときでした。
以下では、選び方に続いて、うまくいったことと、いかなかったことを説明します。
多くのチームは今も AI の試験導入にとどまるか、リスクもリターンも小さい周辺用途に AI を導入しています。ユーザーが毎週利用する業務上重要な製品をリリースしているチームは、ほとんどありません。アシスタント全体を自社で構築するのではなく、ChatGPT 内で提供することが目標なら、ChatGPT Apps SDK はその差を埋める一つの方法です。
今回の知見は、ChatGPT を主な接点とし、完全なオーダーメイドのチャット製品に投資せず迅速に提供することが要件だった、クライアント案件から得たものです。
この要件に対し、クライアントが次の点を必要としていたため、Apps SDK が適していました。
専用チャット製品の構築・ホスティングが不要—別の独立したアシスタント基盤ではなく、ChatGPT 内でユーザーに届けることが求められました
チャットと小規模なタスク特化型 UI—ワークフロー内に第 2 の完全な製品を作るのではなく、目的を絞った数個のウィジェットステップが必要でした
MCP ツールを介したバックエンド動作の公開—エンドツーエンドで所有する独自のエージェントランタイムではなく、標準のツール呼び出しが必要でした
ChatGPT 内での利用導線—ユーザーが普段作業している場所でワークフローを利用できることが必要でした
構築を進めながら、これらの選択をクライアントと検証しました。ただし、トレードオフは変わりません。ChatGPT がセッションをホストする場合、外側のランタイムを自社で所有することはできません。動作を誘導することはできますが、完全には制御できません。
Apps SDK アプリは、次の 3 つを連携させます。
ChatGPT のエージェントランタイム
自社の MCP ツール
自社のウィジェット UI
実際のフロー:
ユーザーが ChatGPT に依頼します。
ChatGPT がいずれかの MCP ツールを呼び出す場合があります。
サーバーが構造化されたツール結果を返します。
ChatGPT が結果を読み、次のステップとして追加のツール呼び出し、ユーザーへの返信、またはその両方を選びます。そのツールにウィジェットを関連付けていれば、このターンで表示できます。
ユーザーはチャットまたはウィジェットで操作を続けます(追加テキスト、選択、ウィジェットからのツール呼び出しなど)。その操作でスレッドが更新され、ChatGPT が次のターンを実行します。タスクが完了するまで手順 2~4 が繰り返されます。
このようにチャット、バックエンド処理、短い UI ステップを組み合わせられることが重要です。一方で、チャット、ツール、UI 間の受け渡しが壊れやすい部分になります。
チャット UI、ツール連携、認証パターン、ウィジェット基盤をゼロから作り直す必要はありません。多くの製品では構築時間を大幅に短縮でき、ドメインロジックとガードレールに集中できます。
ChatGPT 内で構築することは、独自のエージェントを運用することとは異なります。このプロジェクトで難しかったのは、プロンプトの小技ではありませんでした。モデルと UI の足並みがそろうように、ツール、ウィジェット、次のステップを十分明確にすることでした。
Apps SDK は一般的なフロントエンドとは異なる製品形態を提供するため、どのような用途に最適かを把握することが重要です。
Apps SDK が適している場合
ChatGPT のワークフローを迅速にリリースしたい
会話のホスティングを ChatGPT に任せたい
自然言語と目的を絞った数個の UI ステップを組み合わせたい
独自のチャットインターフェース、エージェントコンテナ、ディスカバリー機能の構築を避けたい
ユーザーがすでに ChatGPT を日常的に利用している場合、最後の点は特に重要です。
独自のエージェントが必要な場合
コードで強制できる、固定された段階的なフロー
エンドツーエンドで自社管理する独自の UI と確認フロー
独自のメモリおよび状態モデル
実行のたびに予測可能でなければならない動作
エージェントのトレース、ログ、指標
プランナー、システムプロンプト、ワークフロー全体が製品の中核なら、通常は独自スタックの方が適しています。
質問 | ChatGPT Apps SDK | 独自のエージェント |
|---|---|---|
ユーザー体験を提供する場所 | ChatGPT 内 | 自社製品内 |
会話の各ステップを実行する主体 | ツールと UI で誘導された ChatGPT | 自社のエージェント型システム |
構築する UI の範囲 | チャット内の目的特化型ウィジェット | 必要なものすべて |
プロンプトの制御範囲 | 間接的 | 全面的 |
固定された反復可能なフローの作りやすさ | 慎重な設計が必要 | コードで強制しやすい |
初回リリースまでの期間 | 多くの場合は短い | 初期段階では長くなりがち |
自社が担うプラットフォーム作業 | 少ない | 多い |
後から方向転換できる余地 | 少ない | 多い |
今回の案件で繰り返し話題になったのは「制御」でした。一方にはスピードと使い慣れたホストがあり、もう一方にはランタイムを部分的にしか所有できないという制約があります。クライアントは、スタック全体の所有よりも、ChatGPT 上でユーザーに届けることを優先し、このトレードオフを受け入れました。
順調な場合の流れは簡単に見えます。ユーザーが依頼し、ツールが動き、データが返り、選択が必要な場面でウィジェットが表示されます。
実際に苦労したのは、処理間の受け渡しでした。ウィジェットは単なる装飾ではありません。画面に表示された時点で、モデルが何を認識し、次に何をするかが変わります。ウィジェットの操作は曖昧なチャットではなく、名前付きイベントとして扱います。
この案件の技術スタックは、FastMCP、Pydantic、React、TypeScript というシンプルな構成でした。これらの統合には問題ありませんでした。難しかったのは、次に何が起きるかについて、モデル、ツール、UI の認識を一致させることでした。
各受け渡しを明確にする
ツール結果を未加工のバックエンドペイロードとして扱うことをやめました。各戻り値を、次への受け渡しとして設計しました。
優れたツール結果の条件:
ウィジェットの描画に必要な情報を提供する
ChatGPT が返信の根拠にできる構造化された事実を提供する
フロー上必要な場合は、モデルが推測せずに済むよう、次に行うべきことを示す
ウィジェットの操作から、曖昧な文章をスレッドへ返すべきではありません。ユーザーが何を行い、次に何をすべきかを明示する必要があります。
受け渡しを明確にすると、信頼性が向上しました。
短く明確な指示をツール出力とウィジェット操作に含めると、モデルはその指示に従います。
以下は、私たちが使用した小規模な Pydantic のスキーマです。output フィールドには、表示時にウィジェットが必要とする構造化データと、ChatGPT がセッションで使用する事実を格納します。agent_directions フィールドには、アシスタントが次に行うべきことを示す短い文を格納します。Reason は任意です。
Python
ウィジェットを小さく保つ
うまく機能したウィジェットは、1 つの判断だけを処理し、その後に制御を戻すものでした。ウィジェットを小さなアプリにするより、短いリスト、確認画面、要点を絞ったレビュー画面の方が効果的でした。単純な検証や固定された次のステップなど、少量のロジックをウィジェットに持たせることも、フローの決定性を高めたい場合に役立ちました。
ウィジェットメッセージでは三人称を使う
ウィジェットのフォローアップを、ユーザー自身の発言のように(「私は…を選択しました」「私は確認しました」など)書くことをやめました。代わりに、ユーザーが行ったことを伝える短い報告として(「ユーザーが…を選択しました」「ユーザーが確認しました」など)記述しました。この方法を試したのは、ChatGPT がウィジェットメッセージをユーザーメッセージではなく、ツールメッセージとして追加していたためです。
次のステップが明確なら直接実行する
ボタンから次のツール呼び出しが明確に分かる場合は、チャットの次のターンを強制するより、ウィジェットから直接呼び出す方が効果的でした。これは、次のツール呼び出しに ChatGPT からの入力が不要な場合に限ります。
これにより、決定的なフローを適用しやすくなり、チャットのターンを 1 回省くことで遅延も短縮できました。
エラー処理
ツール呼び出しに失敗した場合は、適切な MCP エラーコードと、短く平易なメッセージをツールから返しました。これにより、ChatGPT は失敗した呼び出しについて実際の情報を読み、ユーザーに問題を説明したり、妥当な次のステップを選んだりできました。
ツールコンテキストの管理
セッション状態は自社サーバーに保持しました。ChatGPT はツール呼び出しとともにセッション単位のコンテキストを送信します。FastMCP では各ツールに Context パラメーターを設定し、ハンドラーがその状態を読み書きできるようにしました。
安定した ID と以前の結果はセッションに保持し、呼び出しのたびに ChatGPT からツール引数として再送させないようにしました。
ツール呼び出しのループが発生した場合は、重複した呼び出しを検出し、ツール結果を通じて明確なエラーを返せました。
デバッグとサポートに使うセッションログも、自社側に保持しました。
当初はウィジェットを表示し、モデルが「理解した」と考えて、適切な追加ツール呼び出しを待っていました。うまくいくこともありました。しかし、うまくいかないことの方が多くありました。
明確な受け渡しがないと、ChatGPT は処理すべき場面で要約したり、ユーザーに選択をやり直させたり、停止すべき場面で計画を続けたりすることがあります。
解決策は、モデルの推測に期待するのではなく、構造化出力とウィジェットのペイロードに次のステップを明記することでした。
Apps SDK のドキュメントに従い、ツール出力、非表示メタデータ、チャットテキストに応答を分けて工夫しようとしました。しかし、ウィジェットから非表示メタデータを読み取れませんでした。そのため、この方法は利用できませんでした。
Apps SDK のドキュメントには、エージェントが選択しないようツールリストから除外しつつ、ウィジェットからは呼び出せるツールが記載されています。可視性を app-only に設定すると、そのツールはエージェントからだけでなく、ウィジェットからも利用できなくなりました。エージェントからは見えず、ウィジェットからは見える設定を実現できませんでした。
有用な処理が行われていないのに無反応だったり、一般的な「成功」だけを返したりする方が、明確なエラーより問題でした。そのため、ツールとウィジェットの失敗を正式な出力として扱いました。ステップを続行できない場合は平易な言葉で伝え、明示的なエラーを返し、表示されたものの先へ進めないウィジェットをユーザーに見せ続けないようにしました。これにより使いやすさが向上し、モデルの動作も安定しました。
独自のプラットフォーム開発を抑えながら ChatGPT 内にワークフローを構築したい場合、Apps SDK は実用的な手段です。一部の制御を手放す代わりに、スピードと、ユーザーが普段作業する場所で接点を持てる利点が得られます。
フローの全分岐、UI、各ステップの決定主体を自社で管理する必要があるなら、最初から独自のエージェントスタックを計画してください。いずれ、ChatGPT 内だけでの構築では要件を満たせなくなる可能性が高いでしょう。
まず Apps SDK を使って MCP サーバーを ChatGPT 内で稼働させ、その後、製品で必要になった段階でチャット、認証、エージェント連携を自社構築し、独自スタックへ移行することもできます。
同じ状況にあるチームは、次のステップとして、成果が明確なワークフローを 1 つ選び、チャット、ツール、ウィジェット間の受け渡しを明文化したうえで、プロンプト調整に時間をかける前に再試行とエラーを徹底的にテストしてください。