> For the complete documentation index, see [llms.txt](https://docs.kyvvu.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kyvvu.com/getting-started/first-agent.md).

# Your First Agent

**What you'll learn:** How `kyvvu init` works, what the demo agent does, and how to read the policy evaluation output.

***

## Scaffold a project

```bash
kyvvu init my-agent
```

This scaffolds project files — `agent.py`, `requirements.txt`, `.env.example`, `.gitignore`, and `README.md`. No authentication is required.

The output looks like:

```
✓ Created my-agent/

Next steps:

    cd my-agent

    # Install requirements:
    pip install -r requirements.txt

    # Set your key:
    # (run `kyvvu register` if you have none)
    cp .env.example .env       # add your KV_API_KEY
    # or: export KV_API_KEY=your-key

    # Run the agent:
    python agent.py

To assign security policies, go to the dashboard:
    Manifests page → select a manifest → assign to your agent
Or use the CLI:
    kyvvu list-manifests
    kyvvu assign-manifest --agent-id <id> --repo-id <id> --manifest <path>
```

{% hint style="info" %}
**Caching:** `register_agent()` results are cached locally at `~/.kyvvu/registrations/`. If you call it on every startup with the same parameters, the SDK reuses the cached registration without a network call. You can safely call `register_agent()` every time your agent starts.
{% endhint %}

{% hint style="info" %}
**Data minimization:** By default, only your agent's opaque ID and operational fields (risk classification, environment, declared tools) are stored in Kyvvu's cloud. Descriptive metadata (name, purpose, owner) is not persisted server-side. Pass `store_metadata=True` to opt in to storing metadata on the platform.
{% endhint %}

## Assign security policies

Policies are managed via **manifests** — YAML files stored in a GitHub repository. To assign policies to your agent:

1. Go to the **Manifests** page in the dashboard
2. Connect a repository containing manifest files (or use the default Kyvvu/manifests repo)
3. Click **Assign** on a manifest and select your agent

Or use the CLI:

```bash
kyvvu list-manifests
kyvvu assign-manifest --agent-id <id> --repo-id <id> --manifest manifests/security/owasp-agentic-default.yaml
```

See [Manifests](/policy-authoring/templates.md) for details on manifest structure and available policy sets.

## Run the demo agent

```bash
cd my-agent
pip install -r requirements.txt
export KV_API_KEY=KvKey-...
python agent.py
```

The full `agent.py` source:

```python
from kyvvu import Kyvvu, KyvvuBlockedError, RiskClassification, StepType, Verb
import os, sys

# --- Constructor ---
# api_url: Kyvvu platform URL (defaults to https://platform.kyvvu.com)
# api_key: your API key from `kyvvu register` (reads KV_API_KEY env var)
# agent_key: stable identifier for this agent (used for policy matching)
# risk_classification: EU AI Act risk tier (MINIMAL, LIMITED, or HIGH)
kv = Kyvvu(
    api_url=os.environ.get("KV_API_URL", "https://platform.kyvvu.com"),
    api_key=os.environ["KV_API_KEY"],
    agent_key="my-agent",
    risk_classification=RiskClassification.MINIMAL,
)

# --- Registration ---
# Registers the agent with the platform. Results are cached locally
# at ~/.kyvvu/registrations/ so repeat calls skip the network request.
# By default, only the opaque agent ID and operational fields are stored
# server-side (name, purpose, owner are NOT persisted unless you pass
# store_metadata=True).
kv.register_agent(
    name="Hello Kyvvu",
    purpose="Demonstration agent — shows behavioral trace and policy evaluation",
    declared_tools=["call_llm", "fetch_user_data", "run_script"],
)

# --- Decorated steps ---
# @kv.step wraps each function in evaluate → execute → record:
#   step_model  = LLM / model call
#   step_resource = external data fetch
#   step_exec   = code execution (triggers OWASP gate requirement)
# Verb describes the HTTP-like action (POST, GET, etc.)

@kv.step(StepType.step_model, Verb.POST)
def call_llm(prompt: str) -> str:
    """Mocked model call — returns a canned response."""
    return f"(mocked response to: {prompt!r})"

@kv.step(StepType.step_resource, Verb.GET)
def fetch_user_data(user_id: str) -> dict:
    """Mocked external resource call."""
    return {"user_id": user_id, "name": "Alice", "tier": "free"}

@kv.step(StepType.step_exec)
def run_script(code: str) -> str:
    """Mocked code execution — intentionally blocked by OWASP policy."""
    return f"(would execute: {code!r})"

# --- Task lifecycle ---
# start_task() begins a new auditable task run.
# end_task() flushes the audit trail. error_task() records a failure.
# KyvvuBlockedError is raised when a policy blocks a step — the agent
# should handle it gracefully (log, skip, or escalate).
if __name__ == "__main__":
    try:
        kv.start_task()
        user = fetch_user_data("user_123")
        answer = call_llm(f"User {user['name']} asks: What's the weather?")
        run_script(f"save_response('{answer}')")  # blocked by OWASP policy
    except KyvvuBlockedError as exc:
        print(f"\n⛔ Policy blocked this step: {exc}")
        print(f"   Risk score: {exc.result.risk_score:.2f}")
        print(f"   Action:     {exc.result.action.value}")
        for p in exc.result.policies:
            if p.violated:
                print(f"   • {p.name} ({p.severity})")
        try:
            kv.error_task(error=exc)
        except Exception:
            pass
        sys.exit(1)
    except Exception as exc:
        try:
            kv.error_task(error=exc)
        except Exception:
            pass
        raise
    else:
        kv.end_task()
```

It starts a task, calls all three steps, and the third one gets blocked:

```
⛔ Policy blocked this step: ...
   Risk score: 1.00
   Action:     block
   * Code execution requires a preceding gate (critical)

This is Kyvvu working as intended — the OWASP security policies
flagged an action that requires oversight. In a real agent, you'd
add a step.gate (human approval) before the blocked action.
```

## What happened

The `@kv.step` decorator wraps each function call in a three-phase cycle:

1. **Evaluate** — the engine checks the intended behaviour against all applicable policies. It reads the task's history to make path-dependent decisions.
2. **Execute** — if the evaluation returns `allow` or `warn`, the function runs.
3. **Record** — the completed step (with output) is appended to the task's history for future evaluations.

Steps 1 and 2 passed for `call_llm` and `fetch_user_data`. When `run_script` was evaluated:

* The engine identified it as `step.exec` (code execution).
* The OWASP policy "Code execution requires a preceding gate" checked whether a `step.gate` existed earlier in the task history.
* No gate was found. The policy has severity `critical`, so the engine returned `block`.
* The decorator raised `KyvvuBlockedError` instead of executing the function.

## Inspect the policies

```bash
kyvvu list-policies
```

This shows policies assigned to your agent via manifests. If no manifest has been assigned yet, the list will be empty.

```
 NAME                                        SCOPE               RULE_TYPE              SEVERITY  SOURCE                       ENABLED
 Agent must declare a substantive purpose    agent_registration  field_matches_regex    high      owasp_agentic_default.yaml   ✓
 Agent must declare a tool allowlist         agent_registration  field_not_empty        high      owasp_agentic_default.yaml   ✓
 Agent must declare a valid risk class.      agent_registration  field_in_list          critical  owasp_agentic_default.yaml   ✓
 Tool calls must be in the declared allow.   step_execution      step_name_in_allowlist critical  owasp_agentic_default.yaml   ✓
 Code execution requires a preceding gate    step_execution      step_requires_gate     critical  owasp_agentic_default.yaml   ✓
 Destructive resource ops require a gate     step_execution      step_requires_gate     critical  owasp_agentic_default.yaml   ✓
 External content taint requires fresh gate  step_execution      not                    critical  owasp_agentic_default.yaml   ✓
 Bound resource calls per task (max 50)      step_execution      execution_max_steps    high      owasp_agentic_default.yaml   ✓
```

Each policy is an instance of a **rule** with specific **parameters**. For example, "Code execution requires a preceding gate" uses the `step_requires_gate` rule with `target_step_types: ["step.exec"]`. The same rule backs "Destructive resource ops require a gate" with different parameters (`target_step_types: ["step.resource"]`, `target_verb: "DELETE"`).

For the full details of each OWASP policy, see [OWASP Default Manifest](/policy-authoring/owasp-default.md).

***

## Next steps

* [Understanding the Output](/getting-started/understanding-output.md) — learn what each field in the behavioral trace means
* [OWASP Default Template](/policy-authoring/owasp-default.md) — the 8 default policies explained in detail
* [Python Decorator](/integrations/decorator.md) — full reference for `@kv.step`


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kyvvu.com/getting-started/first-agent.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
