tell me about livekit cloud agents
# ask-ai
l
tell me about livekit cloud agents
t
Thanks for reaching out, G. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’ll explain LiveKit Cloud Agents and what they let you do. What LiveKit Cloud Agents are • LiveKit Cloud Agents is a managed service for deploying and scaling stateful AI-powered voice, video, and physical agents on LiveKit’s global infrastructure. It handles the infrastructure and lifecycle so you can focus on agent logic. Key capabilities • Stateful load balancing: Keeps long-lived sessions on the same server to preserve state and reduce latency, using metrics like CPU, GPU, memory, and network health. • Capacity management and autoscaling: Because agents are resource-intensive, LiveKit Cloud automatically spins up new instances during traffic spikes and limits agents per machine to maintain performance. • Draining and instant rollbacks: During updates, existing sessions complete gracefully while new sessions go to updated versions; if problems occur you can instantly roll back to a previous version. • Operational observability: Dashboard and tools provide session analytics, build logs, quality metrics (uptime, session start latency, resource consumption). • Integrations with AI tooling: Supports LLMs, STT, and TTS through LiveKit Inference and integrations (for example OpenAI) so you can wire up model providers without managing all API keys yourself. • CLI-based lifecycle management: Create, build, deploy, update, rollback, and delete agents via the LiveKit CLI; secure secret management for API keys and credentials is supported. Billing and availability • Agents are billed per active session minute (the stated rate is $0.01 per agent session minute), and paid plans keep agents warm so they accept connections instantly. • LiveKit Cloud Agents launched as a closed beta and is designed for production-grade use cases. Typical use cases • Real-time conversational AI: mental health support bots, tutoring, appointment scheduling. • High-stakes voice workflows: emergency call triage and other latency-sensitive, stateful voice interactions. • Any application that needs stateful, streaming audio/video agents with scalable, managed infrastructure. If you’d like, I can: • Walk through deploying a sample agent with the LiveKit CLI step-by-step. • Explain how billing or “warm” agent behavior works in more detail. • Show how to connect a specific model provider (e.g., OpenAI) for STT/TTS or LLM-driven conversation. Which of those would you like to do next? Sources: Deploy and scale agents on LiveKit Cloud | Building the all-in-one platform for voice AI agents | server-sdk-go | livekit/livekit_composite | DeepWiki | Agent deployment overview | LiveKit Documentation | OpenAI and LiveKit | LiveKit Documentation
l
i am asking functions and prompt specified questions how does livkeit cloud agents perceive adn gets prompts if there many diffrent prompts want tobe utilizied wahat is the best practices like persona prompt, tools usage prompt, most importnat context prompt, second context prompt - or for second context prompt even RAG connection, how does livekit can help with this specific? When the information all the playbooks are alot of context
t
Thanks — this is a great, detailed question. I’ll address how LiveKit Cloud Agents perceive and receive prompts, how to structure multiple prompt types (persona, tool usage, primary/secondary context), and practical best practices for handling large playbooks (including RAG). If you want, I can follow up with code examples for the exact part you care about. How LiveKit takes prompts and context (high level) • Agents receive an initial instruction set when created — this is the system-level persona and core rules (the “persona prompt” /
instructions
). • Session-specific details (user info, job metadata, initial context) are added to the
ChatContext
at session start. • During conversation, lifecycle hooks (for example
on_user_turn_completed
) let you inject or fetch additional context dynamically before the LLM generates a reply. • Tools are defined and exposed to the agent (via decorators or tool definitions); the agent’s instructions explain when/how to call them. Recommended prompt structure (modules you can combine) • Persona prompt (base system): identity, tone, role, goals, and guardrails. Keep it short and authoritative. • Tools usage prompt: explicit rules about when to call tools, required inputs, and how results should be formatted back to users. Place this with
instructions
or attach to each tool definition. • Primary context (important session data): loaded at session start — user profile, active playbook ID, recent important events. Put the most critical facts here. • Secondary/contextual augmentation (large or rarely-used info): do not preload long playbooks into the initial prompt. Fetch and inject only what is relevant per turn (RAG). Best practices for handling many different prompts and lots of playbook/context 1. Modularize prompts ◦ Keep persona, tools, goals, and guardrails as separate sections in your system instructions (Markdown works well). ◦ This lets you swap or update the persona without touching RAG or tool logic. 1. Use job/room metadata for initial context ◦ Pass user-specific or session-specific fields (IDs, preferences, playbook references) via job or room metadata and load only essential fields into the
ChatContext
at start. 1. Offload big playbooks to a vector store and use RAG ◦ Store detailed playbooks in a vector DB. On each user turn, perform a targeted retrieval for the most relevant playbook excerpts and inject only those snippets into the turn via the lifecycle hook. This keeps token usage manageable and the prompt focused. 1. Inject context dynamically with lifecycle hooks ◦ Use
on_user_turn_completed
(or equivalent pipeline node) to: ◦ Run a vector search or DB lookup for playbook sections relevant to the latest utterance. ◦ Add a concise “Additional info” assistant message or a structured context block to the turn before generating the assistant’s reply. 1. Define clear tool contracts ◦ When exposing tools (APIs, DB lookups, etc.), define their input/output schemas and instruct the agent how to format calls and handle results. This avoids ambiguous or repeated retrievals. 1. Use retrieval + short context snippets, not full docs ◦ Return only the minimal, relevant excerpt plus a short summarizing instruction (e.g., “Use the following playbook excerpt for safety rules; do not quote the full text”). This reduces hallucinations and token costs. 1. Multi-persona and multi-tenant approaches ◦ Single-agent, dynamic persona: Load persona fragments based on the caller (from metadata) when the session starts. ◦ Multi-agent workflows: For complex pipelines, split responsibilities into agents (or workflow tasks) and hand off between them when specialized behavior is required. 1. Testing, guardrails, and observability ◦ Keep test cases for each persona and critical playbook path. ◦ Use the dashboard logs and session analytics to see which retrieved contexts were used and how often, then refine retrieval queries and prompt phrasing. Practical prompt ordering when mixing types (recommended) • System/persona (base rules and tone) • Short user metadata (name, role — from job/room metadata) • Immediate user request or question • Injected RAG snippets (clearly labeled and minimal) • Tool invocation guidance (if agent should call a tool for this task) When to RAG vs. when to include direct context • Use direct context when the info is small and crucial (e.g., user preferences, current case ID). • Use RAG when the info set is large, frequently updated, or seldom needed in full (e.g., full playbooks, compliance manuals). How LiveKit helps specifically • Lifecycle hooks to inject per-turn context (so you avoid oversized initial prompts). • Support for tools and explicit tool definitions so agents can call your retrieval or action endpoints reliably. • Job/room metadata to pass session-scoped variables and playbook IDs at connection time. • Observability (session logs, metrics) to iterate on prompt structure and retrieval relevance. Next steps I can help with • Walk through a concrete example: show a short persona + tool prompt and an
on_user_turn_completed
RAG flow. • Provide a sample implementation (Python or Node) that: 1) initializes persona and metadata, 2) runs a vector search on user turns, and 3) injects retrieved snippets before response generation. • Help design the prompt sections for one of your playbooks to optimize retrieval and clarity. Which of those would be most helpful — a conceptual walkthrough, or a runnable code example in Python/Node? Sources: Prompting guide | LiveKit Documentation | External data and RAG | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation | Context Variables | LiveKit Documentation | Building the all-in-one platform for voice AI agents
l
i need more about specific VOICE realtime agent which talks via SIP. Lets say i am talking about Lead qualifier agent he needs to be salesy, have most important information to straight on shoot like objectives and etc, tools defition, RAG? (how do we utilize then rag) how does that work?
t
Thanks — this is a great, focused question. I’ll walk through how to build a real-time voice Lead‑Qualifier agent that answers via SIP using LiveKit Cloud Agents: how the agent perceives prompts, how to structure persona/tool/context, where and when to run RAG, and practical tips for a salesy qualification flow. Which part would you like first? I can (A) give a compact architecture + step checklist to get a SIP call routed to a LiveKit agent with STT→LLM→TTS and RAG, or (B) provide a runnable example (Python or Node) showing persona instructions, a tool definition (CRM booking), and an
on_user_turn_completed
RAG flow. Pick A or B — or ask for both and I’ll do A then the code example. Below is the compact architecture + best practices so you can get going immediately. 1) Architecture (high level) • Caller dials → SIP trunk → LiveKit telephony bridge → LiveKit Room. • LiveKit Cloud Agent joins the room and processes audio: STT converts speech to text → LLM (with persona, context, and tool availability) decides next action → Tools may be invoked (CRM, calendar) → LLM response → TTS renders audio back to the caller. • RAG is executed between STT and LLM: after STT returns text, run an internal retrieval (vector DB or KB) and inject the retrieved snippet into the chat context before LLM generation. 2) How the agent “perceives” prompts & context (voice pipeline specifics) • System / persona instructions: set as the agent’s core
instructions
at creation. This controls tone, goals, and guardrails (the “salesy” voice). • Session metadata: pass lead/session info (playbook id, user region, campaign) via job/room metadata so the agent has immediate, small critical facts. • Turn-level dynamic injection (RAG): when the user finishes a turn, run retrieval based on the transcribed text, then add that snippet into the ChatContext via the lifecycle hook (e.g.,
on_user_turn_completed
) before LLM replies. This is the key to keeping prompts small but contextually rich. • Tools: expose function-style tools (booking, CRM update, discount check). The LLM will call them when instructed by its prompt rules. 3) Persona design for Lead Qualifier (prompt template) • Keep persona concise, actionable, and with clear goals and constraints. Example sections: ◦ Identity + tone: “You are Alex, an energetic sales agent. Speak confidently and briefly.” ◦ Goals: “Qualify the lead (budget, timeline, use case), gather contact info, and attempt to book a demo.” ◦ Output rules: “Ask one question at a time. If qualified, offer 2 demo slots and confirm email/phone.” ◦ Guardrails: “Do not make pricing commitments beyond X; escalate complex objections to human.” Example (short): "You are Alex, a confident, friendly sales agent for Acme. Goal: qualify leads quickly and book demos. Start with a short hook, ask targeted qualifying questions (budget, timeline, decision maker), and close with a demo booking. Use sales phrasing but never promise discounts unless verified. Keep replies under 20 seconds of speech." 4) Tools you should define (and how to prompt their usage) • book_demo(date, time, email) — schedule demo in CRM/calendar. • check_discount(product, region) — returns active promotions. • update_lead(status, notes) — persists qualification results. • escalate_to_human(reason) — trigger transfer/alert. Best practice: • Include explicit tool-use guidance inside `instructions`: when to call each tool and example input/output. Tools should have strict parameter schemas so the LLM can call them reliably. 5) RAG: where, when, and how to use it in a voice session • When: Immediately after STT of the user’s utterance and before LLM generation for that turn. • What to retrieve: short playbook snippets, objection-handling scripts, up-to-date pricing or promotion lines, competitor notes—only the parts relevant to the user’s utterance. • How: Use a vector DB (or document DB) and run a focused similarity search using the STT text. Return top N small excerpts (or a short synthetic summary). • Injection: Add retrieval result as a short assistant/internal message labeled like “[Internal Sales Guide]” or add a system instruction block so the LLM treats it as internal context but does not read it verbatim to the customer unless instructed. • Token hygiene: limit length (e.g., 2–4 short snippets, or one 200–300 token summary). Ask the retriever to summarize long docs before injection. Example injection pattern (pseudocode): 1. transcribed_text = STT(user_audio) 2. hits = vector_search(transcribed_text, playbooks) 3. rag_snippet = summarize(hits) # keep short 4. turn_ctx.add_message(role="assistant", content=f"[Internal Sales Guide] {rag_snippet}") 5. LLM.generate_reply(turn_ctx) 6) Flow for a single turn (practical) 1. Detect end of user turn (VAD or explicit silence). 2. STT → get text. 3. Run intent classifier (quick) to decide if immediate tool call or RAG needed. 4. If RAG needed, retrieve and inject snippets. 5. LLM runs with persona + injected context + tool definitions. 6. If LLM calls tool: execute tool, return tool result into context, allow LLM to craft final response. 7. TTS speak response back to caller. 8. Optionally persist summary to DB. 7) Example of “what to inject” for lead qualifier • Top-level qualification summary: “Use these three quick questions: 1) What is your timeline? 2) Who’s the decision maker? 3) What’s your budget range?” • Objection micro-scripts: brief counters for “too expensive”, “not now”, “we use competitor X”. • Live promotions or demo slot suggestions pulled from CRM/calendar. 8) Handling long playbooks & many prompts • Don’t preload whole playbook. Keep persona + key rules in the system prompt. Use RAG to fetch playbook bits on demand. • Version control playbooks and ensure RAG index is updated whenever playbooks change (CI job or webhook). • Label RAG inputs as internal guidance so the agent uses them for accuracy but doesn’t necessarily quote them directly. 9) SIP-specific and realtime considerations • Minimize latency: keep retrieval and summarization fast (millisecond–second range). Cache frequently used snippets. • Warm agents (paid plans): keep agents warm if you expect immediate answering to calls. • Interruption & overlap: support short TTS responses and quick turn detection so callers can interrupt. Use short prompts and ask one question at a time to reduce overlap. • Transfer/DTMF fallback: provide DTMF options if real-time voice recognition fails or the agent should hand off. 10) Observability & safety • Log RAG retrieval IDs and tool calls so you can audit which playbook snippets influenced responses. • Monitor session quality, call latency, and common fallback reasons to refine playbook retrieval or persona phrasing. • Implement guardrails: block PII leakage, do not disclose restricted info, and set thresholds for automatic human handoff. 11) Quick checklist to implement (step-by-step) 1. Create agent with
instructions
for sales persona and tool descriptions. 2. Implement SIP trunk + dispatch rule to route calls into a LiveKit room. 3. Define tools (book_demo, update_lead, escalate_to_human) with strict schemas. 4. Set up vector DB for playbooks and an efficient retriever (and summarizer). 5. Hook
on_user_turn_completed
to run retrieval and inject snippet into
ChatContext
. 6. Implement STT→LLM→TTS pipeline and ensure tool execution returns are re-injected before final response. 7. Test flows: greeting, qualification Qs, objection handling, demo booking, escalation. 8. Monitor logs and iterate retrieval prompts and tool schemas. If you want a runnable snippet, I can provide: • A compact Python agent showing persona
instructions
, a
book_demo
tool, and an
on_user_turn_completed
that does vector search + injection, oriented to real-time voice. • Or a Node example if you prefer. Which would you like — the architecture checklist covered, or do you want the code example now (Python or Node)? Sources: Gemini Realtime Agent with Live Vision | LiveKit Documentation | Amazon Nova Sonic integration guide | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation | Tool definition and use | LiveKit Documentation | Agents telephony integration | LiveKit Documentation
l
so you say above stt + llm+ tts should sit another LLM as rag which hears STT and instnatly retrieves info? also what is the sota solutions for voice agents realtime? xml? jinja2? waht else to have alot of information for them but reduce ms as much as possible
t
Thanks — great follow-up. I’ll answer both parts: (A) how RAG fits into the STT→LLM→TTS realtime voice pipeline (it’s a retrieval step, not a separate LLM), and (B) state‑of‑the‑art approaches and templating/formatting options for managing lots of information with minimal latency. Short summary up front • RAG is a fast retrieval + optional summarization step that runs after STT and before the LLM generates a reply. It is not a second “LLM that hears STT”; it supplies the LLM with compact, relevant context. • To minimize milliseconds and keep the experience realtime, optimize retrieval (fast vector DB, caching, short summaries), minimize tokens injected, and use low‑latency models and pipelines. • For templating/structure, use compact structured templates (Jinja2-style or lightweight XML/JSON templates), plus labeled internal snippets (e.g., “[Internal Playbook]”) to keep the LLM focused and safe. A. How RAG fits into the realtime voice pipeline (clear flow) 1. End of user turn detected (VAD or silence). 2. STT converts audio → text immediately. 3. Fast intent/route decision (micro-classifier) decides if: ◦ immediate tool call is required, or ◦ RAG retrieval is needed, or ◦ default persona reply is OK. 1. If RAG required: ◦ Run a vector search with the STT text (and optionally session metadata). ◦ Return top K short hits or a single short summary. Optionally run a small LLM summary step, but keep this step minimal and optimized for latency. ◦ Inject the retrieved snippet into the ChatContext as an internal assistant message or a short system block. Label it clearly so it’s used as guidance, not read verbatim. 1. LLM generates a response using: persona instructions + session metadata + injected RAG snippet + tool definitions. 2. If LLM calls a tool, execute the tool synchronously (or asynchronously with immediate confirmation) and feed the result back into the LLM before finalizing text. 3. TTS renders the final reply and audio is sent back to caller. Key point: RAG is a retrieval (and optionally summarization) step, not an always‑on additional LLM conversational agent. It augments the single LLM that composes replies. B. Latency optimization — practical, prioritized techniques 1. Use a very fast vector DB and index tuning ◦ Choose a low‑latency vector store (HNSW indexes like Faiss/Annoy/Milvus or managed equivalents) with optimized search parameters. ◦ Keep vectors and metadata in memory on hot nodes when possible. 1. Limit and precompute embeddings ◦ Precompute embeddings for playbook chunks. Don’t embed at query time. ◦ Use small chunk sizes (200–500 tokens) so results are precise and short. 1. Cache common retrievals and cold/warm strategies ◦ Cache top retrieval results for frequently asked questions or popular playbook sections per campaign or region. ◦ Warm agents (kept alive) avoid cold start latency in compute. 1. Keep injected context minimal ◦ Inject short summaries (50–250 tokens) or 1–3 concise excerpts. Avoid whole documents. ◦ Prefer directive summaries: “Use these 3 bullets for objection handling: …” 1. Optimize the summary step ◦ If you must summarize retrieved docs, use a very small, fast summarizer model or an in‑process heuristic summarizer rather than a full heavyweight LLM. ◦ Alternatively, store precomputed short summaries for each indexed chunk. 1. Fast routing & cheap intent classifier ◦ Run a tiny, local classifier (rule-based or lightweight ML) to avoid RAG when unnecessary. Often you’ll only need RAG for complex queries. 1. Reduce round trips for tools ◦ Make tool calls idempotent and fast (local cache of CRM lookups). ◦ Use optimistic confirmations in dialog (“I’m checking availability — one sec”) while returning brief TTS fillers if needed. 1. Use low-latency LLM/TTS models ◦ Choose real‑time optimized LLM endpoints (lower latency, streaming output) and low-latency neural TTS. Enable streaming so you can play audio progressively. ◦ If available, choose models with realtime inference SLAs or on‑prem/edge inference for lower RTT. 1. Concurrency and parallelism ◦ Parallelize STT → intent classify → vector search (start retrieval as soon as partial STT transcript becomes available). ◦ Use partial transcripts to begin retrieval before turn fully completes (careful with correctness). 1. Monitoring & auto‑tuning ◦ Measure retrieval latency, LLM latency, TTS latency, and end‑to‑end RTT. Use those metrics to tune K (number of RAG hits), cache TTLs, and index parameters. C. SOTA formats and templating strategies for large information sets • Goal: give the LLM structured, relevant guidance without bloat. 1. Structured short templates (recommended) ◦ Jinja2-like templates or small JSON blobs are ideal because they are compact and machine‑friendly for variable replacement (playbook id, customer name, recent answers). ◦ Example: template with placeholders for hook, qualification Qs, objection script, demo slots. Fill at runtime with session metadata. This avoids re‑writing long natural language prompts each time. 1. Labeled internal blocks (best practice) ◦ When injecting retrieved content, wrap as: [InternalPlaybook: <id>] <short bullets> or JSON: {"internal_playbook": [{"title":..., "bullets": [...] }]} ◦ LLM sees it as guidance and won’t necessarily vocalize it unless instructed. 1. XML or custom markup when you need strict parsing ◦ Use lightweight XML or custom tags if you later need to programmatically extract or enforce output elements (like contact, date, intent). But keep it compact—XML adds overhead and verbosity. 1. JSON for structured tool inputs/outputs ◦ When tools are called, use strict JSON schemas for inputs/outputs. This ensures the LLM’s tool calls can be validated and executed quickly. 1. Use short enumerated scripts instead of long paragraphs ◦ Convert playbook sections into bulletized micro-scripts or question/answer pairs. Easier to retrieve, summarize, and inject. 1. Template examples and ordering (what to inject into LLM) ◦ Persona (static, loaded at agent start) ◦ Session metadata (small JSON) ◦ User utterance (STT text) ◦ RAG snippet (labeled, 1–3 bullets or a 100–200 token summary) ◦ Tool availability spec (small JSON describing available tool names + parameter types) D. When you might use a small summarizer LLM versus none • Use a tiny summarizer only if retrieved docs are longer than your desired injected token budget and precomputed summaries are not available. • If possible, precompute multiple summary granularities (short, medium, long) and index those; choose the short summary at runtime. E. Examples of SOTA building blocks (concepts, not prescriptive product picks) • Low-latency vector search: HNSW (Faiss/Milvus), embedded caches. • Streaming STT/TTS: models that support streaming partial transcripts and progressive audio generation. • Realtime LLM with streaming tokens and tool‑call support. • Templating: Jinja2 or compact JSON templates for runtime fill. • Retrieval pattern: precomputed embeddings + top-k HNSW + short summary injection. F. Quick engineering checklist to reduce ms 1. Precompute embeddings and short summaries for playbook chunks. 2. Use HNSW with tuned search params, mem‑cached for top hits. 3. Start RAG search on partial STT where possible. 4. Limit K to smallest useful number (1–3). 5. Keep injected snippets under ~250 tokens. 6. Use streaming LLM + streaming TTS to send audio ASAP. 7. Cache popular retrievals; warm agents; colocate retrieval and agent nodes when possible. If you want, I can: • Provide a small Python pseudocode snippet that shows STT→quick intent → async vector search → inject snippet → LLM generate → TTS stream. • Show a compact Jinja2 prompt template example tailored for a sales lead qualifier. Which would you prefer to see next: the pseudocode pipeline or the Jinja2 template example?
l
which are better jinja2 or compact json temlpates? which are actually better reducing ms and strenghetning prompt? Which are used by big giants? Whicever you answer i want snippet and prompt template for tailored.
t
Thanks — great, specific questions. I can recommend which template format (Jinja2 vs compact JSON) is better for latency and prompt strength, explain how large companies tend to approach this, and give both a runnable snippet and a tailored prompt/template. To make the recommendation and examples precise and immediately useful, I need a few quick details about your environment and goals: Please answer these 6 short questions: 1. Which LLM and runtime will you use? ◦ e.g., OpenAI Realtime/ChatGPT, other hosted LLM, or a local model. ◦ Language for examples: Python or Node? 1. Latency target for a typical turn (approx): ◦ e.g., <500 ms, 500–1000 ms, 1–2s, etc. 1. Do you perform templating server-side before sending to the LLM, or do you want the LLM to fill templates at runtime? ◦ Server-side rendering (preferred for lowest latency) or LLM-side filling (useful for dynamic slots)? 1. Do you need machine-readable outputs for tool calls (strict JSON) or mainly human-style speech? ◦ If machine-readable, do your tools use function-calling (strict JSON schema)? 1. Do you precompute embeddings/summaries for playbook content, or do you expect to summarize on-the-fly? ◦ Precomputed (best for latency) or on-the-fly? 1. Would you like both examples (Jinja2 + compact JSON) or pick one to start? ◦ If both, do you want server-side rendering snippets and one showing how to inject into the RAG+LLM pipeline? Answer those and I’ll: • Recommend the best format for your constraints and explain trade-offs. • Provide a runnable snippet (Python or Node) showing server-side rendering + RAG injection + LLM call + TTS for a Lead Qualifier. • Include both a Jinja2 template and a compact JSON template, plus a sample system/persona prompt tuned for a salesy voice.
l
gemini flash 2.5 realtime native, i dont do anything yet thats why i want the best practices, and solutions before i start impelmented and yes ofcourse need is less than 500ms
t
Thanks — great choices (Gemini Flash 2.5 realtime and <500 ms targets). Before I give the recommendation and runnable examples (Jinja2 + compact JSON, plus a Gemini-tuned prompt and pipeline snippet), I need four quick answers so the examples match your implementation and latency goals: 1. Which language do you want the runnable snippet in — Python or Node.js? 2. Will you do server-side template rendering (preferred for lowest latency) or have Gemini fill placeholders at runtime? (server-side rendering / LLM-side filling) 3. Do your agent tools require strict machine-readable responses (function-calling / JSON schema) or mainly free-text speech responses? 4. Will you precompute embeddings and short summaries for playbooks (recommended for <500 ms), or expect to run summaries on-the-fly? Once you reply, I’ll: • Recommend Jinja2 or compact JSON with clear trade-offs for latency, robustness, and maintainability. • Provide a Gemini Flash 2.5–tuned system/persona prompt for a salesy Lead Qualifier. • Give a runnable server-side example (STT → fast RAG injection → Gemini Flash 2.5 realtime call → TTS) and both a Jinja2 template and a compact JSON template ready to plug into your pipeline.
l
i dont know answers because i do not care i can make everything happen according to get sota solutions
t
Great — thanks for the clear goal (Gemini Flash 2.5 realtime, target <500 ms). Since you’re agnostic about tooling and want the state‑of‑the‑art approach, I’ll give a concrete recommendation, explain tradeoffs between Jinja2 vs compact JSON templates, and provide both: a compact server‑side rendering pattern plus a lightweight JSON approach you can use for function calls. I’ll also include a Gemini‑tuned persona prompt for a salesy Lead Qualifier and a focused pseudocode pipeline showing STT → fast RAG → LLM → TTS with latency best practices. Short recommendation (pick this if you want the lowest latency and robustness) • Use server-side rendering of compact templates (Jinja2 or string templates) for human‑facing speech content and use compact JSON for machine‑readable tool inputs/outputs. This hybrid gives you the best of both worlds: ◦ Jinja2 (server‑rendered) for natural-sounding utterances you control precisely and can precompute/canonicalize. ◦ Compact JSON for tool schemas and function-calls so the LLM and tools exchange strict, validated data quickly. • Precompute embeddings & short summaries for playbook chunks and keep an in‑memory/cached top‑K retriever (HNSW) colocated to your agent node to meet <500ms. Why this hybrid: • Latency: server-side rendering is essentially zero added LLM latency; you avoid the LLM spending tokens filling templates. Compact JSON is short and parseable, avoiding ambiguous natural language parsing and speeding tool invocation/validation. • Robust prompt behavior: system persona + server-rendered utterance controls voice and phrasing. Compact JSON guarantees machine-readable outputs for bookings, CRM updates, etc. • Industry practice: large teams use server-side templates for deterministic outputs (voice prompts, IVR text) and JSON for tool calls and telemetry. They couple that with fast retrieval caches and precomputed embeddings. Tradeoffs summarized • Jinja2 (server-side): ◦ Pros: Deterministic phrasing, minimal latency, easy A/B testing, safe/guarded output. ◦ Cons: Slightly more engineering to manage template variants and localization. • Compact JSON (LLM-facing for tool IO): ◦ Pros: Strong guarantees for tool calls, easy validation, fewer post-processing errors. ◦ Cons: Less natural if used for spoken content; combine with template text for speech. Now — concrete artifacts you requested: 1) Gemini‑tuned system/persona prompt (short, production-ready) Use this as your static system prompt (keeps token size small, loaded at agent creation): "You are Alex, an energetic and confident lead‑qualifier for Acme. Goal: qualify inbound leads quickly and book product demos. Speak concisely, use a friendly and persuasive tone, and follow these rules: • Start with a 3–5 second hook and clearly state purpose. • Ask one qualifying question at a time (budget, timeline, decision maker). • If qualified, propose two demo slots and confirm email/phone. • Use sales phrasing (e.g., 'exclusive', 'limited seats') but never commit to discounts without check. • If asked for policy/technical specifics, consult [InternalPlaybook] snippets (internal guidance only — do not read verbatim unless asked). • For any action (book_demo, update_lead, escalate_to_human), produce a concise JSON object matching the tool schema. Do not output any free-floating JSON besides tool outputs." 2) Template examples A. Jinja2 template (server-side) — for spoken lines (short) • Template (lead_hook.jinja2): "Hi {{first_name or 'there'}}, this is Alex from Acme — quick question: are you currently evaluating solutions for {{product_line}}? I can check availability for a demo in seconds." • Use-case: render with user metadata and inject into TTS directly. Keeps spoken wording deterministic and fast. B. Compact JSON template — for tool calls and structured payloads • Example schema for booking (what the LLM must return when it decides to book): { "action": "book_demo", "date": "2026-03-03", "time": "15:00", "email": "lead@example.com", "notes": "Qualified: budget $X, timeline <3 months" } • Use this JSON as the canonical payload the LLM must produce when calling a tool. The agent validates it and runs the tool synchronously. 3) Pseudocode pipeline (Python-like, server-side rendering + fast RAG + Gemini Flash 2.5 realtime) High-level steps (optimized for <500ms): 1. Detect end-of-turn (VAD / partial transcript heuristics). 2. STT stream → partial transcript; when turn ends, finalize transcript. 3. Quick intent classifier (tiny local model / rules) — determines if RAG is needed. 4. If RAG: ◦ Run vector search (precomputed embeddings, HNSW, K=1–3) against playbook index. Use a memcached hot path for repeated queries. Keep retrieval under ~50–120ms. ◦ Optionally pick precomputed short summary (50–150 tokens) associated with top hit. 1. Server-side: render Jinja2 response templates for outbound speech fragments where appropriate (greetings, confirmations). For dynamic content the LLM should generate (qualifying questions, responses), pass persona + small injected RAG snippet. 2. Call Gemini Flash 2.5 Realtime with: ◦ system prompt (persona) ◦ short user message (STT text) ◦ injected internal context: a labeled short snippet (e.g., "[InternalPlaybook] bullets...") — no more than 200 tokens. ◦ function schemas for any tools you expose ◦ streaming enabled for partial token output 1. If LLM outputs a tool call (JSON), validate and execute tool synchronously (CRM booking), then immediately inject tool result back to LLM for confirmation message (or use server-side Jinja2 to render a confirmation message). 2. TTS stream response back (use streaming TTS). If Gemini can stream tokens to TTS, start TTS as soon as partial tokens available. Example pseudocode (annotated, simplified): 1. finalize STT transcript = finalize_stt_audio(call_audio) 2. quick intent if quick_intent_is_simple(transcript): use_rag = False else: use_rag = True 3. RAG retrieval (fast) rag_snippet = None if use_rag: hits = vector_search(transcript, k=2) # memcached hot hits rag_snippet = pick_short_summary(hits) # <=200 tokens 4. Prepare LLM input system_prompt = load_persona_prompt() messages = [ {"role":"system","content":system_prompt}, {"role":"user","content":transcript}, ] if rag_snippet: messages.append({"role":"assistant","content":"[InternalPlaybook] " + rag_snippet}) 5. Call Gemini Flash 2.5 realtime (streaming) llm_resp = call_gemini_realtime(messages, function_schemas=[book_demo_schema,...], stream=True) 6. If tool call returned as JSON, validate and execute if llm_resp.contains_function_call(): payload = parse_and_validate_json(llm_resp.function_call.payload) tool_result = execute_tool(payload) # e.g., book_demo - should be fast inject tool result and ask LLM to confirm messages.append({"role":"tool","content":json.dumps(tool_result)}) final_resp = call_gemini_realtime(messages, stream=True) 7. TTS streaming stream_tts_to_caller(final_resp.text) 4) Concrete examples you can copy A. Short Jinja2 server-side rendering (Python pseudo) from jinja2 import Template hook_tpl = Template("Hi {{name_or_guest}}, this is Alex from Acme — quick question: are you evaluating solutions for {{product}}?") text = hook_tpl.render(name_or_guest="Sam", product="Acme CRM") send
text
to TTS immediately (no LLM latency)
B. Compact JSON function schema (example) book_demo_schema = { "name": "book_demo", "description": "Schedule a demo", "parameters": { "type": "object", "properties": { "date": {"type":"string", "format":"date"}, "time": {"type":"string"}, "email": {"type":"string", "format":"email"}, "notes": {"type":"string"} }, "required": ["date","time","email"] } } Gemini is asked to return JSON that matches this schema when it wants to book. 5) Additional latency tips specific to Gemini Flash 2.5 realtime • Use streaming token output from Gemini and feed tokens to TTS progressively. This reduces perceived latency significantly. • Keep injected RAG snippets very short (one 2–3 bullet summary). Precompute summaries for hot playbook sections. • Co-locate retrieval and agent runtime to reduce network RTT. Put vector DB nodes in the same region/availability zone. • Warm LLM connections (keep sessions alive) to avoid handshake/cold overhead. 6) Which to choose finally? • If your priority is the absolute lowest latency and deterministic spoken text: primarily use server-side rendered Jinja2 for speech + compact JSON for tool IO. Use RAG as a short internal snippet injected before LLM generation. • If you want maximum flexibility and fewer engineering steps: you can
l
i can what
t
Thanks — got it. You’re open to any implementation that gives the best results, so I’ll give a clear recommendation, compare Jinja2 vs compact JSON, and include ready-to-use artifacts: a Gemini Flash 2.5–tuned persona prompt, both a Jinja2 speech template and a compact JSON tool schema, and a concise Python pseudocode pipeline optimized for <500 ms (STT → fast RAG → Gemini → TTS). I’ll keep it actionable so you can start implementing. Quick recommendation (summary) • Use a hybrid approach: ◦ Server-side rendered Jinja2 (or lightweight string templates) for spoken utterances you want deterministic and low-latency. ◦ Compact JSON for machine-readable tool inputs/outputs (function calls), validation, and telemetry. ◦ Precompute embeddings and short summaries for playbook chunks and colocate a fast vector index (HNSW/Faiss/Milvus) with caching. • Reason: server-side rendering adds virtually zero LLM latency; compact JSON ensures robust tool calls and avoids parsing ambiguity. Giants use hybrid patterns—deterministic templates for IVR/voice prompts + structured payloads for back-end actions. Tradeoffs: Jinja2 vs compact JSON • Jinja2 (server-side): ◦ Pros: Deterministic phrasing, minimal latency, easy localization/A-B testing, good for TTS text. ◦ Cons: More template management, less flexible for open-ended LLM generation. • Compact JSON (LLM-facing for tool I/O): ◦ Pros: Strong guarantees for tool calls, easy validation, reduces parsing errors and retries. ◦ Cons: Not suitable alone for natural-sounding speech; combine with templates for spoken lines. • Best practice: Jinja2 for speech; compact JSON for tool schema. Use RAG to fetch short internal snippets that the LLM can consume alongside persona + lightweight templates. Deliverables below: 1) Gemini Flash 2.5–tuned system/persona prompt Use this as your system prompt (loaded once per session): You are Alex, an energetic, confident lead‑qualifier agent for Acme. Goal: qualify inbound leads quickly and book demos. Speak concisely and persuasively. Rules: • Start with a 3–5 second hook and state purpose. • Ask one qualifying question at a time: budget, timeline, decision maker. • If qualified, propose two demo slots and confirm email/phone. • Use persuasive phrasing (e.g., “exclusive”, “limited seats”) but never commit to discounts without a check. • Use [InternalPlaybook] snippets for policy/objection handling (internal guidance only — do not read verbatim unless asked). • When performing actions, output a concise JSON object matching the tool schema. Do not produce other free-floating JSON. 2) Jinja2 speech template (server-side) — example Template name: lead_hook.jinja2 "Hi {{first_name or 'there'}}, this is Alex from Acme — quick question: are you currently evaluating solutions for {{product}}? I can check demo availability in seconds." Usage: Render server-side with the session metadata and send directly to TTS (no LLM token cost). 3) Compact JSON tool schema — example (book_demo) Use this schema for function-calling or to validate LLM tool output. { "action": "book_demo", "date": "YYYY-MM-DD", "time": "HH:MM", "email": "lead@example.com", "phone": "+123456789", "notes": "Qualified: budget $X, timeline <3 months" } Require fields: date, time, email (phone optional). Validate server-side before executing. 4) RAG snippet format (what to inject) When you retrieve playbook results, inject a short labeled snippet into the LLM input: [InternalPlaybook] • Bullet 1: Quick objection handling for "too expensive" (1-2 sentences). • Bullet 2: Promo availability note for region X (one line). • Bullet 3: Recommended closing phrasing. Keep this under ~150–200 tokens. 5) Python pipeline snippet (concise, practical) This is a minimal, realistic flow optimized for latency. It assumes precomputed embeddings and a colocated vector index. Replace the placeholder calls with your provider/client code.
Copy code
# PSEUDOCODE / TEMPLATE - adapt to your infra
def on_user_turn_end(call_audio, session_meta):
    # 1) STT finalization (streaming STT recommended)
    transcript = finalize_stt(call_audio)  # fast streaming STT finalize

    # 2) Quick intent check (cheap rules or tiny classifier)
    if cheap_intent_is_simple(transcript):
        need_rag = False
    else:
        need_rag = True

    # 3) Fast RAG (precomputed embeddings, HNSW; memcached hot cache)
    rag_snippet = None
    if need_rag:
        hits = vector_search(transcript, k=2)         # ~10-80ms if colocated
        rag_snippet = get_precomputed_summary(hits[0]) # <=150 tokens

    # 4) Prepare messages for Gemini Flash 2.5 (streaming)
    messages = [
        {"role":"system", "content": SYSTEM_PROMPT},  # persona prompt above
        {"role":"user",   "content": transcript},
    ]
    if rag_snippet:
        messages.append({"role":"assistant", "content":"[InternalPlaybook] " + rag_snippet})

    # 5) Call Gemini Flash 2.5 realtime (streaming). Provide function JSON schemas for tools.
    llm_resp = call_gemini_realtime(messages, function_schemas=[BOOK_DEMO_SCHEMA, ...], stream=True)

    # 6) If LLM issues a function call (book_demo), parse & validate JSON
    if llm_resp.contains_function_call():
        payload = parse_json(llm_resp.function_call.payload)
        validate_against_schema(payload, BOOK_DEMO_SCHEMA)
        tool_result = execute_book_demo(payload)   # keep fast; local cache if possible

        # 7) Inject tool result and ask LLM to confirm (quick)
        messages.append({"role":"tool", "name":"book_demo", "content": json.dumps(tool_result)})
        final_resp = call_gemini_realtime(messages, stream=True)
    else:
        final_resp = llm_resp

    # 8) TTS streaming: start TTS as tokens stream from Gemini (progressive)
    stream_tts_to_caller(final_resp.text)
Latency notes in code: • Use streaming at every stage (STT partials, Gemini streaming tokens, TTS progressive audio). • Precompute embeddings and summaries; keep vector DB colocated. • Keep RAG snippet small and use quick intent classifier to avoid unnecessary retrievals. 6) Example persona + injection usage (end-to-end snippet) • Start call: server-side render the Jinja2 hook and play via TTS immediately. • After user replies: run STT → quick intent → RAG (if needed) → call Gemini (persona + injected RAG) → tool calls validated via JSON schema → confirm via TTS. 7) Practical tuning tips for <500ms • Precompute embeddings and short summaries, store hot items in an in-memory cache. • Co-locate vector DB and Gemini proxy to reduce RTT. • Use K=1 or 2 for RAG and keep snippet under 150 tokens. • Warm LLM connections and reuse sessions. • Stream tokens to TTS to start playback before full LLM completion. • Use optimistic server-side rendered fillers if a small extra fetch would exceed latency. 8) What I can deliver next (pick one) • A runnable Python example wired to a specific STT/Vector/LLM SDK (I can produce concrete code for your chosen libs). • A Node.js equivalent pipeline. • Expanded Jinja2 template set (greeting, qualification questions, confirm booking) plus JSON schemas for other tools (check_discount, update_lead). • A short checklist for production hardening (logging, observability, safety guardrails). Which one would you like next? I can start with a runnable Python snippet wired to Gemini Flash 2.5 and a minimal in-memory vector mock (so you can test the whole flow end-to-end), or provide the Node.js version.
l
node or py i dont care which ar ebetter give me for gemini flash 2.5 realtime native im speaking here also not llm seperate but the 'Gemini LIVE API' where it has stt llm tts in one
t
Great — thanks for confirming Gemini Flash 2.5 Realtime (Gemini LIVE API) and the <500 ms target. Below I’ll give a clear recommendation on Jinja2 vs compact JSON, explain why the hybrid approach is best for realtime voice, and provide both ready-to-use artifacts you asked for: • a Gemini‑tuned persona/system prompt for a salesy Lead Qualifier, • a Jinja2 speech template (server-side), • a compact JSON tool schema for bookings (machine‑readable), • a concise Node.js and Python runnable pattern (focused, minimal) showing STT → fast RAG → Gemini LIVE → TTS with best practices to meet <500 ms. Short recommendation (one line) • Use a hybrid: server‑side Jinja2 (deterministic, near‑zero latency) for spoken utterances + compact JSON for tool I/O and function calls. Precompute embeddings & summaries and colocate the vector index for RAG. Why hybrid is state‑of‑the‑art • Determinism & latency: server‑rendered templates add essentially no LLM cost and give predictable TTS phrasing (used widely at scale for IVR/voice flows). • Robust tool integration: compact JSON makes tool calls machine‑readable and easy to validate (reduces retries and parsing errors). • RAG remains a small, fast internal augmentation step (retrieve short snippets, inject as labeled internal context) — do not preload whole playbooks. 1) Gemini‑tuned persona/system prompt (use as system prompt) You can load this once per session. Keep it short and prescriptive. You are Alex, an energetic and confident lead‑qualifier for Acme. Goal: qualify inbound leads quickly and book demos. Speak concisely and persuasively. Rules: • Start with a 3–5 second hook and state purpose. • Ask one qualifying question at a time: budget, timeline, decision maker. • If qualified, propose two demo slots and confirm email/phone. • Use persuasive phrasing (e.g., “exclusive”, “limited seats”) but do NOT promise discounts without validation. • Use [InternalPlaybook] snippets for policy/objection handling (internal guidance only — don’t read verbatim unless asked). • For actions (book_demo, update_lead, escalate_to_human) output exactly one JSON object matching the tool schema. Do not output other free-floating JSON. 2) Jinja2 speech template (server‑side) — for greeting / deterministic phrases File: lead_hook.jinja2 Hi {{ first_name or "there" }}, this is Alex from Acme — quick question: are you currently evaluating solutions for {{ product }}? I can check demo availability in seconds. Usage: • Render server-side with session metadata and send to TTS immediately for the initial greeting. No LLM latency. 3) Compact JSON schema for tool calls (book_demo) Use this for function-call style validation. LLM must return this JSON when it elects to book. { "action": "book_demo", "date": "YYYY-MM-DD", "time": "HH:MM", "email": "lead@example.com", "phone": "+1234567890", "notes": "Qualified: budget $X, timeline <3 months" } Validation: require date, time, email. Reject or request clarification if invalid. 4) RAG snippet format (what to inject into Gemini) When you retrieve, inject one labeled assistant message: [InternalPlaybook] • Objection: "price" → 1-sentence micro script • Promo: region X: "no current discount" • Close: "Offer two demo times: tomorrow 10am or Thu 3pm" Keep under ~150 tokens. 5) Minimal pipeline patterns (key ideas: streaming where possible, precompute embeddings, colocate vector DB) A) Node.js (concise pseudocode / structure) • Use server-side template rendering for deterministic lines (e.g., Nunjucks/Jinja-like). • Use a fast in-memory HNSW index or managed vector DB colocated with your agent runtime. • Use Gemini LIVE realtime API for STT+LLM+TTS in one session (streaming tokens to TTS). Pseudocode flow (Node style, simplified): 1. Detect end of user turn (VAD). 2. Finalize STT transcript (Gemini LIVE provides streaming STT). 3. Quick intent check (rules or tiny classifier). If simple, skip RAG. 4. If RAG needed, do vector_search(transcript, k=1-2) against precomputed embeddings (colocated) and get short summary. 5. Build messages: ◦ system: persona prompt ◦ user: transcript ◦ assistant (optional): "[InternalPlaybook] " + rag_summary 1. Call Gemini LIVE (streaming). Provide function/schema for
book_demo
. 2. If function call returned, validate JSON, execute booking, inject tool result and ask Gemini to confirm. 3. Stream TTS back to caller as Gemini emits tokens (start playing partial audio ASAP). B) Python (concise pseudocode) Same flow as Node but showing Jinja2 render for greeting. Pseudo: • render = jinja2.Template(template).render(first_name="Alex", product="Acme CRM") • tts.play(render) # immediate • on_turn_end: ◦ transcript = gemini_final_stt() ◦ if need_rag: hits = vector_search(transcript) ◦ messages = [system_prompt, {"role":"user","content":transcript}] ◦ if hits: messages.append({"role":"assistant","content":"[InternalPlaybook] "+summary}) ◦ resp = call_gemini_live(messages, functions=[book_demo_schema], stream=True) ◦ if resp.function_call: payload = validate(resp.payload); tool_result = execute(payload); inject tool result and confirm ◦ stream final text → TTS 6) Latency optimizations to meet <500 ms • Precompute embeddings and short summaries; do not embed at query time. • Use a colocated vector index (HNSW/Faiss) in memory for <50–120 ms retrieval. • Cache hot playbook snippets per campaign/region. • Use K=1 or 2 and limit injected tokens to ~100–200 tokens. • Use Gemini LIVE streaming tokens → TTS pipeline; start TTS on partial tokens. • Warm sessions and keep LLM connections alive. • Run cheap intent classifier to avoid unnecessary RAG calls. • Make tool calls synchronous but extremely fast (local cache/DB) or return optimistic confirmation then finalize. 7) Examples you can copy now • Jinja2 snippet (Python) from jinja2 import Template tpl = Template("Hi {{first_name or 'there'}}, this is Alex from Acme — quick question: are you evaluating solutions for {{product}}?") text = tpl.render(first_name="Sam", product="Acme CRM") send
text
to TTS immediately
• Compact JSON (book_demo) — validate server-side before executing. 8) Next steps I can deliver immediately • A runnable Node.js example using a mock in-memory vector index and pseudo Gemini LIVE client (so you can run locally end-to-end). • A runnable Python example that wires Jinja2 greeting, mocked RAG, and a simulated Gemini LIVE session flow (useful if you prefer Python). • Expanded template set (greeting + 3 qualification Qs + booking confirm + escalation) and JSON schemas for additional tools (check_discount, update_lead). Which do you want next — a runnable Node.js sample or Python sample? I’ll provide the code and explain where to plug in Gemini LIVE SDK calls and your real vector DB. Sources: Gemini Live API plugin | LiveKit Documentation | Gemini Realtime Agent with Live Vision | LiveKit Documentation | Prompting guide | LiveKit Documentation | Context Variables | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation
l
give me python samples only
t
Thanks — I’ll give you a focused, practical Python package you can use as a reference implementation for a Gemini Flash 2.5 Realtime Native voice Lead‑Qualifier agent. It includes: • Recommendation recap (Jinja2 + compact JSON hybrid) • Gemini‑tuned persona/system prompt • Jinja2 speech templates (greeting + confirm) • Compact JSON tool schema (book_demo) • Minimal runnable Python samples: ◦ Mock fast RAG (precomputed summaries) ◦ Pipeline: STT finalization → quick intent → RAG retrieval → Gemini LIVE call (simulated client points) → function-call handling → TTS streaming • Tips on where to plug real Gemini LIVE SDK calls, vector DB, and production hardening for <500 ms I’ve kept code minimal and annotated so you can replace mocks with your real SDKs. 1) Short recommendation recap • Hybrid approach: server-side Jinja2 for deterministic spoken prompts (greeting, confirmations), compact JSON for tool I/O (bookings, CRM updates). • Precompute embeddings and short summaries for playbooks. Keep a colocated, in‑memory fast retriever (HNSW/Faiss/Milvus) and a small cache for hot hits. • Use cheap intent checks to avoid unnecessary RAG. Limit RAG injections to ~100–200 tokens, K=1–2. • Use Gemini LIVE streaming (STT+LLM+TTS combined) and stream tokens to TTS progressively. Warm connections. 2) Persona / system prompt (Gemini‑tuned) Load this once per session as your system prompt: You are Alex, an energetic, confident lead‑qualifier for Acme. Goal: qualify inbound leads quickly and book demos. Speak concisely and persuasively. Rules: • Start with a 3–5 second hook and state purpose. • Ask one qualifying question at a time: budget, timeline, decision maker. • If qualified, propose two demo slots and confirm email/phone. • Use persuasive phrasing (e.g., “exclusive”, “limited seats”) but do NOT promise discounts without validation. • Use [InternalPlaybook] snippets for policy/objection handling (internal guidance only — do not read verbatim unless asked). • For actions (book_demo, update_lead, escalate_to_human) output exactly one JSON object matching the tool schema. Do not output other free-floating JSON. 3) Templates & schemas A. Jinja2 greeting template (lead_hook.jinja2) Hi {{ first_name or "there" }}, this is Alex from Acme — quick question: are you currently evaluating solutions for {{ product }}? I can check demo availability in seconds. B. Jinja2 confirmation template (confirm_booking.jinja2) Thanks {{ first_name or "there" }} — your demo is booked for {{ date }} at {{ time }}. You’ll receive a confirmation at {{ email }}. See you then! C. Compact JSON tool schema (book_demo) — validate server-side { "action": "book_demo", "date": "YYYY-MM-DD", "time": "HH:MM", "email": "lead@example.com", "phone": "+1234567890", "notes": "Qualified: budget $X, timeline <3 months" } Required: date, time, email. Phone optional. 4) Minimal runnable Python example (mocked pieces) • What this does: Simulates end-to-end flow. Replace stub functions with your Gemini LIVE SDK calls, real STT/TTS, and a real vector DB. • Save files as: agent.py, templates/lead_hook.jinja2, templates/confirm_booking.jinja2 File: templates/lead_hook.jinja2 Hi {{ first_name or "there" }}, this is Alex from Acme — quick question: are you currently evaluating solutions for {{ product }}? I can check demo availability in seconds. File: templates/confirm_booking.jinja2 Thanks {{ first_name or "there" }} — your demo is booked for {{ date }} at {{ time }}. You’ll receive a confirmation at {{ email }}. See you then! File: agent.py
Copy code
import time
import json
from jinja2 import Environment, FileSystemLoader, select_autoescape
from typing import Optional, Dict, Any

# ===== Mock / Replace these with real implementations =====
def finalize_stt(call_audio: bytes) -> str:
    """
    Replace with Gemini LIVE final STT (or LiveKit STT).
    Here we simulate a transcript returned quickly.
    """
    # Simulated transcript from caller
    return "Yes, we are evaluating CRM solutions. Our timeline is three months and budget around 50k."

def cheap_intent_is_simple(transcript: str) -> bool:
    """
    Fast rule-based decision to skip RAG when unnecessary.
    """
    # If transcript contains clear booking intent or simple reply, skip RAG
    tokens = transcript.lower()
    if "book" in tokens or "demo" in tokens or "yes" in tokens and "demo" in tokens:
        return False  # still run RAG for details/confirmation in lead-qualifier
    # default: allow RAG
    return False

# Mock fast RAG: precomputed summaries for playbook chunks
PRECOMPUTED_RAG = {
    "pricing": "No active discounts for region X. Standard starter package is $49/month per seat. Use close: 'limited seats available'.",
    "objections_price": "If 'too expensive', ask about business impact and suggest ROI-focused demo. Offer two demo slots."
}

def fast_vector_search(query: str, k: int = 1) -> str:
    """
    Replace with real HNSW vector search against precomputed embeddings.
    Return a short (<=150 token) summary snippet.
    """
    q = query.lower()
    if "price" in q or "expensive" in q or "budget" in q:
        return PRECOMPUTED_RAG["objections_price"]
    return PRECOMPUTED_RAG["pricing"]

def call_gemini_live(messages: list, function_schemas: Optional[list] = None) -> Dict[str, Any]:
    """
    Replace with real Gemini LIVE API call that handles STT->LLM->TTS streaming.
    Here we simulate an LLM response that decides to book a demo by returning a function call JSON.
    """
    # For demo we assume LLM decides to book a demo
    function_call = {
        "name": "book_demo",
        "arguments": {
            "date": "2026-03-03",
            "time": "15:00",
            "email": "<mailto:lead@example.com|lead@example.com>",
            "phone": "+1234567890",
            "notes": "Qualified: budget ~$50k, timeline 3 months"
        }
    }
    # Simulate small processing latency
    time.sleep(0.08)
    return {"type": "function_call", "function_call": function_call, "text": None}

def execute_book_demo(payload: dict) -> dict:
    """
    Replace with real CRM/calendar integration. Keep this fast (cache where possible).
    """
    # Simulate booking latency
    time.sleep(0.05)
    return {"status": "ok", "confirmation_id": "CONF12345", "date": payload["date"], "time": payload["time"], "email": payload["email"]}

def stream_tts_to_caller(text: str):
    """
    Replace with real TTS streaming (Gemini LIVE TTS or other) that starts as soon as tokens stream in.
    """
    print("[TTS START] ", text)
# ===== End mock stubs =====

# Setup Jinja2 env
env = Environment(
    loader=FileSystemLoader("templates"),
    autoescape=select_autoescape(["jinja2", "html", "xml"])
)

SYSTEM_PROMPT = """You are Alex, an energetic, confident lead-qualifier for Acme. Goal: qualify inbound leads quickly and book demos. Speak concisely and persuasively.
Rules:
- Start with a 3–5 second hook and state purpose.
- Ask one qualifying question at a time: budget, timeline, decision maker.
- If qualified, propose two demo slots and confirm email/phone.
- Use [InternalPlaybook] snippets for policy/objection handling (internal guidance only - do not read verbatim unless asked).
- For actions (book_demo, update_lead, escalate_to_human) output exactly one JSON object matching the tool schema.
"""

BOOK_DEMO_SCHEMA = {
    "name": "book_demo",
    "description": "Schedule a demo",
    "parameters": {
        "type": "object",
        "properties": {
            "date": {"type": "string", "format": "date"},
            "time": {"type": "string"},
            "email": {"type": "string", "format": "email"},
            "phone": {"type": "string"},
            "notes": {"type": "string"}
        },
        "required": ["date", "time", "email"]
    }
}

def validate_against_schema(payload: dict, schema: dict) -> bool:
    """
    Replace with jsonschema.validate in production. Here we do minimal validation.
    """
    for r in schema["parameters"].get("required", []):
        if r not in payload:
            return False
    return True

# Main turn handler
def on_user_turn_end(call_audio: bytes, session_meta: dict):
    start = time.time()

    # 1) STT finalize
    transcript = finalize_stt(call_audio)
    stt_time = time.time()

    # 2
import time import json from jinja2 import Environment, FileSystemLoader, select_autoescape from typing import Optional, Dict, Any # ===== Mock / Replace these with real implementations ===== def finalize_stt(call_audio: bytes) -> str: """ Replace with Gemini LIVE final STT (or LiveKit STT). Here we simulate a transcript returned quickly. """ # Simulated transcript from caller return "Yes, we are evaluating CRM solutions. Our timeline is three months and budget around 50k." def cheap_intent_is_simple(transcript: str) -> bool: """ Fast rule-based decision to skip RAG when unnecessary. """ # If transcript contains clear booking intent or simple reply, skip RAG tokens = transcript.lower() if "book" in tokens or "demo" in tokens or "yes" in tokens and "demo" in tokens: return False # still run RAG for details/confirmation in lead-qualifier # default: allow RAG return False # Mock fast RAG: precomputed summaries for playbook chunks PRECOMPUTED_RAG = { "pricing": "No active discounts for region X. Standard starter package is $49/month per seat. Use close: 'limited seats available'.", "objections_price": "If 'too expensive', ask about business impact and suggest ROI-focused demo. Offer two demo slots." } def fast_vector_search(query: str, k: int = 1) -> str: """ Replace with real HNSW vector search against precomputed embeddings. Return a short (<=150 token) summary snippet. """ q = query.lower() if "price" in q or "expensive" in q or "budget" in q: return PRECOMPUTED_RAG["objections_price"] return PRECOMPUTED_RAG["pricing"] def call_gemini_live(messages: list, function_schemas: Optional[list] = None) -> Dict[str, Any]: """ Replace with real Gemini LIVE API call that handles STT->LLM->TTS streaming. Here we simulate an LLM response that decides to book a demo by returning a function call JSON. """ # For demo we assume LLM decides to book a demo function_call = { "name": "book_demo", "arguments": { "date": "2026-03-03", "time": "15:00", "email": "lead@example.com", "phone": "+1234567890", "notes": "Qualified: budget ~$50k, timeline 3 months" } } # Simulate small processing latency time.sleep(0.08) return {"type": "function_call", "function_call": function_call, "text": None} def execute_book_demo(payload: dict) -> dict: """ Replace with real CRM/calendar integration. Keep this fast (cache where possible). """ # Simulate booking latency time.sleep(0.05) return {"status": "ok", "confirmation_id": "CONF12345", "date": payload["date"], "time": payload["time"], "email": payload["email"]} def stream_tts_to_caller(text: str): """ Replace with real TTS streaming (Gemini LIVE TTS or other) that starts as soon as tokens stream in. """ print("[TTS START] ", text) # ===== End mock stubs ===== # Setup Jinja2 env env = Environment( loader=FileSystemLoader("templates"), autoescape=select_autoescape(["jinja2", "html", "xml"]) ) SYSTEM_PROMPT = """You are Alex, an energetic, confident lead-qualifier for Acme. Goal: qualify inbound leads quickly and book demos. Speak concisely and persuasively. Rules: - Start with a 3–5 second hook and state purpose. - Ask one qualifying question at a time: budget, timeline, decision maker. - If qualified, propose two demo slots and confirm email/phone. - Use [InternalPlaybook] snippets for policy/objection handling (internal guidance only - do not read verbatim unless asked). - For actions (book_demo, update_lead, escalate_to_human) output exactly one JSON object matching the tool schema. """ BOOK_DEMO_SCHEMA = { "name": "book_demo", "description": "Schedule a demo", "parameters": { "type": "object", "properties": { "date": {"type": "string", "format": "date"}, "time": {"type": "string"}, "email": {"type": "string", "format": "email"}, "phone": {"type": "string"}, "notes": {"type": "string"} }, "required": ["date", "time", "email"] } } def validate_against_schema(payload: dict, schema: dict) -> bool: """ Replace with jsonschema.validate in production. Here we do minimal validation. """ for r in schema["parameters"].get("required", []): if r not in payload: return False return True # Main turn handler def on_user_turn_end(call_audio: bytes, session_meta: dict): start = time.time() # 1) STT finalize transcript = finalize_stt(call_audio) stt_time = time.time() # 2