メインコンテンツへスキップ

2026 年 3 月

今月のプラットフォーム全体の API、PVC、クライアントライブラリのアップデートは Changelog 2026(2026 年 3 月)に記載されています。

2026 年 2 月 4 日

AgentUpdateCall 出力イベント

会話中に通話設定を動的に更新するための AgentUpdateCall イベントが追加されました:
from line.events import AgentUpdateCall

# In an agent's process method:
yield AgentUpdateCall(voice_id="5ee9feff-1265-424a-9d7f-8e4d431a12c7")
yield AgentUpdateCall(pronunciation_dict_id="dict-123")
フィールド説明
voice_idエージェントのボイスを更新します
pronunciation_dict_id発音辞書を更新します
すべてのフィールドはオプションで、設定したフィールドのみが更新されます。詳細は イベント を参照してください。

2026 年 2 月 1 日

Line SDK v0.2 — メジャーリリース

Line SDK v0.2 をリリースしました。シンプルさ、ストリーミングパフォーマンス、シームレスな LLM 統合に焦点を当てた、音声エージェントフレームワークの全面的な再設計です。このリリースでは、以前のイベントバスシステムを置き換える新しい async iterable アーキテクチャを導入しています。
破壊的変更: v0.2 は v0.1.x との後方互換性がありません。詳細なアップグレード手順については以下の マイグレーションガイド を参照してください。
何が変わるのか? Line SDK v0.2 により、音声エージェントの構築がはるかにシンプルになります。複数のコンポーネント(システム、ブリッジ、ノード)を手動で組み合わせる代わりに、エージェントを返す単一の関数を書くだけになります。SDK が音声、割り込み、会話フローを自動的に処理します。
なぜアップグレードするのか?
  • 開発の高速化 — ボイラープレートコードが減り、エージェントを数日ではなく数時間で構築可能
  • メンテナンスの容易さ — 可動部分が少ないため、バグが減りデバッグが簡単
  • より高い信頼性 — エラー処理、リトライ、フォールバックモデルが組み込み
  • より高い柔軟性 — コード変更なしに 100 以上の AI プロバイダー(OpenAI、Anthropic、Google など)を切り替え可能
  • 強力なツール — Web 検索、通話転送、マルチエージェントのハンドオフなどを 1 行のコードで追加

v0.2 の新機能

シンプルになったエージェントアーキテクチャ

新しいアーキテクチャでは、VoiceAgentSystemBusBridgeReasoningNode パターンを単一の async iterable 関数で置き換えています:
import os
from line import CallRequest
from line.llm_agent import LlmAgent, LlmConfig, end_call
from line.voice_agent_app import AgentEnv, VoiceAgentApp

async def get_agent(env: AgentEnv, call_request: CallRequest):
    return LlmAgent(
        model="anthropic/claude-haiku-4-5-20251001",
        api_key=os.getenv("ANTHROPIC_API_KEY"),
        tools=[end_call],
        config=LlmConfig(
            system_prompt="You are a helpful assistant.",
            introduction="Hello! How can I help you today?",
        ),
    )

app = VoiceAgentApp(get_agent=get_agent)
利点:
  • ボイラープレートコードの削減
  • 手動のイベントルーティングやブリッジ構成が不要
  • 会話履歴の自動管理
  • 組み込みの割り込み処理
  • 迅速で簡単なツール定義

LiteLLM 経由の組み込み LLM サポート

LlmAgentLiteLLM を介して 100 以上の LLM プロバイダーへの統合アクセスを提供します:
# OpenAI
LlmAgent(model="gpt-5-nano", api_key=os.getenv("OPENAI_API_KEY"), ...)

# Anthropic
LlmAgent(model="anthropic/claude-haiku-4-5-20251001", api_key=os.getenv("ANTHROPIC_API_KEY"), ...)

# Google Gemini
LlmAgent(model="gemini/gemini-2.5-flash-preview-09-2025", api_key=os.getenv("GEMINI_API_KEY"), ...)

# With fallbacks
LlmAgent(
    model="gpt-5-nano",
    config=LlmConfig(fallbacks=["anthropic/claude-haiku-4-5-20251001", "gemini/gemini-2.5-flash-preview-09-2025"]),
    ...
)

宣言的なツールシステム

シンプルなデコレーターを使ってエージェントの機能を定義できます。3 種類のツールで一般的なシナリオをカバーします:
ツールタイプデコレーター内容用途の例
Loopback@loopback_tool情報を取得し、その後エージェントが自然に回答を話す注文ステータスの確認、口座残高の照会
Passthrough@passthrough_tool追加の AI 処理なしで即時にアクションを実行通話の終了、電話番号への転送
Handoff@handoff_tool会話を別の専用エージェントに引き継ぐスペイン語サポートへのルーティング、課金部門へのエスカレーション
from typing import Annotated
from line.llm_agent import loopback_tool, passthrough_tool, handoff_tool
from line.events import AgentEndCall

@loopback_tool
async def get_weather(ctx, city: Annotated[str, "City name"]) -> str:
    """Get current weather for a city."""
    return f"72°F and sunny in {city}"

@passthrough_tool
async def end_call(ctx):
    """End the call."""
    yield AgentEndCall()

@handoff_tool
async def transfer_to_support(ctx, event):
    """Transfer to support agent."""
    async for output in support_agent.process(ctx.turn_env, event):
        yield output

バックグラウンドでのツール実行

長時間実行されるツールは、LLM をブロックせずにバックグラウンドで実行できます:
from typing import Annotated
from line.llm_agent import loopback_tool

@loopback_tool(is_background=True)
async def check_bank_balance(ctx, account_id: Annotated[str, "Account ID"]):
    """Check account balance (may take a few seconds)."""
    yield "Checking your balance..."  # Immediate acknowledgment
    balance = await api.get_balance(account_id)  # Long operation
    yield f"Your balance is ${balance:.2f}"  # Triggers new LLM completion

組み込みツール

よく使う操作はすぐに利用できます:
from line.llm_agent import end_call, send_dtmf, transfer_call, web_search, agent_as_handoff

agent = LlmAgent(
    tools=[
        end_call,                    # End the call
        send_dtmf,                   # Send DTMF tones
        transfer_call,               # Transfer to phone number
        web_search,                  # Real-time web search
        agent_as_handoff(other_agent, name="transfer_to_billing"),
    ],
    ...
)

マルチエージェントのワークフロー

agent_as_handoff で高度なエージェントルーティングを作成します:
spanish_agent = LlmAgent(
    model="gpt-5-nano",
    config=LlmConfig(system_prompt="Speak only in Spanish.", ...),
    ...
)

main_agent = LlmAgent(
    tools=[
        agent_as_handoff(
            spanish_agent,
            handoff_message="Transferring to Spanish support...",
            name="transfer_to_spanish",
            description="Transfer when user requests Spanish.",
        ),
    ],
    ...
)

構造化されたイベントシステム

イベントは、エージェントが外部の世界と通信する仕組みです。出力イベント はエージェントが取るアクション(発話、通話終了)です。入力イベント は通話中に発生する出来事(ユーザーの発話、通話開始)です。 出力イベント(エージェント → ハーネス):
  • AgentSendText — 話すテキストを送信
  • AgentEndCall — 通話を終了
  • AgentTransferCall — 別の番号に転送
  • AgentSendDtmf — DTMF トーンを送信
  • AgentToolCalled / AgentToolReturned — ツール実行の追跡
  • LogMetric / LogMessage — オブザーバビリティ
入力イベント(ハーネス → エージェント):
  • CallStarted / CallEnded — 通話ライフサイクル
  • UserTurnStarted / UserTurnEnded — ユーザーの発話
  • UserTextSent / UserDtmfSent — ユーザーのコンテンツ
  • AgentHandedOff — ハンドオフ通知
すべての入力イベントには、完全な会話コンテキストを含む history フィールドが含まれます。

強化された設定

エージェントの思考と応答の調整方法。LlmConfig を使うと、AI のパーソナリティ、応答の長さ、創造性、信頼性を制御できます:
LlmConfig(
    system_prompt="You are a helpful assistant.",
    introduction="Hello! How can I help?",

    # Sampling parameters
    temperature=0.7,
    max_tokens=1024,
    top_p=0.95,

    # Resilience
    num_retries=2,
    fallbacks=["gpt-5-nano"],
    timeout=30.0,

    # Provider-specific options
    extra={"reasoning_effort": "high"},
)

v0.1.x から v0.2 へのマイグレーションガイド

このガイドでは、既存の v0.1.x エージェントを v0.2 にアップグレードする手順を解説します。マイグレーションでは、インポートの更新、エージェントのセットアップの簡素化、新しいツールシステムの採用を行います。ほとんどのエージェントは 1 時間以内にマイグレートできます。

変更点の概要

v0.1.xv0.2
VoiceAgentSystem + Bus + Bridgeget_agent コールバックを持つ VoiceAgentApp
ReasoningNode のサブクラスLlmAgent またはカスタム Agent プロトコル
call_handler(system, request)get_agent(env, request) -> Agent
手動のイベントルーティングフィルターによる自動的なイベントディスパッチ
process_context() メソッドprocess(env, event) の async iterable

ステップ 1: インポートを更新

# v0.1.x
from line.voice_agent_app import VoiceAgentApp
from line.voice_agent_system import VoiceAgentSystem
from line.bridge import Bridge
from line.nodes import ReasoningNode
from line.events import (
    AgentSpeechSent,
    UserTranscriptionReceived,
    EndCall,
    TransferCall,
)

# v0.2
from line.voice_agent_app import VoiceAgentApp, AgentEnv
from line.llm_agent import LlmAgent, LlmConfig
from line.llm_agent import end_call, transfer_call, loopback_tool, passthrough_tool
from line.events import (
    AgentSendText,
    AgentEndCall,
    AgentTransferCall,
    UserTurnEnded,
    CallStarted,
)

ステップ 2: VoiceAgentSystem を get_agent に置き換える

v0.1.x では、bridge.on() を介してイベントルーティングを手動で構成していました。v0.2 では、カスタマイズ可能な run および cancel フィルターによってイベントディスパッチが自動的に行われます。
from line.voice_agent_app import VoiceAgentApp
from line.voice_agent_system import VoiceAgentSystem
from line.bridge import Bridge
from line.nodes import ReasoningNode
from line.events import (
    UserTranscriptionReceived,
    UserStoppedSpeaking,
    DTMFInputEvent,
)

class MyReasoningNode(ReasoningNode):
    async def process_context(self, context):
        # Your LLM logic here
        response = await call_llm(context.messages)
        yield AgentResponse(content=response)

async def call_handler(system: VoiceAgentSystem, call_request):
    node = MyReasoningNode(system_prompt="You are helpful.")
    bridge = Bridge(node)

    system.with_speaking_node(node, bridge)

    # Manual event routing with bridge.on()
    bridge.on(UserTranscriptionReceived).map(node.add_event)
    bridge.on(UserStoppedSpeaking).stream(node.generate).broadcast()

    # DTMF events required explicit routing
    bridge.on(DTMFInputEvent).map(node.handle_dtmf)

    await system.start()
    await system.send_initial_message("Hello!")
    await system.wait_for_shutdown()

app = VoiceAgentApp(call_handler=call_handler)

Run および Cancel フィルター

フィルターは通話中のエージェントの動作を制御します:
  • Run フィルター はエージェントが応答するトリガー(例:ユーザーが発話を終えたとき)を決定します
  • Cancel フィルター はエージェントを中断するイベント(例:ユーザーがエージェントに重ねて話し始めたとき)を決定します
エージェントだけを返す代わりに、タプルを返すことでこれらをカスタマイズできます:
from typing import Union, Tuple

AgentSpec = Union[Agent, Tuple[Agent, run_filter, cancel_filter]]
フィルター目的デフォルト
run_filterエージェント処理をトリガーするイベント[CallStarted, UserTurnEnded, CallEnded]
cancel_filter実行中のエージェントタスクをキャンセルするイベント[UserTurnStarted]
例: DTMF 入力に応答するエージェント
from line.events import (
    CallStarted, CallEnded, UserTurnEnded, UserTurnStarted, UserDtmfSent
)

async def get_agent(env: AgentEnv, call_request: CallRequest):
    agent = LlmAgent(...)

    # Include UserDtmfSent in run_filter to process DTMF
    run_filter = [CallStarted, UserTurnEnded, UserDtmfSent, CallEnded]
    cancel_filter = [UserTurnStarted]

    return (agent, run_filter, cancel_filter)
例: 中断されないエージェント
async def get_agent(env: AgentEnv, call_request: CallRequest):
    agent = LlmAgent(...)

    # Empty cancel_filter = agent won't be interrupted
    run_filter = [CallStarted, UserTurnEnded, CallEnded]
    cancel_filter = []

    return (agent, run_filter, cancel_filter)
例: カスタムフィルター関数
def my_run_filter(event: InputEvent) -> bool:
    """Only process events during business hours."""
    if isinstance(event, CallStarted):
        return is_business_hours()
    return isinstance(event, (UserTurnEnded, CallEnded))

async def get_agent(env: AgentEnv, call_request: CallRequest):
    agent = LlmAgent(...)
    return (agent, my_run_filter, [UserTurnStarted])

ステップ 3: イベント処理をマイグレート

# Event names
AgentSpeechSent        # Agent spoke
UserTranscriptionReceived  # User spoke
EndCall                # End call
TransferCall           # Transfer call

# Manual event handling in ReasoningNode
class MyNode(ReasoningNode):
    async def process_context(self, context):
        for event in context.events:
            if isinstance(event, UserTranscriptionReceived):
                user_message = event.transcription

ステップ 4: カスタムツールをマイグレート

# Manual tool handling in ReasoningNode
class MyNode(ReasoningNode):
    async def process_context(self, context):
        # Parse tool calls from LLM response
        if tool_call := extract_tool_call(response):
            result = await self.execute_tool(tool_call)
            # Manually add to context and call LLM again
            context.add_tool_result(result)
            response = await call_llm(context)

ステップ 5: マルチエージェントパターンをマイグレート

# Manual agent switching
class MainNode(ReasoningNode):
    def __init__(self, spanish_node):
        self.spanish_node = spanish_node
        self.use_spanish = False

    async def process_context(self, context):
        if self.should_switch_to_spanish(context):
            self.use_spanish = True
            # Complex manual state management

削除された API

v0.1.x の以下の API は削除され、直接の代替はありません:
削除済み代替
VoiceAgentSystemget_agent を持つ VoiceAgentApp を使用
Busイベントは自動的にディスパッチされる
BridgeAgentSpec の run/cancel フィルターを使用
ReasoningNodeLlmAgent を使うか、Agent プロトコルを実装
ConversationHarnessConversationRunner によって内部的に処理
EventsRegistry型付きイベントクラスを直接使用

カスタムエージェントプロトコル

LlmAgent を超えるカスタムロジックが必要な場合は、Agent プロトコルを実装します:
from typing import AsyncIterable
from line.events import (
    InputEvent,
    OutputEvent,
    AgentSendText,
    CallStarted,
    UserTurnEnded,
)

class CustomAgent:
    """Custom agent implementing the Agent protocol."""

    async def process(self, env, event: InputEvent) -> AsyncIterable[OutputEvent]:
        if isinstance(event, CallStarted):
            yield AgentSendText(text="Hello from custom agent!")
        elif isinstance(event, UserTurnEnded):
            # Your custom logic here
            user_message = event.content[0].content
            response = await your_custom_logic(user_message, event.history)
            yield AgentSendText(text=response)

破壊的変更のサマリー

このセクションでは、すべての破壊的変更へのクイックリファレンスを提供します。コードのマイグレーション時のチェックリストとしてご利用ください。

イベント名の変更

v0.1.xv0.2
AgentSpeechSentAgentSendText(出力)/ AgentTextSent(入力)
UserTranscriptionReceivedUserTextSent / UserTurnEnded
UserStartedSpeakingUserTurnStarted
UserStoppedSpeakingUserTurnEnded
AgentStartedSpeakingAgentTurnStarted
AgentStoppedSpeakingAgentTurnEnded
EndCallAgentEndCall
TransferCallAgentTransferCall
DTMFInputEventUserDtmfSent
DTMFOutputEventAgentSendDtmf
出力イベント vs. 入力イベント: AgentSendText はエージェントに発話させるために yield する出力イベントです。AgentTextSent は発話された内容を確認する 受信 入力イベントです(履歴に表示されます)。

構造の変更

  • イベントの履歴: すべての入力イベントに、完全な会話コンテキストを含むオプションの history フィールドが追加されました。historyNone の場合、そのイベントは履歴リスト内にあります。リストが含まれている場合、そのイベントには完全なコンテキストが添付されています。
  • ツールイベント: ToolCall/ToolResult は、構造化された AgentToolCalled/AgentToolReturned に置き換えられました
  • イベント ID: すべてのイベントには追跡用の安定した event_id フィールドが追加されました

設定の変更

v0.1.xv0.2
CallRequest.agent.system_promptLlmConfig.system_prompt
CallRequest.agent.introductionLlmConfig.introduction
手動の LLM パラメータLiteLLM の完全サポートを持つ LlmConfig
LlmConfig.from_call_request(call_request, fallback_system_prompt="...", fallback_introduction="...") を使用すると、Cartesia プレイグラウンドからの設定を自動的に継承しつつ、妥当なデフォルトを提供できます。詳細は Agents ドキュメント を参照してください。

新しい依存関係

v0.2 では以下の依存関係が導入されています:
litellm              # Multi-provider LLM support
pydantic             # Type validation for events
phonenumbers >= 9.0  # Phone number validation for transfer_call
例で使用する任意の依存関係:
exa-py               # Exa web search integration
duckduckgo-search    # Fallback web search

ヘルプを得る