---
sidebar_position: 7
title: "Agent system"
slug: /architecture/agent-system
slide: /diagrams/abi-agent.svg
slide_alt: An agent is a model, a system prompt, and tools. That is the unit of work in a module.
question: How does ABI structure its agents?

---

# Agent system

ABI's agent architecture is the capstone of the platform: the layer where the knowledge graph, data plane, and service infrastructure become directly accessible to humans and AI clients. The agent layer is open at every seam: agents are Python classes, tools are Python functions, workflows or SPARQL templates, routing is vector similarity over registered intent strings, and every engine agent is reachable over REST, from an MCP client, and from the CLI.

---

## The core idea

Most AI systems treat data, models, and workflows as separate concerns stitched together with glue code. ABI takes a different approach: **ontologies are the unifying field**.

An ontology is a formal, machine-readable description of the world: what entities exist, how they relate, and what operations can be performed on them. When your data, agents, workflows, and integrations all speak the same ontological language, they become composable by design rather than by accident.

Everything in ABI (agents, pipelines, workflows, integrations) operates on the same shared knowledge graph. Vectors, documents and files sit next to it as engine services, and the graph keeps the typed entities that tie them together. The ontology is the integration layer, not a separate "AI layer".

---

## Why an operating system

An OS provides shared primitives that every application uses without reimplementing: memory, I/O, scheduling, inter-process communication.

ABI provides the same for AI:

| OS primitive | ABI equivalent |
|---|---|
| File system | Knowledge graph (triple store) |
| Process model | Agent runtime (LangGraph state machines) |
| IPC / messaging | Real-time fabric (NATS): kernel services, the JetStream bus, discovery |
| Package manager | Module system (installable, versioned capabilities) |
| Shell / CLI | `abi chat`, MCP server, REST API |
| Scheduler | Module jobs (JetStream schedules and event triggers) |

You bring the domain. ABI provides the runtime.

---

## The agent layer

Agents are the user-facing interface to the system. Each agent (`Agent`, or `IntentAgent` for one that routes by intent) is a LangGraph state machine with:

- A **system prompt** defining its role and behavior
- A **tool list**: functions, workflows, integrations, and other agents it can hand the conversation to
- **Intents** controlling how a routing agent sends messages to it
- **Memory**: in NATS mode, the engine checkpoints each conversation in its [Document service](/architecture/services-and-adapters#document), so it survives restarts and is shared by replicas. Without a `nats:` block, `POSTGRES_URL` keeps precedence when it is set, as in 2.x (the `agent_memory_migrate` job moves that memory to the Document service). Outside an engine, or when the Document service is unavailable (or `naas-abi-sdk` is not installed), an agent falls back to PostgreSQL at `POSTGRES_URL`, and to memory otherwise

### Intent routing

![An IntentAgent turn: check the active agent and mentions, map intents, filter them, check entities, then let the model answer, reply with a raw intent, hand over to an agent, call a tool, or ask the user to choose. A clear winner skips the filter and a single survivor skips the entity check.](/diagrams/abi-intent.svg)

An **`IntentAgent`** matches each incoming message against its registered intents by vector similarity, using the default embedding model of the model registry. An intent has a value (the phrase to match), a type (`agent`, `tool` or `raw`) and a target (an agent name, a tool name, or for `raw` the reply itself). Each turn runs these steps:

1. **Active agent and mentions.** A message that starts with `@<agent name>` goes straight to that sub-agent. While a sub-agent holds the conversation, the next turn goes back to it, unless that agent's `retains_active_turn` hook releases it.
2. **Map.** Intents scoring above `threshold` (0.85 by default) match. One intent at `direct_intent_score` (0.90) or above, ahead of the rest, goes straight to the router, and so does a single match above 0.90. Otherwise the intents within `threshold_neighbor` (0.05) of the best one stay, one per target; with none, the model answers.
3. **Filter and check.** The chat model drops matches that do not fit the message (`filter_out_intents`). When exactly one intent is left, it goes straight to the router. When none or several are left (or the filter call fails), `entity_check` keeps an intent that names an entity only when the message names it too.
4. **Route.** No intent left: the model answers. One `raw` intent: its target is the reply and the turn ends. One `agent` intent: that agent takes the conversation. One `tool` intent: the intent goes into the system prompt and the model calls the tool. Several `agent` or `tool` intents: the agent asks the user to choose from a numbered list.

When the embedding model cannot be reached, mapping is skipped and the model routes the conversation on its own.

An intent also has a scope: `ALL` (the default) or `DIRECT`. Intents of type `agent` or `tool` are always `DIRECT`. `AbiAgent` registers each sub-agent's name and description as intents, plus the sub-agent's own `ALL` intents, and leaves out its `DIRECT` ones: a phrase that points to a sub-agent's tool stays with that sub-agent. How routing came to be scoped is recorded in [Intent Scope Routing](/user-guide/release-notes/intent-scope-agent-routing) (an ADR from 2025, since refined in code).

### Agent composition

An agent uses other agents through **handoff tools**. Pass agents in `agents=[...]` (or in `tools=[...]`), and each one becomes a tool named `transfer_to_<agent name>` on the parent, with the sub-agent's graph added as a node of the parent's graph. When the model calls that tool, the conversation moves to the sub-agent with the full message history, and stays there on the next turns: the active agent is saved with the conversation's checkpoint.

- **Back to the parent.** Agents built with a shared state that names a supervisor (`AgentSharedState(supervisor_agent=...)`) get a `request_help` tool. A sub-agent that cannot handle a request calls it, and the supervisor takes the conversation back.
- **One after another.** A parent with `sequential_supervisor = True` gets control back each time a sub-agent finishes, so it can hand the same turn to several sub-agents in order.
- **Routing without the model.** On an `IntentAgent`, `agent` intents whose target is a sub-agent route to it directly (see the steps above), before any handoff tool is needed.

`AbiAgent` builds this hierarchy for you. Its `New()` collects the agent classes of `naas_abi` and of every loaded engine module, builds each one with a shared state whose supervisor is `Abi`, and passes them as its sub-agents. A supervisor that has `Abi` as a sub-agent itself (`delegates_to_abi = True`) is left out, so the two never build each other. [Add sub-agents](/capabilities/build/agents/add-sub-agents) shows the code.

### Built-in agents

`naas-abi` ships these agents in `naas_abi.agents`:

| Agent | Role |
|---|---|
| `AbiAgent` | Root agent (`Abi`): routes requests to the others by intent, and uses the Nexus admin tools and the agent recommendation SPARQL tools itself. |
| Feature agents: `SlidesAgent`, `AppsAgent`, `MarketplaceAgent`, `OntologyAgent`, `KnowledgeGraphAgent`, `FilesAgent`, `DatasetsAgent`, `SearchAgent`, `MapsAgent`, `CodeAgent`, `SettingsAgent`, `AgentCatalogAgent`, `SkillsAgent` | One per Nexus feature (`naas_abi/agents/feature/registry.py`): they answer questions about the feature, from its code, and act on it. Nexus binds each one to its section's chat pane. |
| `DocumentsAgent`, `SheetsAgent` | Write and edit Nexus documents and HTML workbooks. |
| `PlatformServicesAgent` | Queries and operates the data services: the knowledge graph (SPARQL), object storage, vector store, cache and key-value store. |
| `OntologyEngineerAgent` | Answers ontology questions and proposes models grounded in the BFO 7 Buckets. |
| `CodingAgent` | A coding agent backed by an opencode server. |

Marketplace modules add agents for AI providers, applications and domains. A [remote module](/architecture/remote-modules#agents) can expose agents too.

---

## Model layer

All agents accept a `ChatModel` (a LangChain chat model with its id, provider and context window), not a specific provider. Models come from the model registry, which serves what the enabled provider modules register. A generated project enables **OpenRouter**: one API key gives access to every model it routes. Other cloud providers are marketplace modules.

For local or air-gapped deployments, models are served via **Ollama**. The `ai.ollama` module registers a Qwen2.5 3B chat model (`qwen2.5:3b`) and the `nomic-embed-text` embedding model, the pair `make ollama-models` pulls in the ABI repository. The `ai.qwen`, `ai.deepseek` and `ai.gemma` modules register more models, served by the same local Ollama:

| Model | Module | Command | Use case |
|---|---|---|---|
| Qwen3 8B | `ai.qwen` | `ollama pull qwen3:8b` | Multilingual, code-capable |
| DeepSeek R1 8B | `ai.deepseek` | `ollama pull deepseek-r1:8b` | Advanced reasoning |
| Gemma3 4B | `ai.gemma` | `ollama pull gemma3:4b` | Lightweight and fast |

---

## How agents are exposed

Every agent of a loaded engine module is available on three interfaces without additional configuration:

| Interface | Endpoint |
|---|---|
| REST API | `POST /agents/{agent_name}/completion` and `POST /agents/{agent_name}/stream-completion` |
| MCP tool | The MCP server (`naas_abi_core.apps.mcp`; `make mcp` in the ABI repository) reads the API's OpenAPI document at startup and registers one tool per agent, which calls `/completion` |
| CLI | `abi chat <module> <agent class>`, for example `abi chat naas_abi AbiAgent` |

The REST endpoints take the `ABI_API_KEY` as a bearer token (or `?token=`); the MCP server sends the same key. Agents of [remote modules](/architecture/remote-modules#agents) are not on these routes: Nexus lists them next to the engine's and calls them over NATS.

---

## Further reading

- [The stack](/architecture/the-stack): the packages and how they fit together
- [Services and adapters](/architecture/services-and-adapters): the engine services agents call, and their backends
- [Creating an Agent](/user-guide/build/agents/creating-an-agent): step-by-step guide
- [Agent.New() Pattern](/user-guide/release-notes/agent-class-new-pattern): ADR
- [Intent Scope Routing](/user-guide/release-notes/intent-scope-agent-routing): ADR

---

→ [Overview](/architecture/overview)
