---
title: Start from scratch
description: Run a complete Memrail example: install the SDK, bind a policy to a named decision point, validate in shadow, promote, execute a local tool, and acknowledge success.
eyebrow: GETTING STARTED
keywords: Memrail quickstart, AI agent control tutorial, Python agent guardrails
last_updated: 2026-09-10
---

# Start from scratch

Run one decision from policy definition through execution. This example binds an EMU (executable memory unit) to `support.greeting`, matches an enterprise customer, and executes a local greeting formatter. It sends no email, moves no money, and needs no model or agent framework.

## 1. Install and configure

You need Python 3.9 or newer, a Memrail API key, and an organization with an isolated `development` workspace and `docs-demo` project. Create those scopes in your Memrail console before continuing. API calls use your account's normal usage allowance.

```bash
mkdir memrail-quickstart
cd memrail-quickstart
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade memrail

export AMI_API_KEY='your-api-key'
export AMI_ORG='your-org'
export AMI_WORKSPACE='development'
export AMI_PROJECT='docs-demo'

mkdir emus
curl -fsSLo quickstart.py https://docs.memrail.com/examples/quickstart.py
curl -fsSLo emus/emus.jsonl https://docs.memrail.com/examples/emus.jsonl
```

Inspect the downloaded files before running them. You can also [give your coding agent the Memrail skill](/agents/) to help with setup. Never commit credentials.

## 2. Inspect the policy and executor

The [complete policy file](/examples/emus.jsonl) is one JSON object on one line. It is expanded here for readability:

```json
{
  "emu_key": "support.enterprise_greeting",
  "decision_point": "support.greeting",
  "intent": "Greet enterprise customers with the reviewed local formatter",
  "trigger": "state.customer.name EXISTS AND state.customer.tier == 'enterprise'",
  "action": {
    "type": "tool_call",
    "intent": "Format a greeting locally",
    "tool": {
      "tool_id": "format_greeting",
      "version": "1.0.0",
      "side_effect": false,
      "args": {"name": "{{customer.name}}"}
    }
  },
  "policy": {"mode": "auto", "priority": 7},
  "expected_utility": 0.8,
  "confidence": 0.95,
  "state": "shadow"
}
```

The caller uses the same `decision_point` and supplies both state atoms. `{{customer.name}}` resolves from that context, not from model-written code. The local formatter has no external side effects, so this example intentionally omits action idempotency and cooldown; a validator may report `WARN-POLICY-GAP`. Review that finding as intentional only for this read-only demonstration. For consequential tools, follow the [integration invariants](/concepts/#integration-invariants).

Here is the entire [runnable Python file](/examples/quickstart.py):

<!-- include: examples/quickstart.py -->
```python
"""Run the Memrail quickstart in an isolated development project."""

import argparse
import asyncio
import json

from memrail import AsyncAMIClient
from memrail.atoms import state
from memrail.models import InvokeOptions, TraceOptions
from memrail.tools import ToolRegistry


TOOL_SCHEMA = {
    "type": "object",
    "properties": {"name": {"type": "string", "minLength": 1, "maxLength": 80}},
    "required": ["name"],
    "additionalProperties": False,
}
registry = ToolRegistry()


@registry.tool(
    name="format_greeting",
    description="Format a greeting locally; no external side effects",
    schema=TOOL_SCHEMA,
    projects=["docs-demo"],
)
async def format_greeting(args: dict) -> dict:
    name = args.get("name")
    if set(args) != {"name"} or not isinstance(name, str) or not 1 <= len(name) <= 80:
        raise ValueError("Expected one name between 1 and 80 characters")
    return {"greeting": f"Hello, {name}!"}


async def run(client, *, tier="enterprise", execute=False):
    client.register_tool(
        name="format_greeting",
        description="Format a greeting locally; no external side effects",
        schema=TOOL_SCHEMA,
        projects=["docs-demo"],
        handler=format_greeting,
    )
    return await client.decide_and_execute(
        decision_point="support.greeting",
        context=[state("customer.name", "Ada"), state("customer.tier", tier)],
        options=InvokeOptions(dry_run=not execute),
        trace=TraceOptions(enable=True),
        auto_ack=True,
    )


async def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--tier", choices=["enterprise", "free"], default="enterprise")
    parser.add_argument("--execute", action="store_true", help="Execute the local greeting tool")
    args = parser.parse_args()
    async with AsyncAMIClient(workspace="development", project="docs-demo") as client:
        result = await run(client, tier=args.tier, execute=args.execute)
    print(json.dumps({
        "selected": [item.emu_key for item in result.invoke_result.selected],
        "trace": result.invoke_result.trace,
        "executions": [
            {"executed": item.was_executed, "success": item.success,
             "output": item.output, "error": item.error}
            for item in result.execution_results
        ],
        "acks": [
            {"activation_id": item.activation_id, "success": item.success, "error": item.error}
            for item in result.ack_results
        ],
    }, indent=2, default=str))
    if any(not item.success for item in [*result.execution_results, *result.ack_results]):
        raise SystemExit(1)


if __name__ == "__main__":
    asyncio.run(main())
```
<!-- /include -->

Module-level registration makes the tool discoverable by the CLI. Runtime registration connects the same schema and handler to the client executor. Importing this module does not create a client or make API calls.

## 3. Register, validate, and apply in shadow

```bash
memrail tool-register --file ./quickstart.py \
  -w development --project docs-demo

# Observe the example atoms before checking reachability.
python quickstart.py

memrail emu-plan ./emus/ -w development -p docs-demo --strict
memrail emu-apply ./emus/ -w development -p docs-demo \
  --yes --strict --target-state shadow
memrail emu-validate -w development -p docs-demo
```

The first run has no policy to select yet. Strict preflight checks complete candidate definitions before writes. Inspect all findings; the [diagnostic catalog](/reference/diagnostic-codes.json) explains their codes. Commit `emus/emus.jsonl` and the generated `emus/.emu.lock.jsonl` together.

## 4. Check both branches

```bash
python quickstart.py
python quickstart.py --tier free
```

For enterprise context, the shadow candidate's trace contains `shadow_state` suppression; `selected` and `executions` stay empty. For free context, the trigger does not match. The script defaults to dry run, following the shared [evaluation contract](/concepts/#evaluation-and-execution).

## 5. Promote and execute

After verifying both branches, explicitly promote this read-only policy in the development project and reconcile the local snapshot:

```bash
memrail change-state support.enterprise_greeting active \
  -w development -p docs-demo
memrail emu-pull ./emus/ -w development -p docs-demo

python quickstart.py
python quickstart.py --execute
python quickstart.py --tier free --execute
```

| Run after promotion | Selection | Execution | ACK |
|---|---|---|---|
| Default enterprise dry run | `support.enterprise_greeting` | none | none |
| Enterprise with `--execute` | `support.enterprise_greeting` | `{"greeting": "Hello, Ada!"}` | one successful activation |
| Free with `--execute` | none | none | none |

The combined `decide_and_execute` helper runs the registered handler and, with `auto_ack=True`, acknowledges successful execution. Its returned `execution_results` are separate from `invoke_result.selected`. Inspect failures before retrying; use the [outcome contract](/concepts/#outcomes-and-acknowledgment) when adding real side effects.

## 6. Make the next change reviewable

Change the eligibility condition, add a test that should now match, and retain a test that should not. Keep the named binding and handler contract stable. Use the [EMU JSON Schema](/schemas/emu.schema.json) for structural checks and [production workflow](/production/) for reviewed deployments.

For a TypeScript integration, continue with the [TypeScript SDK](/typescript/). For an existing application, start with [one controlled execution boundary](/existing-project/).
