サムネイル

LiveKit Agentsでリアルタイム音声AIを作る方法

gen
gen

2026/08/10

  • 0

電話番号にかけたらAIが出て、こちらの話に割り込まずに答えて、予約まで取ってくれる。これをPythonのファイル1本と外部プロバイダの契約だけで組めるのがLiveKit Agentsです。GitHubスターは12,820、ライセンスはApache-2.0、音声認識と言語モデルと音声合成は好きな組み合わせに差し替えられます。

音声エージェントの本体はPythonファイル1本で済む

やることは AgentSession に音声認識と言語モデルと音声合成を渡して起動するだけです。WebRTCのシグナリング、音声のバッファリング、割り込みの処理、この辺は全部フレームワーク側が持っています。

from livekit.agents import AgentSession, inference

session = AgentSession(
    stt=inference.STT("deepgram/nova-3", language="multi"),
    llm=inference.LLM(model="google/gemma-4-31b-it"),
    # tts も同じ形でプロバイダとモデル名を渡す
)

inference という名前空間が付いているのがポイントで、プロバイダごとのプラグインを個別に import しなくても文字列でモデルを指名できます。Deepgramをやめて別の音声認識に乗り換えたくなったとき、基本はこの1行の書き換えで済みます。

公式が挙げている機能はこのあたりです。

  • 音声認識 言語モデル 音声合成 リアルタイムAPIの差し替え
  • ジョブスケジューリングとディスパッチAPI
  • 各プラットフォーム向けWebRTCクライアント
  • 電話回線との統合
  • セマンティックなターン検出
  • MCPサーバーのツール連携
  • テストフレームワーク内蔵
  • スタック全体をローカルで動かせる

フォークは3,526、直近のリリースは8月7日の1.6.9で、更新は1〜2週おきに出ています。公式サンプルにレストランの予約受付や外線通話が入っているので、作りたいものが電話系ならそのまま下敷きにできます。

最初の罠は dev モードがもう非推奨になっていること

インストールは1行です。

pip install "livekit-agents[openai,deepgram,cartesia]"

角括弧の中が使うプロバイダで、ここに書いたものだけ依存が入ります。

で、ここからが罠です。既存の解説記事をなぞると、だいたい console モードでマイクを繋いで動作確認して、dev モードで開発サーバーを立てる、という手順で書かれています。この2つ、1.6.8で両方とも非推奨になりました。移行先として案内されているのは lk agent CLIです。

しばらくは動くはずですが、非推奨のまま新機能が乗ってくる保証はありません。今から書き始めるなら最初から lk agent 側で組んだほうが後が楽です。

必要なキーは音声認識と言語モデルと音声合成の3つ分、それとLiveKitサーバーの接続情報です。ローカルでスタック全体を動かすこともできますが、最初の1本はマネージドで通したほうが切り分けが楽です。

STTとLLMとTTSを差し替えて日本語で通るか確かめる

公式サンプルにある音声認識の指定は language="multi" です。多言語モードで、言語を固定しないという意味になります。

ここは正直に書いておきます。READMEにもサンプル構成にも、日本語の認識精度や音声品質についての記載はありません。多言語モードの守備範囲に日本語が入っているかどうかと、日本語で実用に耐えるかどうかは別の話です。

音声は、テキストのチャットボットと違って品質の劣化が一発で気づかれる領域です。固有名詞が化ける、句読点の切れ目がずれて文の意味が変わる、読み上げのイントネーションが不自然。この3つは日本語で特に出やすくて、しかも英語のデモではまず露見しません。

なので順番としては、フレームワークを触る前に、自分が使う想定の日本語で音声認識と音声合成を単体で叩くのが早いです。社名や商品名を混ぜた30秒の音声を1本用意しておけば判断が付きます。ここが許容できないなら、後段をどれだけ作り込んでも意味がありません。

ターン検出を音声モデルに任せて割り込みを減らす

音声エージェントで一番むずかしいのは、認識でも生成でもなく「いつ喋り始めるか」です。無音の長さだけで判断すると、考え込んでいる人の言葉を遮るか、話し終わっているのに3秒黙るかのどちらかになります。

LiveKitが用意しているターン検出は、ユーザーの音声を直接エンコードするモデルです。何を言ったかに加えて、どう言ったかまで見ます。語尾が下がったのか、まだ息を継いでいるだけなのか、というところで判断が変わる。ここを言語モデルの手前で吸収してくれるのがマジで大きいです。

用意されているのは2種類で、LiveKit Inference側で動く高精度版の v1 と、CPUでローカルに動く軽量版の v1-mini です。有効化はこれだけ。

from livekit.agents import AgentSession, TurnHandlingOptions, inference

session = AgentSession(
    turn_handling=TurnHandlingOptions(
        turn_detection=inference.TurnDetector(),
    ),
    # stt, llm, tts はここに続けて指定する
)

プラグインの追加インストールは要りません。livekit-agents 本体の1.6.1以上に同梱されています。

そして対応言語は14で、日本語 韓国語 中国語が含まれます。音声認識と音声合成の日本語は自分で確かめろと書いた直後ですが、ターン検出については公式が日本語対応を明言している。会話のテンポという一番効く部分が最初から日本語圏を視野に入れているのは、実装の安心感が全然違います。

SIP連携で電話番号から呼び出せるようにする

ブラウザのマイクで動くところまで来たら、次は電話です。ここがLiveKit Agentsを選ぶ最大の理由になり得ます。

仕組みはトランクとディスパッチルールの2段構えです。トランクは外部のSIPプロバイダとLiveKitをつなぐ橋で、着信を受けるインバウンドトランクと、こちらから発信するアウトバウンドトランクに分かれます。ディスパッチルールは、かかってきた電話をどのルームに入れるかの割り振りで、全員を1つの部屋に集めることも、発信者ごとに分けることもできます。

着信のときのSIP参加者は自動で作られます。こちらから電話をかける場合だけ CreateSIPParticipant を明示的に叩きます。

動作確認済みとされているプロバイダはTwilio Telnyx Exotel Plivo Wavix didlogicの6社です。設計としてはSIPプロバイダ全般で動くとされていますが、テストされているのはこの6社なので、最初の1本はここから選ぶのが安全です。

電話の一次対応をするAIが、フレームワークの標準機能だけで組める。この意味は最後のセクションで効いてきます。

pytestで会話の中身をテストしてから公開する

ここが個人的に一番アツいポイントです。

音声エージェントは、直したつもりが別の会話パターンを壊す、というのが本当に起きやすい。しかも壊れたことに気づくのが本番の通話中です。プロンプトを1行足しただけで挨拶がそっけなくなる、みたいなやつですね。

LiveKit Agentsにはテストフレームワークが同梱されていて、pytestからそのまま呼べます(Node側はVitest)。Assistant() は自分で定義したエージェントのクラスです。

@pytest.mark.asyncio
async def test_assistant_greeting() -> None:
    async with (
        inference.LLM(model="google/gemma-4-31b-it") as llm,
        AgentSession(llm=llm) as session,
    ):
        await session.start(Assistant())
        result = await session.run(user_input="Hello")
        await result.expect.next_event().is_message(role="assistant").judge(
            llm, intent="Makes a friendly introduction and offers assistance."
        )
        result.expect.no_more_events()

面白いのが judge() です。返答を文字列の完全一致で比べるのではなく、「フレンドリーに自己紹介して手助けを申し出ているか」という意図を言語モデルに判定させます。音声エージェントの出力は毎回文言が揺れるので、そもそも文字列一致のアサーションが成立しないんですよね。そこを意図の一致に置き換えている。

next_event() で発話やツール呼び出しを1つずつ辿れて、no_more_events() で余計な発話が続かないことも確認できます。詳しいログを見たいときは LIVEKIT_EVALS_VERBOSE を立てます。

チュートリアル系の記事はだいたい「動いた」で終わりますが、受託で納品するならここまで書いてCIに乗せて、ようやくスタートラインです。

OpenAI Realtime APIとの使い分けと個人開発で狙える場所

「音声で会話するだけならRealtime APIを直接叩けばよくないですか」というのは正しくて、実際そのほうが速く出せる場面があります。

観点
LiveKit Agents
Realtime APIを直接叩く
構成
音声認識 言語モデル 音声合成を個別に選ぶ
音声から音声まで1社に任せる
電話
SIP連携が標準機能
自前で用意する
差し替え
モデル名の文字列を書き換えるだけ
プロバイダ移行はほぼ作り直し
テスト
pytestベースのフレームワーク同梱
自作
動かす場所
マネージドでもローカルでも
プロバイダ側

雑に言えば、ブラウザで完結するデモを最速で出すならRealtime APIの直叩き、電話が絡むかプロバイダを後から変える可能性があるならLiveKit Agentsです。LiveKit Agents側からRealtime APIを呼ぶこともできるので、二択というより外側の器をどちらにするかという話になります。OpenAI側の実装感はGPT Realtime 2の記事が詳しいので、並べて読むと判断しやすいはずです。

で、個人開発と副業の話です。電話の一次対応、予約受付、営業時間外の折り返し受付。このあたりは、専用のSaaSを契約するほどの通話量はないけれど人を貼るには惜しい、という規模の事業者がまだ大量に残っている領域です。Apache-2.0でスタック全体をローカルに置けるので、プラットフォーム側に売上の一部を持っていかれる構造にもなりません。

ただしコストの見積もりだけは実装より先にやってください。電話系は、通話が続いている間ずっと音声認識と言語モデルと音声合成の3つが同時に課金され、そこに通話料が乗ります。テキストのチャットボットの感覚で月額を決めると、通話が増えた月に一撃で赤字です。1通話あたりの秒数と月間本数を先に置いて、3プロバイダ分の従量課金を足し算しておく。ここを飛ばすと、動くものは作れたのに続けられない、で終わります。

まずは pip install "livekit-agents[openai,deepgram,cartesia]" を叩いて、自分の日本語音声を音声認識に通すところから始めてみてください。そこが通れば、あとはブロックを組むだけです。

会員登録して機能を使おう

この機能を利用するには、無料の会員登録が必要です。
お気に入りの記事を保存して、あとで読み返しましょう!