Gladia、Deepgram、AssemblyAI、Speechmatics、Soniox、ElevenLabsをbot単位で選択できます。自分のAPI key、自分のリージョンを使い、プロバイダー固有のオプションはすべてそのまま渡せます。

AAmr El Shimy
··8 min read
6つの文字起こしプロバイダーを、1つのAPIで

音声認識(speech-to-text)プロバイダーの選定は、一度決めれば終わりという判断ではありません。自社ドメインでの精度、1時間あたりの価格、必要な言語、データをEU内にとどめたままDPAを締結してくれるかどうか、3人が同時に話したときに話者分離がどこまで持ちこたえるか。そのいずれもが変化しますし、すべてのお客様にとって同じ答えになるものは1つもありません。

そこで私たちは、選ぶのをやめました。プロバイダーはbot単位で選択でき、すでに手元にある録音についても後から選び直せます。

選べるプロバイダー

プロバイダーバッチライブ
gladia
deepgram
assemblyai
speechmatics
soniox
elevenlabs

ElevenLabsはストリーミング専用です。他の5つは両方に対応しています。

ほとんどの方が求めるのはバッチでしょう。ミーティングが終わると音声が送信され、話者ラベルと単語単位のタイムスタンプが付いた文字起こしがwebhookで返ってきます。ライブは、ミーティングの進行中にWebSocketで文字起こしのセグメントが届く方式で、その場で反応する必要がある用途向けです。

この2つは独立して設定でき、同じbotで別々のプロバイダーを使って両方を動かすこともできます。速いライブのフィードと、より精度の高い最終的な文字起こしを両立したい場合に有効です。

バッチ

{
  "meeting_url": "https://meet.google.com/abc-defg-hij",
  "bot_name": "Recording Bot",
  "transcription_enabled": true,
  "transcription_config": {
    "provider": "speechmatics",
    "region": "eu2",
    "api_key": null,
    "custom_params": null
  }
}

フィールドは4つです。providerを省略した場合のデフォルトは"gladia"です。regionapi_keycustom_paramsはいずれもデフォルトがnullで、詳細は後述します。

transcription_configtranscription_enabledがtrueのときは必須で、falseのときは無視されます。

1つのプロバイダーフィールドが、同じミーティングのオーディオを6つの交換可能な文字起こしプロバイダーのいずれかにルーティングし、それぞれ独自のリージョンとバッチ/ライブサポートを持ちます

botが終了すると、transcription_providertranscription_idsがbotのレコードとwebhookのpayloadに返されます。またGET /v2/bots/:bot_id/statusは、プロバイダー自体をポーリングして取得した最新のtranscription_statusを返します。値はnot-applicablenot-startedqueuedprocessingdoneerrorのいずれかです。

リージョンの固定

これらのプロバイダーの多くは複数のリージョンで稼働しており、多くのお客様にとってはこの点がすべてです。

プロバイダーバッチのリージョンライブのリージョン
gladiaグローバルendpointのみus-westeu-west
deepgramglobaleuglobaleu
assemblyaiuseuuseu
speechmaticseu1eu2us1us2au1バッチと同じ
sonioxuseujpバッチと同じ
elevenlabsglobaluseuin

プロバイダーが持たないリージョンを指定すると、bot作成の時点で許可されている値の一覧とともに400が返ります。後から不意に発覚することはありません。GladiaのバッチAPIはグローバルのみのため、バッチの設定でprovider: "gladia"とともにregionを渡すと拒否されます。ライブAPIにはリージョンがあります。

regionをnullのままにすると、妥当なデフォルトを選びます。DeepgramとAssemblyAIはeu、Speechmaticsはeu1、Sonioxはusです。

音声を特定の法域から一切出したくない場合は、Bring Your Own Storageと組み合わせてください。プロバイダーはバケット上の署名付きURLから音声を取得するため、バケットの所在地とプロバイダーのリージョンの組み合わせが、音声が実際にどこへ行くかを決定します。

自分のAPI keyを持ち込む

api_keyを渡すと、私たちのアカウントではなく、そのプロバイダーにおけるあなたのアカウントを使用します。

{
  "transcription_config": {
    "provider": "deepgram",
    "region": "eu",
    "api_key": "your-deepgram-key"
  }
}

keyは保存前にAES-256-GCMで暗号化され、botへ渡る間も暗号化されたままで、使用する時点でbotが復号します。botのデータを削除すると、保存されているkeyは上書きされます。

使う理由は2つあります。1つ目はコストです。私たちのkeyでの文字起こしは1時間あたり0.25 token、あなたのkeyなら0.05 tokenで課金されます。2つ目は、自分でトレーニングしたカスタム語彙や、私たちが持っていない条件の契約など、自分のアカウントでしか実現できないことがあるためです。

BYOKはProプラン以上でご利用いただけます。対象外の場合はFST_ERR_BYOK_TRANSCRIPTION_NOT_ENABLED_ON_PLANが返ります。

プロバイダーのオプションは、そのまま素通しする

正規化されたオプション層は意図的に作りませんでした。どのプロバイダーにも他にはない機能があり、それを最大公約数にならしてしまうと、そのプロバイダーを選んだ理由そのものを捨てることになるからです。

custom_paramsは、ほぼ書いたままの形でプロバイダーに渡ります。

{
  "transcription_config": {
    "provider": "speechmatics",
    "region": "eu2",
    "custom_params": {
      "transcription_config": {
        "language": "en",
        "diarization": "speaker",
        "operating_point": "enhanced"
      }
    }
  }
}

組み立てやすければ、ドット記法も使えます。

{ "custom_params": { "diarization_config.min_speakers": 2 } }

ライブの設定では、validationの前にフラット化が解除されるため、プロバイダーが実際に受け取る形でvalidationされます。バッチの設定では、フラットなキーは書かれたままvalidationされ、展開されるのは送信時です。

ライブの設定では、4つのフィールドがpipeline側で設定されるため、送信すると拒否されます。encodingsample_ratebit_depthchannelsです。特にサンプルレートは、ミーティングの音声からプロバイダーごとにネゴシエートされるため、上書きするとストリームが壊れます。代わりにstreaming_config.audio_frequencyを使ってください。

話者分離はデフォルトで有効です。無効にすると話者ラベルのない文章の塊が返ってくるうえ、少なくとも1つのプロバイダーは明示的に指定しない限り発話を1つも返しません。custom_paramsで明示した値が優先されます。

落とし穴:バッチとライブは形が違う

これは半日を溶かしかねない点なので、はっきり書いておきます。同じプロバイダーであっても、ライブAPIとバッチAPIは同じ形のパラメータを受け付けません。

Gladiaが最も分かりやすい例です。バッチは翻訳の設定をトップレベルで受け取ります。ライブではrealtime_processingの下にネストする必要があります。

{
  "streaming_config": {
    "mode": "transcription",
    "output_url": "wss://your-app.example.com/transcripts",
    "audio_frequency": 24000,
    "transcription": {
      "provider": "gladia",
      "region": "eu-west",
      "api_key": "your-gladia-key",
      "custom_params": {
        "language_config": { "languages": ["ru"] },
        "realtime_processing": {
          "translation": true,
          "translation_config": { "target_languages": ["en"] }
        }
      }
    }
  }
}

ライブのパラメータはstrictモードでvalidationされます。ライブセッションのAPIは未知のキーを問答無用で拒否するため、ミーティングを失敗させるくらいならbotの作成を失敗させたい、という判断です。1階層下に属するキーを送った場合は、エラーが正しい場所を教えてくれます。

'translation' is not a top-level field of the gladia live API —
did you mean 'realtime_processing.translation'?
(the live API shape differs from batch)

バッチとライブのどちらのパラメータも、bot作成時に各プロバイダー自身のschemaと突き合わせて検証されます。そのため、入力ミスは1時間後の文字起こし失敗ではなく、1秒後の400として返ってきます。その厳密さについて、注意点が2つあります。バッチのパースは寛容なので、未知のバッチのキーはvalidationを通過し、そのまま転送されてプロバイダーの処理に委ねられます。未知のキーを拒否するのはライブだけです。またElevenLabsのライブのパラメータは、突き合わせるべき公開schemaが存在しないため、validationなしでそのまま渡されます。

ライブの出力

mode: "transcription"を指定すると、プロバイダーのセッションを開き、ミーティングの音声を流し込み、イベントをJSONとしてあなたのoutput_urlに転送します。

{
  "event": "transcript.segment",
  "bot_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "data": {
    "text": "so the migration lands Thursday",
    "isFinal": true,
    "utteranceStart": 12.44,
    "utteranceEnd": 14.91,
    "confidence": 0.97,
    "speaker": { "name": "Alice", "id": 1 },
    "words": []
  }
}

このほか、使用されたプロバイダーを含むsession.started、翻訳を要求した場合のtranslation、そしてerrorがあります。中間のネットワーク機器がアイドル状態のソケットを閉じないよう、30秒ごとにpingを送ります。

制約が1つあります。私たちのプラットフォームのkeyで動作するライブセッションは、管理下のプロバイダー(現時点ではGladia)に限られます。それ以外のプロバイダーでライブの文字起こしを行うにはBYOKのkeyが必要で、ミーティング開始時に発覚するのではなく、bot作成の時点でFST_ERR_STREAMING_TRANSCRIPTION_KEY_UNAVAILABLEとして拒否します。

後から選び直す

POST /v2/bots/:bot_id/retranscribe
{
  "transcription": {
    "provider": "assemblyai",
    "region": "eu",
    "api_key": "your-assemblyai-key"
  }
}

保存された音声が別のプロバイダーに再送信され、botの設定が更新されます。これが、ベンダーのデモ音声ではなく自社のミーティングで2社をA/Bテストする方法であり、失敗した文字起こしを復旧する方法でもあります。

botのデータを削除すると、?delete_from_provider=true(デフォルト)によってプロバイダー側からも文字起こしが削除されます。自分のkeyを指定していた場合は、そのkeyが使われます。

プロバイダーの調子が悪い日に何が起きるか

送信に失敗した場合、5秒後と15秒後に2回再試行し、合計3回試みます。対象となるのは、再試行で成功する見込みがあるエラーだけです。rate limit、5xx、接続およびポーリングのタイムアウト、ネットワークレベルの障害、そしてプロバイダーが2xxを返したもののbodyにジョブIDが含まれていなかった場合です。認証エラー、不正な入力、サポートされていない操作は決して再試行しません。2回目もまったく同じように失敗するからです。

再試行のたびに、再送信の前にcallbackのシークレットをローテーションします。これは実際に起こりうる競合を塞ぐためです。私たち側でresponseがタイムアウトしたジョブを、実際にはプロバイダーが受理していた場合、そのcallbackはすでに存在しないシークレットに対して届くため拒否されます。再試行したジョブと競合して、1つのチャンクから2つの文字起こしができてしまうことはありません。

別のプロバイダーへの自動フェイルオーバーはありません。 障害の最中に気づいていただくより、はっきり書いておきたいと考えています。選択したプロバイダーが再試行後も失敗した場合、botはTRANSCRIPTION_FAILEDとともにfailedで終了しますが、録画も音声のアーティファクトもすべて残っています。復旧手段はretranscribeの呼び出しで、必要であれば別のプロバイダーを指定できます。黙ってベンダーを切り替えれば、同意していない法域で、同意していない価格で文字起こしが行われることになります。そのため、この判断はお客様に委ねています。

関連記事

関連ブログtranscription