- Look up a patient and their upcoming appointments
- Show open appointment slots for a given day or provider
- Book, reschedule, and cancel appointments via real mutations against a database
- Do all of the above out loud, over streamed audio
Run the Customer Support Agent
Directly via Google Colab or download the
.ipynb to run it locally in JupyterArchitecture
1. Setup
Install the Inception SDK, the ElevenLabs SDK, and a few audio helpers.You need two API keys:
INCEPTION_API_KEY— from the Inception dashboard (new accounts get 100M free tokens)ELEVENLABS_API_KEY— from the ElevenLabs dashboard
2. A mock clinic database
We spin up an in-memory SQLite database for a fabricated dental clinic: Diffusion Dental. It contains three tables:providers— the dentists and hygienistspatients— existing patient recordsappointments— a grid of 30-minute slots, eachavailable,booked, orcancelled
3. Define the support tools
Each tool is a plain Python function that reads or writes the database. We keep the functions small and return dictionaries, which then get handed back to Mercury 2 as the tool result. We include a mix of read tools (find_patient, list_available_slots,
get_appointments_for_patient) and write tools (book_appointment,
reschedule_appointment, cancel_appointment). The write tools validate state
first: you can’t book a slot that’s already taken, or cancel an appointment
that doesn’t exist.
If you have a different database setup, you can customize these functions to match your
database-specific query logic. If you do, make sure you update your tool schemas (next section)
accordingly.
Tool schemas
Mercury 2 uses the OpenAI-compatible function-calling format. Each schema tells the model the tool’s name, what it does, and its parameters. This is how the model decides which tool to reach for and how to fill in the arguments. You can also plug in custom tool schemas; just make sure they are compatible with the support tools you’ve defined above.4. The agent loop
The loop is the heart of a tool-calling agent:- Send the conversation plus the tool schemas to Mercury 2.
- If the reply contains
tool_calls, run each one locally and append the result as arole: "tool"message. - Repeat until Mercury returns a normal text answer.
5. Text Customer Service Agent
Let’s walk a caller through a full support flow. We keephistory between calls
so the agent remembers the conversation.
5a. Check availability
5b. Book a slot (a real database write)
booked, and Taylor should exist as a patient.
5c. Reschedule
5d. Cancel
6. Real-time voice with ElevenLabs
Now we give the agent a voice. The pipeline is: caller audio → ElevenLabs STT → Mercury 2 (tool loop) → ElevenLabs TTS → spoken reply We use ElevenLabsscribe_v1 for transcription and eleven_turbo_v2_5 for
low-latency speech. To keep the notebook self-contained (no microphone needed),
we simulate the caller by synthesizing their line with a different voice,
then feed that audio through the real STT → agent → TTS path.
6a. Simulate the caller
We synthesize the caller’s spoken request. In a real deployment this audio would come from a phone line or the browser’s microphone.6b. Transcribe with scribe_v1
Speech-to-text, timed. This is the first hop of the latency budget.
6c. Stream the spoken reply, sentence by sentence
The trick for low perceived latency (borrowed from the voice assistant cookbook): don’t wait for the whole answer. Buffer Mercury’s streamed text until a sentence boundary, then synthesize that sentence while the next one is still generating. Here we run the tool loop for the caller’s request, then stream the final answer into ElevenLabs TTS one sentence at a time.6d. Put it together: one full voice turn
voice_turn takes caller audio and returns the agent’s spoken reply, printing a
latency breakdown for each stage.
6e. Keep the conversation going
Becausevoice_turn threads history back through, the agent remembers context
across spoken turns. Let’s confirm the booking out
loud.
Tips
Getting tool calls right
-
Put your data’s conventions where Mercury can read it. The model can only match your schema if you tell it what your schema looks like, either in tool descriptions or in the system prompt. For example, the line “The clinic’s providers go by Molar (general dentistry), Wyre (orthodontics), and Flossmore (hygiene and cleaning)” is what makes Mercury pass
Flossmore(notDr. Pearl Flossmore) when a caller asks for “Doctor Flossmore.” The same goes for id formats, enum values, and date formats: if a tool wants2026-08-19, say so in the tool’sdescription, not just as{"type": "string"}in the schema. Thedescriptionfields are prompt text, and they are where the model looks when filling arguments. -
Then make the tool tolerant anyway. Prompt guidance reduces malformed arguments; it never eliminates them, and speech-to-text will always run the risk of transcribing speech incorrectly, especially names. That is why
_provider_id_from_namestrips titles and matches on distinctive words. State the contract in the prompt, enforce it in the tool, and return a structured{"error": ...}the model can read and recover from.
Model sampling
- Adjust reasoning effort for your use case. For real-time voice use cases, we recommend reasoning effort low, which runs at ultra-low latencies while still delivering a pleasant conversational experience and the complex tool calls needed for a streamlined customer service agent.
Conversation state
- Manage context length as history grows. Every turn appends the full text response and tool call JSON, increasing prompt size and driving up latency for very long-running conversations. However, most of that history goes stale fast. In order to improve latency over very long conversations, consider incorporating a compaction step in your voice loop, using Mercury-2 to summarize your conversation history, maintaining a smooth conversation experience while saving on tokens and latency.
- Validate every model-supplied id against the database. The write tools here check state before mutating: you cannot book a taken slot or cancel an appointment that does not exist. Keep that discipline in production: treat slot ids and appointment ids from the model as untrusted input, because a plausible-looking hallucinated id could silently corrupt the loop.