From 28f3d178e1aff10e49fd52e8743d976b2be47e07 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Mon, 13 Apr 2026 16:20:18 +0100
Subject: [PATCH] Address text rag sample working
---
dotnet/agent-framework-dotnet.slnx | 3 +
.../Hosted-ChatClientAgent/Program.cs | 13 +-
.../Hosted-FoundryAgent/Program.cs | 13 +-
.../HostedAgentsV2/Hosted-TextRag/.env.local | 5 +
.../HostedAgentsV2/Hosted-TextRag/Dockerfile | 17 +++
.../Hosted-TextRag/Dockerfile.contributor | 19 +++
.../Hosted-TextRag/HostedTextRag.csproj | 32 +++++
.../HostedAgentsV2/Hosted-TextRag/Program.cs | 130 ++++++++++++++++++
.../Properties/launchSettings.json | 11 ++
.../HostedAgentsV2/Hosted-TextRag/README.md | 116 ++++++++++++++++
.../Hosted-TextRag/agent.manifest.yaml | 30 ++++
.../HostedAgentsV2/Hosted-TextRag/agent.yaml | 9 ++
.../AgentThreadAndHITL.csproj | 24 ++++
.../AgentThreadAndHITL/Program.cs | 115 ++++++++++++++++
.../AgentWithLocalTools.csproj | 24 ++++
.../AgentWithLocalTools/Program.cs | 115 ++++++++++++++++
.../AgentWithTextSearchRag.csproj | 24 ++++
.../AgentWithTextSearchRag/Program.cs | 115 ++++++++++++++++
.../AgentsInWorkflows.csproj | 24 ++++
.../AgentsInWorkflows/Program.cs | 115 ++++++++++++++++
20 files changed, 946 insertions(+), 8 deletions(-)
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/.env.local
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Dockerfile
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Dockerfile.contributor
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/HostedTextRag.csproj
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Program.cs
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Properties/launchSettings.json
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/README.md
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/agent.manifest.yaml
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/agent.yaml
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentThreadAndHITL/AgentThreadAndHITL.csproj
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentThreadAndHITL/Program.cs
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithLocalTools/AgentWithLocalTools.csproj
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithLocalTools/Program.cs
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithTextSearchRag/Program.cs
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentsInWorkflows/AgentsInWorkflows.csproj
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentsInWorkflows/Program.cs
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 5ffb5692fd..8de8933bea 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -280,6 +280,9 @@
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-ChatClientAgent/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-ChatClientAgent/Program.cs
index 21cf34852a..e7dcf415f7 100644
--- a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-ChatClientAgent/Program.cs
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-ChatClientAgent/Program.cs
@@ -69,6 +69,12 @@ app.Run();
internal sealed class DevTemporaryTokenCredential : TokenCredential
{
private const string EnvironmentVariable = "AZURE_BEARER_TOKEN";
+ private readonly string? _token;
+
+ public DevTemporaryTokenCredential()
+ {
+ _token = Environment.GetEnvironmentVariable(EnvironmentVariable);
+ }
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
@@ -80,14 +86,13 @@ internal sealed class DevTemporaryTokenCredential : TokenCredential
return new ValueTask(GetAccessToken());
}
- private static AccessToken GetAccessToken()
+ private AccessToken GetAccessToken()
{
- var token = Environment.GetEnvironmentVariable(EnvironmentVariable);
- if (string.IsNullOrEmpty(token))
+ if (string.IsNullOrEmpty(_token))
{
throw new CredentialUnavailableException($"{EnvironmentVariable} environment variable is not set.");
}
- return new AccessToken(token, DateTimeOffset.UtcNow.AddHours(1));
+ return new AccessToken(_token, DateTimeOffset.UtcNow.AddHours(1));
}
}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-FoundryAgent/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-FoundryAgent/Program.cs
index a8f2f249c8..7f509084c0 100644
--- a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-FoundryAgent/Program.cs
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-FoundryAgent/Program.cs
@@ -62,6 +62,12 @@ app.Run();
internal sealed class DevTemporaryTokenCredential : TokenCredential
{
private const string EnvironmentVariable = "AZURE_BEARER_TOKEN";
+ private readonly string? _token;
+
+ public DevTemporaryTokenCredential()
+ {
+ _token = Environment.GetEnvironmentVariable(EnvironmentVariable);
+ }
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
@@ -73,14 +79,13 @@ internal sealed class DevTemporaryTokenCredential : TokenCredential
return new ValueTask(GetAccessToken());
}
- private static AccessToken GetAccessToken()
+ private AccessToken GetAccessToken()
{
- var token = Environment.GetEnvironmentVariable(EnvironmentVariable);
- if (string.IsNullOrEmpty(token))
+ if (string.IsNullOrEmpty(_token))
{
throw new CredentialUnavailableException($"{EnvironmentVariable} environment variable is not set.");
}
- return new AccessToken(token, DateTimeOffset.UtcNow.AddHours(1));
+ return new AccessToken(_token, DateTimeOffset.UtcNow.AddHours(1));
}
}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/.env.local b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/.env.local
new file mode 100644
index 0000000000..cbf693b3a9
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/.env.local
@@ -0,0 +1,5 @@
+AZURE_AI_PROJECT_ENDPOINT=
+ASPNETCORE_URLS=http://+:8088
+ASPNETCORE_ENVIRONMENT=Development
+AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o
+AZURE_BEARER_TOKEN=
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Dockerfile b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Dockerfile
new file mode 100644
index 0000000000..062d0f4f7e
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Dockerfile
@@ -0,0 +1,17 @@
+# Use the official .NET 10.0 ASP.NET runtime as a parent image
+FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
+WORKDIR /app
+
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
+WORKDIR /src
+COPY . .
+RUN dotnet restore
+RUN dotnet publish -c Release -o /app/publish
+
+# Final stage
+FROM base AS final
+WORKDIR /app
+COPY --from=build /app/publish .
+EXPOSE 8088
+ENV ASPNETCORE_URLS=http://+:8088
+ENTRYPOINT ["dotnet", "HostedTextRag.dll"]
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Dockerfile.contributor b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Dockerfile.contributor
new file mode 100644
index 0000000000..9a90c74335
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Dockerfile.contributor
@@ -0,0 +1,19 @@
+# Dockerfile for contributors building from the agent-framework repository source.
+#
+# This project uses ProjectReference to the local Microsoft.Agents.AI.Foundry source,
+# which means a standard multi-stage Docker build cannot resolve dependencies outside
+# this folder. Instead, pre-publish the app targeting the container runtime and copy
+# the output into the container:
+#
+# dotnet publish -c Debug -f net10.0 -r linux-musl-x64 --self-contained false -o out
+# docker build -f Dockerfile.contributor -t hosted-text-rag .
+# docker run --rm -p 8088:8088 -e AGENT_NAME=hosted-text-rag -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN --env-file .env hosted-text-rag
+#
+# For end-users consuming the NuGet package (not ProjectReference), use the standard
+# Dockerfile which performs a full dotnet restore + publish inside the container.
+FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
+WORKDIR /app
+COPY out/ .
+EXPOSE 8088
+ENV ASPNETCORE_URLS=http://+:8088
+ENTRYPOINT ["dotnet", "HostedTextRag.dll"]
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/HostedTextRag.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/HostedTextRag.csproj
new file mode 100644
index 0000000000..9a22108c7b
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/HostedTextRag.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ HostedTextRag
+ HostedTextRag
+ $(NoWarn);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Program.cs
new file mode 100644
index 0000000000..45d027f740
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Program.cs
@@ -0,0 +1,130 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to use TextSearchProvider to add retrieval augmented generation (RAG)
+// capabilities to a hosted 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.Projects;
+using Azure.Core;
+using Azure.Identity;
+using DotNetEnv;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry.Hosting;
+using Microsoft.Extensions.AI;
+using OpenAI.Chat;
+
+// Load .env file if present (for local development)
+Env.TraversePath().Load();
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")
+ ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o";
+
+// Use a chained credential: try a temporary dev token first (for local Docker debugging),
+// then fall back to DefaultAzureCredential (for local dev via dotnet run / managed identity in production).
+TokenCredential credential = new ChainedTokenCredential(
+ new DevTemporaryTokenCredential(),
+ new DefaultAzureCredential());
+
+TextSearchProviderOptions textSearchOptions = new()
+{
+ SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke,
+ RecentMessageMemoryLimit = 6,
+};
+
+AIAgent agent = new AIProjectClient(new Uri(endpoint), credential)
+ .AsAIAgent(new ChatClientAgentOptions
+ {
+ Name = Environment.GetEnvironmentVariable("AGENT_NAME") ?? "hosted-text-rag",
+ ChatOptions = new ChatOptions
+ {
+ ModelId = deploymentName,
+ Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
+ },
+ AIContextProviders = [new TextSearchProvider(MockSearchAsync, textSearchOptions)]
+ });
+
+// Host the agent as a Foundry Hosted Agent using the Responses API.
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddFoundryResponses(agent);
+
+var app = builder.Build();
+app.MapFoundryResponses();
+
+if (app.Environment.IsDevelopment())
+{
+ app.MapFoundryResponses("openai/v1");
+}
+
+app.Run();
+
+// ── Mock search function ─────────────────────────────────────────────────────
+// In production, replace this with a real search provider (e.g., Azure AI Search).
+
+static Task> MockSearchAsync(string query, CancellationToken cancellationToken)
+{
+ List results = [];
+
+ if (query.Contains("return", StringComparison.OrdinalIgnoreCase) || query.Contains("refund", StringComparison.OrdinalIgnoreCase))
+ {
+ results.Add(new()
+ {
+ SourceName = "Contoso Outdoors Return Policy",
+ SourceLink = "https://contoso.com/policies/returns",
+ Text = "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()
+ {
+ SourceName = "Contoso Outdoors Shipping Guide",
+ SourceLink = "https://contoso.com/help/shipping",
+ Text = "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()
+ {
+ SourceName = "TrailRunner Tent Care Instructions",
+ SourceLink = "https://contoso.com/manuals/trailrunner-tent",
+ Text = "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);
+}
+
+///
+/// A for local Docker debugging only.
+/// Reads a pre-fetched bearer token from the AZURE_BEARER_TOKEN environment variable.
+/// This should NOT be used in production — tokens expire (~1 hour) and cannot be refreshed.
+///
+/// Generate a token on your host and pass it to the container:
+/// export AZURE_BEARER_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
+/// docker run -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN ...
+///
+internal sealed class DevTemporaryTokenCredential : TokenCredential
+{
+ private const string EnvironmentVariable = "AZURE_BEARER_TOKEN";
+
+ public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
+ => GetAccessToken();
+
+ public override ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
+ => new(GetAccessToken());
+
+ private static AccessToken GetAccessToken()
+ {
+ var token = Environment.GetEnvironmentVariable(EnvironmentVariable);
+ if (string.IsNullOrEmpty(token))
+ {
+ throw new CredentialUnavailableException($"{EnvironmentVariable} environment variable is not set.");
+ }
+
+ return new AccessToken(token, DateTimeOffset.UtcNow.AddHours(1));
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Properties/launchSettings.json b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Properties/launchSettings.json
new file mode 100644
index 0000000000..932d4e67fc
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "HostedTextRag": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:8088"
+ }
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/README.md b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/README.md
new file mode 100644
index 0000000000..75c9dba797
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/README.md
@@ -0,0 +1,116 @@
+# Hosted-TextRag
+
+A hosted agent with **Retrieval Augmented Generation (RAG)** capabilities using `TextSearchProvider`. The agent grounds its answers in product documentation by running a search before each model invocation, then citing the source in its response.
+
+This sample demonstrates how to add knowledge grounding to a hosted agent without requiring an external search index — using a mock search function that can be replaced with Azure AI Search or any other provider.
+
+## Prerequisites
+
+- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0)
+- An Azure AI Foundry project with a deployed model (e.g., `gpt-4o`)
+- Azure CLI logged in (`az login`)
+
+## Configuration
+
+Copy the template and fill in your project endpoint:
+
+```bash
+cp .env.local .env
+```
+
+Edit `.env` and set your Azure AI Foundry project endpoint:
+
+```env
+AZURE_AI_PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects/
+ASPNETCORE_URLS=http://+:8088
+ASPNETCORE_ENVIRONMENT=Development
+AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o
+AZURE_BEARER_TOKEN=
+```
+
+> **Note:** `.env` is gitignored. The `.env.local` template is checked in as a reference.
+
+## Running directly (contributors)
+
+This project uses `ProjectReference` to build against the local Agent Framework source.
+
+```bash
+cd dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag
+AGENT_NAME=hosted-text-rag dotnet run
+```
+
+The agent will start on `http://localhost:8088`.
+
+### Test it
+
+Using the Azure Developer CLI:
+
+```bash
+azd ai agent invoke --local "What is your return policy?"
+azd ai agent invoke --local "How long does shipping take?"
+azd ai agent invoke --local "How do I clean my tent?"
+```
+
+Or with curl:
+
+```bash
+curl -X POST http://localhost:8088/responses \
+ -H "Content-Type: application/json" \
+ -d '{"input": "What is your return policy?", "model": "hosted-text-rag"}'
+```
+
+## Running with Docker
+
+Since this project uses `ProjectReference`, use `Dockerfile.contributor` which takes a pre-published output.
+
+### 1. Publish for the container runtime (Linux Alpine)
+
+```bash
+dotnet publish -c Debug -f net10.0 -r linux-musl-x64 --self-contained false -o out
+```
+
+### 2. Build the Docker image
+
+```bash
+docker build -f Dockerfile.contributor -t hosted-text-rag .
+```
+
+### 3. Run the container
+
+Generate a bearer token on your host and pass it to the container:
+
+```bash
+# Generate token (expires in ~1 hour)
+export AZURE_BEARER_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
+
+# Run with token
+docker run --rm -p 8088:8088 \
+ -e AGENT_NAME=hosted-text-rag \
+ -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN \
+ --env-file .env \
+ hosted-text-rag
+```
+
+### 4. Test it
+
+Using the Azure Developer CLI:
+
+```bash
+azd ai agent invoke --local "What is your return policy?"
+```
+
+## How RAG works in this sample
+
+The `TextSearchProvider` runs a mock search **before each model invocation**:
+
+| User query contains | Search result injected |
+|---|---|
+| "return" or "refund" | Contoso Outdoors Return Policy |
+| "shipping" | Contoso Outdoors Shipping Guide |
+| "tent" or "fabric" | TrailRunner Tent Care Instructions |
+
+The model receives the search results as additional context and cites the source in its response. In production, replace `MockSearchAsync` with a call to Azure AI Search or your preferred search provider.
+
+## NuGet package users
+
+If you are consuming the Agent Framework as a NuGet package (not building from source), use the standard `Dockerfile` instead of `Dockerfile.contributor`. See the commented section in `HostedTextRag.csproj` for the `PackageReference` alternative.
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/agent.manifest.yaml b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/agent.manifest.yaml
new file mode 100644
index 0000000000..1459925136
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/agent.manifest.yaml
@@ -0,0 +1,30 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/AgentManifest.yaml
+name: hosted-text-rag
+displayName: "Hosted Text RAG Agent"
+
+description: >
+ A support specialist agent for Contoso Outdoors with RAG capabilities.
+ Uses TextSearchProvider to ground answers in product documentation
+ before each model invocation.
+
+metadata:
+ tags:
+ - AI Agent Hosting
+ - Azure AI AgentServer
+ - Responses Protocol
+ - RAG
+ - Text Search
+ - Agent Framework
+
+template:
+ name: hosted-text-rag
+ kind: hosted
+ protocols:
+ - protocol: responses
+ version: 1.0.0
+ resources:
+ cpu: "0.25"
+ memory: 0.5Gi
+parameters:
+ properties: []
+resources: []
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/agent.yaml b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/agent.yaml
new file mode 100644
index 0000000000..c8d6928e2e
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-TextRag/agent.yaml
@@ -0,0 +1,9 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
+kind: hosted
+name: hosted-text-rag
+protocols:
+ - protocol: responses
+ version: 1.0.0
+resources:
+ cpu: "0.25"
+ memory: 0.5Gi
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentThreadAndHITL/AgentThreadAndHITL.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentThreadAndHITL/AgentThreadAndHITL.csproj
new file mode 100644
index 0000000000..69905ed43b
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentThreadAndHITL/AgentThreadAndHITL.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ false
+ AgentThreadAndHITLClient
+ agent-thread-hitl-client
+ $(NoWarn);NU1903;NU1605;OPENAI001
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentThreadAndHITL/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentThreadAndHITL/Program.cs
new file mode 100644
index 0000000000..14ddb29fe8
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentThreadAndHITL/Program.cs
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ClientModel.Primitives;
+using Azure.AI.Extensions.OpenAI;
+using Azure.AI.Projects;
+using Azure.Identity;
+using DotNetEnv;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry;
+
+// Load .env file if present (for local development)
+Env.TraversePath().Load();
+
+Uri agentEndpoint = new(Environment.GetEnvironmentVariable("AGENT_ENDPOINT")
+ ?? "http://localhost:8088");
+
+var agentName = Environment.GetEnvironmentVariable("AGENT_NAME")
+ ?? throw new InvalidOperationException("AGENT_NAME is not set.");
+
+// ── Create an agent-framework agent backed by the remote agent endpoint ──────
+
+var options = new AIProjectClientOptions();
+
+if (agentEndpoint.Scheme == "http")
+{
+ // For local HTTP dev: tell AIProjectClient the endpoint is HTTPS (to satisfy
+ // BearerTokenPolicy's TLS check), then swap the scheme back to HTTP right
+ // before the request hits the wire.
+
+ agentEndpoint = new UriBuilder(agentEndpoint) { Scheme = "https" }.Uri;
+ options.AddPolicy(new HttpSchemeRewritePolicy(), PipelinePosition.BeforeTransport);
+}
+
+var aiProjectClient = new AIProjectClient(agentEndpoint, new AzureCliCredential(), options);
+FoundryAgent agent = aiProjectClient.AsAIAgent(new AgentReference(agentName));
+
+AgentSession session = await agent.CreateSessionAsync();
+
+// ── REPL ──────────────────────────────────────────────────────────────────────
+
+Console.ForegroundColor = ConsoleColor.Cyan;
+Console.WriteLine($"""
+ ══════════════════════════════════════════════════════════
+ HITL Agent Client
+ Connected to: {agentEndpoint}
+ Type a message or 'quit' to exit
+ ══════════════════════════════════════════════════════════
+ """);
+Console.ResetColor();
+Console.WriteLine();
+
+while (true)
+{
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("You> ");
+ Console.ResetColor();
+
+ string? input = Console.ReadLine();
+
+ if (string.IsNullOrWhiteSpace(input)) { continue; }
+ if (input.Equals("quit", StringComparison.OrdinalIgnoreCase)) { break; }
+
+ try
+ {
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write("Agent> ");
+ Console.ResetColor();
+
+ await foreach (var update in agent.RunStreamingAsync(input, session))
+ {
+ Console.Write(update);
+ }
+
+ Console.WriteLine();
+ }
+ catch (Exception ex)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine($"Error: {ex.Message}");
+ Console.ResetColor();
+ }
+
+ Console.WriteLine();
+}
+
+Console.WriteLine("Goodbye!");
+
+///
+/// For Local Development Only
+/// Rewrites HTTPS URIs to HTTP right before transport, allowing AIProjectClient
+/// to target a local HTTP dev server while satisfying BearerTokenPolicy's TLS check.
+///
+internal sealed class HttpSchemeRewritePolicy : PipelinePolicy
+{
+ public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex)
+ {
+ RewriteScheme(message);
+ ProcessNext(message, pipeline, currentIndex);
+ }
+
+ public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList pipeline, int currentIndex)
+ {
+ RewriteScheme(message);
+ await ProcessNextAsync(message, pipeline, currentIndex).ConfigureAwait(false);
+ }
+
+ private static void RewriteScheme(PipelineMessage message)
+ {
+ var uri = message.Request.Uri!;
+ if (uri.Scheme == Uri.UriSchemeHttps)
+ {
+ message.Request.Uri = new UriBuilder(uri) { Scheme = "http" }.Uri;
+ }
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithLocalTools/AgentWithLocalTools.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithLocalTools/AgentWithLocalTools.csproj
new file mode 100644
index 0000000000..0742994449
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithLocalTools/AgentWithLocalTools.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ false
+ AgentWithLocalToolsClient
+ agent-with-local-tools-client
+ $(NoWarn);NU1903;NU1605;OPENAI001
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithLocalTools/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithLocalTools/Program.cs
new file mode 100644
index 0000000000..0caa06c36b
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithLocalTools/Program.cs
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ClientModel.Primitives;
+using Azure.AI.Extensions.OpenAI;
+using Azure.AI.Projects;
+using Azure.Identity;
+using DotNetEnv;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry;
+
+// Load .env file if present (for local development)
+Env.TraversePath().Load();
+
+Uri agentEndpoint = new(Environment.GetEnvironmentVariable("AGENT_ENDPOINT")
+ ?? "http://localhost:8088");
+
+var agentName = Environment.GetEnvironmentVariable("AGENT_NAME")
+ ?? throw new InvalidOperationException("AGENT_NAME is not set.");
+
+// ── Create an agent-framework agent backed by the remote agent endpoint ──────
+
+var options = new AIProjectClientOptions();
+
+if (agentEndpoint.Scheme == "http")
+{
+ // For local HTTP dev: tell AIProjectClient the endpoint is HTTPS (to satisfy
+ // BearerTokenPolicy's TLS check), then swap the scheme back to HTTP right
+ // before the request hits the wire.
+
+ agentEndpoint = new UriBuilder(agentEndpoint) { Scheme = "https" }.Uri;
+ options.AddPolicy(new HttpSchemeRewritePolicy(), PipelinePosition.BeforeTransport);
+}
+
+var aiProjectClient = new AIProjectClient(agentEndpoint, new AzureCliCredential(), options);
+FoundryAgent agent = aiProjectClient.AsAIAgent(new AgentReference(agentName));
+
+AgentSession session = await agent.CreateSessionAsync();
+
+// ── REPL ──────────────────────────────────────────────────────────────────────
+
+Console.ForegroundColor = ConsoleColor.Cyan;
+Console.WriteLine($"""
+ ══════════════════════════════════════════════════════════
+ Hotel Agent Client
+ Connected to: {agentEndpoint}
+ Type a message or 'quit' to exit
+ ══════════════════════════════════════════════════════════
+ """);
+Console.ResetColor();
+Console.WriteLine();
+
+while (true)
+{
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("You> ");
+ Console.ResetColor();
+
+ string? input = Console.ReadLine();
+
+ if (string.IsNullOrWhiteSpace(input)) { continue; }
+ if (input.Equals("quit", StringComparison.OrdinalIgnoreCase)) { break; }
+
+ try
+ {
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write("Agent> ");
+ Console.ResetColor();
+
+ await foreach (var update in agent.RunStreamingAsync(input, session))
+ {
+ Console.Write(update);
+ }
+
+ Console.WriteLine();
+ }
+ catch (Exception ex)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine($"Error: {ex.Message}");
+ Console.ResetColor();
+ }
+
+ Console.WriteLine();
+}
+
+Console.WriteLine("Goodbye!");
+
+///
+/// For Local Development Only
+/// Rewrites HTTPS URIs to HTTP right before transport, allowing AIProjectClient
+/// to target a local HTTP dev server while satisfying BearerTokenPolicy's TLS check.
+///
+internal sealed class HttpSchemeRewritePolicy : PipelinePolicy
+{
+ public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex)
+ {
+ RewriteScheme(message);
+ ProcessNext(message, pipeline, currentIndex);
+ }
+
+ public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList pipeline, int currentIndex)
+ {
+ RewriteScheme(message);
+ await ProcessNextAsync(message, pipeline, currentIndex).ConfigureAwait(false);
+ }
+
+ private static void RewriteScheme(PipelineMessage message)
+ {
+ var uri = message.Request.Uri!;
+ if (uri.Scheme == Uri.UriSchemeHttps)
+ {
+ message.Request.Uri = new UriBuilder(uri) { Scheme = "http" }.Uri;
+ }
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj
new file mode 100644
index 0000000000..028977d0aa
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ false
+ AgentWithTextSearchRagClient
+ agent-with-rag-client
+ $(NoWarn);NU1903;NU1605;OPENAI001
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithTextSearchRag/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithTextSearchRag/Program.cs
new file mode 100644
index 0000000000..efc6c1d982
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentWithTextSearchRag/Program.cs
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ClientModel.Primitives;
+using Azure.AI.Extensions.OpenAI;
+using Azure.AI.Projects;
+using Azure.Identity;
+using DotNetEnv;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry;
+
+// Load .env file if present (for local development)
+Env.TraversePath().Load();
+
+Uri agentEndpoint = new(Environment.GetEnvironmentVariable("AGENT_ENDPOINT")
+ ?? "http://localhost:8088");
+
+var agentName = Environment.GetEnvironmentVariable("AGENT_NAME")
+ ?? throw new InvalidOperationException("AGENT_NAME is not set.");
+
+// ── Create an agent-framework agent backed by the remote agent endpoint ──────
+
+var options = new AIProjectClientOptions();
+
+if (agentEndpoint.Scheme == "http")
+{
+ // For local HTTP dev: tell AIProjectClient the endpoint is HTTPS (to satisfy
+ // BearerTokenPolicy's TLS check), then swap the scheme back to HTTP right
+ // before the request hits the wire.
+
+ agentEndpoint = new UriBuilder(agentEndpoint) { Scheme = "https" }.Uri;
+ options.AddPolicy(new HttpSchemeRewritePolicy(), PipelinePosition.BeforeTransport);
+}
+
+var aiProjectClient = new AIProjectClient(agentEndpoint, new AzureCliCredential(), options);
+FoundryAgent agent = aiProjectClient.AsAIAgent(new AgentReference(agentName));
+
+AgentSession session = await agent.CreateSessionAsync();
+
+// ── REPL ──────────────────────────────────────────────────────────────────────
+
+Console.ForegroundColor = ConsoleColor.Cyan;
+Console.WriteLine($"""
+ ══════════════════════════════════════════════════════════
+ RAG Agent Client
+ Connected to: {agentEndpoint}
+ Type a message or 'quit' to exit
+ ══════════════════════════════════════════════════════════
+ """);
+Console.ResetColor();
+Console.WriteLine();
+
+while (true)
+{
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("You> ");
+ Console.ResetColor();
+
+ string? input = Console.ReadLine();
+
+ if (string.IsNullOrWhiteSpace(input)) { continue; }
+ if (input.Equals("quit", StringComparison.OrdinalIgnoreCase)) { break; }
+
+ try
+ {
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write("Agent> ");
+ Console.ResetColor();
+
+ await foreach (var update in agent.RunStreamingAsync(input, session))
+ {
+ Console.Write(update);
+ }
+
+ Console.WriteLine();
+ }
+ catch (Exception ex)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine($"Error: {ex.Message}");
+ Console.ResetColor();
+ }
+
+ Console.WriteLine();
+}
+
+Console.WriteLine("Goodbye!");
+
+///
+/// For Local Development Only
+/// Rewrites HTTPS URIs to HTTP right before transport, allowing AIProjectClient
+/// to target a local HTTP dev server while satisfying BearerTokenPolicy's TLS check.
+///
+internal sealed class HttpSchemeRewritePolicy : PipelinePolicy
+{
+ public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex)
+ {
+ RewriteScheme(message);
+ ProcessNext(message, pipeline, currentIndex);
+ }
+
+ public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList pipeline, int currentIndex)
+ {
+ RewriteScheme(message);
+ await ProcessNextAsync(message, pipeline, currentIndex).ConfigureAwait(false);
+ }
+
+ private static void RewriteScheme(PipelineMessage message)
+ {
+ var uri = message.Request.Uri!;
+ if (uri.Scheme == Uri.UriSchemeHttps)
+ {
+ message.Request.Uri = new UriBuilder(uri) { Scheme = "http" }.Uri;
+ }
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentsInWorkflows/AgentsInWorkflows.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentsInWorkflows/AgentsInWorkflows.csproj
new file mode 100644
index 0000000000..27e2da3908
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentsInWorkflows/AgentsInWorkflows.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ false
+ AgentsInWorkflowsClient
+ agents-in-workflows-client
+ $(NoWarn);NU1903;NU1605;OPENAI001
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentsInWorkflows/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentsInWorkflows/Program.cs
new file mode 100644
index 0000000000..33e4765fb9
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Using-Samples/AgentsInWorkflows/Program.cs
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ClientModel.Primitives;
+using Azure.AI.Extensions.OpenAI;
+using Azure.AI.Projects;
+using Azure.Identity;
+using DotNetEnv;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry;
+
+// Load .env file if present (for local development)
+Env.TraversePath().Load();
+
+Uri agentEndpoint = new(Environment.GetEnvironmentVariable("AGENT_ENDPOINT")
+ ?? "http://localhost:8088");
+
+var agentName = Environment.GetEnvironmentVariable("AGENT_NAME")
+ ?? throw new InvalidOperationException("AGENT_NAME is not set.");
+
+// ── Create an agent-framework agent backed by the remote agent endpoint ──────
+
+var options = new AIProjectClientOptions();
+
+if (agentEndpoint.Scheme == "http")
+{
+ // For local HTTP dev: tell AIProjectClient the endpoint is HTTPS (to satisfy
+ // BearerTokenPolicy's TLS check), then swap the scheme back to HTTP right
+ // before the request hits the wire.
+
+ agentEndpoint = new UriBuilder(agentEndpoint) { Scheme = "https" }.Uri;
+ options.AddPolicy(new HttpSchemeRewritePolicy(), PipelinePosition.BeforeTransport);
+}
+
+var aiProjectClient = new AIProjectClient(agentEndpoint, new AzureCliCredential(), options);
+FoundryAgent agent = aiProjectClient.AsAIAgent(new AgentReference(agentName));
+
+AgentSession session = await agent.CreateSessionAsync();
+
+// ── REPL ──────────────────────────────────────────────────────────────────────
+
+Console.ForegroundColor = ConsoleColor.Cyan;
+Console.WriteLine($"""
+ ══════════════════════════════════════════════════════════
+ Translation Workflow Agent Client
+ Connected to: {agentEndpoint}
+ Type a message or 'quit' to exit
+ ══════════════════════════════════════════════════════════
+ """);
+Console.ResetColor();
+Console.WriteLine();
+
+while (true)
+{
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("You> ");
+ Console.ResetColor();
+
+ string? input = Console.ReadLine();
+
+ if (string.IsNullOrWhiteSpace(input)) { continue; }
+ if (input.Equals("quit", StringComparison.OrdinalIgnoreCase)) { break; }
+
+ try
+ {
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write("Agent> ");
+ Console.ResetColor();
+
+ await foreach (var update in agent.RunStreamingAsync(input, session))
+ {
+ Console.Write(update);
+ }
+
+ Console.WriteLine();
+ }
+ catch (Exception ex)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine($"Error: {ex.Message}");
+ Console.ResetColor();
+ }
+
+ Console.WriteLine();
+}
+
+Console.WriteLine("Goodbye!");
+
+///
+/// For Local Development Only
+/// Rewrites HTTPS URIs to HTTP right before transport, allowing AIProjectClient
+/// to target a local HTTP dev server while satisfying BearerTokenPolicy's TLS check.
+///
+internal sealed class HttpSchemeRewritePolicy : PipelinePolicy
+{
+ public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex)
+ {
+ RewriteScheme(message);
+ ProcessNext(message, pipeline, currentIndex);
+ }
+
+ public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList pipeline, int currentIndex)
+ {
+ RewriteScheme(message);
+ await ProcessNextAsync(message, pipeline, currentIndex).ConfigureAwait(false);
+ }
+
+ private static void RewriteScheme(PipelineMessage message)
+ {
+ var uri = message.Request.Uri!;
+ if (uri.Scheme == Uri.UriSchemeHttps)
+ {
+ message.Request.Uri = new UriBuilder(uri) { Scheme = "http" }.Uri;
+ }
+ }
+}