---
title: "Python SDK for bounded agent work with typed results"
description: "The Duale AI Python SDK submits bounded agent work from Python and returns a validated typed result."
lang: en
status: public-preview
lastUpdated: 2026-09-04
url: https://duale.ai/en/docs/sdk
---

## AI-generated summary

Submit bounded agent work from Python using the dualeai package, authenticate with DUALE_TOKEN, and receive typed results validated against a Pydantic model.

- Requires Python 3.10 or newer and a DUALE_TOKEN bound to a single agent.
- ask() submits one task; await response.model() validates the result against a Pydantic schema.
- The SDK reads configuration from DUALE_ environment variables; DUALE_ENDPOINT targets another environment.
- DUALE_AGENT_ID is required for hosted tools or task-scoped attachments, not for a first ask() call.
- model() raises ValidationError if the result fails schema validation or DualeError on platform failure.

Summaries were generated by AI. Generative AI is experimental.

---

Submit bounded agent work from Python and get a typed result back. Install the `dualeai` package, set one environment variable, and run a first task in a few lines.

## Before you install the SDK

Meet these requirements before installation:

- Python 3.10 or newer.
- A `DUALE_TOKEN` for one agent in your tenant. The platform binds each token to a single agent, shows it once, at creation, and starts it with `duale_`.
- A centrally provisioned `DUALE_AGENT_ID` for hosted tools or task-scoped attachments. A first task that only calls `ask()` does not need one. [Agents and access](https://duale.ai/en/docs/agents-and-access.md) defines the agent and tenant boundaries.

Installation adds the Python package. It does not create or grant any platform resource.

## Provision access

Every resource below is provisioned before your first task. The Python SDK creates none of them.

| Resource                                              | Who provides it                                                    |
| ----------------------------------------------------- | ------------------------------------------------------------------ |
| Tenant and API token                                  | Duale AI, during the workspace access handoff                      |
| Model providers and their credentials                 | Your organization. The handoff configures the resulting model pool |
| Agent ID, for hosted tools or task-scoped attachments | The same handoff                                                   |
| Library grants                                        | The same handoff                                                   |

If one is missing, ask the workspace administrator or use
[pricing and access](https://duale.ai/en/product/pricing.md). A Library you create later is granted to your agent
automatically; [Library actions](https://duale.ai/en/docs/sdk/reference.md#library-actions) states who grants access to
one you did not create. [Libraries](https://duale.ai/en/docs/libraries.md) explains what an agent can read after it has
that grant. For reusable documents, start with [Manage a Library](https://duale.ai/en/docs/sdk/manage-libraries.md). Use
[task attachments](https://duale.ai/en/docs/sdk/attachments.md) when only one task needs the file.

## Install

The package is published as `dualeai`; the import name is `duale`.

```bash
pip install dualeai
```

## Authenticate

The SDK reads configuration from `DUALE_` environment variables. Set your token:

```bash
export DUALE_TOKEN=duale_your_token_here
```

Requests go to `https://api.duale.ai` by default. Set `DUALE_ENDPOINT` to target another environment.

## Submit your first task

Ask for work, request a typed result with a Pydantic model, and await the validated response:

```python runnable
import asyncio

from pydantic import BaseModel

from duale import ask, create_sdk

class SupportDecision(BaseModel):
    next_action: str
    reason: str

async def main() -> None:
    async with create_sdk() as sdk:
        response = await ask(
            action="Review this support case and return the next safe action.",
            res=SupportDecision,
            sdk=sdk,
        )
        decision = await response.model()
        print(response.task_id, decision.next_action)

asyncio.run(main())
```

`ask()` submits one task and returns an `AgentResponse`. `await response.model()` blocks until the task reaches a terminal state, then validates the result against `SupportDecision`. `response.task_id` is the durable identifier—store it to correlate everything your application later logs about this task.

## Verify the typed result

A successful run prints the task identifier and the model's chosen action:

```text
0192f7c1-6e2a-7c31-9a4d-2b6f8c1d4e90 hold_for_review
```

The result matches `SupportDecision` because the runtime validated it against the schema you passed. If it cannot, `model()` raises `ValidationError`. If the platform ends the task with a failure, `model()` raises `DualeError` carrying the failure detail—see [Errors and reliability](https://duale.ai/en/docs/sdk/errors.md).

You did not name a model. [How the SDK works](https://duale.ai/en/docs/sdk/concepts.md) explains how the platform chose one, and how a routing policy changes that choice.

## Related content

- [Declare and serve Python tools with the SDK](https://duale.ai/en/docs/sdk/tools.md)
- [Application-facing SDK API reference](https://duale.ai/en/docs/sdk/reference.md)
- [SDK task lifecycle, routing, and streaming](https://duale.ai/en/docs/sdk/concepts.md)
- [Integrate model routing in an application](https://duale.ai/en/docs/model-routing/application-integration.md)
- [Production runtime for durable AI agents](https://duale.ai/index.md)
- [Protect data across every boundary](https://duale.ai/en/docs/security/data-protection.md)

---

## Sitemap

See the full [Markdown sitemap](https://duale.ai/sitemap.md) for all pages.
