ストリーミングUI

Goa-AIストリーミングイベントを消費するリアルタイムユーザーインターフェースの構築。

Goa-AIは構造化されたイベントストリームを提供し、レスポンシブなリアルタイムUIの構築を可能にします。

ストリームイベント

Goa-AIは以下のイベントタイプを発行します:

  • AssistantReply – アシスタントテキストのチャンク
  • PlannerThought – 思考/推論ブロック
  • ToolStart – ツール実行の開始
  • ToolUpdate – ツールの進捗更新
  • ToolEnd – 結果を伴うツール完了
  • AgentRunStarted – 子エージェントラン(リンクあり)
  • AwaitClarification – ユーザー入力を待機中
  • AwaitExternalTools – 外部ツールを待機中
  • Usage – トークン使用量メトリクス
  • Workflow – ワークフロー状態の変更

ストリームへの接続

type MySink struct{}

func (s *MySink) OnEvent(ctx context.Context, event stream.Event) error {
    switch e := event.(type) {
    case *stream.AssistantReply:
        fmt.Printf("アシスタント: %s", e.Text)
    case *stream.ToolStart:
        fmt.Printf("ツール開始: %s", e.ToolID)
    case *stream.ToolEnd:
        fmt.Printf("ツール終了: %s", e.ToolID)
    case *stream.AgentRunStarted:
        fmt.Printf("子エージェント: %s (run: %s)", e.AgentID, e.ChildRunID)
    }
    return nil
}

// サブスクライブ
sink := &MySink{}
stop, err := rt.SubscribeRun(ctx, runID, sink)
if err != nil {
    return err
}
defer stop()

UI設計パターン

チャットメッセージ

AssistantReplyイベントをバッファリングして、現在のメッセージコンテンツを構築:

const [message, setMessage] = useState("");

onEvent((event) => {
  if (event.type === "assistant_reply") {
    setMessage(prev => prev + event.text);
  }
});

ツールステータス

ToolStartToolEndを追跡してローディングインジケーターを表示:

const [activeTools, setActiveTools] = useState<Set<string>>(new Set());

onEvent((event) => {
  if (event.type === "tool_start") {
    setActiveTools(prev => new Set([...prev, event.toolCallId]));
  }
  if (event.type === "tool_end") {
    setActiveTools(prev => {
      const next = new Set(prev);
      next.delete(event.toolCallId);
      return next;
    });
  }
});

ネストされたエージェント

AgentRunStartedを使用して子エージェントのUIを作成:

onEvent((event) => {
  if (event.type === "agent_run_started") {
    // childRunIdでサブスクライブして子イベントを取得
    subscribeToRun(event.childRunId);
  }
});

次のステップ