MentorAgent Docs
Menu

TutorialTime to complete: 15 minPackage: Novior.MentorAgent

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.

View as Markdown

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.

Create CaseReports.cs:

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.

Create ClosureReviewTeam.cs:

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.
  • Description tells the coordinator what the team is for. TriggerOn adds 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 a Role and its Instructions. 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 = 4 ends 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.
  • HandoffTo names 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.

In Program.cs, register the class of the analyst's tools:

Program.cs
builder.Services.AddScoped<CaseReports>();   // the Analyst's tools

The 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.

Run the app and sign in as Sam: /dev/sign-in?name=Sam&role=Supervisors.

  1. 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.
  2. Type "Add a note to C-3: the customer confirmed the export works now", and confirm.
  3. 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.

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_review and 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 calls close_case and 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.

  • 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.

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.

  • 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
  • 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.