Skip to main content

Agent architecture guide

One agent, one identity​

Your customers talk to one agent. It has a name, a way of speaking, and a job to do. Whether they ask about a refund or a delivery date, they expect the same agent to handle it. Parlant builds the agent to meet that expectation: one identity, a catalog of everything it might need, and a context assembled on every turn.

What you'll learn
  • Why Parlant builds one agent instead of a team or a graph
  • How Parlant discovers the right rules for each turn
  • How to give a growing catalog structure, in the catalog and in the code
  • What one agent does and doesn't mean for multi-agent setups
Talk this page through with your coding agent

This page explains how an agent is put together: why it is one agent and not a team, what goes into its catalog, how Parlant picks the right parts of the catalog on each turn, and how your project's code mirrors that structure.

One agent, not many​

Let's begin with the biggest structural decision: why a Parlant agent is one agent rather than a team.

Most agent frameworks scale capability by adding agents: a router in front, a billing agent, a shipping agent, and a handoff between them. Others scale it with a graph: classify the intent, route to a node, run that node's narrow prompt.

Both approaches work for linear tasks. Conversations aren't linear. Customers change topic mid-sentence, ask two things at once, and circle back. Every handoff or transition is a seam where context is lost and tone changes.

Parlant is not a multi-agent system, and that is deliberate. You build one agent with one identity, and you give it a catalog of everything it might need: policies for what to do, observations for what to recognize, tools, canned responses, terms, and variables.

The catalog can hold hundreds of items, but your agent doesn't carry them all on every turn. Instead of routing between agents or nodes, Parlant does automatic context discovery. On every turn, it works out which parts of the catalog the conversation has made relevant, puts those in front of the model, and leaves the rest out.

The result is that your agent stays one agent while its context changes with the conversation. There is no path to predefine, no seam to lose context at, and no second identity to keep consistent with the first.

What a catalog is made of​

Now that we know the agent draws from a catalog, let's look at what goes into one.

Everything in the catalog is attached to your agent, and each kind has a job:

KindJobWhere it's covered
InstructionsWho the agent is and what it's for. Always present.Install & run
PoliciesWhat to do in a situation, from a one-line steering rule to a full procedure.Observations and policies, Designing policies
ObservationsRecognize a situation without prescribing a reaction, so other things can hang off it.Observations and policies
Tools and retrieversAct on systems and fetch data, attached to the rules that say when they matter.Creating tools, Retrieving knowledge
Canned responsesThe agent's tone of voice and wording choices: approved example messages it imitates on the turns where they apply.Shaping tone and wording
TermsVocabulary defined once, read the way you mean it everywhere.Scaling complexity
VariablesFacts about the customer, loaded into the context every turn.Guests and customers

Policies and observations are the two kinds of rule, and rules are what discovery judges (more on that in a moment).

Not sure which kind a change needs?

Which construct for which job maps each kind of change to the construct that makes it.

Rules also organize your agent's access to tools and example responses. A tool becomes available when its attached rule applies. Canned responses have their own scope and relevance selection, which is explained on their page.

Note that the other kinds don't hang off a rule: your agent's instructions remain present, variables supply customer facts, and relevant terms clarify vocabulary.

How context is discovered​

Once your catalog is large, Parlant has to make two decisions on every turn: which rules belong in the conversation's context at all, and which of those apply to the current reply. Discovery handles the first with an embedding-based check. Turn matching handles the second.

Discovery​

The purpose of discovery is to make sure your agent is aware of every rule the conversation might have made relevant, without reading the whole catalog on each turn.

Parlant trains each rule into a small classifier from its signals (the phrases that should trigger it) and its anti-signals (the near-misses that shouldn't). Parlant proposes both at startup from the rule's content, and you add the phrases your real customers actually use.

At turn time, Parlant embeds the conversation and scores every rule not yet in play. A rule that clears its threshold is discovered and joins the session's working set: the rules the conversation has brought in so far.

The working set is what your agent reads each turn, marked as context that may or may not apply right now.

Discovery is sticky by design. A rule that came into play earlier stays in the working set, so it doesn't drop in and out as the topic wanders.

Note that the working set doesn't grow forever. When it passes thirty rules, a pass after the reply prunes the ones the conversation has moved on from, and a fresh trigger readmits any of them.

Turn matching​

Turn matching is the second, separate judgment: of the rules in play, which apply to this exact turn? That is a model verdict, not an embedding check.

Turn matching is what fires everything with consequences: offering a rule's tools, putting its canned responses in view, raising effort, running its handlers.

Learn more about the turn

Rule matching places both judgments in the turn.

Structure in the catalog​

Now that we have seen how Parlant picks rules out of the catalog, let's look at how to organize the catalog itself as it grows.

A flat catalog of a few dozen rules needs no structure. Discovery keeps it sharp. As the catalog grows, give it a shallow hierarchy: one topical observation per domain your agent handles, with each policy depending on its topic. A policy that depends on an observation is only considered on turns where that observation holds.

For example, suppose your agent handles account security. Whether the conversation is about account security is then judged once, in one deliberate place, the topical observation. Every policy under it only has to be precise within its topic.

Groups of customers work the same way. An observation judged by code decides membership, and the policies that only apply to that group depend on it.

This hierarchy is Parlant's replacement for routing. A multi-agent system draws the boundary between billing and security as a handoff between two agents. Parlant draws it as an observation that scopes a family of rules on one agent. Your customer sees the same identity on both sides of the line.

Learn more about hierarchy

Scaling complexity walks through building this structure.

Structure in the code​

Once your catalog has a shape, your project's code can mirror it.

A project scaffolded with parlant init mirrors the catalog. Each domain is a package that owns one capability, and its install(agent) function attaches that capability's rules, tools, responses, terms, and variables to your agent. When you ask your coding agent for a new capability, this is where it goes.

The example below installs an orders domain. It defines a term (a word your agent should read the way you mean it), a variable backed by a tool that refreshes on a cron schedule, and one policy with the tool it may call:

import parlant.sdk as p

from domains.orders import tools


async def install(agent: p.Agent) -> None:
await agent.create_term(
name="delivery estimate",
description="The day an order is currently expected to arrive. An estimate, not a guarantee.",
synonyms=["ETA", "expected delivery"], # phrasings that mean the same thing
)

await agent.create_variable(
name="open_orders",
description="The customer's orders that have not been delivered yet",
tool=tools.list_open_orders, # fetches the value for the current customer
freshness_rules="*/15 * * * *", # refresh at most every fifteen minutes
)

await agent.create_policy(
title="Order status",
content="When the customer asks where their order is, look it up with `get_order_status` and share the status and estimate.",
tools=[tools.get_order_status], # offered only on turns where this policy applies
)

main.py creates the one agent and installs each domain into it.

Tools read their backends through services.py, which config.py fills with real implementations in production and fakes in tests. This means a domain's rules and tools can be tested without a model or a backend.

Note that adding a capability means adding a domain, not an agent.

Where this leaves multi-agent​

To bring it all together, let's be precise about what "one agent" does and doesn't mean.

One agent per customer-facing identity doesn't mean one agent per server. A Parlant server can host several agents, each with its own catalog and models, for genuinely different products or brands.

What Parlant doesn't do is split one identity across several agents behind a router. If you find yourself wanting a "billing agent" inside your support agent, what you actually want is a topical observation and the policies under it.

info

The rest of Core concepts covers the pieces around the agent: sessions, the conversations it holds, and customers, the people it holds them with.