mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Adding default providers and tools to HarnessAgent (#5896)
* Adding default providers and tools to HarnessAgent * Address PR comments * Add further comments to clarify certain setings. * Apply suggestion from @SergeyMenshykh Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --------- Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to use a HarnessAgent with the Harness AIContextProviders
|
||||
// (TodoProvider and AgentModeProvider) for interactive research tasks with web search
|
||||
// capabilities powered by Azure AI Foundry.
|
||||
// This sample demonstrates how to use a HarnessAgent for interactive research tasks.
|
||||
// The HarnessAgent comes pre-configured with TodoProvider, AgentModeProvider, FileMemoryProvider,
|
||||
// ToolApproval, WebSearch, and OpenTelemetry — so this sample only needs custom instructions
|
||||
// and a WebBrowsingTool.
|
||||
// The agent plans research tasks, creates a todo list, gets user approval,
|
||||
// and then executes each step — all within an interactive conversation loop.
|
||||
//
|
||||
@@ -34,86 +35,32 @@ const int MaxOutputTokens = 128_000;
|
||||
// and research-focused instructions including the mandatory planning workflow.
|
||||
var instructions =
|
||||
"""
|
||||
## Research Assistant Instructions
|
||||
|
||||
You are a research assistant. When given a research topic, research it thoroughly using web search and web browsing.
|
||||
Use your knowledge to form good search queries and hypotheses, but always verify claims with the tools available to you rather than relying on memory alone.
|
||||
|
||||
## Mandatory planning workflow
|
||||
|
||||
For every new substantive user request, including short factual questions, your behavior is determined by the mode you are in.
|
||||
If you are in plan mode, start with the *Plan Mode* steps, and if you are in execute mode, skip directly to the *Execute Mode* steps below.
|
||||
|
||||
*Plan Mode*
|
||||
|
||||
1. Analyze the request with the purpose of building a research plan.
|
||||
2. Create a list of todo items.
|
||||
3. If needed, use the provided tools to do some exploratory checks to help build a plan and determine what clarifying questions you may need from the user.
|
||||
4. Ask for clarifications from the user where needed.
|
||||
1. Ask each clarification one by one.
|
||||
2. When asking for clarification and you have specific options in mind, present them to the user, so they can choose the option instead of having to retype the entire response.
|
||||
3. Do not proceed until you have received all the needed clarifications.
|
||||
4. Do short exploratory research if it helps with being able to ask sensible clarifications from the user.
|
||||
5. Write the plan to a memory file, so that it is retained even if compaction happens. Make sure to update the plan file if the user requests changes.
|
||||
6. Present the plan to the user and ask for approval to switch to execute mode and process the plan.
|
||||
7. When approval is granted, always switch to execute mode (using the `AgentMode_Set` tool), and follow the steps for *Execute mode*.
|
||||
|
||||
*Execute Mode*
|
||||
|
||||
1. If you don't have a plan or tasks yet, analyse the user request and create tasks and a plan. (**Skip this step if you came from plan mode**)
|
||||
2. Work autonomously — use your best judgement to make decisions and keep progressing without asking the user questions. The goal is to have a complete, useful result ready when the user returns.
|
||||
3. If you encounter ambiguity or an unexpected situation during execution, choose the most reasonable option, note your choice, and keep going.
|
||||
4. Mark tasks as completed as you finish them.
|
||||
5. Continue working, thinking and calling tools until you have the research result for the user.
|
||||
|
||||
## General Instructions
|
||||
|
||||
- You must check the current mode after any user input, since the user may have changed the mode themselves,
|
||||
e.g. the user may have switched to 'plan' mode after a previous research task finished in 'execute' mode, meaning they want to review a plan first before execution.
|
||||
- Explain your reasoning and thought process as you work through tasks.
|
||||
- Explain what you learned and what you are going to do next between tool calls, so the user can follow along with your thought process.
|
||||
- Avoid making more than 4 tool calls in a row without explaining what you are doing.
|
||||
- Do not answer the underlying question before the plan has been presented and approved.
|
||||
- This rule applies even when the answer seems obvious or the task seems small.
|
||||
- For short requests, use a brief micro-plan rather than skipping planning. The only exceptions are:
|
||||
- greetings,
|
||||
- pure acknowledgments,
|
||||
- clarification questions needed to form the plan,
|
||||
- follow-up questions about results you have already presented,
|
||||
- meta-discussion about the workflow itself.
|
||||
|
||||
**Todo management**
|
||||
|
||||
Mark each todo complete as you finish it so the list stays current.
|
||||
If a todo turns out to be unnecessary or is blocked, remove it and briefly explain why.
|
||||
Once the user finishes with a topic and moves onto a new one, clean up old completed todos by deleting them.
|
||||
|
||||
**Research quality**
|
||||
### Research quality
|
||||
|
||||
Consult multiple sources when possible and cross-reference key claims.
|
||||
When sources disagree, note the discrepancy and explain which source you consider more reliable and why.
|
||||
If a web page fails to load or a search returns irrelevant results, try alternative search queries or sources before moving on.
|
||||
Track your sources — you will need them when presenting results.
|
||||
|
||||
**Presenting results**
|
||||
### Presenting results
|
||||
|
||||
When presenting your final findings:
|
||||
- Use Markdown formatting for clarity.
|
||||
- Use clear sections with headings for each major topic or sub-question.
|
||||
- Cite your sources inline (e.g., "According to [source name](URL), ...").
|
||||
- End with a brief summary of key takeaways.
|
||||
- Save the final research report to file memory so it survives compaction and can be referenced later.
|
||||
|
||||
**File memory**
|
||||
|
||||
Use the FileMemory_* tools to:
|
||||
- Store downloaded search results or web pages.
|
||||
- Store plans.
|
||||
- Read the current plan to make sure tasks were done according to plan.
|
||||
- Store findings.
|
||||
- Check for relevant previously downloaded data / findings before starting new research.
|
||||
- In addition to returning the results to the user, save the final research report to file memory so it survives compaction and can be referenced later.
|
||||
""";
|
||||
|
||||
// Create the agent using AsHarnessAgent, which pre-configures function invocation,
|
||||
// per-service-call chat history persistence, and in-loop compaction.
|
||||
// Then wrap with UseToolApproval to allow auto-approving tools once confirmed.
|
||||
// per-service-call chat history persistence, in-loop compaction, TodoProvider, AgentModeProvider,
|
||||
// FileMemoryProvider, ToolApproval, WebSearch, AgentSkillsProvider, and OpenTelemetry.
|
||||
// Only custom instructions, a WebBrowsingTool, and FileAccess opt-out are needed.
|
||||
AIAgent agent =
|
||||
// Create an OpenAIClient that communicates with the Foundry responses service.
|
||||
new OpenAIClient(
|
||||
@@ -127,35 +74,26 @@ AIAgent agent =
|
||||
RetryPolicy = new ClientRetryPolicy(3) // Enable retries to improve resiliency.
|
||||
})
|
||||
.GetResponsesClient()
|
||||
.AsIChatClientWithStoredOutputDisabled(deploymentName) // We want to manage chat history locally (not stored in the responses service), so that we can manage compaction ourselves.
|
||||
.AsIChatClientWithStoredOutputDisabled(deploymentName) // We want to manage chat history locally (not stored in the responses service), so that we can manage compaction ourselves.
|
||||
.AsHarnessAgent(MaxContextWindowTokens, MaxOutputTokens, new HarnessAgentOptions
|
||||
{
|
||||
Name = "ResearchAgent",
|
||||
Description = "A research assistant that plans and executes research tasks.",
|
||||
AIContextProviders =
|
||||
[
|
||||
new TodoProvider(), // Add an AIContextProvider to allow the agent to create a TODO list, which is stored in the session.
|
||||
new AgentModeProvider(), // Add an AIContextProvider that tracks the agent mode and allows switching mode. Current mode is stored in the session.
|
||||
new FileMemoryProvider( // Add an AIContextProvider that can store memories in files under a session specific working folder.
|
||||
new FileSystemAgentFileStore(Path.Combine(AppContext.BaseDirectory, "agent-files")),
|
||||
(_) => new FileMemoryState() { WorkingFolder = DateTime.UtcNow.ToString("yyyyMMdd_HHmmss") + "_" + Guid.NewGuid().ToString() })
|
||||
],
|
||||
DisableFileMemory = true, // If enabled, this would allow the agent to store memories as files in a directory associated with the current session
|
||||
FileMemoryStore = new FileSystemAgentFileStore( // Configure the file memory provider to store files in a local folder called "agent-files".
|
||||
Path.Combine(AppContext.BaseDirectory, "agent-files")),
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = instructions,
|
||||
Tools =
|
||||
[
|
||||
ResponseTool.CreateWebSearchTool().AsAITool(), // Add the foundry hosted web search tool that runs in the service.
|
||||
new WebBrowsingTool( // Add a local web browsing tool that converts html to markdown.
|
||||
new WebBrowsingTool( // Add a local web browsing tool that converts html to markdown.
|
||||
new WebBrowsingToolOptions { AllowPublicNetworks = true }),
|
||||
],
|
||||
MaxOutputTokens = MaxOutputTokens, // Set a high token limit for long research tasks with many tool calls and long outputs.
|
||||
MaxOutputTokens = MaxOutputTokens, // Set a high token limit for long research tasks with many tool calls and long outputs.
|
||||
Reasoning = new() { Effort = ReasoningEffort.Medium },
|
||||
},
|
||||
})
|
||||
.AsBuilder()
|
||||
.UseToolApproval() // Add the ability to auto approve tools once a user has said they don't want to be asked again. Approval rules are tied to the session.
|
||||
.Build();
|
||||
});
|
||||
|
||||
// Run the interactive console session using the shared HarnessConsole helper.
|
||||
await HarnessConsole.RunAgentAsync(
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
// This sample demonstrates how to use the SubAgentsProvider to delegate work to sub-agents.
|
||||
// A parent agent is given a list of stock tickers and instructed to find the closing price
|
||||
// for each ticker on December 31, 2025. It delegates the web searches to a sub-agent
|
||||
// equipped with Foundry's hosted web search tool.
|
||||
// for each ticker on December 31, 2025. It delegates the web searches to a sub-agent.
|
||||
// The HarnessAgent provides built-in WebSearch (HostedWebSearchTool) so no manual web search
|
||||
// tool configuration is needed on the sub-agent.
|
||||
//
|
||||
// Special commands:
|
||||
// /exit — End the session.
|
||||
@@ -26,7 +27,8 @@ const int MaxContextWindowTokens = 1_050_000;
|
||||
const int MaxOutputTokens = 128_000;
|
||||
|
||||
// --- Sub-agent: Web Search Agent ---
|
||||
// This agent can search the web and is used by the parent agent to look up stock prices.
|
||||
// This agent uses the HarnessAgent's built-in HostedWebSearchTool to search the web.
|
||||
// Features not needed by this sub-agent are disabled.
|
||||
AIAgent webSearchAgent =
|
||||
new OpenAIClient(
|
||||
new BearerTokenPolicy(new DefaultAzureCredential(), "https://ai.azure.com/.default"),
|
||||
@@ -41,13 +43,14 @@ AIAgent webSearchAgent =
|
||||
{
|
||||
Name = "WebSearchAgent",
|
||||
Description = "An agent that can search the web to find information.",
|
||||
DisableTodoProvider = true,
|
||||
DisableAgentModeProvider = true,
|
||||
DisableFileMemory = true, // If enabled, this would allow the agent to store memories as files in a directory associated with the current session
|
||||
DisableFileAccess = true, // If enabled, this would allow the agent to read/write files in a working directory
|
||||
DisableToolApproval = true, // If enabled, this allows don't-ask-again approval functionality.
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = "You are a web search assistant. When asked to find information, use the web search tool to look it up and return a concise, factual answer.",
|
||||
Tools =
|
||||
[
|
||||
ResponseTool.CreateWebSearchTool().AsAITool(),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -75,6 +78,9 @@ var parentInstructions =
|
||||
- Present results in a clean markdown table format.
|
||||
""";
|
||||
|
||||
// --- Parent agent: Stock Price Researcher ---
|
||||
// This agent orchestrates the sub-agent to look up stock prices in parallel.
|
||||
// Most features are disabled since the parent only needs SubAgentsProvider.
|
||||
AIAgent parentAgent =
|
||||
new OpenAIClient(
|
||||
new BearerTokenPolicy(new DefaultAzureCredential(), "https://ai.azure.com/.default"),
|
||||
@@ -89,6 +95,12 @@ AIAgent parentAgent =
|
||||
{
|
||||
Name = "StockPriceResearcher",
|
||||
Description = "An agent that researches stock prices using sub-agents.",
|
||||
DisableTodoProvider = true,
|
||||
DisableAgentModeProvider = true,
|
||||
DisableFileMemory = true, // If enabled, this would allow the agent to store memories as files in a directory associated with the current session
|
||||
DisableFileAccess = true, // If enabled, this would allow the agent to read/write files in a working directory
|
||||
DisableToolApproval = true, // If enabled, this allows don't-ask-again approval functionality.
|
||||
DisableWebSearch = true,
|
||||
AIContextProviders =
|
||||
[
|
||||
new SubAgentsProvider([webSearchAgent]),
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="data\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="working\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to use a HarnessAgent with the FileAccessProvider
|
||||
// This sample demonstrates how to use a HarnessAgent with the default FileAccessProvider
|
||||
// to give an agent access to a folder of CSV data files. The agent can read, analyze,
|
||||
// and extract information from the data, then write results back as new files.
|
||||
//
|
||||
// The sample includes a pre-populated `data/` folder with sales transaction data.
|
||||
// The sample includes a pre-populated `working/` folder with sales transaction data.
|
||||
// The HarnessAgent's default FileAccessProvider uses `{cwd}/working` as its working directory,
|
||||
// which matches this sample's folder layout.
|
||||
// Ask the agent to analyze the data, produce summaries, or create new output files.
|
||||
//
|
||||
// Special commands:
|
||||
@@ -27,10 +29,6 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYME
|
||||
const int MaxContextWindowTokens = 1_050_000;
|
||||
const int MaxOutputTokens = 128_000;
|
||||
|
||||
// Point the file store at the data/ folder that ships with the sample.
|
||||
var dataFolder = Path.Combine(AppContext.BaseDirectory, "data");
|
||||
var fileStore = new FileSystemAgentFileStore(dataFolder);
|
||||
|
||||
var instructions =
|
||||
"""
|
||||
You are a data analyst assistant. You have access to a folder of data files via the FileAccess_* tools.
|
||||
@@ -56,7 +54,9 @@ var instructions =
|
||||
- Always explain what you learned and what you are going to do next between tool calls, so the user can follow along with your thought process.
|
||||
""";
|
||||
|
||||
// Create the chat client from the OpenAI provider.
|
||||
// Create the agent using AsHarnessAgent. The FileAccessStore is explicitly set to the
|
||||
// sample's working/ folder (copied to the output directory) so it works regardless of cwd.
|
||||
// Unused features are disabled.
|
||||
AIAgent agent =
|
||||
new OpenAIClient(
|
||||
new BearerTokenPolicy(new DefaultAzureCredential(), "https://ai.azure.com/.default"),
|
||||
@@ -71,10 +71,11 @@ AIAgent agent =
|
||||
{
|
||||
Name = "DataAnalyst",
|
||||
Description = "A data analyst assistant that reads, analyzes, and processes data files.",
|
||||
AIContextProviders =
|
||||
[
|
||||
new FileAccessProvider(fileStore),
|
||||
],
|
||||
FileAccessStore = new FileSystemAgentFileStore(Path.Combine(AppContext.BaseDirectory, "working")),
|
||||
DisableTodoProvider = true,
|
||||
DisableAgentModeProvider = true,
|
||||
DisableFileMemory = true, // If enabled, this would allow the agent to store memories as files in a directory associated with the current session
|
||||
DisableWebSearch = true,
|
||||
ChatOptions = new ChatOptions
|
||||
{
|
||||
Instructions = instructions,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# What this sample demonstrates
|
||||
|
||||
This sample demonstrates how to use a `HarnessAgent` with the `FileAccessProvider` to give an agent access to a folder of data files for reading, analyzing, and writing results. The `HarnessAgent` pre-configures function invocation, per-service-call chat history persistence, and in-loop compaction — so the sample only needs to supply the chat client, token limits, and application-specific options.
|
||||
This sample demonstrates how to use a `HarnessAgent` with the default `FileAccessProvider` to give an agent access to a folder of data files for reading, analyzing, and writing results. The `HarnessAgent` pre-configures function invocation, per-service-call chat history persistence, in-loop compaction, tool approval, and OpenTelemetry — so the sample only needs to supply the chat client, token limits, custom instructions, and opt out of unused features.
|
||||
|
||||
Key features showcased:
|
||||
|
||||
- **HarnessAgent** — a pre-configured agent that wraps a `ChatClientAgent` with function invocation, per-service-call persistence, and context-window compaction
|
||||
- **FileAccessProvider** — gives the agent tools to read, write, list, search, and delete files in a shared data folder
|
||||
- **FileAccessProvider** — the HarnessAgent's default file access provider uses `{cwd}/working` as its working directory, matching this sample's `working/` folder
|
||||
- **CSV data processing** — the agent reads sales transaction data and performs analysis on demand
|
||||
- **Output file creation** — the agent can write summaries, filtered data, or reports back to the data folder
|
||||
- **Streaming output** — responses are streamed token-by-token for a natural experience
|
||||
@@ -39,7 +39,7 @@ dotnet run --project samples/02-agents/Harness/Harness_Step03_DataProcessing
|
||||
|
||||
## What to Expect
|
||||
|
||||
The sample starts an interactive conversation with a data analyst agent. The `data/` folder contains a `sales.csv` file with ~50 rows of sales transaction data (date, product, category, quantity, unit price, region, salesperson).
|
||||
The sample starts an interactive conversation with a data analyst agent. The `working/` folder contains a `sales.csv` file with ~50 rows of sales transaction data (date, product, category, quantity, unit price, region, salesperson).
|
||||
|
||||
You can ask the agent to:
|
||||
|
||||
@@ -53,7 +53,7 @@ E.g. try the following prompt `Please process the sales.csv file by first filter
|
||||
|
||||
## Sample Data
|
||||
|
||||
The included `data/sales.csv` contains sales transactions from January to March 2025 with the following columns:
|
||||
The included `working/sales.csv` contains sales transactions from January to March 2025 with the following columns:
|
||||
|
||||
| Column | Description |
|
||||
| --- | --- |
|
||||
|
||||
Reference in New Issue
Block a user