# The three agent levels

> Actions, specialists and teams: how the three levels of MentorAgent map to Agent Framework, and when to use each one.

- URL: https://docs.mentoragent.net/concepts/agent-levels
- Verified with: MentorAgent 1.0.0
- Last updated: 2026-10-13
- Packages: Novior.MentorAgent, Novior.MentorAgent.Declarative

Every request reaches the **coordinator** first: the main agent that answers the user, built on [Microsoft Agent Framework](https://learn.microsoft.com/agent-framework/). MentorAgent gives it three ways to get work done: actions, specialists and teams. They go from the simplest and cheapest to the slowest and most expensive, and they differ in what the coordinator calls.

| Level | You write | The coordinator sees | What Agent Framework runs |
|---|---|---|---|
| 1 · Action | A method with `[Description]` or `[MentorAction]` | One tool per method, named after it | A function tool (`AIFunction`) |
| 2 · Specialist | A class with `[MentorAgent]` | One `route_to_specialist` tool for all specialists | A `ChatClientAgent` per specialist, in a handoff workflow |
| 3 · Team | A class with `[MentorTeam]` and one `[TeamMember]` property per member | One tool per team, named after it | A `ChatClientAgent` per member, in a group chat workflow |

The coordinator is a `ChatClientAgent` built from your [`ChatClient`](https://docs.mentoragent.net/reference/options#chatclient), or the `AIAgent` you pass in [`Agent`](https://docs.mentoragent.net/reference/options#agent). Specialists and teams need a `ChatClient`: with your own `AIAgent`, the coordinator gets your actions but no specialists or teams. In Agent Framework, a *handoff workflow* lets one agent pass a request to another agent, and a *group chat workflow* lets several agents speak in turn on the same request.

## Level 1: actions

A public method of one of your services becomes a tool the coordinator can call. `[MentorAction]` sets roles, confirmation and navigation. A method that only reads declares `ReadOnly = true`. A method with `[Description]` alone is an action too, but it has no rules.

```csharp title="CaseService.cs"
public class CaseService(ICaseStore store)
{
    [MentorAction(
        Description = "Gets the status of a support case",
        ReadOnly = true)]
    public Task<string> GetCaseStatusAsync(string caseNumber)
        => store.GetStatusAsync(caseNumber);
}
```

The tool is named after the method, in snake case and without a trailing `Async`: `GetCaseStatusAsync` becomes `get_case_status`. The coordinator calls it like any Agent Framework function tool, and reads the result in the same [turn](https://docs.mentoragent.net/get-started/what-is-mentoragent#how-a-request-is-handled), that is, while it answers the same message. The same list also holds the tools of the Model Context Protocol (MCP) servers you connect, in [`McpServers`](https://docs.mentoragent.net/reference/options#mcpservers), and the methods of [skills](https://docs.mentoragent.net/reference/attributes#mentorskill).

MentorAgent finds the class in the assemblies listed in [`ScanAssemblies`](https://docs.mentoragent.net/reference/options#scanassemblies). It gets the instance from dependency injection, so also register the class in the container, for example with `AddScoped`. [Roles, confirmation and governance](https://docs.mentoragent.net/concepts/governance) explains the checks that run before each call.

## Level 2: specialists

A class marked with `[MentorAgent]` is a specialist: an agent with its own instructions and its own tools, which are the `[Description]` and `[MentorAction]` methods of the class.

```csharp title="ContractsAgent.cs"
[MentorAgent(
    Name = "ContractsAgent",
    Description = "Handles support contracts: coverage, renewal dates and refunds")]
public class ContractsAgent(IContractStore contracts)
{
    [MentorAction(
        Description = "Gets the support contract of a customer",
        ReadOnly = true)]
    public Task<string> GetContractAsync(string customerId)
        => contracts.GetAsync(customerId);

    [MentorAction(
        Description = "Issues a refund on a support contract",
        RequiresConfirmation = true,
        RequiredRoles = ["Supervisors"])]
    public Task IssueRefundAsync(string customerId, decimal amount)
        => contracts.RefundAsync(customerId, amount);
}
```

MentorAgent builds a `ChatClientAgent` from the class. Its prompt comes from `Name` and `Description`, unless you write your own in `Instructions`. All the specialists are part of one Agent Framework handoff workflow. The workflow starts with a small router agent: it has none of your tools, and only chooses the specialist.

The coordinator does not see the specialist's methods. It sees one tool, `route_to_specialist`, whose description lists every specialist with its description. When the coordinator calls it, the router hands the request to one specialist, which runs its own tool loop. What the specialist says comes back as the tool's result, and the coordinator writes the reply.

If no specialist takes the request, the result starts with `NOT DELEGATED`, and the coordinator answers with its own tools or says it cannot.

If the user declines an action inside a specialist, the result starts with `DECLINED BY THE USER`. The coordinator is then told to say in one sentence that it did not proceed, to give no reason and not to try again.

A specialist can pass the request on to the specialists named in its `HandoffTo`. Two other kinds of agent join the same workflow: the specialists defined in [YAML](https://docs.mentoragent.net/concepts/agent-levels#specialists-in-yaml), and the remote agents in [`RemoteAgents`](https://docs.mentoragent.net/reference/options#remoteagents), which run on other hosts and answer over the Agent-to-Agent (A2A) protocol.

The specialist's tools keep their checks. In the example, `issue_refund` still requires the `Supervisors` role and a confirmation, even though the coordinator never calls it directly: see [Specialists and teams are gated too](https://docs.mentoragent.net/concepts/governance#specialists-and-teams-are-gated-too).

## Level 3: teams

A class marked with `[MentorTeam]` is a group chat: several agents discuss a request before the coordinator answers.

```csharp title="RefundReviewTeam.cs"
[MentorTeam(
    Name = "RefundReview",
    Description = "Reviews a refund request on a support contract before anything is paid",
    MaxIterations = 4,
    HandoffTo = ["ContractsAgent"])]
public class RefundReviewTeam
{
    [TeamMember(
        Role = "Analyst",
        Instructions = "Check the contract history and propose a decision with reasons.",
        Tools = [typeof(ContractReports)])]
    public object? Analyst { get; set; }

    [TeamMember(
        Role = "Approver",
        Instructions = "Approve or reject the analyst's proposal, with reasons.")]
    public object? Approver { get; set; }

    [TeamTerminationCondition]
    public bool ShouldTerminate(string lastMessage, string lastSpeaker)
        => lastSpeaker == "Approver"
           && (lastMessage.Contains("[TEAM:APPROVED]", StringComparison.Ordinal)
               || lastMessage.Contains("[TEAM:REJECTED]", StringComparison.Ordinal));
}
```

Each `[TeamMember]` property becomes a `ChatClientAgent` with its role and its instructions. MentorAgent puts the members in an Agent Framework group chat workflow, where they speak in turn:

- **Tools.** A member can call the tools of the classes listed in its `Tools`. Register those classes in dependency injection. A listed class is the toolbox of that member only. Its methods are not actions of the coordinator, and they are not published over MCP or on the A2A agent card. A class marked with `[MentorSkill]` is the exception: it keeps its tools for the coordinator. The tools follow the rules of actions: public instance methods of the class itself, marked with `[MentorAction]` or a non-empty `[Description]`.
- **End of the discussion.** The discussion stops when the `[TeamTerminationCondition]` method returns `true`, or after `MaxIterations` turns, `10` by default. Each time the coordinator calls the team, the count starts again.

The coordinator sees the team as one tool named after it: `RefundReview` becomes `refund_review`, and the discussion is the tool's result. With `TriggerOn`, the coordinator's prompt tells it to always use the team for requests that mention those words. The model still decides whether to do it.

A team is not part of the handoff workflow. Its `HandoffTo` does not hand anything off by itself:

1. MentorAgent tells the members to mark their final decision with `[TEAM:APPROVED]` or `[TEAM:REJECTED]`. It removes both markers before the text reaches the user.
2. When the discussion contains `[TEAM:APPROVED]` and the team has `HandoffTo`, the team's result asks the coordinator to carry out the approved actions.
3. The coordinator's model then decides whether to call `route_to_specialist` to do it.

Keep the members' tools read-only and declare them with `ReadOnly = true`. Leave the writes to specialists: their tools keep their role and confirmation checks.

## Which level to use

- **Start with actions.** Most applications need nothing else: the coordinator reads the request, calls the actions it needs and answers. It is the fastest and cheapest level.
- **Add a specialist** when an area has its own set of actions and its own rules for using them, and the coordinator's list of tools grows long. The coordinator then carries one tool for all the specialists, and each specialist works with a short list and focused instructions.
- **Add a team** when a request needs a second opinion before anything happens: an analysis and an approval, a proposal and a review. Leave the writes to the specialists named in `HandoffTo`.
- **Define a specialist in YAML** when its instructions and its description change more often than its tools. The tools stay in C#.

## Cost and latency

Each level adds model calls to the requests that use it.

- **Actions.** No extra agent. The name, description and parameters of every tool travel with each call of the coordinator, so a long list makes every call larger. [`EnableToolFiltering`](https://docs.mentoragent.net/reference/options#enabletoolfiltering) sends only the actions relevant to the message. When none is relevant, it still sends the three best-ranked actions that need no confirmation and no role, which adds about 80 input tokens. `route_to_specialist` and the team tools are always sent, also on the turns where an opt-in option requires the first call to be an action: a request meant for a specialist or a team can still be handed over.
- **Specialists.** A delegated request adds at least one call to the router and one to the specialist, more if the specialist uses its tools, before the coordinator writes the reply. The specialist's tools are sent only to the specialist, so they do not weigh on the requests the coordinator handles alone.
- **Teams.** Each member's turn is at least one model call, up to `MaxIterations` turns, plus the members' tool calls. A team is the slowest and most expensive level: keep `MaxIterations` low, end the discussion with a termination condition, and choose `TriggerOn` words that ordinary requests do not use.

The router, the specialists and the team members run on your `ChatClient` and are counted by MentorAgent's token meter, so a delegated turn appears in full on the cost dashboard.

## Specialists in YAML

The optional `Novior.MentorAgent.Declarative` package lets you declare a Level 2 specialist in a file instead of a class. The file sets the name, the description the router uses to choose the agent, the instructions, the model options and the tools the agent may use:

```yaml title="Agents/case-status.agent.yaml"
kind: Prompt
name: CaseStatusAgent
description: Answers questions about the status of support cases
instructions: |
  You answer questions about support cases only. Use the tools to look up real cases.
  State only facts a tool returned; if nothing provides what is asked, say so.
model:
  options:
    temperature: 0.2
tools:
  - kind: function
    name: get_case_status
```

You register the folder with `AddMentorAgentDeclarative`, and the agent joins the same handoff workflow as the C# specialists: `route_to_specialist` reaches both in the same way. The loader's options are in [`MentorDeclarativeOptions`](https://docs.mentoragent.net/reference/options#mentordeclarativeoptions).

- **`tools` names tools; it does not create them.** Each name must match one of your Level 1 actions exactly, such as `get_case_status` above. The methods of a C# specialist, MCP tools, team tools and the built-in tools cannot be named. A name that matches nothing raises no error: the model sees a tool with no code behind it.
- **Every entry needs `kind: function`.** Entries of the other kinds (`webSearch`, `codeInterpreter`, `fileSearch`, `mcp`) are removed with a warning: those capabilities belong to the host's configuration.
- **The checks hold.** The named tools arrive already gated, so `RequiredRoles` and confirmation apply inside the YAML agent too.
- **`instructions` are used exactly as written.** MentorAgent adds nothing, not even the grounding rule, which tells the model to state only facts a tool returned. Write your own rule, as in the example. Use `|` for text on several lines: folded scalars (`>`) are not supported.
- **The agent returns to the router when it is done.** It cannot hand off to other agents.
- **Definitions are read for each new session**, so an edited file reaches new sessions without a restart. Like the other specialists, YAML agents need a `ChatClient`, and are skipped with a warning without one.

A definition file writes the instructions and names the tools, so treat it as code. Load it only from a folder that ships with the application, never from one users can write to, and review it like a `.cs` file. A good rule is behavior in C#, phrasing in YAML. To load definitions from a database or another store, implement `IMentorAgentSource` from the `Novior.MentorAgent` package: `AddMentorAgentDeclarative` is one implementation of it.

---

- Previous: [Troubleshooting](https://docs.mentoragent.net/get-started/troubleshooting.md)
- Next: [Roles, confirmation and governance](https://docs.mentoragent.net/concepts/governance.md)
