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.
In this tutorial, you'll:
- Define a team of two members with
[MentorTeam]and[TeamMember] - Give a member a read-only tool, and end the discussion when a verdict arrives
- Let an approved decision reach the specialist, which still asks for confirmation
- Tell when a team is worth its cost and when it is not
On this page
Some requests deserve a second opinion before anyone acts. Closing a case is one of them: someone should check the notes first. In this step a team of two agents reviews a case. An analyst reads it and a reviewer decides. When they approve, the specialist from step 3 closes the case.
Before you start: finish 3 · Hand off to a specialist agent.
Give the analyst a read-only tool
Section titled “Give the analyst a read-only tool”Create CaseReports.cs:
using Novior.MentorAgent.Attributes;
public class CaseReports(CaseStore store)
{
[MentorAction(
Description = "Gets a support case with all its notes, for a closure review",
ReadOnly = true)]
public SupportCase? GetCaseForReview(string caseNumber)
=> store.Find(caseNumber);
}Here, team members only read. Changes go through the specialist, where roles and confirmation apply. ReadOnly = true declares that the tool only reads, and MentorAgent then tells the Analyst that its tools are read-only. CaseReports is the toolbox of the Analyst only. The team lists it in the Tools of that member, as you see below, so the coordinator does not get get_case_for_review.
Define the team
Section titled “Define the team”Create ClosureReviewTeam.cs:
using Novior.MentorAgent.Attributes;
[MentorTeam(
Name = "ClosureReview",
Description = "Reviews whether a support case is ready to be closed.",
TriggerOn = ["closure review"],
MaxIterations = 4,
HandoffTo = ["CaseSpecialist"])]
public class ClosureReviewTeam
{
[TeamMember(
Role = "Analyst",
Instructions = "Read the case and its notes. Say whether the customer's problem is solved, quoting the notes.",
Tools = [typeof(CaseReports)])]
public object? Analyst { get; set; }
[TeamMember(
Role = "Reviewer",
Instructions = "Check the analyst's answer against the notes. Approve the closure only if a note shows the problem is solved.")]
public object? Reviewer { get; set; }
[TeamTerminationCondition]
public bool ShouldTerminate(string lastMessage, string lastSpeaker)
=> lastSpeaker == "Reviewer"
&& (lastMessage.Contains("[TEAM:APPROVED]") || lastMessage.Contains("[TEAM:REJECTED]"));
}[MentorTeam]turns the class into a group chat. The coordinator receives it as one tool named after the team:closure_review.Descriptiontells the coordinator what the team is for.TriggerOnadds a rule to the coordinator's instructions: always call the team when a request mentions a closure review. Keep these words specific: a broad word such as "case" would send ordinary requests through the team.- Each
[TeamMember]property is one member: an agent with aRoleand itsInstructions. The members speak in turn, in the order of the properties. The property is only a place for the attribute: MentorAgent ignores its type and its value, so leave it unset. - MentorAgent tells every member to mark a final verdict with
[TEAM:APPROVED]or[TEAM:REJECTED].ShouldTerminate, the method marked with[TeamTerminationCondition], ends the discussion as soon as the Reviewer gives one. The markers are removed before the text reaches the user. MaxIterations = 4ends the discussion after four member turns in any case. Without a termination method, the team always talks until this limit. Each time the coordinator calls the team, the count starts again.HandoffTonames the specialist that carries out an approved decision. When any message of the discussion carries[TEAM:APPROVED], the coordinator is told to execute the approved actions. It hands them to CaseSpecialist. Any member's message counts, not only the Reviewer's. That is one more reason to leave the change itself to the specialist, behind its role check and its confirmation.
Register the tools
Section titled “Register the tools”In Program.cs, register the class of the analyst's tools:
builder.Services.AddScoped<CaseReports>(); // the Analyst's toolsThe team class itself needs no registration. A tool class that is not registered is left out with a warning in the log, and the member works without it.
Try it
Section titled “Try it”Run the app and sign in as Sam: /dev/sign-in?name=Sam&role=Supervisors.
- Type "Run a closure review of case C-3". While the team works, the widget shows which member is speaking. The only note on C-3 says that the customer was asked for the export log. So the reviewer should reject the closure, and the coordinator tells you why.
- Type "Add a note to C-3: the customer confirmed the export works now", and confirm.
- Ask for the closure review again. This time the reviewer can approve. The coordinator passes the closure to CaseSpecialist, and the confirmation box appears before the case is closed. Click Confirm.
The verdict comes from the model, so its wording, and sometimes the decision, can change from one run to the next.
What a team costs
Section titled “What a team costs”Every member turn is at least one model call, plus one for each tool it calls. In this step:
- a review that the reviewer settles in its first turn takes five model calls: the coordinator calls the team, the analyst calls
get_case_for_reviewand answers, the reviewer decides, and the coordinator writes the reply; - an approved review that ends with the closure takes nine, because the handoff adds four: the coordinator calls
route_to_specialist, the router picks CaseSpecialist, and the specialist callsclose_caseand answers; - a discussion where nobody decides stops after four member turns, the value of
MaxIterations.
All of them run on your ChatClient and count in the token usage.
When a team is worth it
Section titled “When a team is worth it”- Use a team when a request needs more than one point of view before anyone acts: a review, an approval, an analysis that a single agent tends to rush.
- Do not use a team for lookups and single changes. An action, or a specialist, answers faster and costs less.
- Keep the tools of the members read-only, and leave changes to actions and specialists, where roles and confirmation apply.
Next steps
Section titled “Next steps”You have finished the tutorial. The Cases agent reads and changes cases, checks roles, asks for confirmation, hands work to a specialist and runs a review team.
- The three agent levels compares actions, specialists and teams, with their costs.
- Roles, confirmation and governance explains the checks on every channel.
- Channels shows how the same actions reach JavaScript clients, MCP clients and other agents.
- Set MentorAgent up for your host and model provider with the Configure pages.
- Before you go to production, replace the test sign-in with your real one and go through the Production checklist.
What you learned
Section titled “What you learned”- Define a team of two members with
[MentorTeam]and[TeamMember] - Give a member a read-only tool, and end the discussion when a verdict arrives
- Let an approved decision reach the specialist, which still asks for confirmation
- Tell when a team is worth its cost and when it is not
See also
Section titled “See also”- Attributes — The parameters of MentorAction, MentorAgent, MentorTeam, TeamMember, MentorPage and MentorSkill, with their defaults and rules.
- The three agent levels — Actions, specialists and teams: how the three levels of MentorAgent map to Agent Framework, and when to use each one.
- Production checklist — Settings to review before you go to production: governance defaults, safety check, rate limits, cost and routing, history, observability, and outgoing data.
