> 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/reference/add-step.md).

# kv.add\_step()

**What you'll learn:** How to record individual steps into the active task without wrapping a function in `@kv.step`.

`kv.add_step()` records a step into the active task imperatively. Use it for inline steps, third-party code you cannot decorate, dynamically chosen tools, or injecting a `step.gate` (human approval) inside a callback-based agent like LangChain. It is the missing primitive for mixing integration patterns cleanly — decorator, callback, and manual — within a single agent.

***

## Method signature

```python
kv.add_step(
    step_type: str | StepType,
    step_name: str | None = None,
    *,
    input: dict | None = None,
    output: dict | None = None,
    verb: str | Verb | None = None,
    properties: dict | None = None,
    meta: dict | None = None,
    task_id: str | None = None,
) -> None
```

***

## Parameters

| Parameter    | Type                  | Default      | Description                                                                                 |
| ------------ | --------------------- | ------------ | ------------------------------------------------------------------------------------------- |
| `step_type`  | `str \| StepType`     | *(required)* | Step type enum or string (e.g. `"step.gate"`, `StepType.step_model`).                       |
| `step_name`  | `str \| None`         | `None`       | Human-readable label. Defaults to the `step_type` value.                                    |
| `input`      | `dict \| None`        | `None`       | Input data to record.                                                                       |
| `output`     | `dict \| None`        | `None`       | Output data to record.                                                                      |
| `verb`       | `str \| Verb \| None` | `None`       | HTTP-style verb (`"GET"`, `"POST"`, etc.).                                                  |
| `properties` | `dict \| None`        | `None`       | Arbitrary key-value metadata.                                                               |
| `meta`       | `dict \| None`        | `None`       | Engine-reserved multi-agent correlation fields.                                             |
| `task_id`    | `str \| None`         | `None`       | Explicit task ID. Falls back to the active task from `start_task()` or a framework adapter. |

***

## Behaviour

* Requires an active task — set by `start_task()`, `@kv.step(step_type="task.start")`, or a framework adapter such as `KyvvuLangChainHandler`.
* Evaluates policies before recording. Raises `KyvvuBlockedError` if the step is blocked by an active policy.
* Unlike `@kv.step`, no template matching is performed — the caller provides all fields explicitly.
* Unlike `@kv.step`, does **not** auto-call `error_task()` on block — the caller decides how to handle it.

***

## Usage examples

### Standalone (manual task lifecycle)

```python
from kyvvu import Kyvvu

kv = Kyvvu(api_url="...", api_key="...")
kv.register_agent(agent_key="my-agent", name="My Agent", purpose="Demo")

kv.start_task()
kv.add_step(
    step_type="step.resource",
    step_name="fetch_user_data",
    verb="GET",
    input={"url": "https://api.example.com/users/42"},
    output={"status": 200, "user": {"name": "Alice"}},
)
kv.end_task()
```

### Inside a @kv.step-decorated agent

```python
@kv.step(step_type="task.start")
def run_agent(prompt: str):
    result = call_llm(prompt)

    # Inject an inline gate — no separate function needed
    kv.add_step(
        step_type="step.gate",
        step_name="safety_check",
        input={"content": result},
        output={"result": "pass", "check_type": "content_filter"},
    )

    return result
```

### Mixed with LangChain handler

```python
from langchain.tools import tool
from kyvvu import Kyvvu
from kyvvu.integrations.langchain import KyvvuLangChainHandler

kv = Kyvvu(api_url="...", api_key="...")
kv.register_agent(agent_key="lc-agent", name="LC Agent", purpose="Demo")
handler = KyvvuLangChainHandler(kv)

@tool
def send_email(recipient: str, body: str) -> str:
    """Send an email after human approval."""
    # The handler has already started a task via on_chain_start.
    # Use add_step() to inject a gate the handler doesn't cover:
    approved = input(f"Approve sending to {recipient}? [y/n] ")
    kv.add_step(
        step_type="step.gate",
        step_name="human_approval",
        input={"action": "send_email", "recipient": recipient},
        output={"result": "pass" if approved == "y" else "fail"},
        properties={"guard": {"check_type": "human_approval"}},
    )
    if approved != "y":
        return "Blocked by human reviewer."
    return do_send(recipient, body)

# Pass the handler when invoking the chain:
agent.invoke({"input": "..."}, config={"callbacks": [handler]})
```

***

## get\_current\_task\_id()

Convenience function that returns the `task_id` active in the current context (set by `start_task()` or a framework adapter), or `None`. Useful for advanced users who need to inspect the active task without a `Kyvvu` instance reference.

```python
from kyvvu import get_current_task_id

task_id = get_current_task_id()  # str or None
```

***

## Next steps

* [Python Decorator](/integrations/decorator.md) — function-level step instrumentation with `@kv.step`
* [LangChain / LangGraph](/integrations/langchain.md) — callback-based integration
* [Atomic Behaviours](/core-concepts/behaviours.md) — the step types and verbs available
* [Policies and Rules](/core-concepts/policies.md) — how policy evaluation works


---

# 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:

```
GET https://docs.kyvvu.com/reference/add-step.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
