トランスクリプトとメッセージパート

Goa-AIのトランスクリプトモデルとプランナーとUIの状態を簡素化する方法を理解する。

トランスクリプトが重要な理由

Goa-AIはトランスクリプトをランの唯一の真実の情報源として扱います:以下を行うのに十分なメッセージとツールインタラクションの順序付きシーケンスです:

  • すべてのモデルコールのプロバイダーペイロード(Bedrock/OpenAI)を再構築、
  • プランナー(リトライとツール修復を含む)を駆動、
  • 正確な履歴でUIを動かす。

トランスクリプトが権威があるため、以下を手動で管理する必要はありません

  • 以前のツールコールとツール結果の個別リスト、
  • アドホックな「会話状態」構造、
  • 以前のユーザー/アシスタントメッセージのターンごとのコピー。

トランスクリプトのみを永続化して渡します。Goa-AIとそのプロバイダーアダプターはそこから必要なものすべてを再構築します。

メッセージとパート

モデル境界では、Goa-AIはトランスクリプトを表すためにmodel.Message値(runtime/agent/modelから)を使用します。各メッセージにはロール(userassistant)と順序付きのパートリストがあります:

  • ThinkingPart
    プロバイダーの推論コンテンツ(プレーンテキスト + 署名または編集されたバイト)。ユーザー向けではありません。監査/リプレイとオプションの「思考」UIに使用されます。

  • TextPart
    ユーザーに表示される可視テキスト(質問、回答、説明)。

  • ToolUsePart
    アシスタント開始のツールコール:

    • ID: ラン内で一意のtool_use識別子。
    • Name: 正規のツールID(ドット区切りのservice.toolset.tool)。
    • Input: ツールのスキーマに一致するJSONペイロード。
  • ToolResultPart
    以前のtool_useと相関するユーザー/ツール結果:

    • ToolUseID: 対応するToolUsePartID
    • Content: ツール結果のJSONペイロード(任意の形状)。

順序は神聖です:

  • ツールを使用するアシスタントメッセージは通常次のようになります:
    • ThinkingPart、次に1つ以上のToolUsePart、次にオプションのTextPart
  • ユーザー/ツール結果メッセージには通常、以前のtool_use IDを参照する1つ以上のToolResultPartと、オプションのユーザーテキストが含まれます。

Goa-AIのプロバイダーアダプター(例:Bedrock Converse)は、これらのパートを順序を変えずにプロバイダー固有のブロックに再エンコードします。

トランスクリプト契約

Goa-AIの高レベルトランスクリプト契約は:

  • アプリケーション(またはランタイム)はランのすべてのイベントを順番に永続化します:
    • アシスタントの思考、テキスト、tool_use(ID + 引数)、
    • ユーザーのtool_result(tool_use_id + コンテンツ)、
    • 後続のアシスタントメッセージなど。
  • 各モデルコールの前に、呼び出し元はそのランのトランスクリプト全体[]*model.Messageとして提供します。最後の要素は新しいデルタ(ユーザーテキストまたはtool_result)です。
  • Goa-AIはそのトランスクリプトを同じ順序でプロバイダーのチャット形式に再エンコードし、以下を保持します:
    • どのツールがどの入力で呼び出されたか、
    • どの結果がどの出力で返されたか、
    • すべての可視テキストと思考。

別の「ツール履歴」APIはありません。トランスクリプトが履歴です。

プランナーとUIの簡素化

トランスクリプトはプランナーとUIの両方にはるかに簡単なメンタルモデルを解放します:

  • プランナー

    • planner.PlanInput.Messagesplanner.PlanResumeInput.Messagesで現在のトランスクリプトを受け取ります。
    • メッセージ(以前のtool_use/tool_resultコンテンツを含む)のみに基づいて何をすべきかを決定でき、追加の状態をスレッドする必要がありません。
    • ツールを呼び出すと、ランタイムはそれらのコール/結果をToolUsePart / ToolResultPartとして記録し、自動的にトランスクリプトを拡張します。
  • UI

    • チャット履歴、ツールリボン、エージェントカードを、モデル用に永続化するのと同じ基礎トランスクリプトからレンダリングできます。
    • 別の「ツールログ」構造は不要です。すべてのツールコール/結果は相関IDを持つトランスクリプトにすでに存在します。
  • プロバイダーアダプター

    • どのツールが呼び出されたか、どの結果がどこに属するかを推測する必要がありません。トランスクリプトパート → プロバイダーブロックを単純にマップします。
    • ダブルエンコーディングや損失のある投影を回避します。トランスクリプトはすでにプロバイダー対応です。

つまり、トランスクリプトを永続化すると、以下ができます:

  • モデルコールを再発行(リトライ、新しいプロンプト、または分析用)、
  • UIビュー(チャット、デバッグ、監査)を再構築、
  • 診断を実行—トランスクリプト以外の追加のブックキーピングなしで

トランスクリプトの出所

トランスクリプトが構築され使用される2つの一般的な方法があります:

  • Goa-AIエージェントラン内
    生成されたエージェントとランタイムを使用する場合:

    • ランタイムはフックイベント(ToolCallScheduledToolResultReceivedAssistantMessageEventThinkingBlockEventなど)を発行します。
    • メモリサブスクライバー(memory.Store)やアプリケーションはこれらをmemory.Eventとして永続化します。
    • プランナーとプロバイダーアダプターはplanner.PlanInput.Messages / PlanResumeInput.Messagesを介して現在のトランスクリプトを見ます。
    • カスタムモデルクライアントを構築しない限り、model.Messageを直接触る必要はほとんどありません。
  • カスタムモデルクライアント / 外部オーケストレーター
    features/model/*を直接呼び出す場合:

    • トランスクリプトを[]*model.Messageとして組み立てるかロードします。
    • 各呼び出しでモデルクライアントに渡します。クライアントはプロバイダーエンコーディングとストリーミングを処理します。
    • ストリームされたtool_use/tool_resultとテキストパートに基づいてトランスクリプトを更新し、次の呼び出しのために永続化します。

両方のパスは同じ不変条件を共有します:トランスクリプトが必要な唯一の状態です。

トランスクリプトとストリーミング

ストリーミングとトランスクリプトは補完的です:

  • ストリーミングレイヤーruntime/agent/stream)はAssistantReplyPlannerThoughtToolStartToolEndAgentRunStartedなどのリアルタイムイベントを発行します。これらはUIと観測可能性に最適化されています。
  • トランスクリプトレイヤーリプレイとプロバイダーコールに最適化されています:model.Message形式で思考/テキスト/tool_use/tool_resultを記録します。

典型的なパターン:

  1. ランが実行されます。ストリームサブスクライバーはUIシンク(SSE、WebSocket、Pulse)にイベントを送信します。
  2. メモリサブスクライバーは永続イベント(メッセージ、ツールコール/結果、プランナーノート、思考)を選択したストアに書き込みます。
  3. 再度モデルを呼び出す必要がある場合、保存されたトランスクリプトからランの[]*model.Messageを再構築し、モデルクライアントに渡します。

ストリームを「ユーザー/オペレーターが今見ているもの」、トランスクリプトを「モデルが見るもの(そして再び見るもの)」と考えることができます。

トランスクリプトフレンドリーなエージェントの設計ガイドライン

トランスクリプトモデルを最大限に活用するには:

  • 常にツール結果を相関させる
    ツール実装とプランナーがtool_use IDを保持し、ToolResultPart.ToolUseIDを介して正しいToolUsePartにツール結果をマップすることを確認してください。

  • 強力で説明的なスキーマを使用する
    Goa設計の豊富なArgs / Returnタイプ、説明、例は、トランスクリプト内でより明確なツールペイロード/結果を生成し、これにより:

    • LLMが無効なコールを修復するのを助け、
    • UIと監査を容易にします。
  • ランタイムに状態を所有させる
    プランナーで並列の「ツール履歴」配列や「以前のメッセージ」スライスを維持することを避けてください。代わりに:

    • PlanInput.Messages / PlanResumeInput.Messagesから読み取り、
    • ランタイムが新しい思考/tool_use/tool_resultパートを追加することに依存します。
  • トランスクリプトを一度永続化し、どこでも再利用
    選択したストア(Mongo、インメモリ、カスタム)が何であれ、トランスクリプトを再利用可能なインフラストラクチャとして扱います:

    • 同じトランスクリプトがモデルコール、チャットUI、デバッグUI、オフライン分析をバックアップします。

詳細なプロバイダーレベルのルール(特にBedrockの思考/tool_use要件)については、goa-aiリポジトリのGoa-AIランタイムドキュメント(例:docs/runtime.mddocs/ui_thinking_rendering.md)を参照してください。