Questa pagina non è ancora tradotta in italiano: qui la trovi in inglese.
The three agent levels
Actions, specialists and teams: how the three levels of MentorAgent map to Agent Framework, and when to use each one.
On this page
MentorAgent can handle a request at three levels: actions, specialists and teams. This page explains each level and helps you choose one.
Every request reaches the coordinator first: the main agent that answers the user, built on Microsoft Agent Framework (external site). The levels go from the simplest and cheapest to the slowest and most expensive.
A tool is a function that the model can call. The glossary defines the other terms on this page.
| 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 |
A handoff workflow is an Agent Framework workflow in which one agent passes a request to another agent. A group chat workflow is one in which several agents speak in turn on the same request.
The coordinator is a ChatClientAgent built from your ChatClient (Path A). Or you build an AIAgent yourself and pass it in Agent (Path B): see Choose a path. Specialists and teams need a ChatClient. With your own AIAgent, the coordinator gets your actions, but MentorAgent skips the specialists, the teams and the remote agents. A warning in the log names each of them when the coordinator is built.
Level 1: actions
Section titled “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 only [Description] is an action too, but it has no rules.
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 (lowercase words joined by underscores) and without a trailing Async: GetCaseStatusAsync becomes get_case_status. The coordinator calls the tool like any Agent Framework function tool. It reads the result in the same turn: while it answers the same message.
The coordinator's list of tools also holds the methods of skills and the tools of the Model Context Protocol (MCP) servers that you connect in McpServers.
MentorAgent finds the class in the assemblies listed in ScanAssemblies. It gets the instance from dependency injection, so also register the class in the container, for example with AddScoped. Roles, confirmation and governance explains the checks that run before each call.
Level 2: specialists
Section titled “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.
[MentorAgent(
Name = "ContractsAgent",
Description = "Handles support contracts: coverage, renewals 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 calls its own tools until it has an answer. 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. One kind is the specialists defined in YAML. The other kind is the remote agents in 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.
Specialists in YAML
Section titled “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:
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_statusYou 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.
toolsnames tools; it does not create them. Each name must match one of your Level 1 actions exactly, such asget_case_statusabove. The methods of a C# specialist, MCP tools, team tools and the built-in tools cannot be named. A name that matches no action gives no error when the file is loaded. The model is offered a tool with that name, but no code runs when it calls the tool, so the specialist has no data to answer with. Check every name.- 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
RequiredRolesand confirmation apply inside the YAML agent too. instructionsare 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. The>style is not supported.- The agent returns to the router when it is done. It cannot hand off to other agents.
- Definitions are read again for each new session, so an edited file reaches new sessions without a restart. Like the other specialists, YAML agents need a
ChatClient. Without one, they are skipped and MentorAgent logs a warning.
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.
Level 3: teams
Section titled “Level 3: teams”A class marked with [MentorTeam] is a group chat: several agents discuss a request before the coordinator answers.
[MentorTeam(
Name = "RefundReview",
Description = "Reviews a contract refund request 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. The property only carries the attribute: MentorAgent never reads or assigns its value, and ignores its type, so object? is enough. ContractReports, listed in the Tools of the analyst, is an ordinary class with a read-only [MentorAction] method that gets the history of a contract. 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. The coordinator does not offer its methods, and they are not published over MCP or on the A2A agent card, the JSON document that describes your agent to other agents.
- 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 with a non-empty[Description].
- End of the discussion. The discussion stops after
MaxIterationsturns,10by default. A turn here is one member speaking. Each time the coordinator calls the team, the count starts again. The discussion can also stop earlier:- with a
[TeamTerminationCondition]method, when the method returnstrue. The method decides alone; - without one, at the first member message that carries
[TEAM:APPROVED]or[TEAM:REJECTED].
- with a
The coordinator sees the team as one tool named after it: RefundReview becomes refund_review, and the discussion is the tool's result. Each message is a paragraph of its own, headed by its author. 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 only names the specialists that carry out what the team approves. This is how it works:
- MentorAgent tells the members to mark their final decision with
[TEAM:APPROVED], or with[TEAM:REJECTED]for a definitive no. A member who only asks for changes writes no marker, and the discussion goes on. MentorAgent removes both markers before the text reaches the user. - The team approves only when the final message carries
[TEAM:APPROVED]and no member wrote[TEAM:REJECTED]. A discussion that ends without a verdict in its final message approves nothing. When the team approves and hasHandoffTo, the team's result asks the coordinator to carry out the approved actions. - The coordinator's model then decides whether to call
route_to_specialistto 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
Section titled “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 a decision, 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
Section titled “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 go to the model with each call of the coordinator, so a long list makes every call larger.
EnableToolFilteringsends only the tools that match the message. When none matches, it still sends the three best-matching actions that need no confirmation and no role. These three actions add about 80 input tokens. See Tool filtering and confirmation.route_to_specialistand the team tools are always sent. A request meant for a specialist or a team can then still be handed over, even on a turn where an option such asRequireActionForChangeRequestsrequires the first call to be an action.
- Specialists. A request handed to a specialist 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
MaxIterationsturns, plus the members' tool calls. A team is the slowest and most expensive level: keepMaxIterationslow, end the discussion with a termination condition, and chooseTriggerOnwords 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 turn with a handoff appears in full on the cost dashboard. The options that reduce tokens and time, and how to measure your app, are in Cost and latency.
Vedi anche
Sezione “Vedi anche”- Attributes — The parameters of MentorAction, MentorAgent, MentorTeam, TeamMember, MentorPage and MentorSkill, with their defaults and rules.
- Ruoli, conferma e governance — Ruoli e approvazione human-in-the-loop per i tool di un agente AI in .NET: controlli nel codice, non nel prompt, su ogni canale, e cosa resta a te.
- 3. Hand off to a specialist agent — Tutorial step 3: add a specialist agent that owns case questions, and let the coordinator hand requests over to it.
- 4. Put agents in a team — Tutorial step 4: let two agents review a case in a group chat before anything changes, while roles and confirmation still apply.
