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

# AI Routes

> Deploy AI agents as LoT Routes — then reach them over MQTT interaction topics or CALL AGENT from Actions, solo or as a multi-agent team

## AI Agents in LoT

Your broker already moves sensor data, drives PLCs, and stores history. **AI agents** add something new: specialists that *reason* over that world — in plain language, on the same machine, with the same MQTT and Routes you already defined.

In Coreflux, each agent is a **LoT Route** with `TYPE AGENT`. One block gives you the model, the persona, the tools, and the guardrails. Spin up an operator that answers shift questions. Add a classifier that never sees a chat window. Run five agents on one broker, each with its own brain and boundaries — no sidecar service, no copy-paste into a separate chatbot.

**Defining the Route is only half the story.** The other half is *who gets to knock on the door*: operators on MQTT topics the Route owns, or LoT Actions that dial the agent when something happens on the plant floor. Same agent definition — two front doors. Use one, use both, mix them across a team of Routes.

> [!TIP]
>
> **Like hiring for the control room, then choosing desk phone vs. internal line.** `DEFINE ROUTE … WITH TYPE AGENT` is the hire. `INTERACTION_TOPIC` is the desk line for people and apps. `CALL AGENT` is what your Actions use when a sensor fires or a timer ticks — no public chat required.

### When to use AI Routes

* **One agent, one job** — chat helper, classifier, or scheduled production/maintenance/solar report; each gets its own Route
* **Multi-agent teams** — split models, prompts, and permissions by role ([Multi-Agent Deployments](#multi-agent-deployments))
* **Ask the plant, don't only dashboard it** — "how many sensors are reporting?" before you wire another panel
* **MQTT-facing chat** — `INTERACTION_TOPIC` on the Route, plus MQTT ask/reply topics (your Action or custom app)
* **Hands-off automation** — `CALL AGENT` when a topic fires, a timer ticks, or a pipeline needs a verdict

> [!NOTE]
>
> First time? Build both patterns in ten minutes: [Creating Agents in Coreflux](/v2.14/quick-start/creating-agents). Then come back here for providers, tools, and production detail.

***

## Complete Example (Copy & Deploy)

Pick one tab to ship something fast, or deploy **both** on the same broker. Each example is self-contained — you do not need to read the rest of this page first.

> [!NOTE]
>
> **Prerequisites (both tabs):** Broker running and an [OpenAI API key](https://platform.openai.com/api-keys) stored on the broker as a secret (once):
>
> ```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
> KEEP SECRET "OPENAI_KEY" WITH "sk-..."
> ```
>
> The examples below reference it with `GET SECRET "OPENAI_KEY"`. Prefer local inference? Swap to `PROVIDER "ollama"` — see [Picking a Provider](#picking-a-provider).

**Talk with your data**

**Pattern:** [Route interaction topics](#pattern-1-route-interaction-topics) — the AGENT Route owns the model and `INTERACTION_TOPIC`; a thin Action exposes **ask/reply MQTT topics** you can hit from MQTT Explorer or the HUB [Data Viewer](/hub/mqtt/data-viewer).

> [!NOTE]
>
> The [HUB AI Assistant](/hub/ai-assistant) (Coreflux icon in the dock) is a **separate** built-in agent installed by the AI Setup wizard — it is not wired to `PlantAssistant` or your custom `INTERACTION_TOPIC`.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE PlantAssistant WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "openai"
        WITH MODEL "gpt-5.5"
        WITH API_KEY GET SECRET "OPENAI_KEY"
        WITH BROKER_TOOLS "true"
        WITH INTERACTION_MODE "interactive"
        WITH INTERACTION_TOPIC "plant/assistant/interact"
        WITH SYSTEM_PROMPT "You are the plant assistant. Use broker tools to answer from live MQTT data. Be concise."

DEFINE ACTION AskPlantAssistant
ON TOPIC "plant/assistant/ask" DO
    CALL AGENT "PlantAssistant.execute"
        WITH (task = PAYLOAD)
        RETURN AS {reply}

    PUBLISH TOPIC "plant/assistant/reply" WITH {reply}
```

**1. Deploy Route and Action**

Paste both blocks into **Coreflux HUB** (LoT Editor → Routes, then Actions), a LoT Notebook, or your usual deploy path. Confirm **PlantAssistant** (AGENT) and **AskPlantAssistant** (Action) are healthy.

**2. Send a question over MQTT**

In **MQTT Explorer** or HUB **Data Viewer** (MQTT app), publish your question to `plant/assistant/ask` and subscribe to `plant/assistant/reply`:

| Direction | Topic | Example payload |
| - | - | - |
| Publish | `plant/assistant/ask` | `Which MQTT topics are active right now?` |
| Subscribe | `plant/assistant/reply` | Agent answer (plain text) |

Try another live-data prompt, for example: `What is the latest payload on sensors/zone1/temperature?`

**3. Handle follow-up questions (interactive mode)**

If the agent needs clarification, it publishes to `plant/assistant/interact/ask`. Reply on `plant/assistant/interact/reply/{request_id}` with the `{request_id}` from that message — separate from the `plant/assistant/ask` topic your Action listens on.

> [!TIP]
>
> You can **talk with your data** over MQTT — the agent uses broker tools, not a static chat transcript.

**Automatic report**

**Pattern:** [`CALL AGENT` in LoT Actions](#pattern-2-call-agent-in-lot-actions) — LoT runs the agent on a schedule; no chat UI.

Example: **hourly production report** from live `production/` MQTT data — throughput, off-target lines, and follow-ups for supervisors.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE ProductionReporter WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "openai"
        WITH MODEL "gpt-5.4-mini"
        WITH API_KEY GET SECRET "OPENAI_KEY"
        WITH BROKER_TOOLS "true"
        WITH INTERACTION_MODE "autonomous"
        WITH SYSTEM_PROMPT "You write production shift reports. Use broker tools on production-related topics. Bullet points: throughput, downtime signals, quality risks."

DEFINE ACTION HourlyProductionReport
ON EVERY 1 HOUR DO
    CALL AGENT "ProductionReporter.execute"
        WITH (task = "Hourly production report: summarise output and efficiency from production/ topics, flag lines or batches that look off-target, note anything that needs follow-up before the next shift.", use_history = "false")
        RETURN AS {report}

    PUBLISH TOPIC "reports/production/hourly" WITH {report}
```

> [!NOTE]
>
> `ProductionReporter` omits `INTERACTION_TOPIC` — only the Action triggers it. Maintenance or solar: reuse the pattern with `maintenance/` or `solar/` topics. More use cases: [Creating Agents in Coreflux](/v2.14/quick-start/creating-agents).

**1. Deploy Route and Action**

Add **ProductionReporter** under Routes, then **HourlyProductionReport** under Actions.

**2. Subscribe to the report**

Subscribe to `reports/production/hourly` in MQTT Explorer or your dashboard.

**3. Wait or speed up the test**

Publish test data under `production/` if needed. Temporarily use `ON EVERY 30 SECONDS`, verify, then restore `ON EVERY 1 HOUR`.

> [!TIP]
>
> **Automatic production reporting** — LoT triggers the agent; supervisors read the output topic.

> [!TIP]
>
> Run **both tabs** together when you want shift chat (`PlantAssistant`) and an hourly production report (`ProductionReporter`) on the same broker. Everything below covers providers, tools, multi-agent teams, and production settings in depth.

***

## Two Ways to Interact

You write the agent once in LoT. **How the world talks to it** splits two ways — pick the door that fits, or run both on the same broker.

| Pattern | Where it lives | Best for |
| - | - | - |
| **[Route interaction topics](#pattern-1-route-interaction-topics)** | `INTERACTION_TOPIC` on the AGENT Route (+ MQTT topics your Action or app uses) | People, custom chat UIs, MQTT Explorer, HUB Data Viewer |
| **[`CALL AGENT` in Actions](#pattern-2-call-agent-in-lot-actions)** | LoT `CALL AGENT "RouteName.execute"` inside an Action | Sensor pipelines, timers, branching logic, structured jobs with no public chat surface |

```mermaid theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
flowchart TB
    subgraph define [Define once in LoT]
        Route[AGENT Route<br/>model, tools, prompt]
    end

    subgraph mqtt [Pattern 1 — Route topics]
        Client[App, HUB, or MQTT client]
        Prefix[INTERACTION_TOPIC prefix]
        Client <-->|ask / reply| Prefix
        Prefix --> Route
    end

    subgraph lot [Pattern 2 — LoT Action]
        Trigger[ON TOPIC or ON EVERY]
        Action[Action body]
        Trigger --> Action
        Action -->|CALL AGENT| Route
    end
```

You can mix them: an **Operator** Route with `INTERACTION_TOPIC` for shift handover chat, plus a **Classifier** Route that only ever runs via `CALL AGENT` from a sensor Action.

***

## Minimal Example

The shortest possible AI Route points at a local Ollama model and nothing else. It has no tools yet — it simply answers questions using the LLM:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Assistant WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH MODEL "llama3.2"
```

> [!NOTE]
>
> This default assumes Ollama is running locally at `http://localhost:11434`. Pull the model first with `ollama pull llama3.2` if you haven't already. If the broker runs in Docker, use the Ollama container hostname in Compose (e.g. `http://ollama:11434`) or `http://host.docker.internal:11434` when Ollama is on the host.

***

## Picking a Provider

AI Routes support four providers out of the box. Pick the one that matches where your model lives — local or cloud. Cloud providers need an API key, which you should store as a secret rather than pasting into the Route.

**Ollama (local)**

Runs entirely on your machine — no API key, no cloud dependency. Good for getting started and for privacy-sensitive workloads. The broker default is `llama3.2`; for stronger local quality in 2026, many teams use `qwen3:8b` or `qwen3:4b` on the same Ollama endpoint.

> [!NOTE]
>
> **Prerequisite:** [Install Ollama](https://ollama.com/download) and have it running before you deploy this Route. Pull your model in Ollama first (e.g. `ollama pull qwen3:8b`) — the `MODEL` value must match a model already available in `ollama list`. Coreflux connects to your Ollama endpoint; it does not install Ollama or download models for you. Lightweight models may work for a quick test, but often are not capable enough for reliable agent work or good answer quality.
>
> If the broker runs in **Docker**, `localhost` inside the container is not the host—set `BASE_URL` to the reachable Ollama endpoint. With **Docker Compose**, use the Ollama service name (e.g. `http://ollama:11434`). If Ollama runs on the host, use `http://host.docker.internal:11434` (Docker Desktop) or the host's LAN IP.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Assistant WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "ollama"
        WITH BASE_URL "http://localhost:11434"
        WITH MODEL "qwen3:8b"
```

**OpenAI**

Uses OpenAI's GPT-5 generation (e.g. `gpt-5.4-mini` for cost-sensitive work, `gpt-5.5` for frontier reasoning). Store the API key as a secret so it never appears in your Route definition.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Assistant WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "openai"
        WITH MODEL "gpt-5.4-mini"
        WITH API_KEY GET SECRET "OPENAI_KEY"
```

**Anthropic**

Uses Claude 4.6/4.7 models. Same secret-based pattern — `claude-sonnet-4-6` for most production agents, `claude-opus-4-7` when you need maximum reasoning depth.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Assistant WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "anthropic"
        WITH MODEL "claude-sonnet-4-6"
        WITH API_KEY GET SECRET "ANTHROPIC_KEY"
```

**Mistral**

Uses Mistral's hosted models — `mistral-large-latest` (Large 3) for quality, `mistral-small-latest` when you want lower cost and latency. Works the same way: set the provider and point to a stored API key.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Assistant WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "mistral"
        WITH MODEL "mistral-large-latest"
        WITH API_KEY GET SECRET "MISTRAL_KEY"
```

> [!TIP]
>
> Model IDs change often. Use the exact slug from your provider's catalog (OpenAI model guide, Anthropic model overview, Mistral API list, `ollama list`). Set secrets with `KEEP SECRET "OPENAI_KEY" WITH "sk-..."` — they never appear in logs or stored Route definitions.

***

## Core Configuration

These are the parameters most users will touch. The `AGENT` route has more advanced knobs (history pruning, tracing, token limits, confirmation flows), but you don't need them on day one.

| Parameter | Type | Default | What it does |
| - | - | - | - |
| `PROVIDER` | string | `ollama` | Which LLM backend to use: `ollama`, `openai`, `anthropic`, `mistral` |
| `MODEL` | string | `llama3.2` | Model name for the chosen provider |
| `API_KEY` | string | — | API key for cloud providers (use `GET SECRET`) |
| `TEMPERATURE` | number | `0.7` | Creativity: `0.0` is deterministic, `1.0+` is more creative |
| `SYSTEM_PROMPT` | string | *(built-in)* | Sets the agent's persona and rules |
| `BROKER_TOOLS` | boolean | `false` | Give the agent built-in tools to read/write MQTT and Routes |
| `MCP_ROUTES` | string | — | Comma-separated names of MCP Routes the agent can call |
| `INTERACTION_MODE` | string | `autonomous` | `autonomous` runs to completion, `interactive` can ask you questions |
| `INTERACTION_TOPIC` | string | *(auto)* | Base MQTT topic for interaction messages |
| `AGENT_MODE` | string | `agent` | `agent` (full control) or `insight` (read-only exploration) |

> [!NOTE]
>
> More options exist for production deployments — conversation history pruning, execution tracing, tool confirmation, max iteration limits. Start with the basics above, then add them as you need them.

***

## What Tools Can the Agent Use?

A fresh agent only knows how to chat. To make it actually *do* things, you give it tools. There are three sources, and you can mix all three.

### Broker Tools

Setting `BROKER_TOOLS true` unlocks built-in tools so the agent can interact with your broker directly — no extra configuration required.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Assistant WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH MODEL "llama3.2"
        WITH BROKER_TOOLS "true"
```

With broker tools enabled, the agent can:

* **List MQTT topics** and peek at their latest payloads
* **Read a specific topic** on demand
* **Publish messages** to any topic
* **List your Routes** to see what's connected

This is the fastest way to get an agent that can answer questions like *"what's the current temperature on factory/sensor/01?"* or *"which Routes are running right now?"*.

### MCP Tools

MCP (Model Context Protocol) servers expose tools like file systems, Slack, Google Drive, or custom integrations. Any [MCP Route](./mcp-routes) you've defined can be wired into an AI Route with `MCP_ROUTES`:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Assistant WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH MODEL "llama3.2"
        WITH MCP_ROUTES "FileMcp, SlackMcp"
```

The agent discovers every tool exposed by those MCP servers automatically. If `SlackMcp` offers a `send-message` tool, the agent can call it when appropriate.

### Your Other Routes

The agent also sees every other Route you've already defined. If you have a PostgreSQL Route with an `InsertReading` event, the agent automatically gets a tool named `SensorDB.trigger_InsertReading`. Same for Modbus, REST, MongoDB, and so on — you don't need to re-declare anything.

> [!TIP]
>
> Define your data Routes first, then add the AI Route. The agent inherits all of them as tools the moment it starts.

***

## Insight vs. Agent Mode

Not every AI Route should be able to publish messages or trigger equipment. Use `AGENT_MODE` to control how much power the agent has:

| Mode | What it can do | Good for |
| - | - | - |
| `insight` | Read-only: list topics, read payloads, explore Routes | Exploring a broker, answering "what's connected?" |
| `agent` (default) | Full: read **and** write, trigger Routes, modify configuration | Automation, chat assistants, hands-on operators |

> [!TIP]
>
> **Start in `insight` mode while you experiment.** The agent can still explain your broker and read data — but it can't accidentally publish to production topics while you're tuning the prompt.

***

## Pattern 1: Route Interaction Topics

Set `INTERACTION_TOPIC` on the AGENT Route and the broker wires **interaction traffic** under that prefix (for example follow-up questions on `{prefix}/ask` and replies on `{prefix}/reply/{request_id}` in interactive mode).

To **start** a conversation from MQTT Explorer or the HUB [Data Viewer](/hub/mqtt/data-viewer), add a thin Action that listens on your ask topic, runs `CALL AGENT "YourRoute.execute"`, and publishes the answer — as in the [Complete Example](#complete-example-copy--deploy) chat tab. Custom apps can implement the same prefix without that Action if they speak the broker's interaction protocol.

> [!NOTE]
>
> The [HUB AI Assistant](/hub/ai-assistant) is not your custom AGENT Route — it is the dock chat installed via **AI Setup** (`BrokerAgent`). Use it to build dashboards and LoT; use `INTERACTION_TOPIC` + MQTT (or your UI) for agents you define in LoT.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Operator WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH MODEL "llama3.2"
        WITH BROKER_TOOLS "true"
        WITH INTERACTION_MODE "interactive"
        WITH INTERACTION_TOPIC "plant/operator"
        WITH INTERACTION_TIMEOUT "120"
        WITH SYSTEM_PROMPT "You are the plant assistant. Help operators using broker tools."
```

| Setting | Role |
| - | - |
| `INTERACTION_TOPIC` | Base MQTT prefix for this Route's chat (here: `plant/operator`) |
| `INTERACTION_MODE` | `interactive` lets the agent pause and ask follow-ups; `autonomous` runs each task to completion |
| `INTERACTION_TIMEOUT` | Seconds to wait for a human reply in interactive mode before continuing |

When the agent needs clarification in **interactive** mode, traffic uses subtopics under your prefix:

| Direction | Topic | What happens |
| - | - | - |
| Agent → client | `{INTERACTION_TOPIC}/ask` | Agent publishes a follow-up question |
| Client → agent | `{INTERACTION_TOPIC}/reply/{request_id}` | Your UI or MQTT client answers; work continues |

Example with `INTERACTION_TOPIC "plant/operator"`: subscribe to `plant/operator/ask`, publish replies to `plant/operator/reply/{request_id}`.

> [!TIP]
>
> Use **Route topics** when humans or external apps drive the agent. Use **`CALL AGENT`** when LoT logic drives it — you do not need both on every Route.

***

## Pattern 2: `CALL AGENT` in LoT Actions

Keep the AGENT Route as the brain, but invoke it **from LoT** with `CALL AGENT "RouteName.execute"`. The Action's trigger (`ON TOPIC`, `ON EVERY`, and so on) decides *when* the agent runs; the `task` argument carries *what* to do. The result lands in a variable you can branch on or publish anywhere.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION ClassifyReading
ON TOPIC "sensors/+/reading" DO
    CALL AGENT "Classifier.execute"
        WITH (task = PAYLOAD, use_history = "false")
        RETURN AS {label}

    PUBLISH TOPIC "sensors/classified" WITH {label}
```

| Argument | Typical use |
| - | - |
| `task` | Instruction or payload text (required) |
| `session_id` | Same id on the next call continues one thread (per device, ticket, or line) |
| `use_history` | `"false"` for one-off labels; `"true"` when follow-up calls should remember earlier steps |

Specialist and watchdog agents often **omit** `INTERACTION_TOPIC` so nothing listens on a public chat prefix — they only run when an Action calls them.

You *can* bridge `CALL AGENT` to custom MQTT topics if you want a bespoke ask/reply pair, but those topics belong to **your Action**, not the Route's built-in interaction surface:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION AskAssistant
ON TOPIC "assistant/ask" DO
    CALL AGENT "Assistant.execute"
        WITH (task = PAYLOAD)
        RETURN AS {reply}

    PUBLISH TOPIC "assistant/reply" WITH {reply}
```

Here `assistant/ask` and `assistant/reply` are Action-defined topics; the agent still runs via `CALL AGENT`, not via `INTERACTION_TOPIC`.

> [!NOTE]
>
> Running **several AI Routes**? Pass `session_id` and `use_history` on each `CALL AGENT` so parallel automation jobs do not share the wrong memory. See [Multi-Agent Deployments](#multi-agent-deployments).

***

## How It All Fits Together

The diagram below shows both patterns on one broker: MQTT clients use the Route's `INTERACTION_TOPIC`; LoT Actions use `CALL AGENT`. Optional tool sources are the same in either case.

```mermaid theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
flowchart TB
    subgraph callers [Callers]
        MQTT[MQTT client or HUB]
        LotAction[LoT Action]
    end

    subgraph route [AGENT Route Assistant]
        Prefix[INTERACTION_TOPIC<br/>plant/operator]
        AgentCore[LLM + tools]
        Prefix --> AgentCore
        LotAction -->|CALL AGENT| AgentCore
    end

    MQTT <-->|chat prefix| Prefix

    BrokerTools[Broker tools]
    MCP[MCP Routes]
    Others[Other Routes]

    AgentCore --> BrokerTools
    AgentCore --> MCP
    AgentCore --> Others
```

Every tool box is optional. A minimal agent with only `MODEL` still answers — it just cannot read your broker until you enable tools.

***

## Multi-Agent Deployments

You are not limited to a single AI Route. The same Coreflux broker can run **several agents at once** — each one a normal Route with its own name, model, system prompt, and permissions. That matters whether you are learning the product or sizing it for production: you can give **operators** a conversational helper, give **automation** a fast classifier that never sees a chat window, and keep **guests** on a read-only view — without buying extra middleware or spinning up separate services for each role.

From a buyer's perspective, multi-agent is how you **match cost to workload** (local models for high-volume tasks, cloud models where quality matters most) and **match risk to audience** (full control for trusted staff, insight-only for everyone else). From a newcomer's perspective, it is simply "define another `DEFINE ROUTE … WITH TYPE AGENT` block with a different name" — the broker runs them side by side.

> [!NOTE]
>
> **Think of it like staffing a control room with specialists instead of one generalist** — a friendly voice for the team at the desk, a quiet expert that only labels sensor readings, and a scheduled check-in that writes a health summary. They share the same broker, not the same job description.

### Why give the broker more than one agent?

One catch-all assistant is hard to tune and expensive to run everywhere. Splitting the work pays off because:

* **Personalities differ.** A chat assistant should be warm and conversational; a data classifier should be terse and deterministic.
* **Access levels differ.** The Route that can trigger equipment should not be the same one you hand to a visitor.
* **Models and costs differ.** A premium cloud model is a poor fit for every sensor reading — a local model can handle volume for free while heavier reasoning stays on the model you pay for.
* **Memory should stay isolated.** The person at shift handover should not inherit someone else's half-finished conversation by accident.

### How people and software reach each agent

Map each Route to [Pattern 1](#pattern-1-route-interaction-topics) or [Pattern 2](#pattern-2-call-agent-in-lot-actions) — or use both patterns on different Routes in the same project:

| Entry | Pattern | Typical setup |
| - | - | - |
| Operators, chat apps, MQTT clients | **Route topics** | `INTERACTION_TOPIC` + ask/reply Action or custom UI; often `INTERACTION_MODE "interactive"` |
| Sensor pipelines, timers, batch jobs | **`CALL AGENT`** | `ON TOPIC` / `ON EVERY` Action, no `INTERACTION_TOPIC` on the Route |
| Background-only specialists | **`CALL AGENT` only** | `autonomous` mode, omit `INTERACTION_TOPIC` — no public chat prefix |

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
CALL AGENT "Classifier.execute"
    WITH (task = {job}, session_id = {deviceId}, use_history = "false")
    RETURN AS {result}
```

### Conversations and memory

Each **named AI Route** keeps its own conversation state. Two different Routes never share the same chat history — they are separate assistants. **Within** one Route, a **session id** (from your MQTT topic path when using interactive chat, or from `session_id` on `CALL AGENT`) keeps parallel conversations apart: two operators, two browser tabs, or two machines can talk to the same Route without their threads colliding. If you omit `session_id`, the broker still runs the task using its default session behaviour for that call — pass an explicit `session_id` when you need a reliable multi-step thread (for example one id per device or per ticket).

### How multi-agent traffic can look

```mermaid theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
flowchart LR
    Human[Operator or app]
    ChatTopic[MQTT chat prefix]
    OpRoute[Operator AI Route]

    SensorEvent[Sensor or event]
    LotAction[LoT Action]
    WorkerRoute[Specialist AI Route]
    ResultTopic[Result topic]

    Human -->|question| ChatTopic
    ChatTopic --> OpRoute
    OpRoute -->|answer| Human

    SensorEvent --> LotAction
    LotAction -->|CALL AGENT| WorkerRoute
    WorkerRoute --> ResultTopic
```

### Four agent archetypes

Most teams mix a few of these patterns — use one on a small setup, combine several when roles split.

**How to read the tabs:** **Operator** and **Reader** are usually **MQTT-facing** (people or HUB). **Specialist** is typically **LoT-only** (no chat topic). **Watchdog** is triggered on a **schedule** from LoT but does not need its own chat line.

**Operator**

The staff-facing assistant. Lives on a chat topic, has broker tools, and runs in interactive mode so it can ask follow-up questions. This is the agent your team talks to.

**Entry:** MQTT under your `INTERACTION_TOPIC` prefix (via a thin ask/reply Action or a custom UI).

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Operator WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "openai"
        WITH MODEL "gpt-5.5"
        WITH API_KEY GET SECRET "OPENAI_KEY"
        WITH BROKER_TOOLS "true"
        WITH INTERACTION_MODE "interactive"
        WITH INTERACTION_TOPIC "plant/operator"
        WITH SYSTEM_PROMPT "You are the plant assistant. Help operators understand what is happening and take action when asked."
```

**Best for:** chat UIs, control-room copilots, customer-facing assistants.

**Specialist**

A silent workhorse with no chat surface — no `INTERACTION_TOPIC`, no MQTT entry point. The only way in is a `CALL AGENT` from another LoT Action. Perfect for structured, repeatable tasks like classification or extraction.

**Entry:** LoT only. Keep `INTERACTION_MODE` **`autonomous`** (the default) and skip `INTERACTION_TOPIC` so the Route does not listen for public chat traffic.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Classifier WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "ollama"
        WITH MODEL "qwen3:8b"
        WITH INTERACTION_MODE "autonomous"
        WITH TEMPERATURE "0.0"
        WITH SYSTEM_PROMPT "Classify sensor readings as NORMAL, WARNING, or CRITICAL. Reply with only the label."
```

Then drive it from any Action that needs a label:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION ClassifySensor
ON TOPIC "sensors/+/reading" DO
    SET "device" WITH TOPIC POSITION 2
    SET "reading" WITH (PAYLOAD AS STRING)
    CALL AGENT "Classifier.execute"
        WITH (task = "Reading from " + {device} + ": " + {reading})
        RETURN AS {label}
    PUBLISH TOPIC "sensors/" + {device} + "/class" WITH {label}
```

**Best for:** classification, tagging, extraction — anything called from automation, not humans.

**Watchdog**

A scheduled sentinel. It wakes up on a timer, inspects broker state via broker tools, and publishes a summary or alert. No human triggers it.

**Entry:** A timed LoT Action that calls `CALL AGENT` — no dedicated chat topic required.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Watchdog WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "ollama"
        WITH MODEL "qwen3:4b"
        WITH INTERACTION_MODE "autonomous"
        WITH BROKER_TOOLS "true"
        WITH SYSTEM_PROMPT "You are a health monitor. Check all routes for errors and publish a concise alert summary if any are unhealthy."
```

Wire it to a timed Action so it runs unattended:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION HourlyHealthCheck
ON EVERY 1 HOUR DO
    CALL AGENT "Watchdog.execute"
        WITH (task = "Check all routes for errors and summarise the broker health.")
        RETURN AS {report}
    PUBLISH TOPIC "plant/health/hourly" WITH {report}
```

**Best for:** health checks, scheduled reports, overnight summaries.

**Reader**

The agent you can hand to anyone. It runs in `insight` mode, so it can read every topic and Route but cannot write anything — even if asked nicely. Safe for visitors, dashboards, and read-only audiences.

**Entry:** MQTT read-only chat on your `INTERACTION_TOPIC` — safe for guests because `AGENT_MODE` is `insight`.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE Reader WITH TYPE AGENT
    ADD AGENT_CONFIG
        WITH PROVIDER "openai"
        WITH MODEL "gpt-5.4-mini"
        WITH API_KEY GET SECRET "OPENAI_KEY"
        WITH BROKER_TOOLS "true"
        WITH AGENT_MODE "insight"
        WITH INTERACTION_MODE "interactive"
        WITH INTERACTION_TOPIC "plant/reader"
        WITH SYSTEM_PROMPT "You are a read-only plant observer. Answer questions about current state. You cannot change anything."
```

**Best for:** management dashboards, visitor demos, on-call engineers who need to look but not touch.

### Mixing providers across agents

`PROVIDER` and `MODEL` are set per Route, so every agent in the team can run on a different brain. Swap any of them by redeploying a single Route definition — no broker restart, no infrastructure change.

| Use case | Suggested model | Why |
| - | - | - |
| High-stakes operator chat | OpenAI `gpt-5.5` or Anthropic `claude-opus-4-7` | Frontier reasoning, handles ambiguity and multi-step tool use |
| Balanced production chat | OpenAI `gpt-5.4` or Anthropic `claude-sonnet-4-6` | Strong quality at lower cost than top-tier models |
| Low-latency classifiers | Ollama `qwen3:8b` (local) | Fast, private, good tool-following for structured labels |
| Scheduled operations reports | OpenAI `gpt-5.4-mini` | Production, maintenance, or solar digests on a timer |
| Management read portal | OpenAI `gpt-5.4-mini` | Cost-effective for low-volume read queries |

### Smart habits when you run several agents

* **Name Routes by role** — `Line3Classifier` or `VisitorGuide`, not `Agent2`, so your team and your LoT Actions stay readable.
* **Give each worker only the tools it needs** — A classifier rarely needs broker-wide publish access or MCP; fewer tools mean faster runs and fewer surprises.
* **Remember that cost adds up** — Each Route can call a cloud model on its own schedule. More agents and more `CALL AGENT` traffic multiply tokens and rate limits.
* **Match power to trust** — Use [Insight vs. Agent Mode](#insight-vs-agent-mode) for read-only audiences; reserve full `agent` mode for staff Routes that must act.
* **Add confirmation where mistakes hurt** — `TOOL_EXECUTION_MODE "confirm"` on any Route that should pause before a destructive tool runs.

### Per-agent safety rails

Safety settings apply **per Route**, so a strict assistant and a powerful one can live on the same broker. Pair the ideas in [Insight vs. Agent Mode](#insight-vs-agent-mode) with the habits above: lock read-only audiences to `insight`, keep trusted operators in full `agent` mode, and use `TOOL_EXECUTION_MODE "confirm"` only where you need a human gate before risky tools. Trace topics, iteration limits, and history pruning are also per agent — turn tracing on for the Route you are debugging so the rest of the team stays quiet.

***

## Best Practices

> **Store API keys as secrets**
>
> Never paste API keys directly into a Route definition. Use `KEEP SECRET "MY_KEY" WITH "sk-..."` once, then reference it with `WITH API_KEY GET SECRET "MY_KEY"`. Secrets are never logged or exposed in stored definitions.

> **Start in insight mode**
>
> When you're still shaping the system prompt or picking a model, run the agent with `WITH AGENT_MODE "insight"`. It can still read topics and explore Routes, but it can't publish or trigger anything — so a bad prompt won't affect production data.

> **Write a focused system prompt**
>
> The default prompt is generic. Tell the agent *who it is* and *what it should refuse to do*:
>
> ```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
> WITH SYSTEM_PROMPT "You are a factory assistant. Answer questions about the production line using broker tools. Do not publish to any topic starting with 'control/' without explicit confirmation."
> ```

> **Keep iteration limits sane**
>
> `MAX_ITERATIONS` caps how many tool calls the agent can make per task. The default (`10`) is fine for most questions. Lift it only if you have complex multi-step workflows — runaway loops are both slow and expensive on cloud providers.

***

## Troubleshooting

> **Ollama not reachable**
>
> * Make sure Ollama is running: `ollama list` should respond without errors
> * Confirm the model is pulled: `ollama pull llama3.2`
> * If Ollama is on a different machine, set `WITH BASE_URL "http://host:11434"` explicitly
> * If the broker runs in **Docker**, `localhost` points at the container—not the host. With **Docker Compose**, use the Ollama service (e.g. `http://ollama:11434`). If Ollama is on the host, try `http://host.docker.internal:11434` (Docker Desktop) or the host LAN IP
> * Check firewall rules between the broker and the Ollama host

> **The agent never calls any tool**
>
> * Confirm `BROKER_TOOLS "true"` is set (or that `MCP_ROUTES` lists valid Routes)
> * Smaller local models sometimes ignore tools — try a more capable pull (e.g. `qwen3:8b` over `llama3.2`, or switch to a cloud model such as `gpt-5.4-mini` or `claude-sonnet-4-6`)
> * Sharpen the `SYSTEM_PROMPT` so the agent knows it *should* use tools for data questions

> **Cloud provider returns 401 or 403**
>
> * Verify the secret actually exists: publishing `LIST SECRETS` via the command console should show its name
> * Check you're using the right `PROVIDER` — Anthropic keys don't work for OpenAI and vice versa
> * For Anthropic, make sure your key has access to the specific model you set in `MODEL`

> **The reply is empty or gets cut off**
>
> * Raise `MAX_TOKENS` — long answers need more room (default is `4096`)
> * Check the model actually supports the request (some smaller models struggle with long context)
> * If you chained several tool calls, the agent may have hit `MAX_ITERATIONS` — increase it and retry

***

## Next Steps

> **[Creating Agents in Coreflux](/v2.14/quick-start/creating-agents)**
>
> Hands-on tutorial — chat over MQTT, automatic reports, broker tools, and multi-agent layouts in minutes.

> **[MCP Routes](./mcp-routes)**
>
> Plug external tools — Slack, file systems, Google Drive, custom servers — into your agent.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.