Skip to content

.NET: Add dynamic tool expansion sample#5425

Merged
westey-m merged 4 commits intomicrosoft:mainfrom
westey-m:dynamic-tool-loading-sample
Apr 23, 2026
Merged

.NET: Add dynamic tool expansion sample#5425
westey-m merged 4 commits intomicrosoft:mainfrom
westey-m:dynamic-tool-loading-sample

Conversation

@westey-m
Copy link
Copy Markdown
Contributor

Motivation and Context

#5326

Description

  • Add a sample that demonstrates how to dynamically expand the available tools via another tool call.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Copilot AI review requested due to automatic review settings April 22, 2026 11:12
@moonbox3 moonbox3 added documentation Improvements or additions to documentation .NET labels Apr 22, 2026
@github-actions github-actions Bot changed the title Add dynamic tool expansion sample .NET: Add dynamic tool expansion sample Apr 22, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new .NET sample demonstrating “dynamic tool expansion” during a function-calling loop by mutating ChatOptions.Tools from the ambient FunctionInvokingChatClient.CurrentContext.

Changes:

  • Add Agent_Step20_DynamicFunctionTools sample (code + README) showing runtime tool registration.
  • Add the sample to the Agents samples index and to the main .NET solution.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
dotnet/samples/02-agents/Agents/README.md Adds the new sample to the “Getting started with agents” list.
dotnet/samples/02-agents/Agents/Agent_Step20_DynamicFunctionTools/README.md Documents the dynamic tool expansion scenario and how to run the sample.
dotnet/samples/02-agents/Agents/Agent_Step20_DynamicFunctionTools/Program.cs Implements the dynamic tool expansion via CurrentContext.Options.Tools plus logging middleware.
dotnet/samples/02-agents/Agents/Agent_Step20_DynamicFunctionTools/Agent_Step20_DynamicFunctionTools.csproj Adds a new sample project referencing Microsoft.Agents.AI.OpenAI.
dotnet/agent-framework-dotnet.slnx Includes the new sample project in the solution.

Comment thread dotnet/samples/02-agents/Agents/README.md
Comment thread dotnet/samples/02-agents/Agents/Agent_Step20_DynamicFunctionTools/Program.cs Outdated
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 4 | Confidence: 92%

✗ Correctness

This PR adds a new sample (Step20) demonstrating dynamic function tool expansion during an agent's function-calling loop. The API usage is correct: BuildAIAgent returns ChatClientAgent which extends AIAgent, and FunctionInvokingChatClient.CurrentContext is a valid ambient context property used elsewhere in the codebase. The logic for dynamic tool registration, duplicate avoidance, and catalog matching is sound. One inconsistency: the new .csproj uses <TargetFramework> (singular) while all 19 existing sample .csproj files consistently use <TargetFrameworks> (plural). While both compile for a single TFM, this breaks the established convention.

✓ Security Reliability

This PR adds a new sample demonstrating dynamic function tool expansion during agent function-calling loops. The code follows established patterns from other samples in the repository: environment variable credential handling, OpenAIClient plain-string constructor, ChatClientBuilder middleware, and no explicit resource disposal (consistent with other console samples). The API usages (BuildAIAgent, FunctionInvokingChatClient.CurrentContext, AsBuilder, .Use middleware) are all verified against the codebase. No security or reliability issues were found — the tool catalog is finite and read-only, LM-sourced input is only used for keyword matching against a hardcoded dictionary, and null cases are properly handled.

✗ Test Coverage

This PR adds a new sample (Agent_Step20_DynamicFunctionTools) demonstrating dynamic tool expansion via FunctionInvokingChatClient.CurrentContext. The sample code itself is well-structured and the core framework types it exercises are well-tested. However, the sample is not registered in the verify-samples tool (dotnet/eng/verify-samples/AgentsSamples.cs), which is the established convention for all other Agent_Step samples. Without a SampleDefinition entry, the sample won't be built/run/verified by the dotnet-verify-samples.yml CI workflow, leaving it with no automated test coverage.

✗ Design Approach

I found one design-level problem. The new Step20 sample is implemented as an OpenAI-specific sample, but it is being added to the samples/02-agents/Agents walkthrough, whose shared README and prerequisites are explicitly Azure OpenAI-based. That mismatch means the sample is solving the right technical scenario in the wrong sample track, so discovery, setup, and execution guidance become misleading for users following the existing Agents path.

Flagged Issues

  • The new sample is not registered in dotnet/eng/verify-samples/AgentsSamples.cs. Every other Agent_Step sample (Step01–Step19) has a SampleDefinition entry there, which is how the repo's CI (dotnet-verify-samples.yml) validates samples. Without this entry the sample has zero automated verification. A definition should be added after the Step19 entry (around line 343) with appropriate Name, ProjectPath, RequiredEnvironmentVariables, MustContain, and ExpectedOutputDescription fields.
  • Program.cs:13-14 uses OPENAI_API_KEY/OpenAIClient, while the parent Agents/README.md:16-23 defines this sample track around Azure OpenAI setup (AZURE_OPENAI_ENDPOINT, Azure CLI auth). The sample should either use AzureOpenAIClient to stay consistent with the Agents/ walkthrough, or be moved into an OpenAI-specific sample area such as AgentWithOpenAI or AgentProviders.

Suggestions

  • The .csproj uses <TargetFramework>net10.0</TargetFramework> (singular), while all 19 other sample .csproj files use <TargetFrameworks>net10.0</TargetFrameworks> (plural). Both compile, but changing to plural would maintain consistency across the sample set.

Automated review by westey-m's agents

@westey-m westey-m added this pull request to the merge queue Apr 22, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 22, 2026
@westey-m westey-m added this pull request to the merge queue Apr 22, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 22, 2026
@westey-m westey-m enabled auto-merge April 23, 2026 08:43
@westey-m westey-m added this pull request to the merge queue Apr 23, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 23, 2026
@westey-m westey-m added this pull request to the merge queue Apr 23, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 23, 2026
@westey-m westey-m added this pull request to the merge queue Apr 23, 2026
Merged via the queue into microsoft:main with commit 6851a9c Apr 23, 2026
23 checks passed
@gjactat
Copy link
Copy Markdown

gjactat commented Apr 24, 2026

Hello,
I've noticed that the dynamically loaded tools are not persisted across "LLM round trips".

Let me explain : In your example, you send the following prompts sequentially :

string[] prompts =
[
    "What's the weather like in Seattle and London?",
    "What time is it in New York?",
    "Can you convert those temperatures to Celsius?"
];

And this works fine.

Now, if I change the prompts to make two distincts calls to the same "GetWeather" methods :

string[] prompts =
[
    "What's the weather like in Seattle ?",
    "What's the weather like in London ?",
    "What time is it in New York?",
    "Can you convert those temperatures to Celsius?"
];

Then, the second prompt (i.e : second call to the previously loaded tool "GetWeather") fails with the following error :
image

Maybe this is the expected behaviour when using FunctionInvokingChatClient.CurrentContext (who might be short lived for the current LLM answer only). But if this is the case, I wonder how to properly add a new tool so that it could be reliably use for the rest of the chat session.

Might be related to #5325

Thank you for your great work !
Guillaume

@westey-m
Copy link
Copy Markdown
Contributor Author

Hi @gjactat, thanks for mentioning this. What you are seeing is definitely unexpected. It may be due to an issue with the model where it assumes that it can call a function if it called it previously, even though the set of advertised functions changed.

I was running the sample with gpt-5.4-mini but haven't reproduced what you are seeing. What model were you using?

Modifying the input as you did, I get the following:

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation .NET

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants