Questa pagina non è ancora tradotta in italiano: qui la trovi in inglese.
1. Turn service methods into actions
Tutorial step 1: expose the methods of a case service as actions the agent can call, with descriptions the model understands.
In questo tutorial:
- Keep the cases in a store that every action shares
- Write two reads and one confirmed write, with descriptions the model can match
- Predict the tool name the model sees for each method
- Watch the agent chain two actions to answer one request
On this page
In this step you give the Cases app sample data and three actions: find a case, list cases and add a note. You also see what the model reads about each action, because the model uses it to choose an action.
Before you start: finish the Quickstart. You have the Cases app with CaseService, the model key in user secrets and the widget in the layout.
Add a case store
Section titled “Add a case store”The Quickstart's methods return fixed text. Give them something to read and change: a small store with three cases. Create CaseStore.cs in the project folder:
public record SupportCase(
string Number, string Title, string Status, string Priority,
IReadOnlyList<string> Notes);
public class CaseStore
{
private readonly Lock gate = new();
private readonly List<SupportCase> cases =
[
new("C-1", "Cannot sign in to the portal", "Open", "High", []),
new("C-2", "Invoice shows the wrong address", "Open", "Normal", []),
new("C-3", "Export to Excel times out", "Waiting for customer", "Normal",
["Asked the customer for the export log."]),
];
public SupportCase? Find(string number)
{
lock (gate) return cases.Find(c => Is(c, number));
}
public IReadOnlyList<SupportCase> List(string? status)
{
lock (gate)
return [.. cases.Where(c => string.IsNullOrEmpty(status)
|| c.Status.Equals(status, StringComparison.OrdinalIgnoreCase))];
}
public void AddNote(string number, string text)
=> Update(number, c => c with { Notes = [.. c.Notes, text] });
public void Close(string number, string resolution)
=> Update(number, c => c with
{
Status = "Closed",
Notes = [.. c.Notes, $"Closed: {resolution}"],
});
private void Update(string number, Func<SupportCase, SupportCase> change)
{
lock (gate)
{
var i = cases.FindIndex(c => Is(c, number));
if (i < 0)
throw new KeyNotFoundException($"There is no case {number}.");
cases[i] = change(cases[i]);
}
}
private static bool Is(SupportCase c, string number)
=> c.Number.Equals(number, StringComparison.OrdinalIgnoreCase);
}The store keeps the cases in memory, so they go back to their starting state each time the app restarts. In your own app, the actions call your database or the services you already have.
Rewrite the service
Section titled “Rewrite the service”Replace the content of CaseService.cs:
using System.ComponentModel;
using Novior.MentorAgent.Attributes;
public class CaseService(CaseStore store)
{
[MentorAction(
Description = "Gets a support case: its status, priority and notes",
ReadOnly = true)]
public SupportCase? GetCase(
[Description("The case number, for example C-1")] string caseNumber)
=> store.Find(caseNumber);
[MentorAction(
Description = "Lists support cases, optionally by status",
ReadOnly = true)]
public IReadOnlyList<SupportCase> ListCases(
[Description("Open, Waiting for customer or Closed; empty for all")]
string? status = null)
=> store.List(status);
[MentorAction(
Description = "Adds a note to a support case",
RequiresConfirmation = true)]
public string AddNote(string caseNumber, string text)
{
store.AddNote(caseNumber, text);
return $"Note added to case {caseNumber}.";
}
}GetCaseandListCasesonly read, so they use[MentorAction]withReadOnly = true. The agent calls them without asking.AddNotechanges data, so it asks for confirmation, as in the Quickstart. HereRequiresConfirmation = truewrites the rule in the code.[Description]on a parameter tells the model what value to pass, for example the format of a case number.- The methods return data, not sentences. MentorAgent sends the returned object to the model as JSON, and the model writes the answer.
Register the store
Section titled “Register the store”In Program.cs, replace the AddScoped<CaseService>() line with:
builder.Services.AddSingleton<CaseStore>(); // one store for the whole app
builder.Services.AddScoped<CaseService>();AddSingleton keeps one store for the whole app, so a note added in one conversation is still there in the next.
Then, inside AddMentorAgent, after options.AppName, describe the app:
options.AppDescription = "Tracks support cases: their status, priority and notes";The description becomes part of the instructions the model receives. A complete sentence helps it tell which requests belong to your app.
See what the model reads
Section titled “See what the model reads”The model never sees your C# methods. It sees tools, each with a name, a description and its parameters. MentorAgent derives the name from the method: snake case, with a trailing Async dropped.
| Method | Tool name | Description the model reads |
|---|---|---|
GetCase | get_case | Gets a support case: its status, priority and notes |
ListCases | list_cases | Lists support cases, optionally by status |
AddNote | add_note | Adds a note to a support case |
- In the Quickstart,
GetCaseStatuswas the toolget_case_status. Renaming a method renames its tool, and a setting that uses the old tool name stops matching. For example,RequiresApprovalasks for confirmation by tool name: see Tools you cannot annotate. - Write each description for the model, not for other developers: what the action does and what it returns, in the words your users use. The model picks a tool by comparing the request with these descriptions.
Try it
Section titled “Try it”Run the app with dotnet run, open it and click the widget button.
- Type "Which cases are open?". The agent calls
list_casesand names C-1 and C-2. C-3 is waiting for the customer, so it is not open. - Type "What do we know about C-3?". The agent calls
get_caseand tells you the case is waiting for the customer, with its note. - Type "Add a note to the open case with high priority: customer called back". The agent finds the case with
list_cases, then callsadd_note. The confirmation box shows the description and the arguments, such as "Adds a note to a support case — Case Number: C-1 · Text: customer called back". - Click Confirm. Then type "Show me C-1". The note is there.
In a new conversation, the request in step 3 uses two actions: the agent chains tools on its own when a request needs more than one. In the conversation above, the agent can skip list_cases, because the conversation keeps the results of earlier tool calls and step 1 already listed the cases. If the agent answers without calling a tool, see The agent does not call your method.
Next step
Section titled “Next step”Anyone who reaches the widget can now add a note. In 2. Require roles and confirmation you decide who may change a case.
To understand where actions fit, read Level 1: actions in The three agent levels, and Roles, confirmation and governance.
Cosa hai imparato
Sezione “Cosa hai imparato”- Keep the cases in a store that every action shares
- Write two reads and one confirmed write, with descriptions the model can match
- Predict the tool name the model sees for each method
- Watch the agent chain two actions to answer one request
Vedi anche
Sezione “Vedi anche”- Attributes — The parameters of MentorAction, MentorAgent, MentorTeam, TeamMember, MentorPage and MentorSkill, with their defaults and rules.
- Add MentorAgent to an existing app — Add an AI agent to a .NET Blazor Web App you already have: install the package, register the services, mark the methods it may call and place the chat widget.
