Skip to content

Commit e72f556

Browse files
authored
Merge pull request #961 from MicrosoftDocs/main
Merge latest docs to live
2 parents 7b460a7 + cfcd7c7 commit e72f556

73 files changed

Lines changed: 1406 additions & 923 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

agent-framework/agents/agent-pipeline.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ zone_pivot_groups: programming-languages
55
author: eavanvalkenburg
66
ms.topic: conceptual
77
ms.author: edvan
8-
ms.date: 03/20/2026
8+
ms.date: 04/02/2026
99
ms.service: agent-framework
1010
---
1111

@@ -40,13 +40,13 @@ The `Agent` class builds a pipeline through class composition with two main comp
4040
**Agent** (outer component):
4141

4242
1. **Agent Middleware + Telemetry** - the `AgentMiddlewareLayer` and `AgentTelemetryLayer` classes handle middleware invocation and OpenTelemetry instrumentation
43-
2. **RawAgent** - Core agent logic that invokes context providers
44-
3. **Context Providers** - Unified `context_providers` list manages history and additional context
43+
2. **RawAgent** - Core agent logic that invokes context providers and collects provider-added middleware
44+
3. **Context Providers** - Unified `context_providers` list manages history, additional context, and per-run chat/function middleware
4545

4646
**ChatClient** (separate and interchangeable component):
4747

4848
1. **FunctionInvocation** - Handles tool calling loop, invoking Function Middleware + Telemetry per tool call
49-
2. **Chat Middleware + Telemetry** - Optional middleware chain and instrumentation layers, running per model call
49+
2. **Chat Middleware + Telemetry** - Optional middleware chain and instrumentation layers, including any chat middleware added by context providers, running per model call
5050
3. **RawChatClient** - Provider-specific implementation (Azure OpenAI, OpenAI, Anthropic, etc.) that communicates with the LLM
5151

5252
When you call `run()`, your request flows through the Agent layers, then into the ChatClient pipeline for LLM communication.
@@ -144,6 +144,8 @@ agent = Agent(
144144
)
145145
```
146146

147+
Context providers can also attach chat or function middleware to a single invocation via `SessionContext.extend_middleware()`. The agent flattens those additions in provider order before entering the ChatClient pipeline.
148+
147149
::: zone-end
148150

149151
For detailed context provider patterns, see [Context Providers](./conversations/context-providers.md).
@@ -157,9 +159,10 @@ The chat client layer handles the actual communication with the LLM service.
157159
`ChatClientAgent` uses an `IChatClient` instance, which can be decorated with additional middleware:
158160

159161
```csharp
160-
var chatClient = new AzureOpenAIClient(endpoint, credential)
161-
.GetChatClient(deploymentName)
162-
.AsIChatClient()
162+
var chatClient = new AIProjectClient(endpoint, credential)
163+
.GetProjectOpenAIClient()
164+
.GetProjectResponsesClient()
165+
.AsIChatClient(deploymentName)
163166
.AsBuilder()
164167
.Use(CustomChatClientMiddleware)
165168
.Build();
@@ -170,9 +173,10 @@ var agent = new ChatClientAgent(chatClient, instructions: "You are helpful.");
170173
You can also use `AIContextProvider` as chat client middleware to enrich messages, tools, and instructions at the client level. This must be used within the context of a running `AIAgent`:
171174

172175
```csharp
173-
var chatClient = new AzureOpenAIClient(endpoint, credential)
174-
.GetChatClient(deploymentName)
175-
.AsIChatClient()
176+
var chatClient = new AIProjectClient(endpoint, credential)
177+
.GetProjectOpenAIClient()
178+
.GetProjectResponsesClient()
179+
.AsIChatClient(deploymentName)
176180
.AsBuilder()
177181
.UseAIContextProviders(new MyContextProvider())
178182
.Build();
@@ -226,14 +230,14 @@ When you invoke an agent, the request flows through the pipeline:
226230
**Agent pipeline:**
227231

228232
1. **Agent Middleware + Telemetry** executes middleware (if configured) and records spans
229-
2. **RawAgent** invokes context providers to load history and add context
233+
2. **RawAgent** invokes context providers to load history, add context, and collect provider-added chat/function middleware
230234
3. Request is passed to the ChatClient
231235

232236
**ChatClient pipeline:**
233237

234238
4. **FunctionInvocation** manages the tool calling loop
235-
- For each tool call, **Function Middleware + Telemetry** executes
236-
5. **Chat Middleware + Telemetry** executes per model call (if configured)
239+
- For each tool call, **Function Middleware + Telemetry** executes, including any function middleware added by context providers
240+
5. **Chat Middleware + Telemetry** executes per model call (if configured), including any chat middleware added by context providers
237241
6. **RawChatClient** handles provider-specific LLM communication
238242
7. Response flows back through the same layers
239243
8. **Context providers** are notified of new messages for storage
@@ -274,6 +278,16 @@ var copilotAgent = originalCopilotAgent
274278
::: zone-end
275279

276280
::: zone pivot="programming-language-python"
281+
282+
## Other agent types
283+
284+
Not every Python agent uses the full `Agent` + `ChatClient` pipeline. `GitHubCopilotAgent`, for example, sends requests through the GitHub Copilot CLI instead of a local chat client.
285+
286+
Even so, Python `GitHubCopilotAgent` still supports agent middleware and now runs `context_providers` around each invocation. Provider-added messages and instructions are included in the prompt sent to Copilot, and providers receive the matching `after_run` callback once a response is available.
287+
288+
> [!NOTE]
289+
> Because `GitHubCopilotAgent` does not use a local chat client, chat client middleware still does not apply.
290+
277291
::: zone-end
278292

279293
## Next steps
@@ -285,4 +299,4 @@ var copilotAgent = originalCopilotAgent
285299

286300
- [Middleware](./middleware/index.md) - Add cross-cutting behavior to your agents
287301
- [Context Providers](./conversations/context-providers.md) - Detailed patterns for history and context injection
288-
- [Running Agents](./running-agents.md) - How to invoke agents
302+
- [Running Agents](./running-agents.md) - How to invoke agents

agent-framework/agents/background-responses.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,10 @@ Some agents may not allow explicit control over background responses. These agen
5555
For non-streaming scenarios, when you initially run an agent, it may or may not return a continuation token. If no continuation token is returned, it means the operation has completed. If a continuation token is returned, it indicates that the agent has initiated a background response that is still processing and will require polling to retrieve the final result:
5656

5757
```csharp
58-
AIAgent agent = new AzureOpenAIClient(
59-
new Uri("https://<myresource>.openai.azure.com"),
58+
AIAgent agent = new AIProjectClient(
59+
new Uri("<your-foundry-project-endpoint>"),
6060
new DefaultAzureCredential())
61-
.GetResponsesClient("<deployment-name>")
62-
.AsAIAgent();
61+
.AsAIAgent(model: "<deployment-name>", instructions: "You are a helpful assistant.");
6362

6463
AgentRunOptions options = new()
6564
{
@@ -100,11 +99,10 @@ Console.WriteLine(response.Text);
10099
In streaming scenarios, background responses work much like regular streaming responses - the agent streams all updates back to consumers in real-time. However, the key difference is that if the original stream gets interrupted, agents support stream resumption through continuation tokens. Each update includes a continuation token that captures the current state, allowing the stream to be resumed from exactly where it left off by passing this token to subsequent streaming API calls:
101100

102101
```csharp
103-
AIAgent agent = new AzureOpenAIClient(
104-
new Uri("https://<myresource>.openai.azure.com"),
102+
AIAgent agent = new AIProjectClient(
103+
new Uri("<your-foundry-project-endpoint>"),
105104
new DefaultAzureCredential())
106-
.GetResponsesClient("<deployment-name>")
107-
.AsAIAgent();
105+
.AsAIAgent(model: "<deployment-name>", instructions: "You are a helpful assistant.");
108106

109107
AgentRunOptions options = new()
110108
{

agent-framework/agents/conversations/context-providers.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,10 @@ internal sealed class AdvancedServiceMemoryProvider : AIContextProvider
282282
```python
283283
from typing import Any
284284

285-
from agent_framework import AgentSession, BaseContextProvider, SessionContext
285+
from agent_framework import AgentSession, ContextProvider, SessionContext
286286

287287

288-
class UserPreferenceProvider(BaseContextProvider):
288+
class UserPreferenceProvider(ContextProvider):
289289
def __init__(self) -> None:
290290
super().__init__("user-preferences")
291291

@@ -314,6 +314,11 @@ class UserPreferenceProvider(BaseContextProvider):
314314
state["favorite_food"] = text.split("favorite food is", 1)[1].strip().rstrip(".")
315315
```
316316

317+
> [!NOTE]
318+
> `ContextProvider` and `HistoryProvider` are the canonical Python base classes. `BaseContextProvider` and `BaseHistoryProvider` still exist as deprecated aliases for compatibility, but new providers should inherit from the new names.
319+
>
320+
> Context providers can also add chat or function middleware for the current invocation by calling `context.extend_middleware(self.source_id, middleware)`. The agent flattens those additions with `context.get_middleware()` and applies them in provider order before invoking the chat client.
321+
317322
:::zone-end
318323

319324
:::zone pivot="programming-language-python"
@@ -326,10 +331,10 @@ History providers are context providers specialized for loading/storing messages
326331
from collections.abc import Sequence
327332
from typing import Any
328333

329-
from agent_framework import BaseHistoryProvider, Message
334+
from agent_framework import HistoryProvider, Message
330335

331336

332-
class DatabaseHistoryProvider(BaseHistoryProvider):
337+
class DatabaseHistoryProvider(HistoryProvider):
333338
def __init__(self, db: Any) -> None:
334339
super().__init__("db-history", load_messages=True)
335340
self._db = db
@@ -365,6 +370,7 @@ class DatabaseHistoryProvider(BaseHistoryProvider):
365370
> [!IMPORTANT]
366371
> In Python, you can configure multiple history providers, but **only one** should use `load_messages=True`.
367372
> Use additional providers for diagnostics/evals with `load_messages=False` and `store_context_messages=True` so they capture context from other providers alongside input/output.
373+
> If you need local history to persist around each model call in a tool loop, see [Storage](./storage.md#per-service-call-local-history-persistence).
368374
>
369375
> Example pattern:
370376
>

agent-framework/agents/conversations/storage.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,32 @@ response = await agent.run("Continue this conversation.", session=session)
120120

121121
:::zone-end
122122

123+
## Per-service-call local history persistence
124+
125+
Tool-calling runs can make multiple model calls before a single `agent.run()` completes. By default, local history providers persist once after the full run. If you want local history to mirror service-managed conversations more closely, set `require_per_service_call_history_persistence=True` so history providers run around each model call instead.
126+
127+
:::zone pivot="programming-language-python"
128+
129+
```python
130+
from agent_framework import Agent, InMemoryHistoryProvider
131+
from agent_framework.openai import OpenAIChatClient
132+
133+
agent = Agent(
134+
client=OpenAIChatClient(),
135+
name="StorageAgent",
136+
instructions="You are a helpful assistant.",
137+
context_providers=[InMemoryHistoryProvider("memory", load_messages=True)],
138+
require_per_service_call_history_persistence=True,
139+
)
140+
```
141+
142+
> [!IMPORTANT]
143+
> Use this mode only for framework-managed local history. If the run is already bound to a service-managed conversation (for example via `session.service_session_id` or `options={"conversation_id": ...}`), Agent Framework raises an error instead of mixing the two persistence models.
144+
>
145+
> This mode is especially useful when middleware can terminate immediately after a tool call: persisting per model call keeps local history aligned with what a service-managed conversation would keep.
146+
147+
:::zone-end
148+
123149
## Third-party/Custom storage pattern
124150

125151
For database/Redis/blob-backed history, implement a custom history provider.

agent-framework/agents/declarative.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@ Declarative agents allow you to define agent configuration using YAML or JSON fi
1818
The following example shows how to create a declarative agent from a YAML configuration:
1919

2020
```csharp
21-
using Azure.AI.OpenAI;
21+
using Azure.AI.Projects;
2222
using Azure.Identity;
2323
using Microsoft.Agents.AI;
24-
using Microsoft.Extensions.AI;
2524

2625
// Create the chat client
27-
IChatClient chatClient = new AzureOpenAIClient(
28-
new Uri("https://<myresource>.openai.azure.com"),
26+
IChatClient chatClient = new AIProjectClient(
27+
new Uri("<your-foundry-project-endpoint>"),
2928
new DefaultAzureCredential())
30-
.GetChatClient("gpt-4o-mini")
31-
.AsIChatClient();
29+
.GetProjectOpenAIClient()
30+
.GetProjectResponsesClient()
31+
.AsIChatClient("gpt-4o-mini");
3232

3333
// Define the agent using a YAML definition.
3434
var yamlDefinition =
@@ -67,6 +67,9 @@ await foreach (var update in agent!.RunStreamingAsync("Tell me a joke about a pi
6767
}
6868
```
6969

70+
> [!WARNING]
71+
> `DefaultAzureCredential` is convenient for development but requires careful consideration in production. In production, consider using a specific credential (e.g., `ManagedIdentityCredential`) to avoid latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
72+
7073
:::zone-end
7174

7275
:::zone pivot="programming-language-python"
@@ -94,7 +97,7 @@ model:
9497
id: =Env.AZURE_OPENAI_MODEL
9598
connection:
9699
kind: remote
97-
endpoint: =Env.AZURE_AI_PROJECT_ENDPOINT
100+
endpoint: =Env.FOUNDRY_PROJECT_ENDPOINT
98101
"""
99102
async with (
100103
AzureCliCredential() as credential,

0 commit comments

Comments
 (0)