Skip to content

Commit 791cd60

Browse files
authored
Merge pull request #385 from MicrosoftDocs/main639111084740505974sync_temp
Repo sync for protected branch
2 parents e34f5af + 13618a1 commit 791cd60

84 files changed

Lines changed: 2171 additions & 1578 deletions

File tree

Some content is hidden

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

agent-framework/TOC.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ items:
180180
href: integrations/m365.md
181181
- name: Neo4j GraphRAG Provider
182182
href: integrations/neo4j-graphrag.md
183+
- name: Chat History Memory Provider
184+
href: integrations/chat-history-memory-provider.md
183185
- name: Neo4j Memory Provider
184186
href: integrations/neo4j-memory.md
185187
- name: A2A Protocol

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: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ ms.service: agent-framework
1313

1414
Context providers run around each invocation to add context before execution and process data after execution.
1515

16+
> [!NOTE]
17+
> For a list of pre-built context providers you can use with your agent, see [Integrations](../../integrations/index.md)
18+
1619
## Built-in pattern
1720

1821
:::zone pivot="programming-language-csharp"
@@ -282,10 +285,10 @@ internal sealed class AdvancedServiceMemoryProvider : AIContextProvider
282285
```python
283286
from typing import Any
284287

285-
from agent_framework import AgentSession, BaseContextProvider, SessionContext
288+
from agent_framework import AgentSession, ContextProvider, SessionContext
286289

287290

288-
class UserPreferenceProvider(BaseContextProvider):
291+
class UserPreferenceProvider(ContextProvider):
289292
def __init__(self) -> None:
290293
super().__init__("user-preferences")
291294

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

320+
> [!NOTE]
321+
> `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.
322+
>
323+
> 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.
324+
317325
:::zone-end
318326

319327
:::zone pivot="programming-language-python"
@@ -326,10 +334,10 @@ History providers are context providers specialized for loading/storing messages
326334
from collections.abc import Sequence
327335
from typing import Any
328336

329-
from agent_framework import BaseHistoryProvider, Message
337+
from agent_framework import HistoryProvider, Message
330338

331339

332-
class DatabaseHistoryProvider(BaseHistoryProvider):
340+
class DatabaseHistoryProvider(HistoryProvider):
333341
def __init__(self, db: Any) -> None:
334342
super().__init__("db-history", load_messages=True)
335343
self._db = db
@@ -365,6 +373,7 @@ class DatabaseHistoryProvider(BaseHistoryProvider):
365373
> [!IMPORTANT]
366374
> In Python, you can configure multiple history providers, but **only one** should use `load_messages=True`.
367375
> 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.
376+
> 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).
368377
>
369378
> Example pattern:
370379
>

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: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,58 @@ 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 System;
22-
using System.IO;
23-
using Azure.AI.OpenAI;
21+
using Azure.AI.Projects;
2422
using Azure.Identity;
2523
using Microsoft.Agents.AI;
26-
using Microsoft.Extensions.AI;
2724

28-
// Load agent configuration from a YAML file
29-
var yamlContent = File.ReadAllText("agent-config.yaml");
30-
31-
// Create the agent from the YAML definition
32-
AIAgent agent = AgentFactory.CreateFromYaml(
33-
yamlContent,
34-
new AzureOpenAIClient(
35-
new Uri("https://<myresource>.openai.azure.com"),
36-
new AzureCliCredential()));
37-
38-
// Run the declarative agent
39-
Console.WriteLine(await agent.RunAsync("Why is the sky blue?"));
25+
// Create the chat client
26+
IChatClient chatClient = new AIProjectClient(
27+
new Uri("<your-foundry-project-endpoint>"),
28+
new DefaultAzureCredential())
29+
.GetProjectOpenAIClient()
30+
.GetProjectResponsesClient()
31+
.AsIChatClient("gpt-4o-mini");
32+
33+
// Define the agent using a YAML definition.
34+
var yamlDefinition =
35+
"""
36+
kind: Prompt
37+
name: Assistant
38+
description: Helpful assistant
39+
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format.
40+
model:
41+
options:
42+
temperature: 0.9
43+
topP: 0.95
44+
outputSchema:
45+
properties:
46+
language:
47+
type: string
48+
required: true
49+
description: The language of the answer.
50+
answer:
51+
type: string
52+
required: true
53+
description: The answer text.
54+
""";
55+
56+
// Create the agent from the YAML definition.
57+
var agentFactory = new ChatClientPromptAgentFactory(chatClient);
58+
var agent = await agentFactory.CreateFromYamlAsync(yamlDefinition);
59+
60+
// Invoke the agent and output the text result.
61+
Console.WriteLine(await agent!.RunAsync("Tell me a joke about a pirate in English."));
62+
63+
// Invoke the agent with streaming support.
64+
await foreach (var update in agent!.RunStreamingAsync("Tell me a joke about a pirate in French."))
65+
{
66+
Console.WriteLine(update);
67+
}
4068
```
4169

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+
4273
:::zone-end
4374

4475
:::zone pivot="programming-language-python"
@@ -66,7 +97,7 @@ model:
6697
id: =Env.AZURE_OPENAI_MODEL
6798
connection:
6899
kind: remote
69-
endpoint: =Env.AZURE_AI_PROJECT_ENDPOINT
100+
endpoint: =Env.FOUNDRY_PROJECT_ENDPOINT
70101
"""
71102
async with (
72103
AzureCliCredential() as credential,

0 commit comments

Comments
 (0)