> ## 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.

# Create an Agent

> Subclass Agent, set its instructions, and wire it into a runnable session.

The `Agent` class is the foundation for every voice agent you build. You define a custom agent by subclassing `Agent`, give it instructions, and override its lifecycle hooks to act when it joins or leaves a session.

## Architecture

The `Agent` defines behavior and carries its `Pipeline` (the STT, LLM, and TTS components). You register the agent with `zrt.serve()`, and `zrt.invoke()` starts a session for it; the runtime orchestrates both into a running workflow inside a session.

<img src="https://mintcdn.com/zeroruntime/q3ZgSvvBl7VLzLk2/images/agent.svg?fit=max&auto=format&n=q3ZgSvvBl7VLzLk2&q=85&s=12179a307eae0a9121765c7dceb540ed" alt="Agent orchestrator wired to instructions, function tools, MCP, and its pipeline" className="block w-full my-4" width="900" height="620" data-path="images/agent.svg" />

## Initialization

Every custom agent calls `super().__init__()` inside its own `__init__`, passing its `instructions`, a unique `agent_id` (required: it's the handle callers reach the agent by), and the `pipeline` it runs on. This runs the base `Agent` so it can register lifecycle hooks and tools, prepare internal state, and store the system prompt.

### Instructions

Provide a clear system prompt via the `instructions` argument. Describe the agent's role, tone, and constraints; it is sent with every conversation turn.

<CodeGroup>
  ```python Python theme={null}
  from zrt import Agent

  class MyAgent(Agent):
      def __init__(self, pipeline):
          super().__init__(
              agent_id="assistant",
              instructions="You are a helpful assistant. Keep replies short and friendly.",
              pipeline=pipeline,
          )
  ```
</CodeGroup>

## Lifecycle

Agents provide two async lifecycle hooks you override to act on join and leave: `on_enter` (runs after the agent joins) and `on_exit` (runs before it leaves). Together with `instructions`, both hooks are expected on every agent you define.

### on\_enter()

`on_enter` is called once when the agent successfully joins the session. A common use case is greeting participants with `session.say()`.

<Note>
  Limitation: it runs only after the room connection succeeds, so it cannot be used for pre-connection setup.
</Note>

<CodeGroup>
  ```python Python theme={null}
  async def on_enter(self):
      print("Agent has entered the session.")
      await self.session.say("Hello everyone! I'm here to help.")
  ```
</CodeGroup>

### on\_exit()

`on_exit` is called when the agent is about to leave the session. A common use case is saying goodbye or running cleanup tasks.

<Note>
  Limitation: the session is shutting down, so long-running work may not complete before resources are released.
</Note>

<CodeGroup>
  ```python Python theme={null}
  async def on_exit(self):
      print("Agent is exiting the session.")
      await self.session.say("It was a pleasure assisting you. Goodbye!")
  ```
</CodeGroup>

## Example

A full agent you can run directly. It builds a cascade pipeline, registers the agent with `zrt.serve()`, and starts a playground session with `zrt.invoke()`.

<CodeGroup>
  ```python title="main.py" Python theme={null}
  import zrt
  from zrt import Agent, Pipeline
  from zrt.plugins import DeepgramSTT, GoogleLLM, CartesiaTTS, TurnDetector
  from zrt.inference import AICousticsDenoise

  AGENT_ID = "assistant"

  pipeline = Pipeline(
      stt=DeepgramSTT(),
      llm=GoogleLLM(),
      tts=CartesiaTTS(),
      turn_detector=TurnDetector(model="echo-large"),
      denoise=AICousticsDenoise(model_id="quail-vf-2.2-l-16khz"),
  )

  class MyAgent(Agent):
      def __init__(self):
          super().__init__(
              agent_id=AGENT_ID,
              instructions="You are a helpful voice assistant.",
              pipeline=pipeline,
          )

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

      async def on_exit(self):
          await self.session.say("Goodbye!")

  if __name__ == "__main__":
      # Pass the class itself (not an instance): serve() builds a fresh MyAgent +
      # pipeline per call, which is required for correct per-call state under concurrent calls.
      zrt.serve(MyAgent, on_ready=lambda: zrt.invoke(AGENT_ID, room=zrt.Room(playground=True)))
  ```
</CodeGroup>

## What's Next

<CardGroup cols={2}>
  <Card title="Configure a Pipeline" icon="diagram-project" href="/build/configure-a-pipeline">
    Wire up STT, LLM, and TTS.
  </Card>

  <Card title="Run Your Agent" icon="play" href="/build/run-the-runtime">
    Register and serve the agent.
  </Card>
</CardGroup>

## References

<Tabs>
  <Tab title="Python">
    #### Examples

    <CardGroup cols={2}>
      <Card title="Cascade Basic" icon="github" href="https://github.com/ZeroRuntimeAI/zrt-python-sdk-examples/blob/main/features/basic_cascade.py">
        Minimal STT-LLM-TTS cascade agent you can run.
      </Card>

      <Card title="Realtime Basic" icon="github" href="https://github.com/ZeroRuntimeAI/zrt-python-sdk-examples/blob/main/features/realtime.py">
        Minimal speech-to-speech realtime agent.
      </Card>
    </CardGroup>

    #### SDK Reference

    <CardGroup cols={2}>
      <Card title="Agent" icon="book" href="/api-reference/python/core/agent-and-session">
        `Agent` in the Python API reference.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
