Skip to main content
This cookbook builds a customer support agent for a dental clinic on top of Mercury 2, Inception’s diffusion large language model. The agent understands natural-language requests, calls tools to read and write a live appointment database, and holds a real-time voice conversation using ElevenLabs for speech-to-text and text-to-speech. By the end you will have a working agent that can:
  • 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
Because Mercury 2 generates tokens with a diffusion process rather than autoregressively, it returns tool calls and final answers faster than comparable models. For a voice customer support agent, where every extra 100 ms is audible, this difference is critical.

Run the Customer Support Agent

Directly via Google Colab or download the .ipynb to run it locally in Jupyter

Architecture

Voice customer support architecture: caller audio to ElevenLabs STT to Mercury 2, which loops with the clinic SQLite database via tool calls and results, then ElevenLabs TTS to spoken reply.
The clinic database is an in-memory SQLite instance seeded with providers, patients, and an availability grid. Two API keys (INCEPTION_API_KEY, ELEVENLABS_API_KEY) are required to run this demo.

1. Setup

Install the Inception SDK, the ElevenLabs SDK, and a few audio helpers.
You need two API keys:The next cell reads them from the environment, and prompts you for any that aren’t already set. Nothing is written to disk.

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 hygienists
  • patients — existing patient records
  • appointments — a grid of 30-minute slots, each available, booked, or cancelled
The availability grid is generated relative to the moment you run the cell. It starts with whatever is left of today and rolls forward over the next five business days, skipping weekends and any hour that has already passed. A couple of slots start out pre-booked so availability looks realistic. This is a placeholder for demo purposes; you can plug in your own custom db here.

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:
  1. Send the conversation plus the tool schemas to Mercury 2.
  2. If the reply contains tool_calls, run each one locally and append the result as a role: "tool" message.
  3. Repeat until Mercury returns a normal text answer.
Using this workflow, the loop naturally handles multi-step requests like “reschedule my cleaning to Thursday” (look up the booking → find a Thursday slot → move it). We query mercury-2 on low reasoning effort: real time customer service is a very latency-sensitive use-case where Mercury’s ability to complete complicated tool-calling tasks at ultra-low latencies shines.

5. Text Customer Service Agent

Let’s walk a caller through a full support flow. We keep history between calls so the agent remembers the conversation.

5a. Check availability

5b. Book a slot (a real database write)

Let’s confirm the write actually landed in the database. The slot Taylor took should now be booked, and Taylor should exist as a patient.

5c. Reschedule

5d. Cancel

Every one of those turns was a real read or write against the SQLite database. The agent discovered slot ids and appointment ids purely through tool calls, exactly as it would against a production scheduling system behind an API.

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 ElevenLabs scribe_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

Because voice_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 (not Dr. Pearl Flossmore) when a caller asks for “Doctor Flossmore.” The same goes for id formats, enum values, and date formats: if a tool wants 2026-08-19, say so in the tool’s description, not just as {"type": "string"} in the schema. The description fields 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_name strips 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.

Resources