External Data and RAG

参照元: LiveKit Agents Documentation ロードマップ: 学習ロードマップ

What(何についてか)

このドキュメントは、LiveKit Agent が外部システムに接続して実用的な対話を行うための設計原則を示す。対象は初期コンテキスト注入、ツール経由の外部操作、会話中RAG、遅延時のユーザー通知、フロント連携までを含む。

Why(なぜ必要か)

素のLLMはユーザー固有情報・最新業務データ・書き込み操作を安全に扱えない。実運用では、外部データ接続と操作フローを明示的に設計しないと、精度・信頼性・体験品質のいずれも不安定になる。特に音声Agentでは、待機中の無音がUX劣化の主要因になるため、処理中フィードバックの設計が必須となる。

How(どう動くか)

AgentSession は空の文脈から始まるため、起動前に job metadata などから必要情報を読み取り ChatContext に注入して開始する。会話中の追加コンテキストは on_user_turn_completed で直近発話をキーにRAG検索し、次ターン生成前に turn_ctx へ追加する。高精度が必要な外部操作はtoolとして分割し、LLMに選択させる。

遅延時は、閾値超過時のみ短い音声ステータスを出す非同期タスクを本処理と並走させ、早期完了時はキャンセルする。さらに BackgroundAudioPlayer を用いて処理中の環境音を提供できる。フロント連携では perform_rpc により長時間処理の開始通知とキャンセル受付を行い、ユーザー操作を戻り値として受けてタスク中断に反映する。

graph TD
    A["User turn completed"] --> B["RAG lookup"]
    B --> C["Inject context into turn_ctx"]
    C --> D["LLM generation"]
    D --> E["Tool call if needed"]
    E --> F["External API/DB"]
    F --> G["User feedback (speech/sound/UI)"]

コードベースで確認した重要論点

1. Initial context は「接続前に仕込む」

metadata = json.loads(ctx.job.metadata)
user_name = metadata["user_name"]
 
initial_ctx = ChatContext()
initial_ctx.add_message(role="assistant", content=f"The user's name is {user_name}.")
 
await session.start(room=ctx.room, agent=Assistant(chat_ctx=initial_ctx))

session.start の時点で chat_ctx を渡すことで、最初の応答からユーザー固有文脈が効く。ctx.connect() 後に取りに行くより初動UXが安定する。

2. metadata/attributes の責務分離

学習時に確認した実務整理は以下。

  • Job metadata: ジョブ単位の可変条件(例: lesson_mode, session_goal
  • Room metadata: ルーム共通条件(例: 言語方針、運用ルール)
  • Participant attributes: 参加者個別条件(例: CEFR、弱点、agent_state

固定ロジックはコードに残し、可変条件だけを metadata 側へ寄せる。

3. 遅延発話タスクは「レース」で理解すると明快

status_update_task = asyncio.create_task(_speak_status_update(0.5))
result = await _perform_search(query)
status_update_task.cancel()
  • 検索が0.5秒未満で終われば進捗発話は出ない
  • 0.5秒超なら発話が走る
  • 検索完了時にタスクをキャンセルして過剰発話を防ぐ

これは「遅い時だけ喋る保険タスク」を本処理と競走させるパターンである。

4. RPCは通知専用ではなく request/response

response = await room.local_participant.perform_rpc(
    destination_identity=..., method="start_deep_search", payload=...
)
if response == "cancelled":
    deep_search_task.cancel()

perform_rpc は相手メソッドを呼び出して戻り値を受ける仕組み。"cancelled" はフロント実装が返すアプリ規約値であり、予約語ではない。これにより「フロントのユーザー操作でAgent側タスクを中断する」双方向制御が可能になる。

sequenceDiagram
    participant Agent
    participant Frontend
    participant User

    Agent->>Frontend: perform_rpc(start_deep_search)
    Frontend->>User: Show popup (running)
    User-->>Frontend: Click cancel
    Frontend-->>Agent: "cancelled"
    Agent->>Agent: deep_search_task.cancel()

AI英会話ユースケースへの適用例

英会話Agentでの具体値は次のように置ける。

  • Job metadata
    • lesson_mode: free_talk / role_play / interview
    • target_topic: business_meeting
    • session_goal: fluency_first
  • Room metadata
    • default_language_policy: en_main_ja_support
    • max_session_minutes: 25
  • Participant attributes
    • cefr_level: B1
    • weak_points: articles,prepositions
    • preferred_feedback_style: end_of_turn_summary

この分離により、同じAgent実装で「今回の目標」「部屋運用」「学習者個性」を独立して切り替えられる。

Key Concepts

用語説明
Initial context起動前にユーザー/タスク情報を ChatContext へ注入する手法
Job metadatadispatch時に渡すジョブ単位の可変条件
Room metadataルーム単位で共有するセッション共通条件
Participant attributes参加者単位の条件・状態を保持する属性
Tool calls外部APIや書き込み操作を明示機能としてLLMに提供する手法
on_user_turn_completedユーザーターン完了直後に会話文脈を補強するフック
Verbal status update長時間処理時のみ短い進捗発話を返すUXパターン
BackgroundAudioPlayer処理中の“thinking sound”を再生する機構
perform_rpcフロントへのメソッド呼び出しと戻り値受信を行う双方向RPC

実装上の判断基準

外部データ連携の責務分離は、固定ロジックをコード側に残し、可変条件だけを metadata/attributes に寄せる形が安定する。短い処理にまで進捗発話を入れると逆にノイズ化するため、遅延閾値を置いた遅延発話タスクの採用が有効である。長時間処理はRPCでフロント側のキャンセル導線を設けると、音声体験と操作性を両立しやすい。

一言まとめ

External Data & RAG の本質は、精度向上のためのデータ接続そのものより、文脈注入・外部操作・待機UX・キャンセル制御を一貫したライフサイクルとして設計する点にある。