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

# Synthetic Worlds

> Iterate on agent behavior with simulated tool responses

## Overview

Synthetic Worlds let you develop and test agents that use tools — without calling the real tools. Instead of hitting live APIs, databases, or services, your tool calls get realistic responses generated on the fly.

This is useful when:

* **Real tools are slow or expensive** — external APIs, database writes, third-party services
* **You need deterministic behavior** — reproducible runs for debugging and CI
* **The real service doesn't exist yet** — build your agent against the contract, not the implementation
* **You want to test failure handling** — simulate timeouts, errors, and rate limits

## How it works

You create a **world** — an isolated session that intercepts your tool calls and returns generated responses. Define your tools as normal Python functions with type hints. The SDK extracts the schema automatically and sends it to the backend, which generates a realistic response that matches the return type.

```python theme={null}
from rdk.synthetic import create_synthetic_world

with create_synthetic_world(project_id="my-project") as world:
    @world.tool
    def get_user(user_id: str) -> dict:
        """Fetch a user by ID."""
        ...

    @world.tool
    def create_order(product_id: str, quantity: int) -> dict:
        """Create a new order."""
        ...

    user = get_user("u1")
    print(user)  # {"id": "u1", "name": "Alice", "email": "alice@acme.com"}

    order = create_order("prod_42", 3)
    print(order)  # {"id": "ord_1", "product_id": "prod_42", "quantity": 3, "status": "created"}
```

When the `with` block exits, the world is destroyed and resources are freed.

## Setup

### 1. Set environment variables

```bash theme={null}
export RDK_API_KEY="your-api-key"
export RDK_PLATFORM_URL="https://your-platform.example.com"  # defaults to http://localhost:7878
```

### 2. Create a world and register tools

```python theme={null}
from rdk.synthetic import create_synthetic_world

world = create_synthetic_world(project_id="my-project")
```

Or use the context manager (recommended):

```python theme={null}
with create_synthetic_world(project_id="my-project") as world:
    # register tools and make calls here
    ...
```

## Registering tools

### Using the `@world.tool` decorator

The simplest way. Your function's type hints and docstring become the tool schema automatically:

```python theme={null}
@world.tool
def search_products(query: str, limit: int = 10) -> dict:
    """Search the product catalog."""
    ...
```

The SDK extracts:

* **name** from the function name (`search_products`)
* **description** from the docstring (`Search the product catalog.`)
* **parameters** from the type hints (`query: str` required, `limit: int` optional with default)
* **return type** from the return annotation (`dict`)

Calling `search_products("shoes", limit=5)` routes through the synthetic backend and returns a generated response.

### Using `register_tool`

For cases where you need manual control over the schema:

```python theme={null}
from rdk.synthetic import ToolSpec

spec = ToolSpec(
    name="get_weather",
    description="Get current weather for a city",
    parameters={
        "type": "object",
        "properties": {
            "city": {"type": "string"},
            "units": {"type": "string"},
        },
        "required": ["city"],
    },
    returns={
        "type": "object",
        "properties": {
            "temperature": {"type": "number"},
            "condition": {"type": "string"},
        },
    },
)

world.register_tool(spec)
result = world.call("get_weather", {"city": "Tokyo"})
```

## Generation modes

Control how responses are generated by setting the `mode` parameter:

| Mode          | Description                                                    | Use case                                                 |
| ------------- | -------------------------------------------------------------- | -------------------------------------------------------- |
| `schema_only` | Generates responses from the tool schema alone                 | Fast iteration, no trace history needed                  |
| `examples`    | Grounds generation in real tool call examples from your traces | More realistic responses based on actual production data |
| `stateful`    | Remembers prior calls within the session                       | Multi-step workflows where consistency matters           |

```python theme={null}
# Schema-only (default, fastest)
with create_synthetic_world(project_id="proj", mode="schema_only") as world:
    ...

# Grounded in real examples from your traces
with create_synthetic_world(project_id="proj", mode="examples") as world:
    ...

# Stateful — "create user" in step 1 is reflected in "get user" in step 5
with create_synthetic_world(project_id="proj", mode="stateful") as world:
    ...
```

## Deterministic output

Pass a `seed` for reproducible responses. The same seed, tool, and input produce the same output every time:

```python theme={null}
with create_synthetic_world(project_id="proj", seed=42) as world:
    @world.tool
    def get_user(user_id: str) -> dict:
        """Fetch a user."""
        ...

    # Same result every run
    user = get_user("u1")
```

## Simulating failures

Test your agent's error handling by injecting failures:

```python theme={null}
with create_synthetic_world(
    project_id="proj",
    seed=42,
    failure_profile={"rate": 0.3, "codes": ["timeout", "internal_error"]},
) as world:
    @world.tool
    def call_api(endpoint: str) -> dict:
        """Call an external API."""
        ...

    try:
        result = call_api("/users")
    except Exception as e:
        print(f"Handled error: {e}")  # 30% chance of timeout or 500
```

Failures are **deterministic** when a seed is set — the same step always produces the same failure or success.

Available error codes: `timeout`, `internal_error`, `rate_limit`, `not_found`, `bad_request`.

## Idempotency

Pass an `idempotency_key` to cache responses. Repeated calls with the same key return the cached result without hitting the backend:

```python theme={null}
result1 = world.call("get_user", {"id": "u1"}, idempotency_key="req-1")
result2 = world.call("get_user", {"id": "u1"}, idempotency_key="req-1")
# result1 == result2, second call is a cache hit
```

## Resetting a world

Reset the step counter and optionally clear all cached and stateful data:

```python theme={null}
world.reset()              # Reset step counter
world.reset(hard=True)     # Reset counter + clear cache + clear stateful memory
```

## Choosing a model

By default, the backend picks the generation model. You can override it:

```python theme={null}
with create_synthetic_world(
    project_id="proj",
    model="gpt-4o",
) as world:
    ...
```

## Full example

```python theme={null}
from rdk.synthetic import create_synthetic_world

with create_synthetic_world(
    project_id="crm-agent",
    mode="stateful",
    seed=42,
) as world:
    @world.tool
    def create_customer(name: str, email: str) -> dict:
        """Create a new customer record."""
        ...

    @world.tool
    def get_customer(customer_id: str) -> dict:
        """Look up a customer by ID."""
        ...

    @world.tool
    def create_ticket(customer_id: str, subject: str) -> dict:
        """Open a support ticket for a customer."""
        ...

    # Simulate a multi-step agent workflow
    customer = create_customer("Alice", "alice@example.com")
    print(f"Created: {customer}")

    looked_up = get_customer(customer["id"])
    print(f"Found: {looked_up}")

    ticket = create_ticket(customer["id"], "Login issue")
    print(f"Ticket: {ticket}")

    # Reset and replay
    world.reset(hard=True)
```

## See Also

* [create\_synthetic\_world()](/api-reference/create-synthetic-world) — Full parameter reference
* [ToolSpec](/api-reference/tool-spec) — Manual schema construction
* [Testing](/guides/testing) — Write tests without real API calls
