> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroruntime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build Your First Voice AI Agent

> Ship a voice agent in minutes using a realtime speech-to-speech model or a cascading STT-LLM-TTS pipeline.

## Prerequisites

Before you begin, ensure you have:

* A ZRT Auth token **or** API key + secret from [app.zeroruntime.ai](https://app.zeroruntime.ai/api-keys) (see [Authentication](/authentication)).
* Python 3.11 or higher.

## Understanding the Architecture

Before diving into implementation, choose the pipeline architecture that fits your use case.

<Tabs>
  <Tab title="Cascade">
    **Cascade** processes audio through distinct stages for maximum control:

    <Frame>
      <img src="https://mintcdn.com/zeroruntime/dSfKUNLGJQu7XhPO/images/cascade.svg?fit=max&auto=format&n=dSfKUNLGJQu7XhPO&q=85&s=8876d95a726fb1deab5c13c325e98965" className="block w-full my-4" width="1160" height="200" data-path="images/cascade.svg" />
    </Frame>

    The cascade processes audio through sequential stages:

    **User Voice Input** → **STT (Speech-to-Text)** → **LLM (Large Language Model)** → **TTS (Text-to-Speech)** → **Agent Voice Output**

    This approach provides better control over each processing stage and supports more complex AI reasoning. Power each stage through the **Zero Runtime Inference gateway** (one token, no per-provider keys) or bring your own provider keys.
  </Tab>

  <Tab title="Realtime">
    **Realtime** provides direct speech-to-speech processing with minimal latency:

    <Frame>
      <img src="https://mintcdn.com/zeroruntime/dSfKUNLGJQu7XhPO/images/realtime.svg?fit=max&auto=format&n=dSfKUNLGJQu7XhPO&q=85&s=545348ab856240015f71226b8ceea2bf" className="block w-full my-4" width="880" height="280" data-path="images/realtime.svg" />
    </Frame>

    The realtime pipeline processes audio directly through a unified model:

    **User Voice Input** → **Speech-to-Speech model** → **Agent Voice Output**

    This approach offers the fastest response times and is ideal for real-time conversations.
  </Tab>
</Tabs>

## Build the Agent

Pick your language. Each track builds the same agent on the **Cascade** pipeline (Deepgram STT → Google Gemini LLM → Cartesia TTS).

<Tabs>
  <Tab title="Python">
    <Steps>
      <Step title="Setup the project" icon="download">
        Use Python 3.11 or higher, then install the Zero Runtime Agents SDK. Create and activate a virtual environment, then install `zrt`. Every provider plugin ships with it.

        <CodeGroup>
          ```bash uv theme={null}
          uv venv --python 3.11
          source .venv/bin/activate     # macOS/Linux
          .\.venv\Scripts\activate      # Windows
          ```

          ```bash pip theme={null}
          python3.11 -m venv venv
          source venv/bin/activate      # macOS/Linux
          .\venv\Scripts\activate       # Windows
          ```
        </CodeGroup>

        <Tip>
          New to uv? See the [uv install guide](https://docs.astral.sh/uv/getting-started/installation/).
        </Tip>

        <Tabs>
          <Tab title="Cascade">
            <CodeGroup>
              ```bash uv theme={null}
              uv pip install zrt
              ```

              ```bash pip theme={null}
              pip install zrt
              ```
            </CodeGroup>

            <Tip>
              Prefer to bring your own provider keys? Every provider ships with `zrt`. Just import from `zrt.plugins.*`. See the plugin pages for [STT](/plugins/stt/openai), [LLM](/plugins/llm/openai), and [TTS](/plugins/tts/elevenlabs).
            </Tip>
          </Tab>

          <Tab title="Realtime">
            <CodeGroup>
              ```bash uv theme={null}
              uv pip install zrt
              ```

              ```bash pip theme={null}
              pip install zrt
              ```
            </CodeGroup>
          </Tab>
        </Tabs>
      </Step>

      <Step title="Configure environment variables" icon="key">
        Store API keys and tokens in a `.env` file in your project root.

        <Tabs>
          <Tab title="Cascade">
            ```shell title=".env" theme={null}
            # The Inference gateway authenticates with your Zero Runtime token -
            # no separate STT, LLM, or TTS provider keys needed.

            # Option A: pre-generated token
            ZRT_AUTH_TOKEN = "ZRT Auth token"

            # Option B: SDK auto-generates token (omit ZRT_AUTH_TOKEN)
            # ZRT_API_KEY = "Your Zero Runtime API Key"
            # ZRT_SECRET_KEY = "Your Zero Runtime Secret Key"
            ```
          </Tab>

          <Tab title="Realtime">
            ```shell title=".env" theme={null}
            # Option A: pre-generated token
            ZRT_AUTH_TOKEN="ZRT Auth token"

            # Option B: SDK auto-generates token (omit ZRT_AUTH_TOKEN)
            # ZRT_API_KEY="Your Zero Runtime API Key"
            # ZRT_SECRET_KEY="Your Zero Runtime Secret Key"

            OPENAI_API_KEY="Your OpenAI API Key"

            # For Google Live API
            # GOOGLE_API_KEY="Google Live API Key"

            # For AWS Nova API
            # AWS_ACCESS_KEY_ID="AWS Key Id"
            # AWS_SECRET_ACCESS_KEY="AWS Secret Key"
            # AWS_DEFAULT_REGION="AWS Region"
            ```

            <Note>
              Get keys from [OpenAI](https://platform.openai.com/api-keys), [Gemini](https://aistudio.google.com/app/apikey), or [AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/security-creds.html). For Zero Runtime, use an auth token from the [Zero Runtime Dashboard](https://app.zeroruntime.ai/api-keys) or an API key + secret. Follow the [guide](/authentication).
            </Note>
          </Tab>
        </Tabs>
      </Step>

      <Step title="Create the pipeline and agent" icon="robot">
        Build the pipeline first, then create the agent that uses it. The `Pipeline` auto-detects its mode from the components you pass it: provide STT, LLM, and TTS for Cascade, or a single realtime model for Realtime. Define it at module scope so the agent class can reference it directly. The agent inherits from the base `Agent` class, the same for both pipelines. Only the pipeline setup and imports differ. `instructions` defines the agent's personality, `on_enter()` runs when it joins the room, and `on_exit()` runs when it leaves.

        <Tabs>
          <Tab title="Cascade">
            ```python title="main.py" theme={null}
            import zrt
            from zrt import Agent, Pipeline, Room
            from zrt.plugins import SileroVAD, TurnDetector, CartesiaSTT, GoogleLLM, CartesiaTTS
            from zrt.inference import AICousticsDenoise

            AGENT_ID = "assistant"

            # Create the pipeline first - STT, LLM, and TTS resolve through the Zero Runtime
            # Inference gateway. Build it at module scope so the agent class can reference it.
            pipeline = Pipeline(
                stt=CartesiaSTT(model="ink-2"),
                llm=GoogleLLM(model="gemini-2.5-flash"),
                tts=CartesiaTTS(model="sonic-2"),
                vad=SileroVAD(threshold=0.4),
                turn_detector=TurnDetector(model="echo-large"),
                denoise=AICousticsDenoise(model_id="quail-vf-2.2-l-16khz"),
            )


            class MyVoiceAgent(Agent):
                def __init__(self):
                    super().__init__(
                        agent_id=AGENT_ID,
                        instructions="You are a helpful voice assistant that can answer questions and help with tasks.",
                        pipeline=pipeline,
                    )

                async def on_enter(self):
                    await self.session.say("Hello! How can I help?")

                async def on_exit(self):
                    await self.session.say("Goodbye!")
            ```
          </Tab>

          <Tab title="Realtime">
            ```python title="main.py" theme={null}
            import zrt
            from zrt import Agent, Pipeline, Room
            from zrt.plugins import OpenAIRealtime, OpenAIRealtimeConfig

            AGENT_ID = "assistant"

            # Create the pipeline first - a single realtime model, built at module scope
            # so the agent class can reference it.
            model = OpenAIRealtime(
                config=OpenAIRealtimeConfig(
                    model="gpt-4o-realtime-preview",
                    voice="alloy",  # alloy, ash, ballad, breeze, cedar, cinnamon, coral, echo, ember, juniper, marin, sage, shimmer, verse
                    modalities=["text", "audio"],
                    # turn_detection defaults to server VAD (threshold 0.5); pass an
                    # OpenAIRealtimeConfig turn_detection to customize it.
                ),
            )
            pipeline = Pipeline(llm=model)


            class MyVoiceAgent(Agent):
                def __init__(self):
                    super().__init__(
                        agent_id=AGENT_ID,
                        instructions="You are a helpful voice assistant that can answer questions and help with tasks.",
                        pipeline=pipeline,
                    )

                async def on_enter(self):
                    await self.session.say("Hello! How can I help?")

                async def on_exit(self):
                    await self.session.say("Goodbye!")
            ```
          </Tab>
        </Tabs>
      </Step>

      <Step title="Start the session" icon="play">
        With the pipeline and agent defined, hand the agent *class* to `zrt.serve(...)`. This wiring is the same for both pipelines.

        ```python title="main.py" theme={null}
        def invoke_agent():
            # room_id="YOUR_MEETING_ID"  # Set to join a pre-created room; omit to auto-create
            zrt.invoke(AGENT_ID, room=Room(playground=True))


        if __name__ == "__main__":
            # Pass the class itself (not an instance): serve() builds a fresh MyVoiceAgent +
            # pipeline per call, which is required for correct per-call state under concurrent calls.
            zrt.serve(MyVoiceAgent, on_ready=invoke_agent)
        ```

        `serve()` registers the agent under its `agent_id` and blocks, starting a fresh session for each caller. Passing the `MyVoiceAgent` class itself (not a pre-built instance) lets `serve()` construct a fresh agent + pipeline per call, which is required for correct per-call state under concurrent calls. `on_ready=invoke_agent` fires `zrt.invoke(...)` once the agent is registered, which resolves a room and returns a playground URL to open the session.
      </Step>

      <Step title="Run the project" icon="terminal">
        With your `.env` configured and dependencies installed, run the project with Python.

        <Tabs>
          <Tab title="Playground">
            Once you run this command, a playground URL appears in your terminal. Use this URL to interact with your AI agent.

            <CodeGroup>
              ```bash uv theme={null}
              uv run python main.py
              ```

              ```bash pip theme={null}
              python main.py
              ```
            </CodeGroup>
          </Tab>

          <Tab title="Console mode">
            <CodeGroup>
              ```bash uv theme={null}
              uv run python main.py console
              ```

              ```bash pip theme={null}
              python main.py console
              ```
            </CodeGroup>

            Want to see the magic instantly? Console mode lets you interact with your agent directly through the terminal, no meeting room needed, just speak and listen through your local system. Perfect for quick testing and development.

            <Frame>
              <img src="https://cdn.videosdk.live/website-resources/docs-resources/ai_agents_console_mode_image.png" alt="Console Mode" />
            </Frame>

            Learn more about [Console Mode](/build/configure-a-pipeline/console-mode).
          </Tab>
        </Tabs>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Verify

* The agent connects to the room and greets you.
* Audio flows both ways: the agent hears you and you hear it.
* In Cascade mode, the LLM's responses are spoken back through TTS; in Realtime mode, the model streams speech directly.

<Warning>
  **Common errors:**

  * **Missing auth token:** set `ZRT_AUTH_TOKEN` in your `.env`.
  * **Plugin import error:** make sure `zrt` is installed in the active virtual environment (every provider ships with it).
  * **No audio:** check microphone permissions, or confirm the client joined the same room.
</Warning>

<Tip>
  Get started quickly with the [Quick Start Example](https://github.com/ZeroRuntimeAI/zrt-python-sdk-examples) for the Zero Runtime AI Agent SDK: everything you need to build your first AI agent fast.
</Tip>

## Troubleshooting

Common issues when setting up the project:

<AccordionGroup>
  <Accordion title="No interpreter found for Python 3.11" icon="code">
    Make sure Python 3.11 is installed, then create the virtual environment with it.

    ```bash theme={null}
    python3.11 -m venv venv
    source venv/bin/activate      # macOS/Linux
    ```
  </Accordion>

  <Accordion title="RuntimeError: There is no current event loop" icon="triangle-exclamation">
    Newer Python versions (3.13+) removed the implicit event loop. Create your virtual environment with Python 3.11.

    ```bash theme={null}
    python3.11 -m venv venv
    source venv/bin/activate      # macOS/Linux
    ```
  </Accordion>

  <Accordion title="ModuleNotFoundError when running main.py" icon="box">
    You're running a Python outside the project's virtual environment. Activate it first, then install the dependencies.

    ```bash theme={null}
    source venv/bin/activate      # macOS/Linux
    pip install zrt
    python main.py
    ```
  </Accordion>
</AccordionGroup>

## What's Next

<CardGroup cols={2}>
  <Card title="Add tools to an agent" icon="wrench" href="/quickstarts/add-tools-to-an-agent">
    Let your agent call functions and act.
  </Card>

  <Card title="Pipelines" icon="diagram-project" href="/concepts/pipeline">
    Understand the STT, LLM, and TTS flow.
  </Card>
</CardGroup>

## References

* [AI Agent (Complete Example)](https://github.com/ZeroRuntimeAI/zrt-python-sdk-examples/blob/main/features/basic_cascade.py)
* [Zero Runtime Inference Agent (Example)](https://github.com/ZeroRuntimeAI/zrt-python-sdk-examples/blob/main/features/inference_gateway.py)
