From 0b843d2b3e52918f6ecc67e56ef4ceb0f6457959 Mon Sep 17 00:00:00 2001
From: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
Date: Mon, 3 Nov 2025 18:25:16 +0000
Subject: [PATCH] .NET: Add simple rag sample for catalog (#1834)
* add simple rag sample for catalog
* Update dotnet/samples/Catalog/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
dotnet/agent-framework-dotnet.slnx | 1 +
.../AgentWithTextSearchRag.csproj | 21 +++++
.../Catalog/AgentWithTextSearchRag/Program.cs | 82 +++++++++++++++++++
.../Catalog/AgentWithTextSearchRag/README.md | 41 ++++++++++
4 files changed, 145 insertions(+)
create mode 100644 dotnet/samples/Catalog/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj
create mode 100644 dotnet/samples/Catalog/AgentWithTextSearchRag/Program.cs
create mode 100644 dotnet/samples/Catalog/AgentWithTextSearchRag/README.md
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index cbda6c2809..d342c06be2 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -137,6 +137,7 @@
+
diff --git a/dotnet/samples/Catalog/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj b/dotnet/samples/Catalog/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj
new file mode 100644
index 0000000000..c6bab8327e
--- /dev/null
+++ b/dotnet/samples/Catalog/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/Catalog/AgentWithTextSearchRag/Program.cs b/dotnet/samples/Catalog/AgentWithTextSearchRag/Program.cs
new file mode 100644
index 0000000000..931ce50014
--- /dev/null
+++ b/dotnet/samples/Catalog/AgentWithTextSearchRag/Program.cs
@@ -0,0 +1,82 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use TextSearchProvider to add retrieval augmented generation (RAG)
+// capabilities to an AI agent. The provider runs a search against an external knowledge base
+// before each model invocation and injects the results into the model context.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Data;
+using Microsoft.Extensions.AI;
+using OpenAI;
+
+var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
+var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+TextSearchProviderOptions textSearchOptions = new()
+{
+ // Run the search prior to every model invocation and keep a short rolling window of conversation context.
+ SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke,
+ RecentMessageMemoryLimit = 6,
+};
+
+AIAgent agent = new AzureOpenAIClient(
+ new Uri(endpoint),
+ new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(new ChatClientAgentOptions
+ {
+ Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
+ AIContextProviderFactory = _ => new TextSearchProvider(MockSearchAsync, textSearchOptions)
+ });
+
+AgentThread thread = agent.GetNewThread();
+
+Console.WriteLine(">> Asking about returns\n");
+Console.WriteLine(await agent.RunAsync("Hi! I need help understanding the return policy.", thread));
+
+Console.WriteLine("\n>> Asking about shipping\n");
+Console.WriteLine(await agent.RunAsync("How long does standard shipping usually take?", thread));
+
+Console.WriteLine("\n>> Asking about product care\n");
+Console.WriteLine(await agent.RunAsync("What is the best way to maintain the TrailRunner tent fabric?", thread));
+
+static Task> MockSearchAsync(string query, CancellationToken cancellationToken)
+{
+ // The mock search inspects the user's question and returns pre-defined snippets
+ // that resemble documents stored in an external knowledge source.
+ List results = new();
+
+ if (query.Contains("return", StringComparison.OrdinalIgnoreCase) || query.Contains("refund", StringComparison.OrdinalIgnoreCase))
+ {
+ results.Add(new()
+ {
+ Name = "Contoso Outdoors Return Policy",
+ Link = "https://contoso.com/policies/returns",
+ Value = "Customers may return any item within 30 days of delivery. Items should be unused and include original packaging. Refunds are issued to the original payment method within 5 business days of inspection."
+ });
+ }
+
+ if (query.Contains("shipping", StringComparison.OrdinalIgnoreCase))
+ {
+ results.Add(new()
+ {
+ Name = "Contoso Outdoors Shipping Guide",
+ Link = "https://contoso.com/help/shipping",
+ Value = "Standard shipping is free on orders over $50 and typically arrives in 3-5 business days within the continental United States. Expedited options are available at checkout."
+ });
+ }
+
+ if (query.Contains("tent", StringComparison.OrdinalIgnoreCase) || query.Contains("fabric", StringComparison.OrdinalIgnoreCase))
+ {
+ results.Add(new()
+ {
+ Name = "TrailRunner Tent Care Instructions",
+ Link = "https://contoso.com/manuals/trailrunner-tent",
+ Value = "Clean the tent fabric with lukewarm water and a non-detergent soap. Allow it to air dry completely before storage and avoid prolonged UV exposure to extend the lifespan of the waterproof coating."
+ });
+ }
+
+ return Task.FromResult>(results);
+}
diff --git a/dotnet/samples/Catalog/AgentWithTextSearchRag/README.md b/dotnet/samples/Catalog/AgentWithTextSearchRag/README.md
new file mode 100644
index 0000000000..614597bed9
--- /dev/null
+++ b/dotnet/samples/Catalog/AgentWithTextSearchRag/README.md
@@ -0,0 +1,41 @@
+# What this sample demonstrates
+
+This sample demonstrates how to use TextSearchProvider to add retrieval augmented generation (RAG) capabilities to an AI agent. The provider runs a search against an external knowledge base before each model invocation and injects the results into the model context.
+
+Key features:
+- Configuring TextSearchProvider with custom search behavior
+- Running searches before AI invocations to provide relevant context
+- Managing conversation memory with a rolling window approach
+- Citing source documents in AI responses
+
+## Prerequisites
+
+Before running this sample, ensure you have:
+
+1. An Azure OpenAI endpoint configured
+2. A deployment of a chat model (e.g., gpt-4o-mini)
+3. Azure CLI installed and authenticated
+
+## Environment Variables
+
+Set the following environment variables:
+
+```powershell
+# Replace with your Azure OpenAI endpoint
+$env:AZURE_OPENAI_ENDPOINT="https://your-openai-resource.openai.azure.com/"
+
+# Optional, defaults to gpt-4o-mini
+$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
+```
+
+## How It Works
+
+The sample uses a mock search function that demonstrates the RAG pattern:
+
+1. When the user asks a question, the TextSearchProvider intercepts it
+2. The search function looks for relevant documents based on the query
+3. Retrieved documents are injected into the model's context
+4. The AI responds using both its training and the provided context
+5. The agent can cite specific source documents in its answers
+
+The mock search function returns pre-defined snippets for demonstration purposes. In a production scenario, you would replace this with actual searches against your knowledge base (e.g., Azure AI Search, vector database, etc.).