The efficient handoff
A working handoff is not the same as a polished one. Callers notice when the specialist repeats a question, when markdown sneaks into TTS and gets read as asterisk asterisk, or when the silence between transfer and first reply feels long. This lesson tightens all of it.
Polish layers on a handoff
Three independent concerns that together make transfers feel natural.
import re
def strip_markdown(text: str) -> str:
"""Remove markdown so TTS does not read formatting as audio."""
if not text:
return text
text = re.sub(r"```[\s\S]*?```", "", text)
text = re.sub(r"`([^`]+)`", r"\1", text)
text = re.sub(r"^#{1,6}\s+(.+)$", r"\1", text, flags=re.MULTILINE)
text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text)
text = re.sub(r"\*([^*]+)\*", r"\1", text)
text = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", text)
text = re.sub(r"^[\s]*[-*+]\s+", "", text, flags=re.MULTILINE)
return text.strip()LLMs produce markdown by reflex. TTS will faithfully pronounce asterisks and backticks as words. Strip the formatting before sending text to TTS so the caller hears natural speech.
import json
from datetime import datetime
from typing import Optional, Any
async def emit_thought(
self,
category: str,
content: str,
metadata: Optional[dict[str, Any]] = None,
) -> None:
"""Publish an agent reasoning event to the frontend."""
userdata: UserData = self.session.userdata
room = userdata.ctx.room if userdata and userdata.ctx else None
if not room:
return
thought = {
"category": category,
"content": content,
"timestamp": datetime.now().isoformat(),
"agent": self.__class__.__name__,
"metadata": metadata or {},
}
await room.local_participant.publish_data(
json.dumps({"thinking": thought}).encode("utf-8")
)Thinking events give your frontend a live view of what the agent is doing. Use categories like analysis, planning, or action so the UI can render a timeline next to the audio.
Quiz: Quiz
Loading practice…