ナビゲーションにスキップ

独自モデルを統合

独自のLLMをエージェントに接続するか、独自のサーバーをホストします。

Custom LLMでは、外部エンドポイントを介して会話を独自のLLMに接続できます。 ElevenLabsはnative統合LLMにも対応しています。

Custom LLMでは、独自のOpenAI APIキーを使用するか、完全にカスタムなLLMサーバーを実行できます。

Custom LLMは、Realtimeモデルと併用できません。

概要

デフォルトでは、OpenAIなどの一般的なモデルに対して内部認証情報を使用します。カスタムLLMサーバーを使用するには、次のいずれかのOpenAI互換リクエスト/レスポンス構造に準拠している必要があります。

Responses APIは、追加機能をサポートするOpenAIの新しいAPI形式です。どちらのAPI形式も、Custom LLM統合で完全にサポートされています。

以下のガイドでは、両方のユースケースを扱います。

  1. 独自のOpenAIキーを使用:独自のOpenAI APIキーをプラットフォームで使用します。
  2. カスタムLLMサーバー:独自のLLMサーバー実装をホストして接続します。

以下の方法を学べます。

  • OpenAI APIキーをElevenLabsに保存する
  • OpenAIのChat CompletionsまたはResponsesエンドポイントを再現するサーバーをホストする
  • ElevenLabsをカスタムエンドポイントに向ける
  • 必要に応じてLLMに追加パラメーターを渡す

推論サマリー

エンドポイントは、最終回答とは別に推論を返す必要があります。ElevenLabsは最終回答から推論を生成しません。

サポートされているエンドポイントから推論をリクエストするには、エージェントのLLM設定で推論サマリーをオンにするか、API経由でenable_reasoning_summaryを設定します。

推論を返す

エンドポイントに対応する形式を使用してください。

各レスポンスデルタのreasoningまたはreasoning_contentフィールドで推論をストリーミングします。

Gemini互換エンドポイントでは、ElevenLabsはgoogle.thinking_config.include_thoughtsで思考をリクエストし、extra_content.google.thoughtでマークされたコンテンツを読み取ります。

保存、配信、制限事項については、推論サマリーを参照してください。

独自のOpenAIキーを使用する

カスタムOpenAIキーを統合するには、ElevenLabsダッシュボードでエージェント設定を更新してカスタムLLMサーバーを指定し、OPENAI_API_KEYを含むシークレットを作成します。

1

ElevenLabsダッシュボードのAgent設定で、右側にある「LLM」ドロップダウンメニューから「Custom LLM」を選択します。

シークレットを追加

2

「LLM」の下にあるフィールドをクリックし、下にスクロールして「Custom LLM」を選択します。

3

カスタムLLMサーバーのサーバーURLとモデルIDを入力します。

URLを入力

4

「API key」の下にあるドロップダウンをクリックし、「Create new secret」を選択します。キーにOPENAI_API_KEYという名前を付け、「value」フィールドにキーを追加して、「Add secret」をクリックします。

5

「x」ボタンをクリックしてLLMモーダルを閉じ、「Publish」をクリックして変更を保存します。

カスタムLLMサーバー

カスタムLLMサーバーを使用するには、OpenAI形式を用いた互換性のあるサーバーエンドポイントをセットアップします。Chat Completions API(/v1/chat/completions)またはResponses API(/v1/responses)のいずれかを実装できます。

どちらのエンドポイントも、Content-Type: text/event-streamを使用したSSE(Server-Sent Events)形式で応答を返す必要があります。

Chat Completions APIでは、/v1/chat/completionsエンドポイントを使用します。

各チャンクはdata: {json}\n\nの形式にし、ストリームはdata: [DONE]\n\nで終了する必要があります。

以下はサーバー実装の例です。

import json
import os
import fastapi
from fastapi.responses import StreamingResponse
from openai import AsyncOpenAI
import uvicorn
import logging
from dotenv import load_dotenv
from pydantic import BaseModel
from typing import List, Optional
# Load environment variables from .env file
load_dotenv()
# Retrieve API key from environment
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
if not OPENAI_API_KEY:
raise ValueError("OPENAI_API_KEY not found in environment variables")
app = fastapi.FastAPI()
oai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)
class Message(BaseModel):
role: str
content: str
class ChatCompletionRequest(BaseModel):
messages: List[Message]
model: str
temperature: Optional[float] = 0.7
max_tokens: Optional[int] = None
stream: Optional[bool] = False
user_id: Optional[str] = None
@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
oai_request = request.dict(exclude_none=True)
if "user_id" in oai_request:
oai_request["user"] = oai_request.pop("user_id")
chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)
async def event_stream():
try:
async for chunk in chat_completion_coroutine:
# Convert the ChatCompletionChunk to a dictionary before JSON serialization
chunk_dict = chunk.model_dump()
yield f"data: {json.dumps(chunk_dict)}\n\n"
yield "data: [DONE]\n\n"
except Exception as e:
logging.error("An error occurred: %s", str(e))
yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8013)

このコード、または独自のサーバーコードを実行します。

サーバー用のパブリックURLを設定する

サーバーにアクセスできるようにするには、ngrokなどのトンネリングツールを使用してパブリックURLを作成します。

ngrok http --url=<Your url>.ngrok.app 8013

ElevenLabs CustomLLMを設定する

次に、ElevenLabsダッシュボードでエージェント設定を更新し、カスタムLLMサーバーを指定します。

サーバーURLをngrokエンドポイントに指定し、「Limit token usage」を5000に設定します。

これで、独自のLLMサーバーを使用してエージェントとの対話を開始できます。

処理が遅いLLMの最適化

カスタムLLMの処理時間が遅い場合(エージェント型推論や前処理が必要な場合など)、ストリーミングレスポンスにバッファワードを実装することで、会話の流れを改善できます。この手法により、LLMが完全な応答を生成している間も、自然な発話の抑揚を維持できます。

バッファワード

LLMが完全な応答を処理するのにさらに時間が必要な場合は、末尾が"... "(省略記号の後にスペース)となる初期応答を返してください。これにより、テキスト読み上げシステムは自然な流れを維持しつつ、会話の動的な感覚を保てます。 これにより、LLMがより長く推論できる後続コンテンツへ自然につながる間が生まれます。後続コンテンツが"..."に追加されるのを防ぐため、余分なスペースは不可欠です。追加されるとオーディオの歪みにつながることがあります。

実装

バッファワードを実装するためにカスタムLLMサーバーを変更する方法は次のとおりです。

@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
oai_request = request.dict(exclude_none=True)
if "user_id" in oai_request:
oai_request["user"] = oai_request.pop("user_id")
async def event_stream():
try:
# Send initial buffer chunk while processing
initial_chunk = {
"id": "chatcmpl-buffer",
"object": "chat.completion.chunk",
"created": 1234567890,
"model": request.model,
"choices": [{
"delta": {"content": "Let me think about that... "},
"index": 0,
"finish_reason": None
}]
}
yield f"data: {json.dumps(initial_chunk)}\n\n"
# Process the actual LLM response
chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)
async for chunk in chat_completion_coroutine:
chunk_dict = chunk.model_dump()
yield f"data: {json.dumps(chunk_dict)}\n\n"
yield "data: [DONE]\n\n"
except Exception as e:
logging.error("An error occurred: %s", str(e))
yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")

システムツールの統合

カスタムLLMは、システムツールをトリガーして、会話のフローと状態を制御できます。エージェントで設定すると、これらのツールはチャット補完リクエストのtoolsパラメーターに自動的に含まれます。

システムツールの仕組み

  1. LLMの判断:カスタムLLMは会話のコンテキストに基づいて、これらのツールをいつ呼び出すかを判断します
  2. ツールの応答:LLMは標準のOpenAI形式で関数呼び出しを返します
  3. バックエンド処理:ElevenLabsがツール呼び出しを処理し、会話の状態を更新します

システムツールの詳細については、ガイドを参照してください。

利用可能なシステムツール

目的:適切な条件が満たされたときに、会話を自動的に終了します。

トリガー条件:LLMは次の場合にこのツールを呼び出す必要があります。

  • 主なタスクが完了し、ユーザーが満足している
  • 双方の合意により、会話が自然な結論に達した
  • ユーザーが会話を終了したいと明示的に示した

パラメーター:

  • reason(string、必須):通話を終了する理由
  • message(string、任意):通話終了前にユーザーへ送る別れのメッセージ

関数呼び出し形式:

{
"type": "function",
"function": {
"name": "end_call",
"arguments": "{\"reason\": \"Task completed successfully\", \"message\": \"Thank you for using our service. Have a great day!\"}"
}
}

実装:エージェント設定でシステムツールとして設定します。LLMは、この関数をいつ呼び出すかについての詳細な指示を受け取ります。

詳細: 通話終了ツール

目的:会話中に、検出されたユーザーの言語へ自動的に切り替えます。

トリガー条件:LLMは次の場合にこのツールを呼び出す必要があります。

  • ユーザーが現在の会話言語とは異なる言語で話している
  • ユーザーが言語の切り替えを明示的にリクエストした
  • 会話に多言語サポートが必要である

パラメーター:

  • reason(string、必須):言語を切り替える理由
  • language(string、必須):切り替え先の言語コード(サポート対象言語リストに含まれている必要があります)

関数呼び出し形式:

{
"type": "function",
"function": {
"name": "language_detection",
"arguments": "{\"reason\": \"User requested Spanish\", \"language\": \"es\"}"
}
}

実装:エージェント設定で対応言語を設定し、言語検出システムツールを追加します。エージェントは、検出された言語に合わせて音声と応答を自動的に切り替えます。

詳細: 言語検出ツール

目的:ユーザーのニーズに基づいて、専門特化したAIエージェント間で会話を転送します。

トリガー条件:LLMは次の場合にこのツールを呼び出す必要があります。

  • ユーザーのリクエストに専門知識や別のエージェント機能が必要である
  • 現在のエージェントではクエリに適切に対応できない
  • 会話の流れから別の種類のエージェントが必要だと判断される

パラメーター:

  • reason(string、任意):エージェント転送の理由
  • agent_number(integer、必須):転送先エージェントのゼロ始まりの番号(設定された転送ルールに基づく)

関数呼び出し形式:

{
"type": "function",
"function": {
"name": "transfer_to_agent",
"arguments": "{\"reason\": \"User needs billing support\", \"agent_number\": 0}"
}
}

実装:条件を特定のエージェントIDにマッピングする転送ルールを定義します。現在のエージェントが転送できるエージェントを設定してください。転送設定では、エージェントはゼロ始まりの番号で参照されます。

詳細: エージェント転送ツール

目的:AIによる支援が十分でない場合に、会話を人間のオペレーターへスムーズに引き継ぎます。

トリガー条件:LLMは次の場合にこのツールを呼び出す必要があります。

  • 人間による判断が必要な複雑な問題
  • ユーザーが人間による支援を明示的にリクエストした
  • AIの能力では特定のリクエストに対応できない
  • エスカレーションプロトコルがトリガーされた

パラメーター:

  • reason(string、任意):転送の理由
  • transfer_number(string、必須):転送先の電話番号(設定済みの番号と一致している必要があります)
  • client_message(string、必須):転送を待つ間にクライアントへ読み上げるメッセージ
  • agent_message(string、必須):通話を受ける人間のオペレーターへのメッセージ

関数呼び出し形式:

{
"type": "function",
"function": {
"name": "transfer_to_number",
"arguments": "{\"reason\": \"Complex billing issue\", \"transfer_number\": \"+15551234567\", \"client_message\": \"I'm transferring you to a billing specialist who can help with your account.\", \"agent_message\": \"Customer has a complex billing dispute about order #12345 from last month.\"}"
}
}

実装:転送先の電話番号と条件を設定します。顧客と通話を受ける人間のオペレーターの両方に向けたメッセージを定義します。TwilioとSIPトランキングの両方で動作します。

詳細: 人間の担当者への転送ツール

目的:エージェントが発話せずに一時停止し、ユーザーの入力を待てるようにします。

トリガー条件:LLMは次の場合にこのツールを呼び出す必要があります。

  • ユーザーが少し時間が必要だと示している(「少し待って」「考えさせて」)
  • ユーザーが会話の流れを一時停止するようリクエストした
  • エージェントが、ユーザーには情報を処理する時間が必要だと検出した

パラメーター:

  • reason(string、任意):一時停止が必要な理由を説明する自由形式の理由

関数呼び出し形式:

{
"type": "function",
"function": {
"name": "skip_turn",
"arguments": "{\"reason\": \"User requested time to think\"}"
}
}

実装:追加設定は必要ありません。このツールは、ユーザーが再び話すまでエージェントが無言で待機するよう通知するだけです。

詳細: ターンをスキップツール

パラメータ:

  • reason(文字列、必須):留守番電話を検出した理由(例:「自動応答メッセージを検出」、「人間からの応答なし」)

関数呼び出し形式:

{
"type": "function",
"function": {
"name": "voicemail_detection",
"arguments": "{\"reason\": \"Automated greeting detected with request to leave message\"}"
}
}

詳細: 留守番電話検出ツール

システムツールを含むリクエスト例

システムツールが設定されている場合、カスタムLLMは標準のOpenAI形式でツールを含むリクエストを受け取ります。

{
"messages": [
{
"role": "system",
"content": "You are a helpful assistant. You have access to system tools for managing conversations."
},
{
"role": "user",
"content": "I think we're done here, thanks for your help!"
}
],
"model": "your-custom-model",
"temperature": 0.7,
"max_tokens": 1000,
"stream": true,
"tools": [
{
"type": "function",
"function": {
"name": "end_call",
"description": "Call this function to end the current conversation when the main task has been completed...",
"parameters": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "The reason for the tool call."
},
"message": {
"type": "string",
"description": "A farewell message to send to the user along right before ending the call."
}
},
"required": ["reason"]
}
}
},
{
"type": "function",
"function": {
"name": "language_detection",
"description": "Change the conversation language when the user expresses a language preference explicitly...",
"parameters": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "The reason for the tool call."
},
"language": {
"type": "string",
"description": "The language to switch to. Must be one of language codes in tool description."
}
},
"required": ["reason", "language"]
}
}
},
{
"type": "function",
"function": {
"name": "skip_turn",
"description": "Skip a turn when the user explicitly indicates they need a moment to think...",
"parameters": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Optional free-form reason explaining why the pause is needed."
}
},
"required": []
}
}
}
]
}

システムツールを使用するには、カスタムLLMが関数呼び出しに対応している必要があります。モデルが OpenAI形式で適切な関数呼び出し応答を生成できることを確認してください。

その他の機能

カスタムLLM実装に追加パラメーターを渡せます。

1

追加パラメーターを定義

カスタムパラメーターを含むオブジェクトを作成します。

from elevenlabs.conversational_ai.conversation import Conversation, ConversationInitiationData
extra_body_for_convai = {
"UUID": "123e4567-e89b-12d3-a456-426614174000",
"parameter-1": "value-1",
"parameter-2": "value-2",
}
config = ConversationInitiationData(
extra_body=extra_body_for_convai,
)
2

LLM実装を更新

追加パラメーターを処理するよう、カスタムLLMコードを変更します。

import json
import os
import fastapi
from fastapi.responses import StreamingResponse
from fastapi import Request
from openai import AsyncOpenAI
import uvicorn
import logging
from dotenv import load_dotenv
from pydantic import BaseModel
from typing import List, Optional
# Load environment variables from .env file
load_dotenv()
# Retrieve API key from environment
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
if not OPENAI_API_KEY:
raise ValueError("OPENAI_API_KEY not found in environment variables")
app = fastapi.FastAPI()
oai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)
class Message(BaseModel):
role: str
content: str
class ChatCompletionRequest(BaseModel):
messages: List[Message]
model: str
temperature: Optional[float] = 0.7
max_tokens: Optional[int] = None
stream: Optional[bool] = False
user_id: Optional[str] = None
elevenlabs_extra_body: Optional[dict] = None
@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
oai_request = request.dict(exclude_none=True)
print(oai_request)
if "user_id" in oai_request:
oai_request["user"] = oai_request.pop("user_id")
if "elevenlabs_extra_body" in oai_request:
oai_request.pop("elevenlabs_extra_body")
chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)
async def event_stream():
try:
async for chunk in chat_completion_coroutine:
chunk_dict = chunk.model_dump()
yield f"data: {json.dumps(chunk_dict)}\n\n"
yield "data: [DONE]\n\n"
except Exception as e:
logging.error("An error occurred: %s", str(e))
yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8013)

リクエスト例

このカスタムメッセージ設定では、LLMは次の形式でリクエストを受け取ります。

{
"messages": [
{
"role": "system",
"content": "\n <Redacted>"
},
{
"role": "assistant",
"content": "Hey I'm currently unavailable."
},
{
"role": "user",
"content": "Hey, who are you?"
}
],
"model": "gpt-4o",
"temperature": 0.5,
"max_tokens": 5000,
"stream": true,
"elevenlabs_extra_body": {
"UUID": "123e4567-e89b-12d3-a456-426614174000",
"parameter-1": "value-1",
"parameter-2": "value-2"
}
}