0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Google Gemini 3.5 Transcribe × Agora Conversational AIでリアルタイム音声AIcを構築する

0
Posted at

6a8f2806b0abdb1b2ab11b79_using-gemini-3.5-transcribe-with-agora-conversational-ai-agora-1.jpg

音声AIエージェントでは、一般的に次のようなカスケード型のパイプラインが利用されています。

ユーザーの音声 → Speech-to-Text → LLM → Text-to-Speech → ユーザー

Speech-to-Text(STT)がユーザーの音声をテキストへ変換し、LLMがその内容を理解して応答を生成、Text-to-Speech(TTS)がその応答を再び音声へ変換します。

Agora Conversational AIでは、この一連のパイプラインをリアルタイムRTCセッション内で管理できます。

Agora Agents SDK for TypeScriptのGeminiTranscribeSTTを利用することで、Google Gemini 3.5 TranscribeをSpeech-to-Text部分として利用しながら、それ以外のコンポーネントは特定のプロバイダーに依存しない構成にできます。

たとえば、Gemini 3.5 TranscribeをSTTとして利用しながら、

  • Gemini / OpenAI互換LLM
  • MiniMax
  • ElevenLabs
  • Google TTS

などを組み合わせることができます。

RTCやRTMについても、他のAgora Conversational AIエージェントと同じアーキテクチャをそのまま利用できます。

この記事では、

  • Gemini 3.5 Transcribeのサーバー側設定
  • AIエージェントの起動
  • ブラウザからRTCへ接続
  • RTM経由でTranscriptを受信
  • RTC + RTM Tokenの生成
  • Agentの停止
  • よくあるトラブル
  • 本番環境でのチェックポイント
  • Gemini 3.5のSmart Transcription

まで、実装の流れをまとめて紹介します。


アーキテクチャ

Geminiとのインテグレーションはサーバー側で動作します。

ブラウザがGeminiへ直接接続することはありません。

そのため、Google API Keyがクライアント側へ渡ることもありません。

処理の流れはシンプルです。

ユーザー
   │
   │ マイク音声
   ▼
Browser
   │
   ▼
Agora RTC
   │
   ▼
Gemini 3.5 Transcribe
   │
   │ Transcript
   ▼
LLM
   │
   │ Response
   ▼
TTS
   │
   ▼
Agora RTC
   │
   ▼
ユーザー

ブラウザからマイク音声をAgora RTCへPublishします。

Agoraはその音声をGemini 3.5 Transcribeへ渡し、文字起こし結果をLLMへの入力として利用します。

LLMが生成した回答はTTSへ送信され、生成された音声がAgora RTCを経由してユーザーへ返されます。

一方で、RTM(Real-Time Messaging)は別のデータパスとして利用されます。

RTMでは、

  • Transcript
  • Agent State
  • Metrics
  • Error

などのアプリケーションイベントを扱います。

簡単に整理すると、

RTC → 音声
RTM → アプリケーションイベント

という役割分担です。


SDKをインストールする

Node.jsまたはNext.jsサーバーへAgora Agents SDKをインストールします。

npm install agora-agents

ブラウザ側でRTC音声、RTMイベント、Agent Client Toolkitを利用する場合は、以下をインストールします。

npm install agora-rtc-react agora-rtc-sdk-ng agora-rtm agora-agent-client-toolkit agora-token

[!NOTE]
AI Agentの作成処理はサーバー側に置いてください。

特に以下の情報をブラウザBundleへ含めないように注意してください。

  • Agora App Certificate
  • Google API Key

環境変数を設定する

Next.jsの場合、たとえば以下の環境変数を利用できます。

NEXT_PUBLIC_AGORA_APP_ID=your_agora_app_id
NEXT_AGORA_APP_CERTIFICATE=your_agora_app_certificate
GOOGLE_API_KEY=your_google_api_key
NEXT_PUBLIC_AGENT_UID=123456

Agora App IDについてはブラウザ側へ公開しても問題ありません。

一方で、

NEXT_AGORA_APP_CERTIFICATE
GOOGLE_API_KEY

はサーバー専用の値です。

これらのSecretにはNEXT_PUBLIC_を付けないでください。

NEXT_PUBLIC_AGENT_UIDはRTC Channel内でAI Agentを識別するためのUIDです。

ブラウザ側では、このUIDを使って人間のParticipantとAI Agentを区別します。

そのため、ClientとServerで同じAgent UIDを使用する必要があります。


Agent Pipelineを作成する

次のTypeScriptサンプルでは、

  • Gemini 3.5 Transcribe:STT
  • Gemini:LLM
  • Google TTS:音声出力

という構成でAgentを作成します。

import {
  Agent,
  AgoraClient,
  Area,
  ExpiresIn,
  GeminiTranscribeSTT,
  Gemini,
  GoogleTTS,
} from 'agora-agents';

function requireEnv(name: string): string {
  const value = process.env[name];

  if (!value) {
    throw new Error(`Missing environment variable: ${name}`);
  }

  return value;
}

const appId = requireEnv('NEXT_PUBLIC_AGORA_APP_ID');
const appCertificate = requireEnv('NEXT_AGORA_APP_CERTIFICATE');
const googleApiKey = requireEnv('GOOGLE_API_KEY');

const client = new AgoraClient({
  area: Area.US,
  appId,
  appCertificate,
});

const greeting = 'Hello! How can I help?';

const agent = new Agent({
  client,
  instructions: 'You are a concise and helpful voice assistant.',
  greeting,
  failureMessage: 'Please wait a moment.',
  maxHistory: 50,

  turnDetection: {
    config: {
      speech_threshold: 0.5,

      start_of_speech: {
        mode: 'vad',
        vad_config: {
          interrupt_duration_ms: 160,
          prefix_padding_ms: 300,
        },
      },

      end_of_speech: {
        mode: 'vad',
        vad_config: {
          silence_duration_ms: 480,
        },
      },
    },
  },

  advancedFeatures: {
    enable_rtm: true,
  },

  parameters: {
    data_channel: 'rtm',
    enable_error_message: true,
    enable_metrics: true,
  },
})
  .withStt(
    new GeminiTranscribeSTT({
      apiKey: googleApiKey,
      model: 'models/gemini-3.5-transcribe-live',
    }),
  )
  .withLlm(
    new Gemini({
      apiKey: googleApiKey,
      model: 'gemini-3.6-flash',
      systemMessages: [
        {
          parts: [
            {
              text: 'You are a concise and helpful voice assistant.',
            },
          ],
          role: 'user',
        },
      ],
      greetingMessage: greeting,
      failureMessage: 'Please wait a moment.',
      maxHistory: 15,
    }),
  )
  .withTts(
    new GoogleTTS({
      key: googleTtsCredentials,
      voiceName: 'en-US-Chirp3-HD-Charon',
    }),
  );

export async function startAgent(
  channelName: string,
  requesterUid: string,
): Promise<string> {
  const session = agent.createSession({
    name: `gemini-transcribe-${Date.now()}`,
    channel: channelName,
    agentUid: process.env.NEXT_PUBLIC_AGENT_UID ?? '123456',
    remoteUids: [requesterUid],
    idleTimeout: 30,
    expiresIn: ExpiresIn.hours(1),
    debug: false,
  });

  return await session.start();
}

AgoraClientは、App IDとApp Certificateを使用してConversational AIのLifecycle APIを認証します。

const client = new AgoraClient({
  area: Area.US,
  appId,
  appCertificate,
});

Agent.createSession()では、Agent用のRTC Tokenを生成し、RTC Channelへ参加するためのリクエストを作成します。

const session = agent.createSession(...)

そして、

session.start()

を実行すると、Runtime Agent IDが返されます。

アプリケーション側で後からAgentを停止したりSessionを確認したりする場合は、このAgent IDを保存しておきましょう。


remoteUidsについて

remoteUidsは、AI AgentがどのRTCユーザーの音声をListenするかを指定します。

本番環境では、

remoteUids: [requesterUid]

のように特定ユーザーを指定します。

一方、開発時にChannel内のすべてのユーザーを対象にしたい場合は、

remoteUids: ['*']

とすることもできます。

ただし、['*']を指定するとAgentはChannel内のすべてのユーザーをSubscribeします。

そのため、本番環境では基本的に対象となるParticipantのみを指定することを推奨します。


Gemini 3.5 Transcribeを設定する

リアルタイム文字起こしに使用するモデルは以下です。

models/gemini-3.5-transcribe-live

Gemini 3.5 Transcribeでは、言語を指定することもできます。

例えばスペイン語の場合、

language_codes=["es-ES"]

のように指定します。

一方で、

  • Automatic Language Identification
  • Multilingual Transcription
  • Code-Switching Detection

を有効にしたい場合は、language_codes自体を省略するか、空の配列を渡します。

language_codes=[]

これはVoice AI Agentでは特に便利です。

実際のサービスでは、通話が始まる前にユーザーがどの言語を話すのか分からないケースも多いためです。

例えば、

日本語 → 英語 → 日本語

のように会話途中で言語が切り替わるケースにも対応しやすくなります。


custom_vocabularyを利用する

Gemini APIには、

custom_vocabulary

も用意されています。

これを利用すると、特定の単語を優先的に認識するようSpeech Recognitionを調整できます。

例えば、

  • 会社名
  • 製品名
  • 略語
  • 技術用語
  • 業界固有の専門用語

などです。

通常のSpeech Recognitionでは認識しづらい固有名詞が多いサービスで特に役立ちます。


Server RouteからAgentを起動する

BrowserはまずServerへリクエストを送り、

  • Channel Name
  • RTC + RTM Token

を取得します。

その後、取得したChannelとUIDをProtected Agent Start Routeへ送信します。

Next.jsの場合は、例えば以下のように実装できます。

export async function POST(request: Request) {
  const body = await request.json();
  const { channel_name, requester_id } = body;

  if (!channel_name || !requester_id) {
    return Response.json(
      {
        error: 'channel_name and requester_id are required',
      },
      {
        status: 400,
      },
    );
  }

  const agentId = await startAgent(
    channel_name,
    requester_id,
  );

  return Response.json({
    agent_id: agentId,
    state: 'STARTING',
  });
}

ここで重要なのは、Start Requestが成功したからといって、AgentがすでにRTC ChannelへJoinしたとは限らないという点です。

ブラウザ側では、設定したAgent UIDに対するRTCの

user-joined

イベントを待ってから、Agent Audioを期待するようにしてください。

本番環境では、このRouteに対して以下の対策も必要です。

  • Route Authentication
  • Channelへのアクセス権限チェック
  • RTC UID Validation
  • Agent CreationのRate Limit
  • Unique Agent Nameの生成

BrowserをRTCとRTMへ接続する

Browser側には主に3つの役割があります。

  1. RTCへJoinしてMicrophoneをPublishする
  2. RTMへ接続する
  3. AgoraVoiceAIを初期化する

1. RTCへJoinしてMicrophoneをPublishする

Agora React SDKを利用する場合は、以下のようにRTC Channelへ参加できます。

const { isConnected } = useJoin(
  {
    appid: process.env.NEXT_PUBLIC_AGORA_APP_ID!,
    channel,
    token,
    uid: Number(uid),
  },
  isReady,
);

const { localMicrophoneTrack } =
  useLocalMicrophoneTrack(isReady);

usePublish([localMicrophoneTrack]);

useJoin()でRTC Channelへ参加し、

useLocalMicrophoneTrack()

でMicrophone Trackを作成します。

その後、

usePublish([localMicrophoneTrack])

でマイク音声をChannelへPublishします。


2. RTMへ接続する

次にRTMへLoginします。

Tokenを生成したときと**同じIdentity(UID)**を利用してください。

さらにRTCと同じChannelをSubscribeします。

const rtm = new AgoraRTM.RTM(appId, uid);

await rtm.login({ token });
await rtm.subscribe(channel);

RTCとRTMで異なるUIDを使わないように注意してください。


3. AgoraVoiceAIを初期化する

RTCへ正常にJoinしたら、Agent Client Toolkitを初期化します。

const ai = await AgoraVoiceAI.init({
  rtcEngine: rtcClient,
  rtmConfig: {
    rtmEngine: rtm,
  },
  renderMode: TranscriptHelperMode.TEXT,
});

ai.subscribeMessage(channel);

ai.on(
  AgoraVoiceAIEvents.TRANSCRIPT_UPDATED,
  (transcript) => {
    setTranscript([...transcript]);
  },
);

ai.on(
  AgoraVoiceAIEvents.AGENT_METRICS,
  (_agentUid, metrics) => {
    setLatestMetrics(metrics);
  },
);

このToolkitは、RTMから送られてくるPayloadを、

  • Transcript
  • Agent State
  • Metrics
  • Error

などの扱いやすいイベントへ変換してくれます。

例えば、

AgoraVoiceAIEvents.TRANSCRIPT_UPDATED

を利用すれば、リアルタイムでTranscriptを画面へ表示できます。

また、

AgoraVoiceAIEvents.AGENT_METRICS

を使えば、Agentに関連するMetricsも取得できます。


RTC + RTM権限を持つTokenを生成する

RTMを利用する場合、TokenにはRTM Privilegeも必要です。

RTC専用Token Builderではなく、

RtcTokenBuilder.buildTokenWithRtm

を使用します。

import {
  RtcRole,
  RtcTokenBuilder,
} from 'agora-token';

const token = RtcTokenBuilder.buildTokenWithRtm(
  appId,
  appCertificate,
  channel,
  uid.toString(),
  RtcRole.PUBLISHER,
  expirationTime,
  expirationTime,
);

ここで重要なのがUIDです。

以下の3つはすべて一致している必要があります。

RTC UID
RTM Login UID
Token Subject

また、RTM ClientもRTCおよびAgent Sessionと同じChannelをSubscribeする必要があります。


Agentを正しく停止する

Agentを停止するには、Server側でAgentSessionオブジェクトを保持しておきます。

停止する際は、

await session.stop();

を実行します。

Agentの停止をRequestした後は、

  1. BrowserをRTMからLogout
  2. RTC Conversation ViewをUnmount

します。

Agora React Hooksを利用している場合は、

  • Leave
  • Unpublish
  • Microphone Track Cleanup

などはHooks側へ任せるのがおすすめです。

Hooksがすでに管理しているResourceを手動でCloseすると、二重Cleanupなどの問題が発生する可能性があります。


トラブルシューティング

Agentは起動するがユーザーの音声を聞けない

まず、Browserが有効なMicrophone TrackをPublishしているか確認してください。

さらに、

remoteUids

にBrowser側の実際のRTC UIDが文字列として含まれているか確認します。

例えば、

remoteUids: [requesterUid]

です。


音声は動くがTranscriptやMetricsが取得できない

この場合はRTM全体の流れを確認します。

まずTokenに、

  • RTC Privilege
  • RTM Privilege

の両方が含まれている必要があります。

RTM Clientについても、Token生成時と同じUIDでLoginしてください。

そしてRTCと同じChannelをSubscribeします。

Agent側ではRTMを有効化する必要があります。

advancedFeatures: {
  enable_rtm: true,
}

さらにData ChannelとしてRTMを指定します。

parameters: {
  data_channel: 'rtm',
}

Metricsを取得したい場合は、

enable_metrics: true

も有効にしてください。


Start APIは成功するがAgentの音声が来ない

Lifecycle APIから成功Responseが返ってきても、

AgentがRTCへJoin済み

とは限りません。

設定したAgent UIDがRTC Channelへ表示されるまで待ってください。

つまりBrowser側では、

user-joined

を確認してからAgentとの会話を開始します。


RTMでinvalid-tokenエラーが出る

Server側で、

buildTokenWithRtm

を利用しているか確認してください。

また、

RTM Loginに渡したUID

と、

Token Subject

が完全に一致しているか確認してください。


本番環境向けチェックリスト

Productionへリリースする前に、以下を確認しておきましょう。

  • Google API KeyをServer Sideだけで管理する
  • Agora App CertificateをServer Sideだけで管理する
  • Token RouteをAuthenticationする
  • Agent Start RouteをAuthenticationする
  • Agent Stop RouteをAuthenticationする
  • 各RouteへRate Limitを設定する
  • Runtime Agent IDをアプリケーションユーザーと紐付ける
  • Agent Nameを毎回Uniqueに生成する
  • RTC Tokenを期限前にRenewする
  • RTM Tokenを期限前にRenewする
  • RTM PayloadをUntrusted Inputとして扱う
  • ProductionではVerbose SDK Loggingを無効にする
  • STT Latencyを計測する
  • LLM Latencyを計測する
  • TTS Latencyを計測する

特に、

STT
LLM
TTS

のLatencyはまとめて計測するのではなく、それぞれ個別に計測することをおすすめします。

そうすることで、Speech Recognition、Reasoning Model、TTS Providerなどを切り替えた際に、

「どのStageでLatencyが増えたのか?」

を簡単に確認できます。


Smart Transcriptionへの対応も予定

Gemini 3.5 Transcribeでは、input_audio_transcription内へ新しくmodeパラメータが追加されました。

現在、2つのTranscription Modeがあります。

VERBATIM
SMART

VERBATIM

VERBATIMはDefault Modeです。

話された内容をできるだけそのまま保持します。

例えば、

  • Filler Words
  • Repetition
  • False Starts
  • Self-Correction

なども残ります。

つまり、

「えーっと、電話番号は415……あ、ごめんなさい、410……」

のような話し方も、そのままTranscriptへ反映されます。


SMART

一方、SMARTはTranscriptをリアルタイムで整理します。

例えば、

  • Disfluencyの除去
  • 言い直しの解決
  • Grammarの改善
  • 大文字・小文字の整理
  • Number Formatting
  • Date Formatting
  • List Formatting
  • Paragraph Break

などを自動的に行います。

例えばユーザーが、

My number is four one five, uh sorry, four one zero, five five five, twelve thirty.

と話したとします。

VERBATIMでは、

415 → 言い直し → 410

という情報もそのまま保持されます。

一方Smart Transcriptionでは、False Startを解決して、最終的にユーザーが意図した正しい情報を生成できます。


なぜSmart TranscriptionがVoice AIで重要なのか?

人間同士の会話では、

「あ、違った」
「えーっと」
「いや、こっちです」

といった発話があっても、文脈から自然に理解できます。

しかしVoice AIの場合、Transcriptはそのまま、

  • Tool
  • CRM
  • Phone Number Field
  • Email Address
  • Scheduling System
  • Database
  • API

などへ渡される可能性があります。

こうしたシステムは、人間ほどConversation Ambiguityに強くありません。

例えば、

「電話番号は415……すみません410です」

という音声から、

415

がCRMへ保存されてしまえば、Voice Agentが会話の意味を理解していても最終的なTaskは失敗します。

そのため、SMART TranscriptionはProduction Voice AIで非常に重要な機能になり得ます。


RESTful APIからSMARTを利用する

AgoraのRESTful APIを利用する場合、Gemini APIではSmart Transcriptionを以下のように指定できます。

{
  "setup": {
    "model": "models/gemini-3.5-transcribe-live",
    "generationConfig": {
      "responseModalities": ["TEXT"]
    },
    "inputAudioTranscription": {
      "mode": "SMART"
    }
  }
}

ポイントは、

"inputAudioTranscription": {
  "mode": "SMART"
}

です。

これによってSmart Transcription Modeを有効にできます。


Agora Agents SDKでのSMART対応について

現在、GeminiTranscribeSTTからSmart Transcriptionを設定するAgora Agents SDK対応も進められています。

SDK側の対応が追加されれば、RESTful APIだけでなくAgora Agents SDKからも簡単にSMART Modeを指定できるようになります。

対応後、設定方法についてもアップデートされる予定です。


まとめ

今回は、Google Gemini 3.5 TranscribeとAgora Conversational AIを組み合わせてリアルタイム音声AIエージェントを構築する方法を紹介しました。

Agora Conversational AIでは、

ユーザー音声
↓
Gemini 3.5 Transcribe
↓
LLM
↓
TTS
↓
ユーザー

というVoice AI PipelineをRTC Session内で構築できます。

さらに、

RTC → 音声
RTM → Transcript / State / Metrics / Error

と役割を分けることで、リアルタイム音声とアプリケーションデータの両方を扱えます。

特にGemini 3.5 Transcribeでは、

  • リアルタイムSpeech-to-Text
  • Multilingual Transcription
  • Automatic Language Identification
  • Code-Switching
  • Custom Vocabulary
  • Smart Transcription

など、Voice AIで重要となる機能が提供されています。

またAgora側ではSTT、LLM、TTSを特定Providerへ固定する必要がないため、サービスの要件に応じてAI Stackを柔軟に組み替えることができます。

Voice Agent、AIカスタマーサポート、AI受付、多言語アシスタントなどを開発している方は、ぜひ試してみてください。


参考リンク

Agora Conversational AI

Gemini 3.5 Transcribe × Agora 元記事

Agora Documentation

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?