Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,4 @@ docs/

# Examples for development
examples/dev/*
.env.local
391 changes: 23 additions & 368 deletions README.md

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions examples/.env.example

This file was deleted.

98 changes: 98 additions & 0 deletions examples/voice_agents/intelligent_interruption.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
Example: Intelligent Interruption Handling with Backchannel Filtering

This example demonstrates the backchannel filter feature that allows the agent to:
1. Continue speaking when user says backchannel words ("yeah", "ok", "hmm")
2. Stop immediately when user says interrupt commands ("stop", "wait", "no")
3. Handle mixed input ("yeah wait") as an interruption
4. Respond to backchannels when the agent is silent

Usage:
uv run python examples/voice_agents/intelligent_interruption.py console
"""

import logging

from dotenv import load_dotenv

from livekit.agents import (
Agent,
AgentServer,
AgentSession,
JobContext,
JobProcess,
cli,
room_io,
)
from livekit.agents.voice import DEFAULT_BACKCHANNEL_WORDS, DEFAULT_INTERRUPT_WORDS
from livekit.plugins import silero

logger = logging.getLogger("intelligent-interruption")

# Load environment from .env.local
load_dotenv(".env.local")


class StorytellerAgent(Agent):
"""An agent that tells stories and demonstrates intelligent interruption handling."""

def __init__(self) -> None:
super().__init__(
instructions="""You are a storyteller assistant. When asked to tell a story,
provide an engaging, moderately long story (about 3-5 paragraphs).

Important behavior:
- If the user says "yeah", "ok", "hmm" while you're speaking, continue your story.
- If the user says "stop", "wait", or "actually", stop and listen.
- If the user is silent and says "yeah" or "ok", ask what they'd like to hear about.

Keep your responses without emojis or markdown formatting since this is voice-only.
"""
)

async def on_enter(self) -> None:
"""Called when the agent joins the session."""
self.session.generate_reply(
instructions="Greet the user briefly and offer to tell them a story."
)


server = AgentServer()


def prewarm(proc: JobProcess):
proc.userdata["vad"] = silero.VAD.load()


server.setup_fnc = prewarm


@server.rtc_session()
async def entrypoint(ctx: JobContext):
"""Main entry point for the agent."""
# Create agent session with backchannel filtering enabled (default)
session = AgentSession(
stt="deepgram/nova-3",
llm="openai/gpt-4.1-mini",
tts="cartesia/sonic-2:9626c31c-bec5-4cca-baa8-f8ba9e84c8bc",
vad=ctx.proc.userdata["vad"],
# Backchannel filtering is enabled by default; override lists via
# LIVEKIT_BACKCHANNEL_WORDS / LIVEKIT_INTERRUPT_WORDS if needed
# Optional: customize backchannel words (uses defaults if None)
# backchannel_words=frozenset({"yeah", "yep", "ok", "okay", "hmm"}),
# Optional: customize interrupt words (uses defaults if None)
# interrupt_words=frozenset({"stop", "wait", "no", "pause"}),
# Enable false interruption handling
resume_false_interruption=True,
false_interruption_timeout=1.5,
)

await session.start(
agent=StorytellerAgent(),
room=ctx.room,
room_options=room_io.RoomOptions(),
)


if __name__ == "__main__":
cli.run_app(server)
8 changes: 8 additions & 0 deletions livekit-agents/livekit/agents/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,13 @@
AgentSession,
AgentStateChangedEvent,
AgentTask,
BackchannelFilter,
BackchannelFilterOptions,
CloseEvent,
CloseReason,
ConversationItemAddedEvent,
DEFAULT_BACKCHANNEL_WORDS,
DEFAULT_INTERRUPT_WORDS,
ErrorEvent,
FunctionToolsExecutedEvent,
MetricsCollectedEvent,
Expand Down Expand Up @@ -157,6 +161,10 @@ def __getattr__(name: str) -> typing.Any:
"ModelSettings",
"Agent",
"AgentTask",
"BackchannelFilter",
"BackchannelFilterOptions",
"DEFAULT_BACKCHANNEL_WORDS",
"DEFAULT_INTERRUPT_WORDS",
"AssignmentTimeoutError",
"APIConnectionError",
"APIError",
Expand Down
10 changes: 10 additions & 0 deletions livekit-agents/livekit/agents/voice/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
from . import io, run_result
from .agent import Agent, AgentTask, ModelSettings
from .agent_session import AgentSession, VoiceActivityVideoSampler
from .backchannel_filter import (
BackchannelFilter,
BackchannelFilterOptions,
DEFAULT_BACKCHANNEL_WORDS,
DEFAULT_INTERRUPT_WORDS,
)
from .events import (
AgentEvent,
AgentFalseInterruptionEvent,
Expand Down Expand Up @@ -45,6 +51,10 @@
"FunctionToolsExecutedEvent",
"AgentFalseInterruptionEvent",
"TranscriptSynchronizer",
"BackchannelFilter",
"BackchannelFilterOptions",
"DEFAULT_BACKCHANNEL_WORDS",
"DEFAULT_INTERRUPT_WORDS",
"io",
"room_io",
"run_result",
Expand Down
Loading