Provides research patterns for Azure AI Foundry Agent Service SDK. Use when implementing agent features, looking up SDK methods, finding code samples, or troubleshooting Azure.AI.Projects API usage.
CRITICAL: Don't guess SDK usage. Follow this research workflow.
Multi-repo research blows up context (1000+ tokens per file). Delegate to subagent for:
runSubagent(
prompt: "RESEARCH task - do NOT write code.
**Question**: [specific SDK question]
**Search these sources in order**:
1. Azure.AI.Projects SDK: github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Projects
2. Azure.AI.Agents.Persistent samples: .../Azure.AI.Agents.Persistent/samples
3. Microsoft Foundry Samples: github.com/microsoft-foundry/foundry-samples
**Find**:
- Method signatures for [specific API]
- Usage examples (pseudocode only)
- Any gotchas or edge cases
**Return** (max 20 lines):
- Key method name and signature
- Code pattern (pseudocode)
- File path where found (for later reference)
Do NOT include full file contents.",
description: "SDK research: [topic]"
)
| Delegate to Subagent | Keep Inline |
|---|---|
| Multi-repo code search | Local codebase grep |
| Finding all usages | Known method lookup |
| API surface exploration | Single file read |
| Pattern comparison | Quick signature check |
| Sample discovery | Using known pattern |
The Foundry Agent Service SDK has two API surfaces for agents:
| API | Endpoint | ID Format | SDK Access |
|---|---|---|---|
| v2 Agents API | /agents/ |
Human-readable (e.g., dadjokes) |
AIProjectClient.AgentAdministrationClient |
| OpenAI Assistants API | /assistants/ |
OpenAI format (e.g., asst_xxx) |
PersistentAgentsClient |
This project uses v2 Agents API for human-readable agent IDs.
Azure.AI.Projects (Main Entry Point)
βββ AIProjectClient
β βββ .AgentAdministrationClient.GetAgentVersionAsync() β ProjectsAgentVersion (v2 Agents API)
β βββ .GetPersistentAgentsClient() β PersistentAgentsClient (Assistants API)
β βββ .ProjectOpenAIClient.GetProjectResponsesClientForAgent() β ProjectResponsesClient (Responses API)
βββ Companion packages:
βββ Azure.AI.Projects.Agents (ProjectsAgentVersion, DeclarativeAgentDefinition, β¦)
βββ Azure.AI.Extensions.OpenAI (ProjectConversationsClient, ProjectOpenAIClient, β¦)
βββ OpenAI.Responses (streaming types)
Azure.AI.Projects SDK: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Projects
tests/Samples/ folder with full examplesAzure.AI.Agents.Persistent SDK: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Agents.Persistent
Sample9_PersistentAgents_Streaming.md - Basic streaming patternSample8_PersistentAgents_FunctionsWithStreaming.md - Tool calls with streamingSample27_PersistentAgents_MCP_Streaming.md - MCP server integrationMicrosoft Foundry Samples: https://github.com/microsoft-foundry/foundry-samples
samples/csharp/quickstart/quickstart-chat-with-agent.cs - Responses API patternsamples/csharp/quickstart/ - Multiple quickstart examplesKey pattern from official quickstart:
AIProjectClient projectClient = new(new Uri(projectEndpoint), new AzureCliCredential());
ProjectConversation conversation = projectClient.ProjectOpenAIClient.GetProjectConversationsClient().CreateProjectConversation();
ProjectResponsesClient responsesClient = projectClient.ProjectOpenAIClient.GetProjectResponsesClientForAgent(
defaultAgent: agentName,
defaultConversationId: conversation.Id);
ResponseResult response = responsesClient.CreateResponse("Your prompt");
Baseline Chat App: https://github.com/Azure-Samples/microsoft-foundry-baseline
website/chatui/Controllers/ChatController.cs - SSE streaming patternBasic Chat Example: https://github.com/Azure-Samples/microsoft-foundry-basic
Semantic Kernel + Foundry: https://github.com/Azure-Samples/app-service-agentic-semantic-kernel-ai-foundry-agent
Azure AI Agents React Sample: https://github.com/Azure-Samples/get-started-with-ai-agents
This is the primary UI reference for this project. Many UI patterns were borrowed from here:
Agent Framework DevUI: https://github.com/microsoft/agent-framework/tree/main/python/packages/devui
Alternative UI patterns for agent development:
When implementing new UI features, check these sources in order:
get-started-with-ai-agents - React + TypeScript patterns for chat UIagent-framework/devui - Development UI patternsRepository: https://github.com/microsoft/semantic-kernel
Relevant paths:
dotnet/src/Agents/OpenAI/ - OpenAI Responses API integrationdotnet/samples/GettingStartedWithAgents/AzureAIAgent/dotnet/samples/Concepts/Agents/ (Step##_*.cs files)Repository: https://github.com/openai/openai-dotnet
docs/guides/streaming-responses/ - Streaming patternsStreamingResponseOutputTextDeltaUpdate and related typesUse GitHub search to find usage examples:
# Find streaming patterns
"StreamingResponseOutputTextDeltaUpdate language:csharp"
# Find Responses API usage
"ProjectResponsesClient CreateResponseStreamingAsync language:csharp"
# Find conversation patterns
"ProjectConversation GetProjectResponsesClientForAgent language:csharp"
| Package | Purpose |
|---|---|
Azure.AI.Projects |
Main entry point, AIProjectClient, v2 Agents API, Responses API |
Azure.Identity |
Authentication (AzureDeveloperCliCredential, ManagedIdentityCredential) |
Microsoft.Identity.Web |
JWT Bearer authentication for API |
Note: Check WebApp.Api.csproj for current versions. This project requires Azure.AI.Projects GA with v2 Agents API support (AIProjectClient.AgentAdministrationClient).
Companion packages used:
Azure.AI.Projects.Agents β ProjectsAgentVersion, ProjectsAgentRecord, DeclarativeAgentDefinition / HostedAgentDefinition / WorkflowAgentDefinition, AgentAdministrationClientAzure.AI.Extensions.OpenAI β ProjectOpenAIClient, ProjectConversationsClient, ProjectResponsesClient, ProjectConversationOpenAI.Responses β streaming typesAgent Framework (Microsoft.Agents.AI.AzureAI): Not referenced. As of rc5, incompatible with Azure.AI.Projects 2.0.0 GA. See "Compatibility blocker" below.
Key Resources:
Start here when researching agent capabilities, limits, or new features:
Agent Framework (Microsoft.Agents) docs:
| Topic | URL |
|---|---|
| Agent Framework overview | https://learn.microsoft.com/microsoft-agents/overview |
| Agent Framework .NET SDK | https://github.com/microsoft/Agents-for-net |
| NuGet package | https://www.nuget.org/packages/Microsoft.Agents.AI.AzureAI |
| IChatClient abstraction | https://learn.microsoft.com/dotnet/api/microsoft.extensions.ai.ichatclient |
The SDK provides several annotation types for citations (from OpenAI.Responses namespace):
| Type | Class | Use Case | Key Properties |
|---|---|---|---|
| URI Citation | UriCitationMessageAnnotation |
Bing, Azure AI Search, SharePoint | Uri, Title, StartIndex, EndIndex |
| File Citation | FileCitationMessageAnnotation |
File search (vector stores) | FileId, Filename, Index |
| File Path | FilePathMessageAnnotation |
Code interpreter output | FileId, Index |
| Container Citation | ContainerFileCitationMessageAnnotation |
Container file citations | FileId, Filename, ContainerId, StartIndex, EndIndex |
Note: FileCitationMessageAnnotation uses Index (not StartIndex/EndIndex) per the SDK. See ExtractAnnotations() in AgentFrameworkService.cs for mapping to AnnotationInfo.
The C# SDK does not yet have a typed client for container file downloads. Use the REST API directly with a bearer token scoped to https://ai.azure.com/.default:
GET {projectEndpoint}/openai/v1/containers/{containerId}/files/{fileId}/content
Authorization: Bearer {token}
For standard (non-container) files (cfile_ prefix absent), use OpenAI.Files.FileClient instead. The backend endpoint GET /api/files/{fileId}?containerId={id} abstracts this: it routes cfile_-prefixed files through the REST API and standard files through FileClient.
| Type | Purpose |
|---|---|
StreamingResponseOutputTextDeltaUpdate |
Text content delta chunks |
StreamingResponseOutputItemDoneUpdate |
Item completion signals |
StreamingResponseCompletedUpdate |
Response completion with usage |
ResponseItem |
Base type for response items |
Pattern used in this project:
await foreach (var update in responsesClient.CreateResponseStreamingAsync(...))
{
if (update is StreamingResponseOutputTextDeltaUpdate textUpdate)
yield return new StreamChunk { Text = textUpdate.Delta };
if (update is StreamingResponseOutputItemDoneUpdate itemDone)
// Extract annotations from itemDone.Item
}
Package: Microsoft.Agents.AI.AzureAI (prerelease, not referenced)
Status: β Not installed. Blocked on compatibility with Azure.AI.Projects 2.0.0 GA.
Microsoft.Agents.AI.AzureAI 1.0.0-rc5 pins Azure.AI.Projects 2.0.0-beta.2 and references types removed in the GA release. Attempting to use rc5 with Azure.AI.Projects 2.0.0 throws TypeLoadException at runtime. Re-evaluate when rc6+ ships.
Even when the compat blocker is lifted, this project's streaming path needs direct access to ProjectResponsesClient and typed response items that are not surfaced by the IChatClient abstraction:
McpToolCallApprovalRequestItem for MCP approval flowsFileSearchCallResponseItem for file search quotesMessageResponseItem.OutputTextAnnotations for citationsResponseItem.CreateMcpApprovalResponseItem() to respond to MCP approvalsRouting through ChatClientAgent.RunStreamingAsync() would require casting RawRepresentation for each of these, which defeats the abstraction benefit.
// Agent metadata β no "latest" keyword in the REST spec; enumerate versions descending.
ProjectsAgentVersion? agentVersion = null;
await foreach (var v in projectClient.AgentAdministrationClient.GetAgentVersionsAsync(
agentName: agentName,
limit: 1,
order: AgentListOrder.Descending,
after: null,
before: null,
cancellationToken: ct))
{
agentVersion = v;
break;
}
var definition = agentVersion?.Definition as DeclarativeAgentDefinition;
string model = definition?.Model ?? "";
string instructions = definition?.Instructions ?? "";
// Streaming β pin the resolved version so streaming hits the same version as metadata.
ProjectResponsesClient responsesClient = projectClient.ProjectOpenAIClient
.GetProjectResponsesClientForAgent(
new AgentReference(agentId, agentVersion?.Version),
conversationId);
await foreach (var update in responsesClient.CreateResponseStreamingAsync(...)) { }
AIProjectClient requires a project endpoint URI (not a connection string):
var projectClient = new AIProjectClient(new Uri(projectEndpoint), new DefaultAzureCredential());
Connection-string constructors are deprecated. See: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/ai/Azure.AI.Projects/AGENTS_MIGRATION_GUIDE.md
Type definitions live in these reposβread them directly:
Search across all .NET codebases for real-world usage:
"ProjectResponsesClient CreateResponseStreamingAsync" language:csharp
"StreamingResponseOutputTextDeltaUpdate" language:csharp
This finds how other projects use these APIs, revealing patterns and edge cases.
Use when SDK docs are outdated or incomplete β the DLLs are the ground truth.
Works even when dotnet build fails (loads from NuGet cache):
cd backend/WebApp.Api; dotnet restore
# Option A: Load from build output (requires successful build)
$asm = [Reflection.Assembly]::LoadFrom((Resolve-Path "bin/Debug/net10.0/Azure.AI.Projects.dll"))
# Option B: Load from NuGet cache (works even if build fails β use for pre-release migrations)
$dll = Get-ChildItem "$env:USERPROFILE\.nuget\packages\azure.ai.projects" -Recurse -Filter "Azure.AI.Projects.dll" | Select-Object -Last 1
$asm = [Reflection.Assembly]::LoadFrom($dll.FullName)
# Find types matching a pattern
$asm.GetExportedTypes() | Where-Object { $_.Name -like "*Streaming*" } | ForEach-Object { $_.FullName }
# Get method signatures with parameter details
$type = $asm.GetType("Azure.AI.Extensions.OpenAI.ProjectResponsesClient")
$type.GetMethods() | Where-Object { $_.Name -like "*Async*" } | Select-Object Name, ReturnType, @{N='Params';E={($_.GetParameters() | ForEach-Object { "$($_.ParameterType.Name) $($_.Name)" }) -join ', '}}
Agent Framework assemblies (for Microsoft.Agents.AI.AzureAI migrations):
# Load Agent Framework DLL from NuGet cache
$pkg = Get-ChildItem "$env:USERPROFILE\.nuget\packages\microsoft.agents.ai.azureai" -Recurse -Filter "Microsoft.Agents.AI.AzureAI.dll" | Select-Object -Last 1
$asm = [Reflection.Assembly]::LoadFrom($pkg.FullName)
# Dump all exported types to see what changed between versions
$asm.GetExportedTypes() | ForEach-Object { $_.FullName } | Sort-Object
# Check if types you depend on still exist
@("ChatClientAgent", "AgentVersion", "PromptAgentDefinition", "AgentReference") | ForEach-Object {
$match = $asm.GetExportedTypes() | Where-Object { $_.Name -eq $_ }
if ($match) { Write-Host "FOUND: $($match.FullName)" } else { Write-Host "MISSING: $_" -ForegroundColor Red }
}
# Inspect extension methods (GetAIAgentAsync, etc.)
$asm.GetExportedTypes() | Where-Object { $_.GetMethods([Reflection.BindingFlags]::Static -bor [Reflection.BindingFlags]::Public) | Where-Object { $_.IsDefined([Runtime.CompilerServices.ExtensionAttribute], $false) } } | ForEach-Object {
$_.GetMethods() | Where-Object { $_.IsDefined([Runtime.CompilerServices.ExtensionAttribute], $false) } | ForEach-Object { Write-Host "$($_.DeclaringType.Name).$($_.Name)" }
}
When to use: SDK upgrade with breaking changes, pre-release packages where docs lag, verifying actual API surface before writing migration code.
Key insight: Load from NuGet cache ($env:USERPROFILE\.nuget\packages\) to inspect the new version's types even when the build is broken.