> This page is for version v5.0 (default).
> For other versions, use one of these documentation indexes:
> - v5.0 (default): https://humanloop.com/docs/v5/llms.txt
> - v4.0: https://humanloop.com/docs/v4/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://humanloop.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://humanloop.com/docs/_mcp/server.

> Humanloop Flows trace and evaluate complex AI workflows, from LLM agents to retrieval-augmented generation (RAG). By unifying all components, Flows provide the context needed to debug and iterate with confidence.

![](/docs/_fern-img/e92b65cf53e0155a55551e65d5eb6abede112635763e06ecc980c652139a4217.webp)

## Introduction

LLM-powered systems are multi-step processes, leveraging information sources, delegating computation to tools, or iterating with the LLM to generate the final answer.

Looking at the inputs and output of such a system is not enough to reason about its behavior. Flows address this by tracing all components of the feature, unifying Logs in a comprehensive view of the system.

## Basics

To integrate Flows in your project, add the SDK flow decorator on the entrypoint of your AI feature.

#### Python

```python maxLines=50
@humanloop.flow(path="QA Agent/Answer Question")
def call_agent(question: str) -> str:
    # A simple question answering agent

    ...

    return answer
```

#### TypeScript

```typescript maxLines=50
const callAgent = humanloop.flow({
    path: "QA Agent/Answer Question",
    callable: async (question: string) => {
        // A simple question answering agent

        ...

        return answer
    }
})
```

The decorator will capture the inputs and output of the agent on Humanloop.

You can then start evaluating the system's performance through [code](/docs/v5/guides/evals/run-evaluation-api) or through the [platform UI](/docs/v5/guides/evals/run-evaluation-ui).

## Tracing

Additional Logs can be added to the trace to provide further insight into the system's behavior.

> **Info**
>
> On Humanloop, a trace is the collection of Logs associated with a Flow Log.

#### Python

**`Question answering agent`**

```python maxLines=50 title="Question answering agent"
@humanloop.tool(path="QA Agent/Search Wikipedia")
def search_wikipedia(query: str) -> dict:
    """LLM function calls this to search Wikipedia."""
    ...


@humanloop.prompt(path="QA Agent/Call Model")
def call_model(messages: list[dict]) -> dict:
    """Interact with the LLM model."""
    ...


@humanloop.flow(
    path="QA Agent/Answer Question",
    attributes={"version": "v1", "wikipedia": True}
)
def call_agent(question: str) -> str:
    """A simple question answering agent."""
    ...
```

The agent makes multiple provider calls to refine the response to the question. It makes function calls to `search_wikipedia` to retrieve additional information from an external source.

Calling the other functions inside `call_agent` creates Logs and adds them to the trace created by `call_agent`.

#### TypeScript

**`Question answering agent`**

```typescript maxLines=50 title="Question answering agent"

const searchWikipedia = humanloop.tool({
    path: "QA Agent/Search Wikipedia",
    callable: async (query: string) => {
        // LLM function calls this to search Wikipedia
        ...
    }
})

const callModel = humanloop.prompt({
    path: "QA Agent/Call Model",
    callable: async (messages: string[]) => {
        // Interact with the LLM model
        ...
    }
})

const callAgent = humanloop.flow({
    path: "QA Agent/Answer Question",
    attributes: { version: "v1", wikipedia: true },
    callable: async (question: string) => {
        // A simple question answering agent
        ...
    }
})
```

The agent makes multiple provider calls to refine the response to the question. It makes function calls to `searchWikipedia` to retrieve additional information from an external source.

Calling the other functions inside `callAgent` creates Logs and adds them to the trace created by `callAgent`.

![](/docs/_fern-img/efe8244b1e2351424984159850eacd25ab6405c945828fe982d2faa99b810529.webp)

### Manual Tracing

If you don't want to use decorators, first create a Flow Log, then pass its `id` when creating Logs you want to add to the trace.

#### Code for Manual Tracing

#### Python

**`Tracing via API`**

```python title="Tracing via API" maxLines=50 highlight={17,22-26}
def call_agent(question: str) -> str:
    trace_id = humanloop.flows.log(
        name="QA Agent/Answer Question",
        flow={
            "attributes": {
                "version": "v1",
                "wikipedia": True
            }
        },
        inputs={"question": question}
    ).id

    llm_output = humanloop.prompts.call(
        name="QA Agent/Answer",
        prompt={...},
        messages=[...],
        parent_trace_id=trace_id
    )

    ...
    
    humanloop.flows.update_log(
        log_id=trace_id,
        output=answer
        log_status="complete"
    )
```

#### TypeScript

**`Tracing via API`**

```typescript title="Tracing via API" maxLines=50 highlight={17,22-25}
async function callAgent(question: string) {
    const traceId = (await humanloop.flows.log({
        name: "QA Agent/Answer Question",
        flow: {
            attributes: {
                version: "v1",
                wikipedia: true
            }
        },
        inputs: { question }
    })).id;

    const llmOutput = await humanloop.prompts.call({
        name: "QA Agent/Answer",
        prompt: {...},
        messages: [...],
        parentTraceId: traceId
    });
    
    ...
    
    await humanloop.flows.updateLog(traceId, {
        output: answer,
        logStatus: "complete"
    });
}
```

## Versioning

Any data you pass into `attributes` will contribute to the version of the Flow. If you pass in a new value, the version will be updated.

#### Python

**`Question answering agent`**

```python maxLines=50 title="Question answering agent" highlight={3}
@humanloop.flow(
    path="QA Agent/Answer Question",
    attributes={"version": "v1", "wikipedia": True}
)
def call_agent(question: str) -> str:
    """A simple question answering agent."""
    ...
```

#### TypeScript

**`Question answering agent`**

```typescript maxLines=50 title="Question answering agent" highlight={3}

const callAgent = humanloop.flow({
    path: "QA Agent/Answer Question",
    attributes: { version: "v1", wikipedia: true },
    callable: async (question: string) => {
        // A simple question answering agent
        ...
    }
})
```

## Completing Flow Logs

Flow Logs can be marked as complete in order to prevent further Logs from being added to the trace. The flow decorator will mark a trace as complete when the function returns.

**`Monitoring Evaluator on Humanloop`**

```python title="Monitoring Evaluator on Humanloop"
def count_logs_evaluator(log):
    """Count the number of Logs in a trace."""
    if log["children"]:
        # Use the `children` attribute to access all Logs in the trace
        return 1 + sum([count_logs_evaluator(child) for child in log["children"]])
    return 1
```

A Flow Log's metrics, such as cost, latency and tokens, are computed as Logs are added to the trace.

A Flow Log's `start_time` and `end_time` are computed automatically to span the earliest start and latest end of the Logs in its trace. If `start_time` and `end_time` already span the Logs' timestamps, they are kept as they are.

If you don't want to use the decorator, you can complete the Flow Log via the SDK directly.

#### Code for Manual Tracing

#### Python

```python
humanloop.flows.update_log(
    log_id=trace_id,
    log_status="complete"
)
```

#### TypeScript

```typescript
humanloop.flows.updateLog(traceId, {
    logStatus: "complete"
})
```

## Evaluation

Unlike [Prompts](/docs/v5/explanation/prompts), which can be evaluated via the Humanloop UI, you must run evaluations on your Flows through code.

To do this, provide a `callable` argument to the `evaluations.run` SDK method.

Unlike other Logs, Evaluators added to Flows can access all Logs inside a trace:

#### Python

**`Evaluating a Flow`**

```python maxLines=50 title="Evaluating a Flow" highlight={5}
humanloop.evaluations.run(
    name="Comprehensiveness Evaluation",
    file={
        "path": "QA Agent/Answer Question",
        "callable": call_agent,
    },
    evaluators=[
        {"path": "QA Agent/Answer Comprehensiveness"},
    ],
    dataset={"path": "QA Agent/Simple Answers"},
)
```

#### TypeScript

**`Evaluating a Flow`**

```typescript maxLines=50 title="Evaluating a Flow" highlight={5}
humanloop.evaluations.run(
    name="Comprehensiveness Evaluation",
    file={
        "path": "QA Agent/Answer Question",
        "callable": callAgent,
    },
    evaluators=[
        { "path": "QA Agent/Answer Comprehensiveness" },
    ],
    dataset={ "path": "QA Agent/Simple Answers" },
)
```

Every time a Log is added to a Flow trace, monitoring Evaluators and Evaluations re-evaluate the Log. This behaviour is throttled, so adding
multiple Logs in quick succession will result in a single re-evaluation.

## Next steps

You now understand the role of Flows in the Humanloop ecosystem. Explore the following resources to apply Flows to your AI project:

* Check out our [logging quickstart](/docs/v5/quickstart/set-up-logging) for an example project instrumented with Flows.

* Learn how to evaluate an AI agent [in UI](/docs/tutorials/evaluate-agent-in-ui) or [in code](/docs/tutorials/agent-evaluation-code).