LiveKit × Bedrock × LangGraphで実現するリアルタイム営業ロープレAI
はじめに
この記事は レバテック開発部 Advent Calendar 2025 7日目の記事です。
前回の記事に続き、全然SREっぽいことを投稿しないSREチームの鈴木です。
最近、Voice AI(音声対話AI) の進化が止まりません。OpenAIのAdvanced Voice ModeやGoogleのGemini Liveなど、人間と自然に会話できるAIが急速に身近になってきました。
こうした技術的背景の中、私たちレバテックでは、社内の人材育成を加速させる施策として 「営業ロールプレイングのAI化」 の技術検証に取り組んでいます。
そもそも「営業ロープレ」とは?
営業ロープレとは、実際の商談場面を想定し、営業担当者と顧客役(上司や同僚)が模擬商談を行うトレーニングのことです。 営業スキルの向上には不可欠なプロセスですが、これを人間同士で行う場合、現場には常に以下の課題がつきまといます。
- コスト: ベテラン社員(上司)の時間を拘束してしまう
- 質のバラつき: 相手役のスキルによって、練習の質やフィードバックが属人化する
もし、これらをAIに置き換えることができれば、「いつでも、何度でも練習して、客観的な評価が得られる」 理想的な練習環境が実現します。
AI化に必要な「2つの能力」
しかし、実用レベルの「AIロープレ相手」を作るためには、相反する2つの能力を同時に満たさなければなりません。
- リアルタイム性: 会話のテンポを崩さない、即応能力
- 深い思考力: 会話内容を論理的に分析し、的確なスキル評価を行う能力
単に「反応が速い」だけでも、逆に「賢いけれど返答が遅い」だけでも、商談の練習相手としては不十分です。
本記事では、LiveKit Agents FrameworkとAWS Bedrock、そしてLangGraphを組み合わせ、リアルタイム性と深い思考能力を両立させた「営業ロープレAI」の実装事例を紹介します。
営業ロープレAI
ユーザー(営業担当)がAI(顧客役)に対して模擬商談を行うシステムです。
AIは顧客として振る舞い、ロールプレイング終了後、ユーザーの営業スキル(ヒアリング力、提案力など)を詳細に評価します。
アーキテクチャー図
以下のような環境を構築して運用しています。POCプロジェクトとして、フロントエンド環境・リアルタイム通信基盤・データベース/認証部分はSaaSプロダクトを活用し、コアであるAIエージェントの実装に集中できるようにしています。

システム構成のポイント
このシステムの最大の肝は、「会話」と「評価」の分離です。
-
Real-time Agent (LiveKit):
- 役割: ユーザーとの対話。0.5秒〜1秒以内の応答速度を目指す。
- 技術: LiveKit Agents, Transcribe, Polly, Claude (軽量な推論)
-
Evaluation Agent (LangGraph):
- 役割: 対話終了後の深い分析。数分間かかっても良いので、深く思考する。
- 技術: LangGraph, Claude (Thinking有効化)
1. LiveKit Agents Frameworkによる音声パイプライン
従来のWebRTC開発では、シグナリングサーバーやメディアサーバーの構築、STT/TTSのパイプライン管理など、インフラ周りの負担が甚大でした。
LiveKit Agents Frameworkは、これらの複雑さを隠蔽し、開発者が「エージェントのロジック」だけに集中できる環境を提供してくれます。
実装コード: エージェントの定義
以下のコードは、サーバーサイドのWorkerとして稼働します。ユーザーがルームに入室すると自動的にジョブが割り当てられ、音声対話が始まります。
今回のプロジェクトではECS Fargateにデプロイして運用しています。
from livekit import agents, rtc
from livekit.plugins import aws, silero
class Assistant(agents.Agent):
def __init__(self, transcript_repo, session_id):
super().__init__()
self._transcript_repo = transcript_repo
self._session_id = session_id
async def entrypoint(ctx: agents.JobContext):
# AWS Bedrockの各サービスを初期化
# LiveKitのプラグインがAWS SDKのラッパーとして機能します
stt = aws.STT(language="ja-JP")
llm = aws.LLM(model="jp.anthropic.claude-sonnet-4-5-20250929-v1:0")
tts = aws.TTS(voice="Takumi", speech_engine="neural")
# エージェントセッションの構築
# VAD(音声区間検出)などもプラグインで完結します
session = agents.AgentSession(
stt=stt,
llm=llm,
tts=tts,
vad=silero.VAD.load(),
)
# 会話開始
await session.start(ctx.room, Assistant(...))
livekit.plugins.aws を使うことで、Transcribe (STT) → Claude (LLM) → Polly (TTS) のパイプラインがわずか数行で構築できます。「音声データのバッファリング」や「割り込み(Interruption)時の発話キャンセル」といった面倒な処理はフレームワークが全自動で行ってくれます。
会話履歴の保存
会話中、各発話は非同期でデータベースに保存されます。これが後の評価フェーズでの入力データとなります。
@session.on("conversation_item_added")
def on_conversation_item_added(event: agents.ConversationItemAddedEvent):
"""会話履歴にアイテムが追加された時に保存"""
speaker = "user" if event.item.role == "user" else "agent"
content = event.item.text_content
# 対話のレイテンシに影響しないよう非同期タスクとして実行
task = asyncio.create_task(
transcript_repo.save(session_id, speaker, content)
)
2. 評価アーキテクチャとLangGraph
リアルタイム対話において、LLMに「深い思考(CoT: Chain of Thought)」をさせると、応答までに数秒〜のラグが発生し、会話体験が損なわれます。
そこで、評価プロセスを会話から完全に切り離し、事後処理(Post-processing)として実装しました。ここで活躍するのがLangGraphです。
LangGraphによる評価フロー
評価エージェントは、LangGraphを用いてReActパターンを拡張したフローで実装しています。
通常のReActで必要なツールを実行した後、最終的に厳格なJSONフォーマットで出力するフローを組んでいます。

https://langchain-ai.github.io/langgraph/how-tos/react-agent-structured-output
「推論」と「出力」の使い分け
ここで工夫したのは、LLMのパラメータ設定です。
-
Thinking Phase:
temperature=1.0& Thinking有効化- あえて創造性を高め、多角的な視点(営業としての共感性、論理性、強引さなど)から会話ログを分析させます。
-
Output Phase:
temperature=0- システム連携のため、Pydanticモデルに基づいたブレのないJSONを出力させます。
# 1. 推論フェーズ用LLM: Extended Thinkingを有効化
session_llm = ChatBedrockConverse(
model=config.model,
client=bedrock_client,
temperature=1.0, # 創造的な推論を促す
max_tokens=config.max_tokens,
additional_model_request_fields=thinking_config # Bedrock固有のThinking設定など
)
# 2. 出力フェーズ用LLM: 構造化出力
output_llm = ChatBedrockConverse(
model=config.model,
client=bedrock_client,
temperature=0, # 決定論的な出力を保証
).with_structured_output(
Evaluation, # Pydanticモデル
method="tool_calling"
)
得られる評価データの例
このパイプラインを通すことで、以下のような構造化された評価データが得られます。これをフロントエンドでグラフ表示します。
{
"total_score": 85,
"categories": {
"listening": 90,
"proposal": 75,
"closing": 80
},
"feedback_summary": "ヒアリングは非常に丁寧でしたが、クロージングに少し迷いが見られました。",
"missed_opportunities": [
"競合他社の利用状況について深掘りできた可能性があります",
"導入時期のを確認していません"
]
}
※上記は記事用に用意したダミーの評価構造&データです
3. RAGによる「嘘をつかない」仕組み
営業ロールプレイングでは、顧客役が適当な応答をすると練習になりません。
そこで、FAQ検索ツールを実装し、FAQ(よくある質問集)を参照して各フェーズで適切な質問を投げかけてくれるようにしています。
このRAGの検索結果はログに残るため、評価エージェントは「ユーザーが正しく質問に回答をできていたか?」を評価することができます。
4. 画面共有への対応(マルチモーダル)
LiveKitはWebRTCベースなので、ビデオストリームの扱いも得意です。
ユーザーが画面共有をした場合、その映像フレームをキャプチャしてLLMに渡すことで、資料を見ながらの会話も実現しています。
def _create_video_stream(self, track: rtc.VideoTrack):
"""ビデオストリームを作成し、最新フレームをバッファリング"""
async for event in self._video_stream:
# 有効なフレームのみを保存(帯域節約のためサイズチェックなども可能)
if event.frame.width >= 320:
self._latest_frame = event.frame
async def on_user_turn_completed(self, turn_ctx, new_message):
# ユーザーの発話完了時、最新フレームがあればメッセージに添付
if self._latest_frame:
new_message.content.append(ImageContent(image=self._latest_frame))
これにより、「この資料の右上のグラフについてどう思いますか?」といった、視覚情報を前提とした指示語を含む質問にも的確に答えられるようになります。
まとめ
LiveKit Agents Frameworkを使用することで、これまで高度な技術力が必要だった「リアルタイム音声AI」が、驚くほど簡単に実装できるようになりました。
さらに、「会話」 と 「評価」 を明確に分離し、後者にLangGraphを採用することで、対話のテンポを崩すことなく、実用的な深さを持つフィードバック生成を実現できました。
今後は、評価結果に基づいて次回のロープレ時にAIの性格(難易度)を自動調整する機能や、ユーザーの表情・声色からの感情分析を評価に組み込む実装を検討しています。
この記事が、Voice AIプロダクト開発の参考になれば幸いです。
参考リンク
Discussion