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

# defineAgent

> Configure the LLM brain that drives your conversation.

## `defineAgent`

`defineAgent` declares the AI agent that will run on a specific WhatsApp / SMS
sender. Calling it inside your function source binds the agent to that sender
on every `zavu deploy`.

```ts theme={null}
import { defineAgent } from "@zavu/functions"

defineAgent({
  senderId: process.env.SENDER_ID!,
  name: "Bella",
  provider: "zavu",
  model: "openai/gpt-4o-mini",
  prompt: "You are Bella, host of Bella Pizzeria…",
  channels: ["whatsapp"],
})
```

After `zavu deploy`:

* The agent is created (or updated) under that sender.
* Tagged as **managed by this function** — the dashboard disables manual
  edits to prevent drift.
* Marked `enabled: true` automatically.

## Required fields

| Field      | Type                                               | Notes                                                                             |
| ---------- | -------------------------------------------------- | --------------------------------------------------------------------------------- |
| `senderId` | string                                             | The `_id` of an existing sender in your project. Usually `process.env.SENDER_ID`. |
| `name`     | string                                             | Display name. Shown in `zavu agents executions` and the dashboard.                |
| `provider` | `zavu \| openai \| anthropic \| google \| mistral` | LLM provider.                                                                     |
| `model`    | string                                             | Model id (depends on provider — see below).                                       |
| `prompt`   | string                                             | System prompt. Multi-line OK (use backticks).                                     |

## Optional fields

| Field                    | Default          | Description                                                                    |
| ------------------------ | ---------------- | ------------------------------------------------------------------------------ |
| `channels`               | `["*"]`          | Which channels trigger the agent: `whatsapp`, `sms`, `telegram`, `email`, `*`. |
| `messageTypes`           | `["text"]`       | Message types: `text`, `image`, `audio`, …                                     |
| `apiKey`                 | —                | Required when `provider !== "zavu"` (or pre-create a secret in the dashboard). |
| `contextWindowMessages`  | `10`             | How many previous messages to include in each LLM call.                        |
| `temperature`            | provider default | 0–2.                                                                           |
| `maxTokens`              | unbounded        | Cap on response length.                                                        |
| `sessionTimeoutMinutes`  | `60`             | After this idle gap, the agent starts a new conversation.                      |
| `includeContactMetadata` | `true`           | If true, contact name / metadata is included as context.                       |
| `enabled`                | `true`           | Set to `false` to deploy the agent but keep it inactive.                       |

## Providers

### `zavu` — managed gateway (recommended for getting started)

No API key needed. LLM costs are billed directly from your Zavu balance at
pass-through rates.

```ts theme={null}
defineAgent({
  ...,
  provider: "zavu",
  model: "openai/gpt-4o-mini",   // provider/model id, the gateway picks the right backend
})
```

Available models on the gateway:

| `model`                                | Backend            | Best for                  |
| -------------------------------------- | ------------------ | ------------------------- |
| `openai/gpt-4o-mini`                   | OpenAI GPT-4o mini | Cheapest, fastest         |
| `openai/gpt-4o`                        | OpenAI GPT-4o      | High-quality reasoning    |
| `anthropic/claude-3-5-haiku-20241022`  | Anthropic Haiku    | Fast, good tool-use       |
| `anthropic/claude-3-5-sonnet-20241022` | Anthropic Sonnet   | Best multi-step reasoning |
| `google/gemini-1.5-flash`              | Google Flash       | Cheap multilingual        |
| `google/gemini-1.5-pro`                | Google Pro         | Long context windows      |

### Bring your own key (BYOK)

Pass `provider` matching the vendor and `apiKey` (or pre-create a secret in
the dashboard and rely on it being already stored).

```ts theme={null}
defineAgent({
  ...,
  provider: "openai",
  model: "gpt-4o-mini",
  apiKey: process.env.OPENAI_API_KEY!,   // set via `zavu fn secrets set`
})
```

Models follow each vendor's own naming (`gpt-4o-mini`, `claude-3-5-sonnet-20241022`,
`gemini-1.5-flash`, etc) — no `provider/` prefix when using BYOK.

<Tip>
  On first deploy, if you pass `apiKey`, we create a row in `apiSecrets`
  encrypted with AES-256-GCM and reference it from the agent. Subsequent
  deploys without `apiKey` reuse the same stored secret. To rotate, pass a
  new `apiKey` and redeploy.
</Tip>

## Prompts

The `prompt` field is the system message every conversation starts with. It's
where you set the persona, rules, and guardrails.

### Patterns that work

```ts theme={null}
prompt: `You are Bella, host of Bella Pizzeria.

Your job:
- Help guests view the menu and book reservations.
- ONLY use the tools provided — never invent prices or availability.
- Confirm every reservation by reading back the time and code before closing.

Tone:
- Friendly, brief (WhatsApp messages should fit one screen).
- Match the language the customer writes in.

Edge cases:
- If asked something outside your scope (delivery, jobs, complaints), say:
  "Para eso te paso con un humano." and stop responding.
`
```

### Anti-patterns

<Warning>
  **Don't** put prices, menu items, or any data that changes in the prompt. Put
  them in tool handlers so they stay current without redeploys.

  **Don't** make the prompt longer than \~2,000 characters. LLMs lose focus on
  the rules with verbose prompts; tools are how you scope behavior.
</Warning>

## Triggers (which messages reach the agent)

By default the agent fires on every inbound message to its sender. Restrict
with `channels` and `messageTypes`:

```ts theme={null}
defineAgent({
  ...,
  channels: ["whatsapp"],         // only WhatsApp, ignore SMS to the same sender
  messageTypes: ["text"],         // ignore images, audio, etc
})
```

If you need fine-grained event triggers (e.g. only when a specific sender
fires `message.inbound`), use [explicit triggers](/guides/functions/triggers)
in addition.

## One agent per file

`defineAgent` is allowed multiple times only when each call has a different
`(senderId, name)` pair, in which case each tool must explicitly bind to one:

```ts theme={null}
defineAgent({ senderId: process.env.SENDER_A!, name: "Sales",   ...  })
defineAgent({ senderId: process.env.SENDER_B!, name: "Support", ...  })

defineTool({
  name: "lookup_order",
  agent: "Support",                // explicit binding
  description: "...",
  parameters: { ... },
  handler: async (args) => { ... },
})
```

For most use cases you want one agent per function file. Multiple senders
sharing identical logic? Use the same code in multiple functions, each with
its own `SENDER_ID` secret.

## Updates and ownership

Every `zavu deploy` reconciles the live agent to match what's in the code.

| State                                               | Reconcile behavior                                                         |
| --------------------------------------------------- | -------------------------------------------------------------------------- |
| No agent exists yet                                 | Create it, mark as managed by this function.                               |
| Manually-created agent with same `(senderId, name)` | **Takes ownership**. Future edits are blocked in the dashboard.            |
| Agent managed by THIS function                      | Update fields to match the manifest.                                       |
| Agent managed by a DIFFERENT function               | Skipped with a warning. Rename in code or delete the other function first. |

When you remove `defineAgent` from your code and redeploy, the managed agent
is **deleted** (along with its managed tools). Manual agents are never
touched.

## Disabling without deleting

To pause an agent without removing the code:

```ts theme={null}
defineAgent({
  ...,
  enabled: false,
})
```

Redeploy. The agent stays in the database; messages to its sender will be
processed by webhooks instead.

## Common patterns

<AccordionGroup>
  <Accordion title="Multi-language agent">
    Don't hard-code a language. Tell the prompt to match the user:

    ```ts theme={null}
    prompt: `…
    Always reply in the language the customer wrote in.
    If unclear, default to Spanish.`
    ```

    The LLM handles language detection per turn — no need for routing logic.
  </Accordion>

  <Accordion title="Escalation to human">
    Add a tool that signals escalation (e.g., creates a ticket in your CRM
    or notifies an operator on Slack). Tell the prompt when to use it:

    ```ts theme={null}
    prompt: `…
    If the customer asks for a refund, asks to "speak to a human", or
    expresses strong frustration, call escalate_to_agent and tell them
    someone will reach out within 15 minutes.`
    ```
  </Accordion>

  <Accordion title="Different agent per sender (multi-tenant)">
    Same code, different `SENDER_ID` secret in each function. The prompt can
    even include `process.env.BRAND_NAME` to customize per tenant:

    ```ts theme={null}
    defineAgent({
      ...,
      name: process.env.BRAND_NAME!,
      prompt: `You are the assistant for ${process.env.BRAND_NAME}…`,
    })
    ```
  </Accordion>

  <Accordion title="Reading contact metadata">
    With `includeContactMetadata: true` (default), the LLM sees:

    * Contact's display name (if known).
    * Custom metadata fields on the contact.
    * Channel they wrote from.

    Set metadata via `client.contacts.update(contactId, { metadata: {...} })`.
    Useful for "Hello \$name" style personalization without a tool call.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={2}>
  <Card title="defineTool" icon="wrench" href="/guides/functions/defining-tools">
    Give the agent actions to execute.
  </Card>

  <Card title="Secrets" icon="key" href="/guides/functions/secrets">
    Store SENDER\_ID, API keys, and other env vars.
  </Card>
</CardGroup>
