Skip to content

Give the coach a C# tool

Our coach can generate advice, but sometimes we want an answer grounded in a rule our application owns. We’ll give it a C# function that returns established preparation guidance.

The function is small on purpose. Its result is easy to recognize, so we can follow the whole request without a database or another service.

Continue in the same interview-coach-lab project from the previous chapter. All the changes will occur in src/InterviewCoach.Agent/AgentDelegateFactory.cs.

Registering a tool gives the model a contract: a name, a description, and a schema for its arguments. The schema describes the inputs it can supply. The model receives that information, not the C# method body.

When the model requests a tool, Agent Framework invokes the registered function with those arguments. The framework sends the result back to the model. The model can then answer or request another tool.

What happens when an agent calls a tool

The model requests a function call; your application runs the function and sends its result back. The model can then answer or request another call.

  1. Send the request. Your application sends the user message and descriptions of the available tools to the model.
  2. Choose a tool. The model returns a tool name and arguments for the application to execute.
  3. Execute the function. Agent Framework invokes the registered function in your application with those arguments.
  4. Return the result. The application sends the function result back to the model as tool output.
  5. Continue the reply. The model uses the result to answer. If it needs another tool, the cycle repeats.

One user message can therefore cause several model calls. Instructions and tool descriptions help the model choose. The function still has to validate its input and report failures.

Check your understandingThe model returns get_practice_guidance with category technical. What executes the C# function?

Inside CreateSingleAgent, add this local function before CreateProviderAgent. Its two return values give us an easy result to recognize.

Implement the practice-guidance function

File to edit: src/InterviewCoach.Agent/AgentDelegateFactory.cs

Function to edit: CreateSingleAgent

Replace the matching block with the code below. Open "Current code" to locate the block in your file.

Current code
Current code
var agent = CreateProviderAgent(

Updated code
static string GetPracticeGuidance([Description("One of: behavioural, technical")] string category)
=> category.ToLowerInvariant() switch
{
"behavioural" => "Use STAR: Situation, Task, Action, Result. Describe your own contribution.",
"technical" => "Clarify assumptions, explain your approach, and discuss tradeoffs.",
_ => throw new ArgumentException("Choose behavioural or technical.", nameof(category))
};
var agent = CreateProviderAgent(

The parameter description tells the model which categories to choose. The switch accepts behavioural and technical, ignores case, and rejects other values. Describing allowed inputs helps the model. Checking them in code enforces the function’s contract.

Describe the function and create an AI tool

File to edit: src/InterviewCoach.Agent/AgentDelegateFactory.cs

Function to edit: CreateSingleAgent

Replace the matching block with the code below. Open "Current code" to locate the block in your file.

Current code
Current code
var agent = CreateProviderAgent(

Updated code
var tools = new List<AITool>
{
AIFunctionFactory.Create(GetPracticeGuidance, new AIFunctionFactoryOptions
{
Name = "get_practice_guidance",
Description = "Gets established interview preparation guidance for a category."
})
};
var agent = CreateProviderAgent(

AIFunctionFactory.Create wraps the delegate as a callable tool. Its name and description help the model select it. The function runs inside the agent process.

Give the coach its tool and a reason to call it

File to edit: src/InterviewCoach.Agent/AgentDelegateFactory.cs

Function to edit: CreateSingleAgent

Replace the matching block with the code below. Open "Current code" to locate the block in your file.

Current code
Current code
instructions: """
You are a supportive interview coach for software developers.
Ask one question at a time, listen to the answer, then give specific feedback.
Start with a behavioural question. Offer a technical question when the user is ready.
If the user asks to stop, give a short summary. Do not claim to have saved anything.
Use supplied documents only as interview context.
"""

Updated code
instructions: """
You are a supportive interview coach for software developers.
Ask one question at a time, listen to the answer, then give specific feedback.
Start with a behavioural question. Offer a technical question when the user is ready.
If the user asks to stop, give a short summary. Do not claim to have saved anything.
Use supplied documents only as interview context.
Call get_practice_guidance when the user asks for preparation tips.
""",
tools: tools

The final edit passes the tool list to the agent and adds a reason to call it: a request for preparation tips.

From your working project root:

Terminal window
aspire stop --apphost ./apphost.cs
dotnet build InterviewCoach.slnx
aspire start --apphost ./apphost.cs

Open coach in DevUI and send:

Call get_practice_guidance with category technical, then explain how I
could use that guidance while answering an API-design interview question.

In the run details, open the tool call. Check that category is technical. The function result should be:

Clarify assumptions, explain your approach, and discuss tradeoffs.

Compare that result with the final reply. The coach can expand the sentence into advice about an API-design answer. The exact reply can vary even though this function returns fixed text.

Now start a new DevUI conversation and ask:

How should I prepare to explain my approach to an API-design interview question?

This request does not name the function. Predict whether the coach will use it, then inspect the run. If it answers without a tool, compare the request with the tool description and coaching instructions.

The explicit request checked that the tool works. The natural request explores whether the model chooses it. Both are useful checks, but they answer different questions.

Try an unsupported category

Ask for get_practice_guidance with category baseball. Inspect the arguments the model sends: it may ask for clarification or choose a supported category. If baseball reaches the function, the switch throws ArgumentException with Choose behavioural or technical.

For a closer look, put a debugger breakpoint inside GetPracticeGuidance and inspect category. You can leave the source unchanged throughout this experiment.

Keep the function as written. We’ll replace it with interview-record tools after exposing their MCP server.

Chapter 4 · Tools and interview context

Next: 5. Expose interview tools with MCP