mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d744b3a0b9 | ||
|
|
00368719cd | ||
|
|
f6fdcd9b22 | ||
|
|
b20d6aec37 | ||
|
|
3d6fa76708 | ||
|
|
249a43c0e9 | ||
|
|
0079a92324 |
@@ -60,8 +60,9 @@ jobs:
|
||||
environment: integration
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }}
|
||||
OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI__CHATMODELID }}
|
||||
OPENAI_RESPONSES_MODEL_ID: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_EMBEDDINGS_MODEL_ID: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
||||
OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }}
|
||||
@@ -95,10 +96,10 @@ jobs:
|
||||
environment: integration
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME }}
|
||||
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__EMBEDDINGDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }}
|
||||
defaults:
|
||||
run:
|
||||
@@ -125,9 +126,7 @@ jobs:
|
||||
uv run pytest --import-mode=importlib
|
||||
packages/openai/tests/openai/test_openai_chat_completion_client_azure.py
|
||||
packages/openai/tests/openai/test_openai_chat_client_azure.py
|
||||
packages/openai/tests/openai/test_openai_embedding_client_azure.py
|
||||
packages/azure-ai/tests/azure_openai
|
||||
--ignore=packages/azure-ai/tests/azure_openai/test_azure_responses_client_foundry.py
|
||||
-m integration
|
||||
-n logical --dist worksteal
|
||||
--timeout=120 --session-timeout=900 --timeout_method thread
|
||||
@@ -203,16 +202,15 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
UV_PYTHON: "3.11"
|
||||
OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }}
|
||||
OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI__CHATMODELID }}
|
||||
OPENAI_RESPONSES_MODEL_ID: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }}
|
||||
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
||||
AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }}
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
|
||||
FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }}
|
||||
FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }}
|
||||
FOUNDRY_MODEL: ${{ vars.AZUREAI__DEPLOYMENTNAME }}
|
||||
FOUNDRY_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }}
|
||||
FUNCTIONS_WORKER_RUNTIME: "python"
|
||||
DURABLE_TASK_SCHEDULER_CONNECTION_STRING: "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
|
||||
AzureWebJobsStorage: "UseDevelopmentStorage=true"
|
||||
@@ -250,19 +248,17 @@ jobs:
|
||||
--timeout=360 --session-timeout=900 --timeout_method thread
|
||||
--retries 2 --retry-delay 5
|
||||
|
||||
# Foundry integration tests
|
||||
python-tests-foundry:
|
||||
name: Python Integration Tests - Foundry
|
||||
# Azure AI integration tests
|
||||
python-tests-azure-ai:
|
||||
name: Python Integration Tests - Azure AI
|
||||
runs-on: ubuntu-latest
|
||||
environment: integration
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
AZURE_AI_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }}
|
||||
AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREAI__DEPLOYMENTNAME }}
|
||||
FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }}
|
||||
FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }}
|
||||
FOUNDRY_AGENT_NAME: ${{ vars.FOUNDRY_AGENT_NAME }}
|
||||
FOUNDRY_AGENT_VERSION: ${{ vars.FOUNDRY_AGENT_VERSION }}
|
||||
FOUNDRY_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }}
|
||||
FOUNDRY_MODEL: ${{ vars.AZUREAI__DEPLOYMENTNAME }}
|
||||
LOCAL_MCP_URL: ${{ vars.LOCAL_MCP__URL }}
|
||||
defaults:
|
||||
run:
|
||||
@@ -286,14 +282,9 @@ jobs:
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
- name: Test with pytest
|
||||
timeout-minutes: 15
|
||||
run: >
|
||||
uv run pytest --import-mode=importlib
|
||||
packages/azure-ai/tests/azure_openai/test_azure_responses_client_foundry.py
|
||||
packages/foundry/tests
|
||||
-m integration
|
||||
-n logical --dist worksteal
|
||||
--timeout=120 --session-timeout=900 --timeout_method thread
|
||||
--retries 2 --retry-delay 5
|
||||
run: |
|
||||
uv run --directory packages/azure-ai poe integration-tests -n logical --dist worksteal --timeout=120 --session-timeout=900 --timeout_method thread --retries 2 --retry-delay 5
|
||||
uv run --directory packages/foundry poe integration-tests -n logical --dist worksteal --timeout=120 --session-timeout=900 --timeout_method thread --retries 2 --retry-delay 5
|
||||
|
||||
# Azure Cosmos integration tests
|
||||
python-tests-cosmos:
|
||||
@@ -350,7 +341,7 @@ jobs:
|
||||
python-tests-azure-openai,
|
||||
python-tests-misc-integration,
|
||||
python-tests-functions,
|
||||
python-tests-foundry,
|
||||
python-tests-azure-ai,
|
||||
python-tests-cosmos
|
||||
]
|
||||
steps:
|
||||
|
||||
@@ -141,8 +141,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
environment: integration
|
||||
env:
|
||||
OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }}
|
||||
OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI__CHATMODELID }}
|
||||
OPENAI_RESPONSES_MODEL_ID: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_EMBEDDINGS_MODEL_ID: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
||||
OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }}
|
||||
@@ -194,10 +195,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
environment: integration
|
||||
env:
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME }}
|
||||
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__EMBEDDINGDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }}
|
||||
defaults:
|
||||
run:
|
||||
@@ -222,9 +223,7 @@ jobs:
|
||||
uv run pytest --import-mode=importlib
|
||||
packages/openai/tests/openai/test_openai_chat_completion_client_azure.py
|
||||
packages/openai/tests/openai/test_openai_chat_client_azure.py
|
||||
packages/openai/tests/openai/test_openai_embedding_client_azure.py
|
||||
packages/azure-ai/tests/azure_openai
|
||||
--ignore=packages/azure-ai/tests/azure_openai/test_azure_responses_client_foundry.py
|
||||
-m integration
|
||||
-n logical --dist worksteal
|
||||
--timeout=120 --session-timeout=900 --timeout_method thread
|
||||
@@ -334,16 +333,15 @@ jobs:
|
||||
environment: integration
|
||||
env:
|
||||
UV_PYTHON: "3.11"
|
||||
OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }}
|
||||
OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI__CHATMODELID }}
|
||||
OPENAI_RESPONSES_MODEL_ID: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
||||
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }}
|
||||
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
||||
AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }}
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
|
||||
FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }}
|
||||
FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }}
|
||||
FOUNDRY_MODEL: ${{ vars.AZUREAI__DEPLOYMENTNAME }}
|
||||
FOUNDRY_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }}
|
||||
FUNCTIONS_WORKER_RUNTIME: "python"
|
||||
DURABLE_TASK_SCHEDULER_CONNECTION_STRING: "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
|
||||
AzureWebJobsStorage: "UseDevelopmentStorage=true"
|
||||
@@ -389,8 +387,8 @@ jobs:
|
||||
fail-on-empty: false
|
||||
title: Functions integration test results
|
||||
|
||||
python-tests-foundry:
|
||||
name: Python Integration Tests - Foundry
|
||||
python-tests-azure-ai:
|
||||
name: Python Tests - Azure AI
|
||||
needs: paths-filter
|
||||
if: >
|
||||
github.event_name != 'pull_request' &&
|
||||
@@ -403,10 +401,8 @@ jobs:
|
||||
env:
|
||||
AZURE_AI_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }}
|
||||
AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREAI__DEPLOYMENTNAME }}
|
||||
FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }}
|
||||
FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }}
|
||||
FOUNDRY_AGENT_NAME: ${{ vars.FOUNDRY_AGENT_NAME }}
|
||||
FOUNDRY_AGENT_VERSION: ${{ vars.FOUNDRY_AGENT_VERSION }}
|
||||
FOUNDRY_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }}
|
||||
FOUNDRY_MODEL: ${{ vars.AZUREAI__DEPLOYMENTNAME }}
|
||||
LOCAL_MCP_URL: ${{ vars.LOCAL_MCP__URL }}
|
||||
defaults:
|
||||
run:
|
||||
@@ -428,14 +424,9 @@ jobs:
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
- name: Test with pytest
|
||||
timeout-minutes: 15
|
||||
run: >
|
||||
uv run pytest --import-mode=importlib
|
||||
packages/azure-ai/tests/azure_openai/test_azure_responses_client_foundry.py
|
||||
packages/foundry/tests
|
||||
-m integration
|
||||
-n logical --dist worksteal
|
||||
--timeout=120 --session-timeout=900 --timeout_method thread
|
||||
--retries 2 --retry-delay 5
|
||||
run: |
|
||||
uv run --directory packages/azure-ai poe integration-tests -n logical --dist worksteal --timeout=120 --session-timeout=900 --timeout_method thread --retries 2 --retry-delay 5
|
||||
uv run --directory packages/foundry poe integration-tests -n logical --dist worksteal --timeout=120 --session-timeout=900 --timeout_method thread --retries 2 --retry-delay 5
|
||||
working-directory: ./python
|
||||
- name: Test Azure AI samples
|
||||
timeout-minutes: 10
|
||||
@@ -522,7 +513,7 @@ jobs:
|
||||
python-tests-azure-openai,
|
||||
python-tests-misc-integration,
|
||||
python-tests-functions,
|
||||
python-tests-foundry,
|
||||
python-tests-azure-ai,
|
||||
python-tests-cosmos,
|
||||
]
|
||||
steps:
|
||||
|
||||
@@ -74,37 +74,6 @@ Contributions must maintain API signature and behavioral compatibility. Contribu
|
||||
that include breaking changes will be rejected. Please file an issue to discuss
|
||||
your idea or change if you believe that a breaking change is warranted.
|
||||
|
||||
#### Automated API Compatibility Validation
|
||||
|
||||
The .NET projects use [Package Validation](https://learn.microsoft.com/dotnet/fundamentals/package-validation/overview)
|
||||
to automatically detect API breaking changes. This validation runs during `dotnet build`
|
||||
(Release configuration) and `dotnet pack`, comparing the current API surface against the
|
||||
latest published NuGet baseline version.
|
||||
|
||||
**What gets validated:** By default, packable RC packages (`IsReleaseCandidate=true`) and
|
||||
GA packages (`IsGenerallyAvailable=true`) that have a published NuGet baseline and do not
|
||||
override validation settings are automatically validated. The shared baseline version and
|
||||
default validation settings are defined in `dotnet/nuget/nuget-package.props`, but
|
||||
individual projects may opt out (for example by setting `EnablePackageValidation=false`).
|
||||
|
||||
**If the build fails with CP errors (e.g., CP0001, CP0002):**
|
||||
|
||||
1. **Unintentional breaking change** — Refactor your code to maintain backward compatibility.
|
||||
2. **Intentional breaking change** (approved by maintainers) — Generate a suppression file:
|
||||
```bash
|
||||
dotnet build <project>.csproj -c Release /p:ApiCompatGenerateSuppressionFile=true
|
||||
```
|
||||
This creates or updates a `CompatibilitySuppressions.xml` in the project directory.
|
||||
Include this file in your PR with justification for the breaking change.
|
||||
|
||||
**After each release:**
|
||||
|
||||
1. Delete all `CompatibilitySuppressions.xml` files from validated projects.
|
||||
2. Update `PackageValidationBaselineVersion` in `dotnet/nuget/nuget-package.props` to the
|
||||
newly published version.
|
||||
|
||||
For more details, see the [Package Validation diagnostic IDs](https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids).
|
||||
|
||||
### Suggested Workflow
|
||||
|
||||
We use and recommend the following workflow:
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
---
|
||||
status: accepted
|
||||
contact: rogerbarreto
|
||||
date: 2026-03-06
|
||||
deciders: rogerbarreto, alliscode
|
||||
consulted: ""
|
||||
informed: ""
|
||||
---
|
||||
|
||||
# Foundry agent surface stays centered on `ChatClientAgent`
|
||||
|
||||
## Context
|
||||
|
||||
The Microsoft Foundry integration exposes two distinct usage patterns:
|
||||
|
||||
1. Direct Responses usage, where callers provide model, instructions, and tools at runtime.
|
||||
2. Server-side versioned agents, where callers create and manage `AgentVersion` resources through `AIProjectClient.Agents`.
|
||||
|
||||
We briefly explored adding public wrapper types such as `FoundryAgent`, `FoundryVersionedAgent`, and `FoundryResponsesChatClient` to make those paths feel more specialized. That direction created extra public types, duplicated existing `ChatClientAgent` behavior, and pushed samples toward compatibility helpers instead of the native Azure SDK flow.
|
||||
|
||||
## Decision
|
||||
|
||||
Keep the public surface centered on `ChatClientAgent`.
|
||||
|
||||
- Direct Responses scenarios use `AIProjectClient.AsAIAgent(...)`.
|
||||
- Server-side versioned scenarios use native `AIProjectClient.Agents` APIs to create or retrieve agent resources, then wrap `AgentRecord` or `AgentVersion` with `AIProjectClient.AsAIAgent(...)`.
|
||||
- Compatibility helpers such as `AIProjectClient.CreateAIAgentAsync(...)` and `AIProjectClient.GetAIAgentAsync(...)` remain only as obsolete migration shims.
|
||||
- Public wrapper types `FoundryAgent`, `FoundryVersionedAgent`, `FoundryResponsesChatClient`, and `FoundryResponsesChatClientAgent` are not part of the chosen direction.
|
||||
|
||||
## Why
|
||||
|
||||
- `ChatClientAgent` is already the framework abstraction used everywhere else.
|
||||
- `AIProjectClient` is the native Azure SDK entry point for versioned agent lifecycle operations.
|
||||
- A single agent abstraction avoids parallel type hierarchies for the same backend.
|
||||
- Samples become clearer when they show either:
|
||||
- direct Responses construction via `AIProjectClient.AsAIAgent(...)`, or
|
||||
- native Foundry resource management via `AIProjectClient.Agents`.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Direct Responses path
|
||||
|
||||
Use the convenience overloads on `AIProjectClient`:
|
||||
|
||||
```csharp
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
|
||||
|
||||
ChatClientAgent agent = aiProjectClient.AsAIAgent(
|
||||
model: deploymentName,
|
||||
instructions: "You are good at telling jokes.",
|
||||
name: "JokerAgent");
|
||||
```
|
||||
|
||||
Or use composed `ChatClientAgent`
|
||||
|
||||
```csharp
|
||||
ProjectResponsesClient projectResponsesClient = new(new Uri(endpoint), new DefaultAzureCredential(), new AgentReference($"model:{deploymentName}"));
|
||||
|
||||
ChatClientAgent agent = new(
|
||||
chatClient: projectResponsesClient.AsIChatClient(),
|
||||
instructions: "You are good at telling jokes.",
|
||||
name: "JokerAgent");
|
||||
```
|
||||
|
||||
This path is code-first and does not create a persistent server-side agent.
|
||||
|
||||
### Versioned agent path
|
||||
|
||||
Use the convenience overloads on `AIProjectClient`:
|
||||
|
||||
```csharp
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
|
||||
|
||||
AgentVersion version = await aiProjectClient.Agents.CreateAgentVersionAsync(
|
||||
"JokerAgent",
|
||||
new AgentVersionCreationOptions(
|
||||
new PromptAgentDefinition(deploymentName)
|
||||
{
|
||||
Instructions = "You are good at telling jokes."
|
||||
}));
|
||||
|
||||
ChatClientAgent agent = aiProjectClient.AsAIAgent(version);
|
||||
```
|
||||
|
||||
Or use composed `ChatClientAgent`
|
||||
|
||||
```csharp
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
|
||||
|
||||
AgentVersion version = await aiProjectClient.Agents.CreateAgentVersionAsync(
|
||||
"JokerAgent",
|
||||
new AgentVersionCreationOptions(
|
||||
new PromptAgentDefinition(deploymentName)
|
||||
{
|
||||
Instructions = "You are good at telling jokes."
|
||||
}));
|
||||
|
||||
ProjectResponsesClient projectResponsesClient = aiProjectClient
|
||||
.GetProjectOpenAIClient()
|
||||
.GetProjectResponsesClientForAgent(new AgentReference(version.Name, version.Version));
|
||||
|
||||
ChatClientAgent agent = new(
|
||||
chatClient: projectResponsesClient.AsIChatClient(),
|
||||
name: "JokerAgent");
|
||||
```
|
||||
|
||||
### Samples
|
||||
|
||||
- `FoundryAgents/` samples show the direct Responses path with `AIProjectClient.AsAIAgent(...)`.
|
||||
- `FoundryVersionedAgents/` samples should show native `AIProjectClient.Agents` create/get/delete flows plus `AsAIAgent(...)`.
|
||||
|
||||
### Compatibility APIs
|
||||
|
||||
Obsolete helper extensions remain only to ease migration of existing code. New samples and new guidance should not be written against them.
|
||||
|
||||
## Rejected direction
|
||||
|
||||
Do not introduce or preserve separate public wrapper types whose main purpose is to forward to `ChatClientAgent` while carrying Foundry-specific naming.
|
||||
|
||||
That approach:
|
||||
|
||||
- duplicates lifecycle concepts already present on `AIProjectClient`,
|
||||
- fragments the public API,
|
||||
- complicates samples and docs,
|
||||
- and makes migration harder by encouraging wrapper-specific affordances.
|
||||
@@ -31,6 +31,8 @@ The persistence timing and `FunctionResultContent` trimming behaviors are interr
|
||||
|
||||
- **Per-run persistence**: When messages are batched and persisted at the end of the full run, trailing `FunctionResultContent` trimming becomes necessary to match the service's behavior. Without trimming, the stored history contains `FunctionResultContent` that the service would never have stored.
|
||||
|
||||
This means the trimming feature (introduced in [PR #4792](https://github.com/microsoft/agent-framework/pull/4792)) is primarily needed as a complement to per-run persistence. The `PersistChatHistoryAtEndOfRun` setting (introduced in [PR #4762](https://github.com/microsoft/agent-framework/pull/4762)) inverts the default so that per-service-call persistence is the standard behavior, and per-run persistence is opt-in.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
- **A. Consistency**: The default behavior of `ChatHistoryProvider` should produce stored history that closely matches what the underlying AI service would store, minimizing surprise when switching between framework-managed and service-managed chat history.
|
||||
@@ -41,30 +43,33 @@ The persistence timing and `FunctionResultContent` trimming behaviors are interr
|
||||
|
||||
## Considered Options
|
||||
|
||||
- Option 1: Per-run persistence with opt-in FRC (FunctionResultContent) trimming
|
||||
- Option 2: Opt-in per-service-call persistence (via `SimulateServiceStoredChatHistory`)
|
||||
- Option 1: Default to per-run persistence with `FunctionResultContent` trimming (opt-in to per-service-call)
|
||||
- Option 2: Default to per-service-call persistence (opt-in to per-run)
|
||||
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### Option 1: Per-run persistence with opt-in FRC trimming
|
||||
### Option 1: Default to per-run persistence with `FunctionResultContent` trimming
|
||||
|
||||
Keep the current default behavior of persisting chat history only at the end of the full agent run. Add `FunctionResultContent` trimming as an opt-in behavior to improve consistency with service storage.
|
||||
Keep the current default behavior of persisting chat history only at the end of the full agent run. Add `FunctionResultContent` trimming as the default to improve consistency with service storage. Provide an opt-in setting for users who want per-service-call persistence.
|
||||
|
||||
Settings:
|
||||
- `PersistChatHistoryAtEndOfRun` = `true`
|
||||
|
||||
- Good, because runs are atomic — chat history is only updated when the full run succeeds, satisfying driver B.
|
||||
- Good, because the mental model is simple: one run = one history update, satisfying driver D.
|
||||
- Good, because trimming trailing `FunctionResultContent` improves consistency with service storage, partially satisfying driver A.
|
||||
- Good, because users can opt in to per-service-call persistence for checkpointing/recovery scenarios, satisfying drivers C and E.
|
||||
- Bad, because the default persistence timing still differs from the service's behavior (per-run vs. per-service-call), only partially satisfying driver A.
|
||||
- Bad, because if the process crashes mid-loop, all intermediate progress from the current run is lost, not satisfying driver C.
|
||||
- Bad, because this option alone does not provide a way for users to opt into per-service-call persistence, not satisfying driver E.
|
||||
- Bad, because if the process crashes mid-loop, all intermediate progress from the current run is lost, not satisfying driver C by default.
|
||||
|
||||
### Option 2: Opt-in per-service-call persistence (via `SimulateServiceStoredChatHistory`)
|
||||
### Option 2: Default to per-service-call persistence
|
||||
|
||||
Introduce an optional SimulateServiceStoredChatHistory setting to persist chat history after each individual service call within the FIC loop, matching the AI service's behavior. Trailing `FunctionResultContent` trimming is unnecessary with this approach (it is naturally handled).
|
||||
Change the default to persist chat history after each individual service call within the FIC loop, matching the AI service's behavior. Trailing `FunctionResultContent` trimming is unnecessary with this approach (it is naturally handled). Provide an opt-in setting for users who want per-run atomicity with trimming.
|
||||
|
||||
Settings:
|
||||
- `SimulateServiceStoredChatHistory` = `true`
|
||||
- `PersistChatHistoryAtEndOfRun` = `false` (default)
|
||||
|
||||
- Good, because the stored history matches the service's behavior when opting in for both timing and content, fully satisfying driver A.
|
||||
- Good, because the stored history matches the service's behavior by default for both timing and content, fully satisfying driver A.
|
||||
- Good, because intermediate progress is preserved if the process is interrupted, satisfying driver C.
|
||||
- Good, because no separate `FunctionResultContent` trimming logic is needed, reducing complexity.
|
||||
- Bad, because chat history may be left in an incomplete state if the run fails mid-loop (e.g., `FunctionCallContent` stored without corresponding `FunctionResultContent`), not satisfying driver B. A subsequent run cannot proceed without manually providing the missing `FunctionResultContent`.
|
||||
@@ -73,36 +78,39 @@ Settings:
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: **Option 2: Opt-in per-service-call persistence (via `SimulateServiceStoredChatHistory`)**. The existing per-run persistence behavior is retained as-is, requiring no changes from users. Per-service-call persistence is available as an opt-in feature via the `SimulateServiceStoredChatHistory` setting. This satisfies drivers B (atomicity) and D (simplicity) for the common case, while fully satisfying driver A (consistency) for users who opt into simulated service-stored behavior. Users who need per-service-call persistence for recoverability (driver C) can enable it explicitly.
|
||||
Chosen option: **Option 2 — Default to per-service-call persistence**, because it fully satisfies the consistency driver (A), naturally handles `FunctionResultContent` trimming without additional logic, and provides better recoverability for long-running tool-calling loops. Per-run persistence remains available via the `PersistChatHistoryAtEndOfRun` setting for users who prefer atomic run semantics.
|
||||
|
||||
### Configuration Matrix
|
||||
|
||||
The behavior depends on the combination of `UseProvidedChatClientAsIs` and `SimulateServiceStoredChatHistory`:
|
||||
The behavior depends on the combination of `UseProvidedChatClientAsIs` and `PersistChatHistoryAtEndOfRun`:
|
||||
|
||||
| `UseProvidedChatClientAsIs` | `SimulateServiceStoredChatHistory` | Behavior |
|
||||
| `UseProvidedChatClientAsIs` | `PersistChatHistoryAtEndOfRun` | Behavior |
|
||||
|---|---|---|
|
||||
| `false` (default) | `false` (default) | **Per-run persistence.** Messages are persisted at the end of the full agent run via the `ChatHistoryProvider`. |
|
||||
| `false` | `true` | **Per-service-call persistence (simulated).** A `ServiceStoredSimulatingChatClient` middleware is automatically injected into the chat client pipeline between `FunctionInvokingChatClient` and the leaf `IChatClient`. Messages are persisted after each service call. A sentinel `ConversationId` causes FIC to treat the conversation as service-managed. |
|
||||
| `true` | `false` | **Per-run persistence.** No middleware is injected because the user has provided a custom chat client stack. Messages are persisted at the end of the run. |
|
||||
| `true` | `true` | **User responsibility.** The system checks whether the custom chat client stack includes a `ServiceStoredSimulatingChatClient`. If not, a warning is emitted — the user is expected to have added their own per-service-call persistence mechanism. End-of-run persistence is skipped. |
|
||||
| `false` (default) | `false` (default) | **Per-service-call persistence.** A `ChatHistoryPersistingChatClient` middleware is automatically injected into the chat client pipeline between `FunctionInvokingChatClient` and the leaf `IChatClient`. Messages are persisted after each service call. |
|
||||
| `true` | `false` | **User responsibility.** No middleware is injected because the user has provided a custom chat client stack. The user is responsible for ensuring correct persistence behavior (e.g., by including their own persisting middleware). |
|
||||
| `false` | `true` | **Per-run persistence with marking.** A `ChatHistoryPersistingChatClient` middleware is injected, but configured to *mark* messages with metadata rather than store them immediately. At the end of the run, marked messages are stored. Trailing `FunctionResultContent` is trimmed. |
|
||||
| `true` | `true` | **Per-run persistence with warning.** The system checks whether the custom chat client stack includes a `ChatHistoryPersistingChatClient`. If not, a warning is emitted (particularly relevant for workflow handoff scenarios where trimming cannot be guaranteed). If no `ChatHistoryPersistingChatClient` is preset, all messages are stored at the end of the run, otherwise marked messages are stored. |
|
||||
|
||||
### Consequences
|
||||
|
||||
- Good, because per-run persistence is atomic by default — chat history is only updated when the full run succeeds, satisfying driver B.
|
||||
- Good, because the default mental model is simple: one run = one history update, satisfying driver D.
|
||||
- Good, because users who opt into `SimulateServiceStoredChatHistory` get stored history that matches the service's behavior for both timing and content, fully satisfying driver A.
|
||||
- Good, because per-service-call persistence preserves intermediate progress if the process is interrupted, satisfying driver C when opted in.
|
||||
- Good, because no separate `FunctionResultContent` trimming logic is needed when per-service-call persistence is active — it is naturally handled.
|
||||
- Good, because conflict detection (configurable via `ThrowOnChatHistoryProviderConflict`, `WarnOnChatHistoryProviderConflict`, `ClearOnChatHistoryProviderConflict`) prevents misconfiguration when a service returns a `ConversationId` alongside a configured `ChatHistoryProvider`.
|
||||
- Bad, because per-service-call persistence (when opted in) may leave chat history in an incomplete state if the run fails mid-loop (e.g., `FunctionCallContent` stored without corresponding `FunctionResultContent`), requiring manual recovery in rare cases.
|
||||
- Neutral, because users who want per-service-call consistency can opt in via `SimulateServiceStoredChatHistory = true`, satisfying driver E.
|
||||
- Good, because the stored history matches the service's behavior by default for both timing and content, fully satisfying consistency (driver A).
|
||||
- Good, because intermediate progress is preserved if the process is interrupted, satisfying recoverability (driver C).
|
||||
- Good, because no separate `FunctionResultContent` trimming logic is needed in the default path, reducing complexity.
|
||||
- Good, because marking persisted messages with metadata enables deduplication and aids debugging.
|
||||
- Good, because warnings for custom chat client configurations without the persisting middleware help prevent silent failures in workflow handoff scenarios.
|
||||
- Bad, because chat history may be left in an incomplete state if the run fails mid-loop (e.g., `FunctionCallContent` stored without corresponding `FunctionResultContent`), requiring manual recovery in rare cases.
|
||||
- Bad, because the mental model is more complex for the default path: a single run may produce multiple history updates.
|
||||
- Neutral, because users who prefer atomic run semantics can opt in to per-run persistence via `PersistChatHistoryAtEndOfRun = true`.
|
||||
- Neutral, because increased write frequency from per-service-call persistence may impact performance for some storage backends; this can be mitigated with a caching decorator.
|
||||
|
||||
### Implementation Notes
|
||||
|
||||
#### Conversation ID Consistency
|
||||
|
||||
We should introduce a separate `ConversationIdPersistingChatClient`, middleware which allows us to
|
||||
persist response `ConversationIds` during the FICC loop. This could be used with or without
|
||||
`ServiceStoredSimulatingChatClient`.
|
||||
The `ChatHistoryPersistingChatClient` middleware must also update the session's `ConversationId` consistently for both response-based and conversation-based service interactions, ensuring the session always reflects the latest service-provided identifier.
|
||||
|
||||
## More Information
|
||||
|
||||
- [PR #4762: Persist messages during function call loop](https://github.com/microsoft/agent-framework/pull/4762) — introduces `PersistChatHistoryAfterEachServiceCall` option and `ChatHistoryPersistingChatClient` decorator
|
||||
- [PR #4792: Trim final FRC to match service storage](https://github.com/microsoft/agent-framework/pull/4792) — introduces `StoreFinalFunctionResultContent` option and `FilterFinalFunctionResultContent` logic
|
||||
- [Issue #2889](https://github.com/microsoft/agent-framework/issues/2889) — original issue tracking chat history persistence during function call loops
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<IsReleaseCandidate>false</IsReleaseCandidate>
|
||||
<IsGenerallyAvailable>false</IsGenerallyAvailable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Solution>
|
||||
<Solution>
|
||||
<Configurations>
|
||||
<BuildType Name="Debug" />
|
||||
<BuildType Name="Publish" />
|
||||
@@ -104,8 +104,7 @@
|
||||
</Folder>
|
||||
<Folder Name="/Samples/02-agents/AgentSkills/">
|
||||
<File Path="samples/02-agents/AgentSkills/README.md" />
|
||||
<Project Path="samples/02-agents/AgentSkills/Agent_Step01_FileBasedSkills/Agent_Step01_FileBasedSkills.csproj" />
|
||||
<Project Path="samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/Agent_Step02_CodeDefinedSkills.csproj" />
|
||||
<Project Path="samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Agent_Step01_BasicSkills.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/02-agents/AGUI/Step05_StateManagement/">
|
||||
<Project Path="samples/02-agents/AGUI/Step05_StateManagement/Client/Client.csproj" />
|
||||
@@ -122,34 +121,6 @@
|
||||
<Project Path="samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Agent_Anthropic_Step03_UsingFunctionTools.csproj" />
|
||||
<Project Path="samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/02-agents/AgentsWithFoundry/">
|
||||
<File Path="samples/02-agents/AgentsWithFoundry/README.md" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Agent_Step00_FoundryAgentLifecycle.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Agent_Step01_Basics.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Agent_Step02.1_MultiturnConversation.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Agent_Step02.2_MultiturnWithServerConversations.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Agent_Step07_Observability.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Agent_Step08_DependencyInjection.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Agent_Step09_UsingMcpClientAsTools.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Agent_Step10_UsingImages.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Agent_Step11_AsFunctionTool.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Agent_Step12_Middleware.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/Agent_Step13_Plugins.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Agent_Step14_CodeInterpreter.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Agent_Step15_ComputerUse.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Agent_Step16_FileSearch.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Agent_Step17_OpenAPITools.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Agent_Step18_BingCustomSearch.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Agent_Step19_SharePoint.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Agent_Step20_MicrosoftFabric.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj" />
|
||||
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/02-agents/AgentWithMemory/">
|
||||
<File Path="samples/02-agents/AgentWithMemory/README.md" />
|
||||
<Project Path="samples/02-agents/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/AgentWithMemory_Step01_ChatHistoryMemory.csproj" />
|
||||
@@ -172,6 +143,35 @@
|
||||
<Project Path="samples/02-agents/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/AgentWithRAG_Step03_CustomRAGDataSource.csproj" />
|
||||
<Project Path="samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/02-agents/FoundryAgents/">
|
||||
<File Path="samples/02-agents/FoundryAgents/README.md" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/FoundryAgents_Step16_FileSearch.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/FoundryAgents_Step17_OpenAPITools.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/FoundryAgents_Step18_BingCustomSearch.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/FoundryAgents_Step19_SharePoint.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/FoundryAgents_Step20_MicrosoftFabric.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/FoundryAgents_Step21_WebSearch.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj" />
|
||||
<Project Path="samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/FoundryAgents_Step23_LocalMCP.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/02-agents/ModelContextProtocol/">
|
||||
<File Path="samples/02-agents/ModelContextProtocol/README.md" />
|
||||
<Project Path="samples/02-agents/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj" />
|
||||
@@ -318,8 +318,8 @@
|
||||
<Folder Name="/Samples/05-end-to-end/AspNetAgentAuthorization/">
|
||||
<File Path="samples/05-end-to-end/AspNetAgentAuthorization/docker-compose.yml" />
|
||||
<File Path="samples/05-end-to-end/AspNetAgentAuthorization/README.md" />
|
||||
<Project Path="samples/05-end-to-end/AspNetAgentAuthorization/RazorWebClient/RazorWebClient.csproj" />
|
||||
<Project Path="samples/05-end-to-end/AspNetAgentAuthorization/Service/Service.csproj" />
|
||||
<Project Path="samples/05-end-to-end/AspNetAgentAuthorization/RazorWebClient/RazorWebClient.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Solution Items/">
|
||||
<File Path=".editorconfig" />
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
<IsPackable>true</IsPackable>
|
||||
|
||||
<!-- Package validation. Baseline Version should be the latest version available on NuGet. -->
|
||||
<PackageValidationBaselineVersion>1.0.0-rc4</PackageValidationBaselineVersion>
|
||||
<!-- Enable validation for RC packages and GA packages -->
|
||||
<EnablePackageValidation Condition="'$(IsReleaseCandidate)' == 'true' OR '$(IsGenerallyAvailable)' == 'true'">true</EnablePackageValidation>
|
||||
<PackageValidationBaselineVersion>0.0.1</PackageValidationBaselineVersion>
|
||||
<!-- Validate assembly attributes only for Publish builds -->
|
||||
<NoWarn Condition="'$(Configuration)' != 'Publish'">$(NoWarn);CP0003</NoWarn>
|
||||
<!-- Do not validate reference assemblies -->
|
||||
|
||||
@@ -70,7 +70,7 @@ while ((input = Console.ReadLine()) != null && !input.Equals("exit", StringCompa
|
||||
|
||||
if (approvalRequest.AdditionalProperties != null)
|
||||
{
|
||||
approvalResponse.AdditionalProperties = [];
|
||||
approvalResponse.AdditionalProperties = new AdditionalPropertiesDictionary();
|
||||
foreach (var kvp in approvalRequest.AdditionalProperties)
|
||||
{
|
||||
approvalResponse.AdditionalProperties[kvp.Key] = kvp.Value;
|
||||
|
||||
+4
-4
@@ -131,9 +131,9 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent
|
||||
transformedContents ??= CopyContentsUpToIndex(message.Contents, contentIndex);
|
||||
approvalCalls.Remove(functionResult.CallId);
|
||||
}
|
||||
else
|
||||
else if (transformedContents != null)
|
||||
{
|
||||
transformedContents?.Add(content);
|
||||
transformedContents.Add(content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,10 +155,10 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent
|
||||
result ??= CopyMessagesUpToIndex(messages, messageIndex);
|
||||
result.Add(newMessage);
|
||||
}
|
||||
else
|
||||
else if (result != null)
|
||||
{
|
||||
// We're already copying messages, so copy this unchanged message too
|
||||
result?.Add(message);
|
||||
result.Add(message);
|
||||
}
|
||||
// If result is null, we haven't made any changes yet, so keep processing
|
||||
}
|
||||
|
||||
+20
-8
@@ -57,10 +57,16 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
|
||||
throw new InvalidOperationException("Invalid request_approval tool call");
|
||||
}
|
||||
|
||||
var request = (toolCall.Arguments.TryGetValue("request", out var reqObj) &&
|
||||
var request = toolCall.Arguments.TryGetValue("request", out var reqObj) &&
|
||||
reqObj is JsonElement argsElement &&
|
||||
argsElement.Deserialize(jsonSerializerOptions.GetTypeInfo(typeof(ApprovalRequest))) is ApprovalRequest approvalRequest &&
|
||||
approvalRequest != null ? approvalRequest : null) ?? throw new InvalidOperationException("Failed to deserialize approval request from tool call");
|
||||
approvalRequest != null ? approvalRequest : null;
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to deserialize approval request from tool call");
|
||||
}
|
||||
|
||||
return new ToolApprovalRequestContent(
|
||||
requestId: request.ApprovalId,
|
||||
new FunctionCallContent(
|
||||
@@ -71,11 +77,17 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
|
||||
|
||||
private static ToolApprovalResponseContent ConvertToolResultToApprovalResponse(FunctionResultContent result, ToolApprovalRequestContent approval, JsonSerializerOptions jsonSerializerOptions)
|
||||
{
|
||||
var approvalResponse = (result.Result is JsonElement je ?
|
||||
var approvalResponse = result.Result is JsonElement je ?
|
||||
(ApprovalResponse?)je.Deserialize(jsonSerializerOptions.GetTypeInfo(typeof(ApprovalResponse))) :
|
||||
result.Result is string str ?
|
||||
(ApprovalResponse?)JsonSerializer.Deserialize(str, jsonSerializerOptions.GetTypeInfo(typeof(ApprovalResponse))) :
|
||||
result.Result as ApprovalResponse) ?? throw new InvalidOperationException("Failed to deserialize approval response from tool result");
|
||||
result.Result as ApprovalResponse;
|
||||
|
||||
if (approvalResponse == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to deserialize approval response from tool result");
|
||||
}
|
||||
|
||||
return approval.CreateResponse(approvalResponse.Approved);
|
||||
}
|
||||
#pragma warning restore MEAI001
|
||||
@@ -109,7 +121,7 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
|
||||
// Track approval ID to original call ID mapping
|
||||
_ = new Dictionary<string, string>();
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||
Dictionary<string, ToolApprovalRequestContent> trackedRequestApprovalToolCalls = []; // Remote approvals
|
||||
Dictionary<string, ToolApprovalRequestContent> trackedRequestApprovalToolCalls = new(); // Remote approvals
|
||||
for (int messageIndex = 0; messageIndex < messages.Count; messageIndex++)
|
||||
{
|
||||
var message = messages[messageIndex];
|
||||
@@ -134,7 +146,7 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
|
||||
});
|
||||
}
|
||||
else if (content is FunctionResultContent toolResult &&
|
||||
trackedRequestApprovalToolCalls.TryGetValue(toolResult.CallId, out var approval))
|
||||
trackedRequestApprovalToolCalls.TryGetValue(toolResult.CallId, out var approval) == true)
|
||||
{
|
||||
result ??= CopyMessagesUpToIndex(messages, messageIndex);
|
||||
transformedContents ??= CopyContentsUpToIndex(message.Contents, j);
|
||||
@@ -149,9 +161,9 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent
|
||||
AdditionalProperties = message.AdditionalProperties
|
||||
});
|
||||
}
|
||||
else
|
||||
else if (result != null)
|
||||
{
|
||||
result?.Add(message);
|
||||
result.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,9 +72,10 @@ internal sealed class StatefulAgent<TState> : DelegatingAIAgent
|
||||
if (content is DataContent dataContent && dataContent.MediaType == "application/json")
|
||||
{
|
||||
// Deserialize the state
|
||||
if (JsonSerializer.Deserialize(
|
||||
TState? newState = JsonSerializer.Deserialize(
|
||||
dataContent.Data.Span,
|
||||
this._jsonSerializerOptions.GetTypeInfo(typeof(TState))) is TState newState)
|
||||
this._jsonSerializerOptions.GetTypeInfo(typeof(TState))) as TState;
|
||||
if (newState != null)
|
||||
{
|
||||
this.State = newState;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ using Azure.AI.Projects;
|
||||
using Azure.AI.Projects.Agents;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.AzureAI;
|
||||
|
||||
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
|
||||
var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
@@ -31,18 +30,14 @@ var createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: J
|
||||
// agentVersion.Name = <agentName>
|
||||
|
||||
// You can use an AIAgent with an already created server side agent version.
|
||||
FoundryAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
|
||||
AIAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
|
||||
|
||||
// You can also create another AIAgent version by providing the same name with a different definition.
|
||||
AgentVersion newJokerAgentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
|
||||
JokerName,
|
||||
new AgentVersionCreationOptions(new PromptAgentDefinition(model: deploymentName) { Instructions = "You are extremely hilarious at telling jokes." }));
|
||||
FoundryAgent newJokerAgent = aiProjectClient.AsAIAgent(newJokerAgentVersion);
|
||||
AIAgent newJokerAgent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
|
||||
|
||||
// You can also get the AIAgent latest version just providing its name.
|
||||
AgentRecord jokerAgentRecord = await aiProjectClient.Agents.GetAgentAsync(JokerName);
|
||||
FoundryAgent jokerAgentLatest = aiProjectClient.AsAIAgent(jokerAgentRecord);
|
||||
AgentVersion latestAgentVersion = jokerAgentRecord.GetLatestVersion();
|
||||
AIAgent jokerAgentLatest = await aiProjectClient.GetAIAgentAsync(name: JokerName);
|
||||
var latestAgentVersion = jokerAgentLatest.GetService<AgentVersion>()!;
|
||||
|
||||
// The AIAgent version can be accessed via the GetService method.
|
||||
Console.WriteLine($"Latest agent version id: {latestAgentVersion.Id}");
|
||||
|
||||
-4
@@ -14,10 +14,6 @@
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SubprocessScriptRunner.cs" Link="SubprocessScriptRunner.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to use Agent Skills with a ChatClientAgent.
|
||||
// Agent Skills are modular packages of instructions and resources that extend an agent's capabilities.
|
||||
// Skills follow the progressive disclosure pattern: advertise -> load -> read resources.
|
||||
//
|
||||
// This sample includes the expense-report skill:
|
||||
// - Policy-based expense filing with references and assets
|
||||
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using OpenAI.Responses;
|
||||
|
||||
// --- Configuration ---
|
||||
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
|
||||
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
|
||||
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
// --- Skills Provider ---
|
||||
// Discovers skills from the 'skills' directory and makes them available to the agent
|
||||
var skillsProvider = new FileAgentSkillsProvider(skillPath: Path.Combine(AppContext.BaseDirectory, "skills"));
|
||||
|
||||
// --- Agent Setup ---
|
||||
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
|
||||
.GetResponsesClient()
|
||||
.AsAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Name = "SkillsAgent",
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a helpful assistant.",
|
||||
},
|
||||
AIContextProviders = [skillsProvider],
|
||||
},
|
||||
model: deploymentName);
|
||||
|
||||
// --- Example 1: Expense policy question (loads FAQ resource) ---
|
||||
Console.WriteLine("Example 1: Checking expense policy FAQ");
|
||||
Console.WriteLine("---------------------------------------");
|
||||
AgentResponse response1 = await agent.RunAsync("Are tips reimbursable? I left a 25% tip on a taxi ride and want to know if that's covered.");
|
||||
Console.WriteLine($"Agent: {response1.Text}\n");
|
||||
|
||||
// --- Example 2: Filing an expense report (multi-turn with template asset) ---
|
||||
Console.WriteLine("Example 2: Filing an expense report");
|
||||
Console.WriteLine("---------------------------------------");
|
||||
AgentSession session = await agent.CreateSessionAsync();
|
||||
AgentResponse response2 = await agent.RunAsync("I had 3 client dinners and a $1,200 flight last week. Return a draft expense report and ask about any missing details.",
|
||||
session);
|
||||
Console.WriteLine($"Agent: {response2.Text}\n");
|
||||
@@ -0,0 +1,63 @@
|
||||
# Agent Skills Sample
|
||||
|
||||
This sample demonstrates how to use **Agent Skills** with a `ChatClientAgent` in the Microsoft Agent Framework.
|
||||
|
||||
## What are Agent Skills?
|
||||
|
||||
Agent Skills are modular packages of instructions and resources that enable AI agents to perform specialized tasks. They follow the [Agent Skills specification](https://agentskills.io/) and implement the progressive disclosure pattern:
|
||||
|
||||
1. **Advertise**: Skills are advertised with name + description (~100 tokens per skill)
|
||||
2. **Load**: Full instructions are loaded on-demand via `load_skill` tool
|
||||
3. **Resources**: References and other files loaded via `read_skill_resource` tool
|
||||
|
||||
## Skills Included
|
||||
|
||||
### expense-report
|
||||
Policy-based expense filing with spending limits, receipt requirements, and approval workflows.
|
||||
- `references/POLICY_FAQ.md` — Detailed expense policy Q&A
|
||||
- `assets/expense-report-template.md` — Submission template
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
Agent_Step01_BasicSkills/
|
||||
├── Program.cs
|
||||
├── Agent_Step01_BasicSkills.csproj
|
||||
└── skills/
|
||||
└── expense-report/
|
||||
├── SKILL.md
|
||||
├── references/
|
||||
│ └── POLICY_FAQ.md
|
||||
└── assets/
|
||||
└── expense-report-template.md
|
||||
```
|
||||
|
||||
## Running the Sample
|
||||
|
||||
### Prerequisites
|
||||
- .NET 10.0 SDK
|
||||
- Azure OpenAI endpoint with a deployed model
|
||||
|
||||
### Setup
|
||||
1. Set environment variables:
|
||||
```bash
|
||||
export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/"
|
||||
export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
2. Run the sample:
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
The sample runs two examples:
|
||||
|
||||
1. **Expense policy FAQ** — Asks about tip reimbursement; the agent loads the expense-report skill and reads the FAQ resource
|
||||
2. **Filing an expense report** — Multi-turn conversation to draft an expense report using the template asset
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Agent Skills Specification](https://agentskills.io/)
|
||||
- [Microsoft Agent Framework Documentation](../../../../../docs/)
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: expense-report
|
||||
description: File and validate employee expense reports according to Contoso company policy. Use when asked about expense submissions, reimbursement rules, receipt requirements, spending limits, or expense categories.
|
||||
metadata:
|
||||
author: contoso-finance
|
||||
version: "2.1"
|
||||
---
|
||||
|
||||
# Expense Report
|
||||
|
||||
## Categories and Limits
|
||||
|
||||
| Category | Limit | Receipt | Approval |
|
||||
|---|---|---|---|
|
||||
| Meals — solo | $50/day | >$25 | No |
|
||||
| Meals — team/client | $75/person | Always | Manager if >$200 total |
|
||||
| Lodging | $250/night | Always | Manager if >3 nights |
|
||||
| Ground transport | $100/day | >$15 | No |
|
||||
| Airfare | Economy | Always | Manager; VP if >$1,500 |
|
||||
| Conference/training | $2,000/event | Always | Manager + L&D |
|
||||
| Office supplies | $100 | Yes | No |
|
||||
| Software/subscriptions | $50/month | Yes | Manager if >$200/year |
|
||||
|
||||
## Filing Process
|
||||
|
||||
1. Collect receipts — must show vendor, date, amount, payment method.
|
||||
2. Categorize per table above.
|
||||
3. Use template: [assets/expense-report-template.md](assets/expense-report-template.md).
|
||||
4. For client/team meals: list attendee names and business purpose.
|
||||
5. Submit — auto-approved if <$500; manager if $500–$2,000; VP if >$2,000.
|
||||
6. Reimbursement: 10 business days via direct deposit.
|
||||
|
||||
## Policy Rules
|
||||
|
||||
- Submit within 30 days of transaction.
|
||||
- Alcohol is never reimbursable.
|
||||
- Foreign currency: convert to USD at transaction-date rate; note original currency and amount.
|
||||
- Mixed personal/business travel: only business portion reimbursable; provide comparison quotes.
|
||||
- Lost receipts (>$25): file Lost Receipt Affidavit from Finance. Max 2 per quarter.
|
||||
- For policy questions not covered above, consult the FAQ: [references/POLICY_FAQ.md](references/POLICY_FAQ.md). Answers should be based on what this document and the FAQ state.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
# Expense Report Template
|
||||
|
||||
| Date | Category | Vendor | Description | Amount (USD) | Original Currency | Original Amount | Attendees | Business Purpose | Receipt Attached |
|
||||
|------|----------|--------|-------------|--------------|-------------------|-----------------|-----------|------------------|------------------|
|
||||
| | | | | | | | | | Yes or No |
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
# Expense Policy — Frequently Asked Questions
|
||||
|
||||
## Meals
|
||||
|
||||
**Q: Can I expense coffee or snacks during the workday?**
|
||||
A: Daily coffee/snacks under $10 are not reimbursable (considered personal). Coffee purchased during a client meeting or team working session is reimbursable as a team meal.
|
||||
|
||||
**Q: What if a team dinner exceeds the per-person limit?**
|
||||
A: The $75/person limit applies as a guideline. Overages up to 20% are accepted with a written justification (e.g., "client dinner at venue chosen by client"). Overages beyond 20% require pre-approval from your VP.
|
||||
|
||||
**Q: Do I need to list every attendee?**
|
||||
A: Yes. For client meals, list the client's name and company. For team meals, list all employee names. For groups over 10, you may attach a separate attendee list.
|
||||
|
||||
## Travel
|
||||
|
||||
**Q: Can I book a premium economy or business class flight?**
|
||||
A: Economy class is the standard. Premium economy is allowed for flights over 6 hours. Business class requires VP pre-approval and is generally reserved for flights over 10 hours or medical accommodation.
|
||||
|
||||
**Q: What about ride-sharing (Uber/Lyft) vs. rental cars?**
|
||||
A: Use ride-sharing for trips under 30 miles round-trip. Rent a car for multi-day travel or when ride-sharing would exceed $100/day. Always choose the compact/standard category unless traveling with 3+ people.
|
||||
|
||||
**Q: Are tips reimbursable?**
|
||||
A: Tips up to 20% are reimbursable for meals, taxi/ride-share, and hotel housekeeping. Tips above 20% require justification.
|
||||
|
||||
## Lodging
|
||||
|
||||
**Q: What if the $250/night limit isn't enough for the city I'm visiting?**
|
||||
A: For high-cost cities (New York, San Francisco, London, Tokyo, Sydney), the limit is automatically increased to $350/night. No additional approval is needed. For other locations where rates are unusually high (e.g., during a major conference), request a per-trip exception from your manager before booking.
|
||||
|
||||
**Q: Can I stay with friends/family instead and get a per-diem?**
|
||||
A: No. Contoso reimburses actual lodging costs only, not per-diems.
|
||||
|
||||
## Subscriptions and Software
|
||||
|
||||
**Q: Can I expense a personal productivity tool?**
|
||||
A: Software must be directly related to your job function. Tools like IDE licenses, design software, or project management apps are reimbursable. General productivity apps (note-taking, personal calendar) are not, unless your manager confirms a business need in writing.
|
||||
|
||||
**Q: What about annual subscriptions?**
|
||||
A: Annual subscriptions over $200 require manager approval before purchase. Submit the approval email with your expense report.
|
||||
|
||||
## Receipts and Documentation
|
||||
|
||||
**Q: My receipt is faded/damaged. What do I do?**
|
||||
A: Try to obtain a duplicate from the vendor. If not possible, submit a Lost Receipt Affidavit (available from the Finance SharePoint site). You're limited to 2 affidavits per quarter.
|
||||
|
||||
**Q: Do I need a receipt for parking meters or tolls?**
|
||||
A: For amounts under $15, no receipt is required — just note the date, location, and amount. For $15 and above, a receipt or bank/credit card statement excerpt is required.
|
||||
|
||||
## Approval and Reimbursement
|
||||
|
||||
**Q: My manager is on leave. Who approves my report?**
|
||||
A: Expense reports can be approved by your skip-level manager or any manager designated as an alternate approver in the expense system.
|
||||
|
||||
**Q: Can I submit expenses from a previous quarter?**
|
||||
A: The standard 30-day window applies. Expenses older than 30 days require a written explanation and VP approval. Expenses older than 90 days are not reimbursable except in extraordinary circumstances (extended leave, medical emergency) with CFO approval.
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to use file-based Agent Skills with a ChatClientAgent.
|
||||
// Skills are discovered from SKILL.md files on disk and follow the progressive disclosure pattern:
|
||||
// 1. Advertise — skill names and descriptions in the system prompt
|
||||
// 2. Load — full instructions loaded on demand via load_skill tool
|
||||
// 3. Read resources — reference files read via read_skill_resource tool
|
||||
// 4. Run scripts — scripts executed via run_skill_script tool with a subprocess executor
|
||||
//
|
||||
// This sample uses a unit-converter skill that converts between miles, kilometers, pounds, and kilograms.
|
||||
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using OpenAI.Responses;
|
||||
|
||||
// --- Configuration ---
|
||||
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
|
||||
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
// --- Skills Provider ---
|
||||
// Discovers skills from the 'skills' directory containing SKILL.md files.
|
||||
// The script runner runs file-based scripts (e.g. Python) as local subprocesses.
|
||||
var skillsProvider = new AgentSkillsProvider(
|
||||
Path.Combine(AppContext.BaseDirectory, "skills"),
|
||||
SubprocessScriptRunner.RunAsync);
|
||||
// --- Agent Setup ---
|
||||
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
|
||||
.GetResponsesClient()
|
||||
.AsAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Name = "UnitConverterAgent",
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a helpful assistant that can convert units.",
|
||||
},
|
||||
AIContextProviders = [skillsProvider],
|
||||
},
|
||||
model: deploymentName);
|
||||
|
||||
// --- Example: Unit conversion ---
|
||||
Console.WriteLine("Converting units with file-based skills");
|
||||
Console.WriteLine(new string('-', 60));
|
||||
|
||||
AgentResponse response = await agent.RunAsync(
|
||||
"How many kilometers is a marathon (26.2 miles)? And how many pounds is 75 kilograms?");
|
||||
|
||||
Console.WriteLine($"Agent: {response.Text}");
|
||||
@@ -1,51 +0,0 @@
|
||||
# File-Based Agent Skills Sample
|
||||
|
||||
This sample demonstrates how to use **file-based Agent Skills** with a `ChatClientAgent`.
|
||||
|
||||
## What it demonstrates
|
||||
|
||||
- Discovering skills from `SKILL.md` files on disk via `AgentFileSkillsSource`
|
||||
- The progressive disclosure pattern: advertise → load → read resources → run scripts
|
||||
- Using the `AgentSkillsProvider` constructor with a skill directory path and script executor
|
||||
- Running file-based scripts (Python) via a subprocess-based executor
|
||||
|
||||
## Skills Included
|
||||
|
||||
### unit-converter
|
||||
|
||||
Converts between common units (miles↔km, pounds↔kg) using a multiplication factor.
|
||||
|
||||
- `references/conversion-table.md` — Conversion factor table
|
||||
- `scripts/convert.py` — Python script that performs the conversion
|
||||
|
||||
## Running the Sample
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- .NET 10.0 SDK
|
||||
- Azure OpenAI endpoint with a deployed model
|
||||
- Python 3 installed and available as `python3` on your PATH
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/"
|
||||
export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
|
||||
### Expected Output
|
||||
|
||||
```
|
||||
Converting units with file-based skills
|
||||
------------------------------------------------------------
|
||||
Agent: Here are your conversions:
|
||||
|
||||
1. **26.2 miles → 42.16 km** (a marathon distance)
|
||||
2. **75 kg → 165.35 lbs**
|
||||
```
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
---
|
||||
name: unit-converter
|
||||
description: Convert between common units using a multiplication factor. Use when asked to convert miles, kilometers, pounds, or kilograms.
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
When the user requests a unit conversion:
|
||||
1. First, review `references/conversion-table.md` to find the correct factor
|
||||
2. Run the `scripts/convert.py` script with `--value <number> --factor <factor>` (e.g. `--value 26.2 --factor 1.60934`)
|
||||
3. Present the converted value clearly with both units
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
# Conversion Tables
|
||||
|
||||
Formula: **result = value × factor**
|
||||
|
||||
| From | To | Factor |
|
||||
|-------------|-------------|----------|
|
||||
| miles | kilometers | 1.60934 |
|
||||
| kilometers | miles | 0.621371 |
|
||||
| pounds | kilograms | 0.453592 |
|
||||
| kilograms | pounds | 2.20462 |
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
# Unit conversion script
|
||||
# Converts a value using a multiplication factor: result = value × factor
|
||||
#
|
||||
# Usage:
|
||||
# python scripts/convert.py --value 26.2 --factor 1.60934
|
||||
# python scripts/convert.py --value 75 --factor 2.20462
|
||||
|
||||
import argparse
|
||||
import json
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Convert a value using a multiplication factor.",
|
||||
epilog="Examples:\n"
|
||||
" python scripts/convert.py --value 26.2 --factor 1.60934\n"
|
||||
" python scripts/convert.py --value 75 --factor 2.20462",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
parser.add_argument("--value", type=float, required=True, help="The numeric value to convert.")
|
||||
parser.add_argument("--factor", type=float, required=True, help="The conversion factor from the table.")
|
||||
args = parser.parse_args()
|
||||
|
||||
result = round(args.value * args.factor, 4)
|
||||
print(json.dumps({"value": args.value, "factor": args.factor, "result": result}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to define Agent Skills entirely in code using AgentInlineSkill.
|
||||
// No SKILL.md files are needed — skills, resources, and scripts are all defined programmatically.
|
||||
//
|
||||
// Three approaches are shown using a unit-converter skill:
|
||||
// 1. Static resources — inline content provided via AddResource
|
||||
// 2. Dynamic resources — computed at runtime via a factory delegate
|
||||
// 3. Code scripts — executable delegates the agent can invoke directly
|
||||
|
||||
using System.Text.Json;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using OpenAI.Responses;
|
||||
|
||||
// --- Configuration ---
|
||||
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
|
||||
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
// --- Build the code-defined skill ---
|
||||
var unitConverterSkill = new AgentInlineSkill(
|
||||
name: "unit-converter",
|
||||
description: "Convert between common units using a multiplication factor. Use when asked to convert miles, kilometers, pounds, or kilograms.",
|
||||
instructions: """
|
||||
Use this skill when the user asks to convert between units.
|
||||
|
||||
1. Review the conversion-table resource to find the factor for the requested conversion.
|
||||
2. Check the conversion-policy resource for rounding and formatting rules.
|
||||
3. Use the convert script, passing the value and factor from the table.
|
||||
""")
|
||||
// 1. Static Resource: conversion tables
|
||||
.AddResource(
|
||||
"conversion-table",
|
||||
"""
|
||||
# Conversion Tables
|
||||
|
||||
Formula: **result = value × factor**
|
||||
|
||||
| From | To | Factor |
|
||||
|-------------|-------------|----------|
|
||||
| miles | kilometers | 1.60934 |
|
||||
| kilometers | miles | 0.621371 |
|
||||
| pounds | kilograms | 0.453592 |
|
||||
| kilograms | pounds | 2.20462 |
|
||||
""")
|
||||
// 2. Dynamic Resource: conversion policy (computed at runtime)
|
||||
.AddResource("conversion-policy", () =>
|
||||
{
|
||||
const int Precision = 4;
|
||||
return $"""
|
||||
# Conversion Policy
|
||||
|
||||
**Decimal places:** {Precision}
|
||||
**Format:** Always show both the original and converted values with units
|
||||
**Generated at:** {DateTime.UtcNow:O}
|
||||
""";
|
||||
})
|
||||
// 3. Code Script: convert
|
||||
.AddScript("convert", (double value, double factor) =>
|
||||
{
|
||||
double result = Math.Round(value * factor, 4);
|
||||
return JsonSerializer.Serialize(new { value, factor, result });
|
||||
});
|
||||
|
||||
// --- Skills Provider ---
|
||||
var skillsProvider = new AgentSkillsProvider(unitConverterSkill);
|
||||
|
||||
// --- Agent Setup ---
|
||||
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
|
||||
.GetResponsesClient()
|
||||
.AsAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Name = "UnitConverterAgent",
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a helpful assistant that can convert units.",
|
||||
},
|
||||
AIContextProviders = [skillsProvider],
|
||||
},
|
||||
model: deploymentName);
|
||||
|
||||
// --- Example: Unit conversion ---
|
||||
Console.WriteLine("Converting units with code-defined skills");
|
||||
Console.WriteLine(new string('-', 60));
|
||||
|
||||
AgentResponse response = await agent.RunAsync(
|
||||
"How many kilometers is a marathon (26.2 miles)? And how many pounds is 75 kilograms?");
|
||||
|
||||
Console.WriteLine($"Agent: {response.Text}");
|
||||
@@ -1,52 +0,0 @@
|
||||
# Code-Defined Agent Skills Sample
|
||||
|
||||
This sample demonstrates how to define **Agent Skills entirely in code** using `AgentInlineSkill`.
|
||||
|
||||
## What it demonstrates
|
||||
|
||||
- Creating skills programmatically with `AgentInlineSkill` — no SKILL.md files needed
|
||||
- **Static resources** via `AddResource` with inline content
|
||||
- **Dynamic resources** via `AddResource` with a factory delegate (computed at runtime)
|
||||
- **Code scripts** via `AddScript` with a delegate handler
|
||||
- Using the `AgentSkillsProvider` constructor with inline skills
|
||||
|
||||
## Skills Included
|
||||
|
||||
### unit-converter (code-defined)
|
||||
|
||||
Converts between common units using multiplication factors. Defined entirely in C# code:
|
||||
|
||||
- `conversion-table` — Static resource with factor table
|
||||
- `conversion-policy` — Dynamic resource with formatting rules (generated at runtime)
|
||||
- `convert` — Script that performs `value × factor` conversion
|
||||
|
||||
## Running the Sample
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- .NET 10.0 SDK
|
||||
- Azure OpenAI endpoint with a deployed model
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/"
|
||||
export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
|
||||
### Expected Output
|
||||
|
||||
```
|
||||
Converting units with code-defined skills
|
||||
------------------------------------------------------------
|
||||
Agent: Here are your conversions:
|
||||
|
||||
1. **26.2 miles → 42.16 km** (a marathon distance)
|
||||
2. **75 kg → 165.35 lbs**
|
||||
```
|
||||
@@ -1,24 +1,7 @@
|
||||
# AgentSkills Samples
|
||||
|
||||
Samples demonstrating Agent Skills capabilities. Each sample shows a different way to define and use skills.
|
||||
Samples demonstrating Agent Skills capabilities.
|
||||
|
||||
| Sample | Description |
|
||||
|--------|-------------|
|
||||
| [Agent_Step01_FileBasedSkills](Agent_Step01_FileBasedSkills/) | Define skills as `SKILL.md` files on disk with reference documents. Uses a unit-converter skill. |
|
||||
| [Agent_Step02_CodeDefinedSkills](Agent_Step02_CodeDefinedSkills/) | Define skills entirely in C# code using `AgentInlineSkill`, with static/dynamic resources and scripts. |
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### File-Based vs Code-Defined Skills
|
||||
|
||||
| Aspect | File-Based | Code-Defined |
|
||||
|--------|-----------|--------------|
|
||||
| Definition | `SKILL.md` files on disk | `AgentInlineSkill` instances in C# |
|
||||
| Resources | All files in skill directory (filtered by extension) | `AddResource` (static value or delegate-backed) |
|
||||
| Scripts | Supported via script executor delegate | `AddScript` delegates |
|
||||
| Discovery | Automatic from directory path | Explicit via constructor |
|
||||
| Dynamic content | No (static files only) | Yes (factory delegates) |
|
||||
| Reusability | Copy skill directory | Inline or shared instances |
|
||||
|
||||
For single-source scenarios, use the `AgentSkillsProvider` constructors directly. To combine multiple skill types, use the `AgentSkillsProviderBuilder`.
|
||||
|
||||
| [Agent_Step01_BasicSkills](Agent_Step01_BasicSkills/) | Using Agent Skills with a ChatClientAgent, including progressive disclosure and skill resources |
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// Sample subprocess-based skill script runner.
|
||||
// Executes file-based skill scripts as local subprocesses.
|
||||
// This is provided for demonstration purposes only.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Executes file-based skill scripts as local subprocesses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This runner uses the script's absolute path, converts the arguments
|
||||
/// to CLI flags, and returns captured output. It is intended for
|
||||
/// demonstration purposes only.
|
||||
/// </remarks>
|
||||
internal static class SubprocessScriptRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs a skill script as a local subprocess.
|
||||
/// </summary>
|
||||
public static async Task<object?> RunAsync(
|
||||
AgentFileSkill skill,
|
||||
AgentFileSkillScript script,
|
||||
AIFunctionArguments arguments,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!File.Exists(script.FullPath))
|
||||
{
|
||||
return $"Error: Script file not found: {script.FullPath}";
|
||||
}
|
||||
|
||||
string extension = Path.GetExtension(script.FullPath);
|
||||
string? interpreter = extension switch
|
||||
{
|
||||
".py" => "python3",
|
||||
".js" => "node",
|
||||
".sh" => "bash",
|
||||
".ps1" => "pwsh",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WorkingDirectory = Path.GetDirectoryName(script.FullPath) ?? ".",
|
||||
};
|
||||
|
||||
if (interpreter is not null)
|
||||
{
|
||||
startInfo.FileName = interpreter;
|
||||
startInfo.ArgumentList.Add(script.FullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
startInfo.FileName = script.FullPath;
|
||||
}
|
||||
|
||||
if (arguments is not null)
|
||||
{
|
||||
foreach (var (key, value) in arguments)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
if (boolValue)
|
||||
{
|
||||
startInfo.ArgumentList.Add(NormalizeKey(key));
|
||||
}
|
||||
}
|
||||
else if (value is not null)
|
||||
{
|
||||
startInfo.ArgumentList.Add(NormalizeKey(key));
|
||||
startInfo.ArgumentList.Add(value.ToString()!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process? process = null;
|
||||
try
|
||||
{
|
||||
process = Process.Start(startInfo);
|
||||
if (process is null)
|
||||
{
|
||||
return $"Error: Failed to start process for script '{script.Name}'.";
|
||||
}
|
||||
|
||||
Task<string> outputTask = process.StandardOutput.ReadToEndAsync(cancellationToken);
|
||||
Task<string> errorTask = process.StandardError.ReadToEndAsync(cancellationToken);
|
||||
|
||||
await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
string output = await outputTask.ConfigureAwait(false);
|
||||
string error = await errorTask.ConfigureAwait(false);
|
||||
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
output += $"\nStderr:\n{error}";
|
||||
}
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
output += $"\nScript exited with code {process.ExitCode}";
|
||||
}
|
||||
|
||||
return string.IsNullOrEmpty(output) ? "(no output)" : output.Trim();
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Kill the process on cancellation to avoid leaving orphaned subprocesses.
|
||||
process?.Kill(entireProcessTree: true);
|
||||
throw;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Error: Failed to execute script '{script.Name}': {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
process?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a parameter key to a consistent --flag format.
|
||||
/// Models may return keys with or without leading dashes (e.g., "value" vs "--value").
|
||||
/// </summary>
|
||||
private static string NormalizeKey(string key) => "--" + key.TrimStart('-');
|
||||
}
|
||||
+10
-3
@@ -5,13 +5,20 @@
|
||||
using Anthropic;
|
||||
using Anthropic.Core;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set.");
|
||||
var model = Environment.GetEnvironmentVariable("ANTHROPIC_CHAT_MODEL_NAME") ?? "claude-haiku-4-5";
|
||||
|
||||
AIAgent agent =
|
||||
new AnthropicClient(new ClientOptions { ApiKey = apiKey })
|
||||
AIAgent agent = new AnthropicClient(new ClientOptions { ApiKey = apiKey })
|
||||
.AsAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker");
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
|
||||
var response = await agent.RunAsync("Tell me a joke about a pirate.");
|
||||
Console.WriteLine(response);
|
||||
|
||||
// Invoke the agent with streaming support.
|
||||
await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate."))
|
||||
{
|
||||
Console.WriteLine(update);
|
||||
}
|
||||
|
||||
+3
-11
@@ -11,7 +11,6 @@ using System.Text.Json;
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.AzureAI;
|
||||
using Microsoft.Agents.AI.FoundryMemory;
|
||||
|
||||
string foundryEndpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
|
||||
@@ -20,9 +19,6 @@ string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLO
|
||||
string embeddingModelName = Environment.GetEnvironmentVariable("AZURE_AI_EMBEDDING_DEPLOYMENT_NAME") ?? "text-embedding-ada-002";
|
||||
|
||||
// Create an AIProjectClient for Foundry with Azure Identity authentication.
|
||||
// WARNING: 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.
|
||||
DefaultAzureCredential credential = new();
|
||||
AIProjectClient projectClient = new(new Uri(foundryEndpoint), credential);
|
||||
|
||||
@@ -37,15 +33,11 @@ FoundryMemoryProvider memoryProvider = new(
|
||||
memoryStoreName,
|
||||
stateInitializer: _ => new(new FoundryMemoryProviderScope("sample-user-123")));
|
||||
|
||||
FoundryAgent agent = projectClient.AsAIAgent(
|
||||
new ChatClientAgentOptions()
|
||||
AIAgent agent = await projectClient.CreateAIAgentAsync(deploymentName,
|
||||
options: new ChatClientAgentOptions()
|
||||
{
|
||||
Name = "TravelAssistantWithFoundryMemory",
|
||||
ChatOptions = new()
|
||||
{
|
||||
ModelId = deploymentName,
|
||||
Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details."
|
||||
},
|
||||
ChatOptions = new() { Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details." },
|
||||
AIContextProviders = [memoryProvider]
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Agent Framework Retrieval Augmented Generation (RAG)
|
||||
# Agent Framework Retrieval Augmented Generation (RAG)
|
||||
|
||||
These samples show how to create an agent with the Agent Framework that uses Memory to remember previous conversations or facts from previous conversations.
|
||||
|
||||
@@ -10,4 +10,4 @@ These samples show how to create an agent with the Agent Framework that uses Mem
|
||||
|[Memory with Azure AI Foundry](./AgentWithMemory_Step04_MemoryUsingFoundry/)|This sample demonstrates how to create and run an agent that uses Azure AI Foundry's managed memory service to extract and retrieve individual memories.|
|
||||
|[Bounded Chat History with Overflow](./AgentWithMemory_Step05_BoundedChatHistory/)|This sample demonstrates how to create a bounded chat history provider that overflows older messages to a vector store and recalls them as memories.|
|
||||
|
||||
> **See also**: [Memory Search with Foundry Agents](../AgentsWithFoundry/Agent_Step22_MemorySearch/) - demonstrates using the built-in Memory Search tool with Azure Foundry agents.
|
||||
> **See also**: [Memory Search with Foundry Agents](../FoundryAgents/FoundryAgents_Step22_MemorySearch/) - demonstrates using the built-in Memory Search tool with Azure Foundry Agents.
|
||||
|
||||
@@ -4,14 +4,28 @@
|
||||
|
||||
using System.ClientModel;
|
||||
using Microsoft.Agents.AI;
|
||||
using OpenAI.Responses;
|
||||
using OpenAI;
|
||||
using OpenAI.Chat;
|
||||
|
||||
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
|
||||
var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
AIAgent agent =
|
||||
new ResponsesClient(new ApiKeyCredential(apiKey))
|
||||
.AsAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker");
|
||||
AIAgent agent = new OpenAIClient(apiKey)
|
||||
.GetChatClient(model)
|
||||
.AsAIAgent(instructions: "You are good at telling jokes.", name: "Joker");
|
||||
|
||||
// Once you have the agent, you can invoke it like any other AIAgent.
|
||||
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
|
||||
UserChatMessage chatMessage = new("Tell me a joke about a pirate.");
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
ChatCompletion chatCompletion = await agent.RunAsync([chatMessage]);
|
||||
Console.WriteLine(chatCompletion.Content.Last().Text);
|
||||
|
||||
// Invoke the agent with streaming support.
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates = agent.RunStreamingAsync([chatMessage]);
|
||||
await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
|
||||
{
|
||||
if (completionUpdate.ContentUpdate.Count > 0)
|
||||
{
|
||||
Console.WriteLine(completionUpdate.ContentUpdate[0].Text);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ using Qdrant.Client;
|
||||
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";
|
||||
var embeddingDeploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME") ?? "text-embedding-3-large";
|
||||
var afOverviewUrl = "https://raw.githubusercontent.com/MicrosoftDocs/semantic-kernel-docs/refs/heads/main/agent-framework/overview/index.md";
|
||||
var afOverviewUrl = "https://github.com/MicrosoftDocs/semantic-kernel-docs/blob/main/agent-framework/overview/agent-framework-overview.md";
|
||||
var afMigrationUrl = "https://raw.githubusercontent.com/MicrosoftDocs/semantic-kernel-docs/refs/heads/main/agent-framework/migration-guide/from-semantic-kernel/index.md";
|
||||
|
||||
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
|
||||
|
||||
+8
-16
@@ -4,13 +4,11 @@
|
||||
|
||||
using System.ClientModel;
|
||||
using Azure.AI.Projects;
|
||||
using Azure.AI.Projects.Agents;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.AzureAI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI;
|
||||
using OpenAI.Files;
|
||||
using OpenAI.Responses;
|
||||
using OpenAI.VectorStores;
|
||||
|
||||
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
|
||||
@@ -39,20 +37,14 @@ ClientResult<VectorStore> vectorStoreCreate = await vectorStoreClient.CreateVect
|
||||
FileIds = { uploadResult.Value.Id }
|
||||
});
|
||||
|
||||
// Use the native OpenAI SDK FileSearchTool directly with the vector store ID.
|
||||
#pragma warning disable OPENAI001
|
||||
FileSearchTool fileSearchTool = new([vectorStoreCreate.Value.Id]);
|
||||
#pragma warning restore OPENAI001
|
||||
var fileSearchTool = new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreCreate.Value.Id)] };
|
||||
|
||||
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
|
||||
"AskContoso",
|
||||
new AgentVersionCreationOptions(
|
||||
new PromptAgentDefinition(model: deploymentName)
|
||||
{
|
||||
Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
|
||||
Tools = { fileSearchTool }
|
||||
}));
|
||||
FoundryAgent agent = aiProjectClient.AsAIAgent(agentVersion);
|
||||
AIAgent agent = await aiProjectClient
|
||||
.CreateAIAgentAsync(
|
||||
model: deploymentName,
|
||||
name: "AskContoso",
|
||||
instructions: "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
|
||||
tools: [fileSearchTool]);
|
||||
|
||||
AgentSession session = await agent.CreateSessionAsync();
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// This sample shows how to expose an AI agent as an MCP tool.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.AI.Projects.Agents;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -19,17 +18,11 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYME
|
||||
var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
// Create a server side agent and expose it as an AIAgent.
|
||||
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
|
||||
"Joker",
|
||||
new AgentVersionCreationOptions(
|
||||
new PromptAgentDefinition(model: deploymentName)
|
||||
{
|
||||
Instructions = "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.",
|
||||
})
|
||||
{
|
||||
Description = "An agent that tells jokes.",
|
||||
});
|
||||
AIAgent agent = aiProjectClient.AsAIAgent(agentVersion);
|
||||
AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
|
||||
model: deploymentName,
|
||||
instructions: "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.",
|
||||
name: "Joker",
|
||||
description: "An agent that tells jokes.");
|
||||
|
||||
// Convert the agent to an AIFunction and then to an MCP tool.
|
||||
// The agent name and description will be used as the mcp tool name and description.
|
||||
|
||||
@@ -189,9 +189,9 @@ async Task<AgentResponse> PIIMiddleware(IEnumerable<ChatMessage> messages, Agent
|
||||
// Regex patterns for PII detection (simplified for demonstration)
|
||||
Regex[] piiPatterns =
|
||||
[
|
||||
MyRegex(), // Phone number (e.g., 123-456-7890)
|
||||
EmailRegex(), // Email address
|
||||
FullNameRegex() // Full name (e.g., John Doe)
|
||||
new(@"\b\d{3}-\d{3}-\d{4}\b", RegexOptions.Compiled), // Phone number (e.g., 123-456-7890)
|
||||
new(@"\b[\w\.-]+@[\w\.-]+\.\w+\b", RegexOptions.Compiled), // Email address
|
||||
new(@"\b[A-Z][a-z]+\s[A-Z][a-z]+\b", RegexOptions.Compiled) // Full name (e.g., John Doe)
|
||||
];
|
||||
|
||||
foreach (var pattern in piiPatterns)
|
||||
@@ -309,15 +309,3 @@ internal sealed class DateTimeContextProvider : MessageAIContextProvider
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class Program
|
||||
{
|
||||
[GeneratedRegex(@"\b\d{3}-\d{3}-\d{4}\b", RegexOptions.Compiled)]
|
||||
private static partial Regex MyRegex();
|
||||
|
||||
[GeneratedRegex(@"\b[\w\.-]+@[\w\.-]+\.\w+\b", RegexOptions.Compiled)]
|
||||
private static partial Regex EmailRegex();
|
||||
|
||||
[GeneratedRegex(@"\b[A-Z][a-z]+\s[A-Z][a-z]+\b", RegexOptions.Compiled)]
|
||||
private static partial Regex FullNameRegex();
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ var bingConnectionId = Environment.GetEnvironmentVariable("AZURE_AI_BING_CONNECT
|
||||
PersistentAgentsAdministrationClientOptions persistentAgentsClientOptions = new();
|
||||
persistentAgentsClientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(20);
|
||||
|
||||
// Get a client to create/retrieve server side agents with.
|
||||
// WARNING: 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.
|
||||
// Get a client to create/retrieve server side agents with.
|
||||
PersistentAgentsClient persistentAgentsClient = new(endpoint, new DefaultAzureCredential(), persistentAgentsClientOptions);
|
||||
|
||||
// Define and configure the Deep Research tool.
|
||||
|
||||
@@ -23,14 +23,12 @@ Before running this sample, ensure you have:
|
||||
|
||||
Pay special attention to the purple `Note` boxes in the Azure documentation.
|
||||
|
||||
**Note**: The Bing Grounding Connection ID must be the **full ARM resource URI** from the project, not just the connection name. It has the following format:
|
||||
**Note**: The Bing Connection ID must be from the **project**, not the resource. It has the following format:
|
||||
|
||||
```
|
||||
/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.CognitiveServices/accounts/<account>/projects/<project>/connections/<connection-name>
|
||||
/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/<provider_name>/accounts/<account_name>/projects/<project_name>/connections/<connection_name>
|
||||
```
|
||||
|
||||
You can find this in the Azure AI Foundry portal under **Management > Connected resources**, or retrieve it programmatically via the connections API (`.id` property).
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Set the following environment variables:
|
||||
@@ -39,8 +37,8 @@ Set the following environment variables:
|
||||
# Replace with your Azure AI Foundry project endpoint
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/"
|
||||
|
||||
# Replace with your Bing Grounding connection ID (full ARM resource URI)
|
||||
$env:AZURE_AI_BING_CONNECTION_ID="/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.CognitiveServices/accounts/<account>/projects/<project>/connections/<connection-name>"
|
||||
# Replace with your Bing connection ID from the project
|
||||
$env:AZURE_AI_BING_CONNECTION_ID="/subscriptions/.../connections/your-bing-connection"
|
||||
|
||||
# Optional, defaults to o3-deep-research
|
||||
$env:AZURE_AI_REASONING_DEPLOYMENT_NAME="o3-deep-research"
|
||||
|
||||
@@ -24,12 +24,12 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT
|
||||
Func<Task<string[]>> loadNextThreeCalendarEvents = async () =>
|
||||
{
|
||||
// In a real implementation, this method would connect to a calendar service
|
||||
return
|
||||
[
|
||||
return new string[]
|
||||
{
|
||||
"Doctor's appointment today at 15:00",
|
||||
"Team meeting today at 17:00",
|
||||
"Birthday party today at 20:00"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
// Create an agent with an AI context provider attached that aggregates two other providers:
|
||||
@@ -87,7 +87,7 @@ namespace SampleApp
|
||||
internal sealed class TodoListAIContextProvider : AIContextProvider
|
||||
{
|
||||
private static List<string> GetTodoItems(AgentSession? session)
|
||||
=> session?.StateBag.GetValue<List<string>>(nameof(TodoListAIContextProvider)) ?? [];
|
||||
=> session?.StateBag.GetValue<List<string>>(nameof(TodoListAIContextProvider)) ?? new List<string>();
|
||||
|
||||
private static void SetTodoItems(AgentSession? session, List<string> items)
|
||||
=> session?.StateBag.SetValue(nameof(TodoListAIContextProvider), items);
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how the ChatClientAgent persists chat history after each individual
|
||||
// call to the AI service, using the SimulateServiceStoredChatHistory option.
|
||||
// call to the AI service.
|
||||
// When an agent uses tools, FunctionInvokingChatClient may loop multiple times
|
||||
// (service call → tool execution → service call), and intermediate messages (tool calls and
|
||||
// results) are persisted after each service call. This allows you to inspect or recover them
|
||||
// even if the process is interrupted mid-loop, but may also result in chat history that is not
|
||||
// yet finalized (e.g., tool calls without results) being persisted, which may be undesirable in some cases.
|
||||
//
|
||||
// To use end-of-run persistence instead (atomic run semantics), remove the
|
||||
// SimulateServiceStoredChatHistory = true setting (or set it to false). End-of-run
|
||||
// persistence is the default behavior.
|
||||
// To opt into end-of-run persistence instead (atomic run semantics), set
|
||||
// PersistChatHistoryAtEndOfRun = true on ChatClientAgentOptions.
|
||||
//
|
||||
// The sample runs two multi-turn conversations: one using non-streaming (RunAsync) and one
|
||||
// using streaming (RunStreamingAsync), to demonstrate correct behavior in both modes.
|
||||
@@ -54,7 +53,7 @@ static string GetTime([Description("The city name.")] string city) =>
|
||||
_ => $"{city}: time data not available."
|
||||
};
|
||||
|
||||
// Create the agent — per-service-call persistence is enabled via SimulateServiceStoredChatHistory.
|
||||
// Create the agent — per-service-call persistence is the default behavior.
|
||||
// The in-memory ChatHistoryProvider is used by default when the service does not require service stored chat
|
||||
// history, so for those cases, we can inspect the chat history via session.TryGetInMemoryChatHistory().
|
||||
IChatClient chatClient = string.Equals(store, "TRUE", StringComparison.OrdinalIgnoreCase) ?
|
||||
@@ -64,7 +63,6 @@ AIAgent agent = chatClient.AsAIAgent(
|
||||
new ChatClientAgentOptions
|
||||
{
|
||||
Name = "WeatherAssistant",
|
||||
SimulateServiceStoredChatHistory = true,
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a helpful assistant. When asked about multiple cities, call the appropriate tool for each city.",
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
# In-Function-Loop Checkpointing
|
||||
|
||||
This sample demonstrates how `ChatClientAgent` can persist chat history after each individual call to the AI service using the `SimulateServiceStoredChatHistory` option. This per-service-call persistence ensures intermediate progress is saved during the function invocation loop.
|
||||
This sample demonstrates how `ChatClientAgent` persists chat history after each individual call to the AI service by default. This per-service-call persistence ensures intermediate progress is saved during the function invocation loop.
|
||||
|
||||
## What This Sample Shows
|
||||
|
||||
When an agent uses tools, the `FunctionInvokingChatClient` loops multiple times (service call → tool execution → service call → …). By enabling `SimulateServiceStoredChatHistory = true`, chat history is persisted after each service call via the `ServiceStoredSimulatingChatClient` decorator:
|
||||
When an agent uses tools, the `FunctionInvokingChatClient` loops multiple times (service call → tool execution → service call → …). By default, chat history is persisted after each service call via the `ChatHistoryPersistingChatClient` decorator:
|
||||
|
||||
- A `ServiceStoredSimulatingChatClient` decorator is inserted into the chat client pipeline
|
||||
- Before each service call, the decorator loads history from the `ChatHistoryProvider` and prepends it to the request
|
||||
- A `ChatHistoryPersistingChatClient` decorator is automatically inserted into the chat client pipeline
|
||||
- After each service call, the decorator notifies the `ChatHistoryProvider` (and any `AIContextProvider` instances) with the new messages
|
||||
- Only **new** messages are sent to providers on each notification — messages that were already persisted in an earlier call within the same run are deduplicated automatically
|
||||
|
||||
By default (without `SimulateServiceStoredChatHistory`), chat history is persisted at the end of the full agent run instead. To use per-service-call persistence, set `SimulateServiceStoredChatHistory = true` on `ChatClientAgentOptions`.
|
||||
|
||||
With `SimulateServiceStoredChatHistory` = true, the behavior matches that of chat history stored in the underlying AI service exactly.
|
||||
To opt into end-of-run persistence instead (atomic run semantics), set `PersistChatHistoryAtEndOfRun = true` on `ChatClientAgentOptions`. In that mode, the decorator marks messages with metadata rather than persisting them immediately, and `ChatClientAgent` persists only the marked messages at the end of the run.
|
||||
|
||||
Per-service-call persistence is useful for:
|
||||
- **Crash recovery** — if the process is interrupted mid-loop, the intermediate tool calls and results are already persisted
|
||||
@@ -29,7 +26,7 @@ The sample asks the agent about the weather and time in three cities. The model
|
||||
```
|
||||
ChatClientAgent
|
||||
└─ FunctionInvokingChatClient (handles tool call loop)
|
||||
└─ ServiceStoredSimulatingChatClient (persists after each service call)
|
||||
└─ ChatHistoryPersistingChatClient (persists after each service call)
|
||||
└─ Leaf IChatClient (Azure OpenAI)
|
||||
```
|
||||
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create, use, and clean up a FoundryAgent backed by a server-side
|
||||
// versioned agent in Azure AI Foundry. It demonstrates the full lifecycle:
|
||||
// create agent version -> wrap as FoundryAgent -> run -> delete.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.AI.Projects.Agents;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI.AzureAI;
|
||||
|
||||
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-mini";
|
||||
|
||||
const string JokerName = "JokerAgent";
|
||||
|
||||
// Create the AIProjectClient to manage server-side agents.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential());
|
||||
|
||||
// Create a server-side agent version using the native SDK.
|
||||
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
|
||||
JokerName,
|
||||
new AgentVersionCreationOptions(
|
||||
new PromptAgentDefinition(model: deploymentName)
|
||||
{
|
||||
Instructions = "You are good at telling jokes.",
|
||||
}));
|
||||
|
||||
// Wrap the agent version as a FoundryAgent using the AsAIAgent extension.
|
||||
FoundryAgent agent = aiProjectClient.AsAIAgent(agentVersion);
|
||||
|
||||
// Once you have the agent, you can invoke it like any other AIAgent.
|
||||
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
|
||||
|
||||
// Cleanup: deletes the agent and all its versions.
|
||||
await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
# Agent Step 00 - FoundryAgent Lifecycle
|
||||
|
||||
This sample demonstrates the full lifecycle of a `FoundryAgent` backed by a server-side versioned agent in Microsoft Foundry: create → run → delete.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Microsoft Foundry project endpoint
|
||||
- A model deployment name (defaults to `gpt-4o-mini`)
|
||||
- Azure CLI installed and authenticated
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description | Required |
|
||||
| --- | --- | --- |
|
||||
| `AZURE_AI_PROJECT_ENDPOINT` | Microsoft Foundry project endpoint | Yes |
|
||||
| `AZURE_AI_MODEL_DEPLOYMENT_NAME` | Model deployment name | No (defaults to `gpt-4o-mini`) |
|
||||
|
||||
## Running the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step00_FoundryAgentLifecycle
|
||||
```
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create and run a basic agent with AIProjectClient.AsAIAgent(...).
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
|
||||
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-mini";
|
||||
|
||||
// WARNING: 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.
|
||||
AIAgent agent =
|
||||
new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
|
||||
.AsAIAgent(model: deploymentName, instructions: "You are good at telling jokes.", name: "JokerAgent");
|
||||
|
||||
// Once you have the agent, you can invoke it like any other AIAgent.
|
||||
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
|
||||
@@ -1,55 +0,0 @@
|
||||
# Creating and Running a Basic Agent with the Responses API
|
||||
|
||||
This sample demonstrates how to create and run a basic AI agent using the `ChatClientAgent`, which uses the Microsoft Foundry Responses API directly without creating server-side agent definitions.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating a `ChatClientAgent` with instructions and a model
|
||||
- Running a simple single-turn conversation
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||
|
||||
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the AgentsWithFoundry sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step01_Basics
|
||||
```
|
||||
|
||||
## Alternative: Composable approach
|
||||
|
||||
You can also create the same agent by composing the underlying `IChatClient` directly. This gives you full control over the chat client pipeline:
|
||||
|
||||
```csharp
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
AIAgent agent = new ChatClientAgent(
|
||||
chatClient: aiProjectClient.GetProjectOpenAIClient().GetProjectResponsesClient().AsIChatClient(deploymentName),
|
||||
instructions: "You are good at telling jokes.",
|
||||
name: "JokerAgent");
|
||||
```
|
||||
|
||||
This approach is useful when you need to customize the chat client pipeline or swap providers (e.g., Anthropic, OpenAI) while keeping the same agent code.
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create a multi-turn conversation agent using sessions.
|
||||
// Context is preserved across multiple runs via response ID chaining in the session.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
|
||||
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-mini";
|
||||
|
||||
// WARNING: 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.
|
||||
AIAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
|
||||
.AsAIAgent(deploymentName, instructions: "You are good at telling jokes.", name: "JokerAgent");
|
||||
|
||||
// Create a session to maintain context across multiple runs.
|
||||
AgentSession session = await agent.CreateSessionAsync();
|
||||
|
||||
// First turn
|
||||
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
|
||||
|
||||
// Second turn — the agent remembers the first turn via the session.
|
||||
Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session));
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
# Multi-turn Conversation
|
||||
|
||||
This sample demonstrates how to implement multi-turn conversations where context is preserved across multiple agent runs using sessions and response ID chaining.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating an agent with instructions
|
||||
- Using sessions to maintain conversation context across multiple runs
|
||||
- Response ID chaining for multi-turn conversations
|
||||
- No server-side conversation creation required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||
|
||||
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the AgentsWithFoundry sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step02.1_MultiturnConversation
|
||||
```
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to use server-side conversations with a FoundryAgent.
|
||||
// Server-side conversations persist on the Foundry service and are visible in the Foundry Project UI.
|
||||
// Use this when you need conversation history to be stored and accessible server-side.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.AzureAI;
|
||||
|
||||
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-mini";
|
||||
|
||||
// WARNING: 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.
|
||||
FoundryAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
|
||||
.AsAIAgent(deploymentName, instructions: "You are good at telling jokes.", name: "JokerAgent");
|
||||
|
||||
// CreateConversationSessionAsync creates a server-side ProjectConversation
|
||||
// that persists on the Foundry service and is visible in the Foundry Project UI.
|
||||
AgentSession session = await agent.CreateConversationSessionAsync();
|
||||
|
||||
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
|
||||
Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session));
|
||||
|
||||
// Streaming with server-side conversation context.
|
||||
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Tell me another joke, but about a ninja this time.", session))
|
||||
{
|
||||
Console.Write(update);
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
# Multi-turn Conversation with Server-Side Conversations
|
||||
|
||||
This sample demonstrates how to use server-side conversations with a `FoundryAgent`. Server-side conversations persist on the Foundry service and are visible in the Foundry Project UI, making them ideal when you need conversation history to be stored and accessible server-side.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating a `FoundryAgent` with instructions
|
||||
- Using `CreateConversationSessionAsync` to create a server-side `ProjectConversation`
|
||||
- Multi-turn conversations with both text and streaming output
|
||||
- Server-side conversation persistence visible in the Foundry Project UI
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||
|
||||
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the AgentsWithFoundry sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step02.2_MultiturnWithServerConversations
|
||||
```
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to use function tools.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather([Description("The location to get the weather for.")] string location)
|
||||
=> $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
|
||||
// Define the function tool.
|
||||
AITool tool = AIFunctionFactory.Create(GetWeather);
|
||||
|
||||
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-mini";
|
||||
|
||||
// WARNING: 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.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
// Create a AIAgent with function tools.
|
||||
AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
|
||||
instructions: "You are a helpful assistant that can get weather information.",
|
||||
name: "WeatherAssistant",
|
||||
tools: [tool]);
|
||||
|
||||
// Non-streaming agent interaction with function tools.
|
||||
AgentSession session = await agent.CreateSessionAsync();
|
||||
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", session));
|
||||
|
||||
// Streaming agent interaction with function tools.
|
||||
session = await agent.CreateSessionAsync();
|
||||
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("What is the weather like in Amsterdam?", session))
|
||||
{
|
||||
Console.Write(update);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
# Using Function Tools with the Responses API
|
||||
|
||||
This sample demonstrates how to use function tools with the `ChatClientAgent`, allowing the agent to call custom functions to retrieve information.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating function tools using `AIFunctionFactory`
|
||||
- Passing function tools to a `ChatClientAgent`
|
||||
- Running agents with function tools (text output)
|
||||
- Running agents with function tools (streaming output)
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||
|
||||
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Microsoft Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the AgentsWithFoundry sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step03_UsingFunctionTools
|
||||
```
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
# Using Function Tools with Approvals via the Responses API
|
||||
|
||||
This sample demonstrates how to use function tools that require human-in-the-loop approval before execution.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating function tools that require approval using `ApprovalRequiredAIFunction`
|
||||
- Handling approval requests from the agent
|
||||
- Passing approval responses back to the agent
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step04_UsingFunctionToolsWithApprovals
|
||||
```
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,29 +0,0 @@
|
||||
# Structured Output with the Responses API
|
||||
|
||||
This sample demonstrates how to configure an agent to produce structured output using JSON schema.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Using `RunAsync<T>()` to get typed structured output from the agent
|
||||
- Deserializing streamed responses into structured types
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step05_StructuredOutput
|
||||
```
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
# Persisted Conversations with the Responses API
|
||||
|
||||
This sample demonstrates how to persist and resume agent conversations using session serialization.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Serializing agent sessions to JSON for persistence
|
||||
- Saving and loading sessions from disk
|
||||
- Resuming conversations with preserved context
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step06_PersistedConversations
|
||||
```
|
||||
@@ -1,31 +0,0 @@
|
||||
# Observability with the Responses API
|
||||
|
||||
This sample demonstrates how to add OpenTelemetry observability to an agent using console and Azure Monitor exporters.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Configuring OpenTelemetry tracing with console exporter
|
||||
- Optional Azure Application Insights integration
|
||||
- Using `.AsBuilder().UseOpenTelemetry()` to add telemetry to the agent
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
$env:APPLICATIONINSIGHTS_CONNECTION_STRING="..." # Optional
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step07_Observability
|
||||
```
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to use dependency injection to register a AIAgent and use it from a hosted service.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using SampleApp;
|
||||
|
||||
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-mini";
|
||||
|
||||
// WARNING: 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.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
|
||||
instructions: "You are good at telling jokes.",
|
||||
name: "JokerAgent");
|
||||
|
||||
// Create a host builder that we will register services with and then run.
|
||||
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
// Add the AI agent to the service collection.
|
||||
builder.Services.AddSingleton(agent);
|
||||
|
||||
// Add a sample service that will use the agent to respond to user input.
|
||||
builder.Services.AddHostedService<SampleService>();
|
||||
|
||||
// Build and run the host.
|
||||
using IHost host = builder.Build();
|
||||
await host.RunAsync().ConfigureAwait(false);
|
||||
|
||||
namespace SampleApp
|
||||
{
|
||||
/// <summary>
|
||||
/// A sample service that uses an AI agent to respond to user input.
|
||||
/// </summary>
|
||||
internal sealed class SampleService(AIAgent agent, IHostApplicationLifetime appLifetime) : IHostedService
|
||||
{
|
||||
private AgentSession? _session;
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this._session = await agent.CreateSessionAsync(cancellationToken);
|
||||
_ = this.RunAsync(appLifetime.ApplicationStopping);
|
||||
}
|
||||
|
||||
public async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(100, cancellationToken);
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
Console.WriteLine("\nAgent: Ask me to tell you a joke about a specific topic. To exit just press Ctrl+C or enter without any input.\n");
|
||||
Console.Write("> ");
|
||||
string? input = Console.ReadLine();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
appLifetime.StopApplication();
|
||||
break;
|
||||
}
|
||||
|
||||
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, this._session, cancellationToken: cancellationToken))
|
||||
{
|
||||
Console.Write(update);
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Console.WriteLine("\nShutting down...");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
# Dependency Injection with the Responses API
|
||||
|
||||
This sample demonstrates how to register a `ChatClientAgent` in a dependency injection container and use it from a hosted service.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Registering `ChatClientAgent` as an `AIAgent` in the service collection
|
||||
- Using the agent from a `IHostedService` with an interactive chat loop
|
||||
- Streaming responses in a hosted service context
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step08_DependencyInjection
|
||||
```
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to use MCP client tools with an agent.
|
||||
// It connects to the Microsoft Learn MCP server via HTTP and uses its tools.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using ModelContextProtocol.Client;
|
||||
|
||||
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-mini";
|
||||
|
||||
// Connect to the Microsoft Learn MCP server via HTTP (Streamable HTTP transport).
|
||||
Console.WriteLine("Connecting to MCP server at https://learn.microsoft.com/api/mcp ...");
|
||||
|
||||
await using McpClient mcpClient = await McpClient.CreateAsync(new HttpClientTransport(new()
|
||||
{
|
||||
Endpoint = new Uri("https://learn.microsoft.com/api/mcp"),
|
||||
Name = "Microsoft Learn MCP",
|
||||
}));
|
||||
|
||||
// Retrieve the list of tools available on the MCP server.
|
||||
IList<McpClientTool> mcpTools = await mcpClient.ListToolsAsync();
|
||||
Console.WriteLine($"MCP tools available: {string.Join(", ", mcpTools.Select(t => t.Name))}");
|
||||
|
||||
List<AITool> agentTools = [.. mcpTools.Cast<AITool>()];
|
||||
|
||||
// WARNING: 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.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
|
||||
instructions: "You are a helpful assistant that can help with Microsoft documentation questions. Use the Microsoft Learn MCP tool to search for documentation.",
|
||||
name: "DocsAgent",
|
||||
tools: agentTools);
|
||||
|
||||
Console.WriteLine($"Agent '{agent.Name}' created. Asking a question...\n");
|
||||
|
||||
const string Prompt = "How does one create an Azure storage account using az cli?";
|
||||
Console.WriteLine($"User: {Prompt}\n");
|
||||
Console.WriteLine($"Agent: {await agent.RunAsync(Prompt)}");
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
# Using MCP Client as Tools with the Responses API
|
||||
|
||||
This sample shows how to use MCP (Model Context Protocol) client tools with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Connecting to an MCP server via HTTP client transport
|
||||
- Retrieving MCP tools and passing them to a `ChatClientAgent`
|
||||
- Using MCP tools for agent interactions without server-side agent creation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
- Node.js installed (for npx/MCP server)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="assets\walkway.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,30 +0,0 @@
|
||||
# Using Images with the Responses API
|
||||
|
||||
This sample demonstrates how to use image multi-modality with an agent.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Loading images using `DataContent.LoadFromAsync`
|
||||
- Sending images alongside text to the agent
|
||||
- Streaming the agent's image analysis response
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and a vision-capable model deployment (e.g., `gpt-4o`)
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step10_UsingImages
|
||||
```
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,30 +0,0 @@
|
||||
# Agent as a Function Tool with the Responses API
|
||||
|
||||
This sample demonstrates how to use one agent as a function tool for another agent.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating a specialized agent (weather) with function tools
|
||||
- Exposing an agent as a function tool using `.AsAIFunction()`
|
||||
- Composing agents where one agent delegates to another
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step11_AsFunctionTool
|
||||
```
|
||||
@@ -1,31 +0,0 @@
|
||||
# Middleware with the Responses API
|
||||
|
||||
This sample demonstrates multiple middleware layers working together: PII filtering, guardrails, function invocation logging, and human-in-the-loop approval.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Agent-level run middleware (PII filtering, guardrail enforcement)
|
||||
- Function-level middleware (logging, result overrides)
|
||||
- Human-in-the-loop approval workflows for sensitive function calls
|
||||
- Using `.AsBuilder().Use()` to compose middleware
|
||||
- No server-side agent creation or cleanup required
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\Agent_Step12_Middleware
|
||||
```
|
||||
@@ -1,153 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to use plugins with an AI agent. Plugin classes can
|
||||
// depend on other services that need to be injected. In this sample, the
|
||||
// AgentPlugin class uses the WeatherProvider and CurrentTimeProvider classes
|
||||
// to get weather and current time information. Both services are registered
|
||||
// in the service collection and injected into the plugin.
|
||||
// Plugin classes may have many methods, but only some are intended to be used
|
||||
// as AI functions. The AsAITools method of the plugin class shows how to specify
|
||||
// which methods should be exposed to the AI agent.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SampleApp;
|
||||
|
||||
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-mini";
|
||||
|
||||
const string AssistantInstructions = "You are a helpful assistant that helps people find information.";
|
||||
const string AssistantName = "PluginAssistant";
|
||||
|
||||
// Create a service collection to hold the agent plugin and its dependencies.
|
||||
ServiceCollection services = new();
|
||||
services.AddSingleton<WeatherProvider>();
|
||||
services.AddSingleton<CurrentTimeProvider>();
|
||||
services.AddSingleton<AgentPlugin>(); // The plugin depends on WeatherProvider and CurrentTimeProvider registered above.
|
||||
|
||||
IServiceProvider serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
// WARNING: 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.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
// Create a ChatClientAgent with the options-based constructor to pass services.
|
||||
AIAgent agent = aiProjectClient.AsAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Name = AssistantName,
|
||||
ChatOptions = new() { ModelId = deploymentName, Instructions = AssistantInstructions, Tools = serviceProvider.GetRequiredService<AgentPlugin>().AsAITools().ToList() }
|
||||
},
|
||||
services: serviceProvider);
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
AgentSession session = await agent.CreateSessionAsync();
|
||||
Console.WriteLine(await agent.RunAsync("Tell me current time and weather in Seattle.", session));
|
||||
|
||||
namespace SampleApp
|
||||
{
|
||||
/// <summary>
|
||||
/// The agent plugin that provides weather and current time information.
|
||||
/// </summary>
|
||||
internal sealed class AgentPlugin
|
||||
{
|
||||
private readonly WeatherProvider _weatherProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AgentPlugin"/> class.
|
||||
/// </summary>
|
||||
/// <param name="weatherProvider">The weather provider to get weather information.</param>
|
||||
public AgentPlugin(WeatherProvider weatherProvider)
|
||||
{
|
||||
this._weatherProvider = weatherProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the weather information for the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method demonstrates how to use the dependency that was injected into the plugin class.
|
||||
/// </remarks>
|
||||
/// <param name="location">The location to get the weather for.</param>
|
||||
/// <returns>The weather information for the specified location.</returns>
|
||||
public string GetWeather(string location)
|
||||
{
|
||||
return this._weatherProvider.GetWeather(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current date and time for the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method demonstrates how to resolve a dependency using the service provider passed to the method.
|
||||
/// </remarks>
|
||||
/// <param name="sp">The service provider to resolve the <see cref="CurrentTimeProvider"/>.</param>
|
||||
/// <param name="location">The location to get the current time for.</param>
|
||||
/// <returns>The current date and time as a <see cref="DateTimeOffset"/>.</returns>
|
||||
public DateTimeOffset GetCurrentTime(IServiceProvider sp, string location)
|
||||
{
|
||||
CurrentTimeProvider currentTimeProvider = sp.GetRequiredService<CurrentTimeProvider>();
|
||||
return currentTimeProvider.GetCurrentTime(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the functions provided by this plugin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In real world scenarios, a class may have many methods and only a subset of them may be intended to be exposed as AI functions.
|
||||
/// This method demonstrates how to explicitly specify which methods should be exposed to the AI agent.
|
||||
/// </remarks>
|
||||
/// <returns>The functions provided by this plugin.</returns>
|
||||
public IEnumerable<AITool> AsAITools()
|
||||
{
|
||||
yield return AIFunctionFactory.Create(this.GetWeather);
|
||||
yield return AIFunctionFactory.Create(this.GetCurrentTime);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class WeatherProvider
|
||||
{
|
||||
private readonly string _weatherSummary = "cloudy with a high of 15°C";
|
||||
|
||||
/// <summary>
|
||||
/// The weather provider that returns weather information.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Gets the weather information for the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The weather information is hardcoded for demonstration purposes.
|
||||
/// In a real application, this could call a weather API to get actual weather data.
|
||||
/// </remarks>
|
||||
/// <param name="location">The location to get the weather for.</param>
|
||||
/// <returns>The weather information for the specified location.</returns>
|
||||
public string GetWeather(string location)
|
||||
{
|
||||
return $"The weather in {location} is {this._weatherSummary}.";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CurrentTimeProvider
|
||||
{
|
||||
private readonly TimeProvider _timeProvider = TimeProvider.System;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the current date and time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class returns the current date and time using the system's clock.
|
||||
/// </remarks>
|
||||
/// <summary>
|
||||
/// Gets the current date and time.
|
||||
/// </summary>
|
||||
/// <param name="location">The location to get the current time for (not used in this implementation).</param>
|
||||
/// <returns>The current date and time as a <see cref="DateTimeOffset"/>.</returns>
|
||||
public DateTimeOffset GetCurrentTime(string location)
|
||||
{
|
||||
return this._timeProvider.GetLocalNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
# Using Plugins with the Responses API
|
||||
|
||||
This sample shows how to use plugins with a `ChatClientAgent` using the Responses API directly, with dependency injection for plugin services.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating plugin classes with injected dependencies
|
||||
- Registering services and building a service provider
|
||||
- Passing `services` to the `ChatClientAgent` via the options-based constructor
|
||||
- Using `AIFunctionFactory` to expose plugin methods as AI tools
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
@@ -1,28 +0,0 @@
|
||||
# Code Interpreter with the Responses API
|
||||
|
||||
This sample shows how to use the Code Interpreter tool with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Using `HostedCodeInterpreterTool` with `ChatClientAgent`
|
||||
- Extracting code input and output from agent responses
|
||||
- Handling code interpreter annotations and file citations
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
@@ -1,29 +0,0 @@
|
||||
# Computer Use with the Responses API
|
||||
|
||||
This sample shows how to use the Computer Use tool with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Using `FoundryAITool.CreateComputerTool()` with `ChatClientAgent`
|
||||
- Processing computer call actions (click, type, key press)
|
||||
- Managing the computer use interaction loop with screenshots
|
||||
- Handling the Azure Agents API workaround for `previous_response_id` with `computer_call_output`
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="computer-use-preview"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
@@ -1,29 +0,0 @@
|
||||
# File Search with the Responses API
|
||||
|
||||
This sample shows how to use the File Search tool with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Uploading files and creating vector stores via `AIProjectClient`
|
||||
- Using `HostedFileSearchTool` with `ChatClientAgent`
|
||||
- Handling file citation annotations in agent responses
|
||||
- Cleaning up file resources after use
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,29 +0,0 @@
|
||||
# OpenAPI Tools with the Responses API
|
||||
|
||||
This sample shows how to use OpenAPI tools with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Defining an OpenAPI specification inline
|
||||
- Creating an `OpenAPIFunctionDefinition` for the REST Countries API
|
||||
- Using `FoundryAITool.CreateOpenApiTool()` with `ChatClientAgent`
|
||||
- Server-side execution of OpenAPI tool calls
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
# Bing Custom Search with the Responses API
|
||||
|
||||
This sample shows how to use the Bing Custom Search tool with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Configuring `BingCustomSearchToolParameters` with connection ID and instance name
|
||||
- Using `FoundryAITool.CreateBingCustomSearchTool()` with `ChatClientAgent`
|
||||
- Processing search results from agent responses
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
- Bing Custom Search resource configured with a connection ID
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
$env:AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID="your-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/your-bing-connection"
|
||||
$env:AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME="your-instance-name" # The Bing Custom Search configuration name (from Azure portal)
|
||||
```
|
||||
|
||||
### Finding the connection ID and instance name
|
||||
|
||||
- **Connection ID** (`AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID`): The full ARM resource URI including the `/projects/<name>/connections/<connection-name>` segment. Find the connection name in your Foundry project under **Management center** → **Connected resources**.
|
||||
- **Instance Name** (`AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME`): The **configuration name** from your Bing Custom Search resource (Azure portal → your Bing Custom Search resource → **Configurations**). This is _not_ the Azure resource name or the connection name — it's the name of the specific search configuration that defines which domains/sites to search against.
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,30 +0,0 @@
|
||||
# SharePoint Grounding with the Responses API
|
||||
|
||||
This sample shows how to use the SharePoint Grounding tool with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Configuring `SharePointGroundingToolOptions` with project connections
|
||||
- Using `FoundryAITool.CreateSharepointTool()` with `ChatClientAgent`
|
||||
- Displaying grounding annotations from agent responses
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
- SharePoint connection configured in your Microsoft Foundry project
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
$env:SHAREPOINT_PROJECT_CONNECTION_ID="your-sharepoint-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/SharepointTestTool"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to use Microsoft Fabric Tool with a ChatClientAgent.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.AI.Projects.Agents;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.AzureAI;
|
||||
|
||||
string fabricConnectionId = Environment.GetEnvironmentVariable("FABRIC_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("FABRIC_PROJECT_CONNECTION_ID is not set.");
|
||||
|
||||
const string AgentInstructions = "You are a helpful assistant with access to Microsoft Fabric data. Answer questions based on data available through your Fabric connection.";
|
||||
|
||||
// Configure Microsoft Fabric tool options with project connection
|
||||
var fabricToolOptions = new FabricDataAgentToolOptions();
|
||||
fabricToolOptions.ProjectConnections.Add(new ToolProjectConnection(fabricConnectionId));
|
||||
|
||||
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-mini";
|
||||
|
||||
// WARNING: 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.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
// Create a AIAgent with Microsoft Fabric tool.
|
||||
AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
|
||||
instructions: AgentInstructions,
|
||||
name: "FabricAgent-RAPI",
|
||||
tools: [FoundryAITool.CreateMicrosoftFabricTool(fabricToolOptions)]);
|
||||
|
||||
Console.WriteLine($"Created agent: {agent.Name}");
|
||||
|
||||
// Run the agent with a sample query
|
||||
AgentResponse response = await agent.RunAsync("What data is available in the connected Fabric workspace?");
|
||||
|
||||
Console.WriteLine("\n=== Agent Response ===");
|
||||
foreach (var message in response.Messages)
|
||||
{
|
||||
Console.WriteLine(message.Text);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
# Microsoft Fabric with the Responses API
|
||||
|
||||
This sample shows how to use the Microsoft Fabric tool with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Configuring `FabricDataAgentToolOptions` with project connections
|
||||
- Using `FoundryAITool.CreateMicrosoftFabricTool()` with `ChatClientAgent`
|
||||
- Querying data available through a Fabric connection
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
- Microsoft Fabric connection configured in your Microsoft Foundry project
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
$env:FABRIC_PROJECT_CONNECTION_ID="your-fabric-connection-id" # The full ARM resource URI, e.g., "/subscriptions/.../connections/FabricTestTool"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to use the Web Search Tool with a ChatClientAgent.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI.Responses;
|
||||
|
||||
const string AgentInstructions = "You are a helpful assistant that can search the web to find current information and answer questions accurately.";
|
||||
const string AgentName = "WebSearchAgent-RAPI";
|
||||
|
||||
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-mini";
|
||||
|
||||
// WARNING: 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.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
// Create a AIAgent with HostedWebSearchTool.
|
||||
AIAgent agent = aiProjectClient.AsAIAgent(deploymentName,
|
||||
instructions: AgentInstructions,
|
||||
name: AgentName,
|
||||
tools: [new HostedWebSearchTool()]);
|
||||
|
||||
AgentResponse response = await agent.RunAsync("What's the weather today in Seattle?");
|
||||
|
||||
// Get the text response
|
||||
Console.WriteLine($"Response: {response.Text}");
|
||||
|
||||
// Getting any annotations/citations generated by the web search tool
|
||||
foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents).SelectMany(c => c.Annotations ?? []))
|
||||
{
|
||||
Console.WriteLine($"Annotation: {annotation}");
|
||||
if (annotation.RawRepresentation is UriCitationMessageAnnotation urlCitation)
|
||||
{
|
||||
Console.WriteLine($$"""
|
||||
Title: {{urlCitation.Title}}
|
||||
URL: {{urlCitation.Uri}}
|
||||
""");
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
# Web Search with the Responses API
|
||||
|
||||
This sample shows how to use the Web Search tool with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Using `HostedWebSearchTool` with `ChatClientAgent`
|
||||
- Processing web search citations and annotations
|
||||
- Extracting URL citation details (title, URL) from responses
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,31 +0,0 @@
|
||||
# Memory Search with the Responses API
|
||||
|
||||
This sample demonstrates how to use the Memory Search tool with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Configuring `MemorySearchPreviewTool` with a memory store and user scope
|
||||
- Using memory search for cross-conversation recall
|
||||
- Inspecting `MemorySearchToolCallResponseItem` results
|
||||
- User profile persistence across conversations
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
- A memory store created beforehand via Azure Portal or Python SDK
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
$env:AZURE_AI_MEMORY_STORE_ID="your-memory-store-name"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
@@ -1,29 +0,0 @@
|
||||
# Local MCP with the Responses API
|
||||
|
||||
This sample demonstrates how to use a local MCP (Model Context Protocol) client with a `ChatClientAgent` using the Responses API directly.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Connecting to an MCP server via HTTP (Streamable HTTP transport)
|
||||
- Resolving MCP tools locally and wrapping them with logging
|
||||
- Using `DelegatingAIFunction` to add custom behavior to MCP tools
|
||||
- Passing locally-resolved MCP tools to `ChatClientAgent`
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Microsoft Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (`az login`)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
@@ -1,81 +0,0 @@
|
||||
# Getting started with Foundry Agents
|
||||
|
||||
These samples demonstrate how to use Azure AI Foundry with Agent Framework.
|
||||
|
||||
## Quick start
|
||||
|
||||
The simplest way to create a Foundry agent is using the `FoundryAgent` type directly:
|
||||
|
||||
```csharp
|
||||
FoundryAgent agent = new(
|
||||
new Uri(endpoint),
|
||||
new AzureCliCredential(),
|
||||
model: "gpt-4o-mini",
|
||||
instructions: "You are good at telling jokes.",
|
||||
name: "JokerAgent");
|
||||
|
||||
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
|
||||
```
|
||||
|
||||
Or using the `AIProjectClient.AsAIAgent(...)` extensions:
|
||||
|
||||
```csharp
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
FoundryAgent agent = aiProjectClient.AsAIAgent(
|
||||
model: deploymentName,
|
||||
instructions: "You are good at telling jokes.",
|
||||
name: "JokerAgent");
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Foundry project endpoint
|
||||
- Azure CLI installed and authenticated
|
||||
|
||||
Set:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
```
|
||||
|
||||
Some samples require extra tool-specific environment variables. See each sample for details.
|
||||
|
||||
## Samples
|
||||
|
||||
| Sample | Description |
|
||||
| --- | --- |
|
||||
| [FoundryAgent lifecycle](./Agent_Step00_FoundryAgentLifecycle/) | Create a FoundryAgent directly with endpoint and credentials |
|
||||
| [Basics (Responses API)](./Agent_Step01_Basics/) | Create and run an agent using AsAIAgent extensions |
|
||||
| [Multi-turn conversation](./Agent_Step02.1_MultiturnConversation/) | Multi-turn using sessions and response ID chaining |
|
||||
| [Multi-turn with server conversations](./Agent_Step02.2_MultiturnWithServerConversations/) | Server-side conversations visible in Foundry UI |
|
||||
| [Using function tools](./Agent_Step03_UsingFunctionTools/) | Function tools |
|
||||
| [Function tools with approvals](./Agent_Step04_UsingFunctionToolsWithApprovals/) | Human-in-the-loop approval |
|
||||
| [Structured output](./Agent_Step05_StructuredOutput/) | Structured output with JSON schema |
|
||||
| [Persisted conversations](./Agent_Step06_PersistedConversations/) | Persisting and resuming conversations |
|
||||
| [Observability](./Agent_Step07_Observability/) | OpenTelemetry observability |
|
||||
| [Dependency injection](./Agent_Step08_DependencyInjection/) | DI with a hosted service |
|
||||
| [Using MCP client as tools](./Agent_Step09_UsingMcpClientAsTools/) | MCP client tools |
|
||||
| [Using images](./Agent_Step10_UsingImages/) | Image multi-modality |
|
||||
| [Agent as function tool](./Agent_Step11_AsFunctionTool/) | Agent as a function tool for another |
|
||||
| [Middleware](./Agent_Step12_Middleware/) | Multiple middleware layers |
|
||||
| [Plugins](./Agent_Step13_Plugins/) | Plugins with dependency injection |
|
||||
| [Code interpreter](./Agent_Step14_CodeInterpreter/) | Code interpreter tool |
|
||||
| [Computer use](./Agent_Step15_ComputerUse/) | Computer use tool |
|
||||
| [File search](./Agent_Step16_FileSearch/) | File search tool |
|
||||
| [OpenAPI tools](./Agent_Step17_OpenAPITools/) | OpenAPI tools |
|
||||
| [Bing custom search](./Agent_Step18_BingCustomSearch/) | Bing Custom Search tool |
|
||||
| [SharePoint](./Agent_Step19_SharePoint/) | SharePoint grounding tool |
|
||||
| [Microsoft Fabric](./Agent_Step20_MicrosoftFabric/) | Microsoft Fabric tool |
|
||||
| [Web search](./Agent_Step21_WebSearch/) | Web search tool |
|
||||
| [Memory search](./Agent_Step22_MemorySearch/) | Memory search tool |
|
||||
| [Local MCP](./Agent_Step23_LocalMCP/) | Local MCP client with HTTP transport |
|
||||
|
||||
## Running the samples
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/AgentsWithFoundry
|
||||
dotnet run --project .\FoundryAgent_Step01
|
||||
```
|
||||
+2
-1
@@ -9,7 +9,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
<PackageReference Include="Azure.AI.Projects" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to use Azure AI Foundry's Red Teaming service to assess
|
||||
// the safety and resilience of an AI model against adversarial attacks.
|
||||
//
|
||||
// It uses the RedTeam API from Azure.AI.Projects to run automated attack simulations
|
||||
// with various attack strategies (encoding, obfuscation, jailbreaks) across multiple
|
||||
// risk categories (Violence, HateUnfairness, Sexual, SelfHarm).
|
||||
//
|
||||
// For more details, see:
|
||||
// https://learn.microsoft.com/azure/ai-foundry/concepts/ai-red-teaming-agent
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
|
||||
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-mini";
|
||||
|
||||
Console.WriteLine("=" + new string('=', 79));
|
||||
Console.WriteLine("RED TEAMING EVALUATION SAMPLE");
|
||||
Console.WriteLine("=" + new string('=', 79));
|
||||
Console.WriteLine();
|
||||
|
||||
// Initialize Azure credentials and clients
|
||||
// WARNING: 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.
|
||||
DefaultAzureCredential credential = new();
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
|
||||
|
||||
// Configure the target model for red teaming
|
||||
AzureOpenAIModelConfiguration targetConfig = new(deploymentName);
|
||||
|
||||
// Create the red team run configuration
|
||||
RedTeam redTeamConfig = new(targetConfig)
|
||||
{
|
||||
DisplayName = "FinancialAdvisor-RedTeam",
|
||||
ApplicationScenario = "A financial advisor assistant that provides general financial advice and information.",
|
||||
NumTurns = 3,
|
||||
RiskCategories =
|
||||
{
|
||||
RiskCategory.Violence,
|
||||
RiskCategory.HateUnfairness,
|
||||
RiskCategory.Sexual,
|
||||
RiskCategory.SelfHarm,
|
||||
},
|
||||
AttackStrategies =
|
||||
{
|
||||
AttackStrategy.Easy,
|
||||
AttackStrategy.Moderate,
|
||||
AttackStrategy.Jailbreak,
|
||||
},
|
||||
};
|
||||
|
||||
Console.WriteLine($"Target model: {deploymentName}");
|
||||
Console.WriteLine("Risk categories: Violence, HateUnfairness, Sexual, SelfHarm");
|
||||
Console.WriteLine("Attack strategies: Easy, Moderate, Jailbreak");
|
||||
Console.WriteLine($"Simulation turns: {redTeamConfig.NumTurns}");
|
||||
Console.WriteLine();
|
||||
|
||||
// Submit the red team run to the service
|
||||
Console.WriteLine("Submitting red team run...");
|
||||
RedTeam redTeamRun = await aiProjectClient.RedTeams.CreateAsync(redTeamConfig, options: null);
|
||||
|
||||
Console.WriteLine($"Red team run created: {redTeamRun.Name}");
|
||||
Console.WriteLine($"Status: {redTeamRun.Status}");
|
||||
Console.WriteLine();
|
||||
|
||||
// Poll for completion
|
||||
Console.WriteLine("Waiting for red team run to complete (this may take several minutes)...");
|
||||
while (redTeamRun.Status != "Completed" && redTeamRun.Status != "Failed" && redTeamRun.Status != "Canceled")
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(15));
|
||||
redTeamRun = await aiProjectClient.RedTeams.GetAsync(redTeamRun.Name);
|
||||
Console.WriteLine($" Status: {redTeamRun.Status}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
if (redTeamRun.Status == "Completed")
|
||||
{
|
||||
Console.WriteLine("Red team run completed successfully!");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Results:");
|
||||
Console.WriteLine(new string('-', 80));
|
||||
Console.WriteLine($" Run name: {redTeamRun.Name}");
|
||||
Console.WriteLine($" Display name: {redTeamRun.DisplayName}");
|
||||
Console.WriteLine($" Status: {redTeamRun.Status}");
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Review the detailed results in the Azure AI Foundry portal:");
|
||||
Console.WriteLine($" {endpoint}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Red team run ended with status: {redTeamRun.Status}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(new string('=', 80));
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
# Red Teaming with Azure AI Foundry (Classic)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This sample uses the **classic Azure AI Foundry** red teaming API (`/redTeams/runs`) via `Azure.AI.Projects`. Results are viewable in the classic Foundry portal experience. The **new Foundry** portal's red teaming feature uses a different evaluation-based API that is not yet available in the .NET SDK.
|
||||
|
||||
This sample demonstrates how to use Azure AI Foundry's Red Teaming service to assess the safety and resilience of an AI model against adversarial attacks.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Configuring a red team run targeting an Azure OpenAI model deployment
|
||||
- Using multiple `AttackStrategy` options (Easy, Moderate, Jailbreak)
|
||||
- Evaluating across `RiskCategory` categories (Violence, HateUnfairness, Sexual, SelfHarm)
|
||||
- Submitting a red team scan and polling for completion
|
||||
- Reviewing results in the Azure AI Foundry portal
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Azure AI Foundry project (hub and project created)
|
||||
- Azure OpenAI deployment (e.g., gpt-4o or gpt-4o-mini)
|
||||
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||
|
||||
### Regional Requirements
|
||||
|
||||
Red teaming is only available in regions that support risk and safety evaluators:
|
||||
- **East US 2**, **Sweden Central**, **US North Central**, **France Central**, **Switzerland West**
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project" # Replace with your Azure Foundry project endpoint
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## Expected behavior
|
||||
|
||||
The sample will:
|
||||
|
||||
1. Configure a `RedTeam` run targeting the specified model deployment
|
||||
2. Define risk categories and attack strategies
|
||||
3. Submit the scan to Azure AI Foundry's Red Teaming service
|
||||
4. Poll for completion (this may take several minutes)
|
||||
5. Display the run status and direct you to the Azure AI Foundry portal for detailed results
|
||||
|
||||
## Understanding Red Teaming
|
||||
|
||||
### Attack Strategies
|
||||
|
||||
| Strategy | Description |
|
||||
|----------|-------------|
|
||||
| Easy | Simple encoding/obfuscation attacks (ROT13, Leetspeak, etc.) |
|
||||
| Moderate | Moderate complexity attacks requiring an LLM for orchestration |
|
||||
| Jailbreak | Crafted prompts designed to bypass AI safeguards (UPIA) |
|
||||
|
||||
### Risk Categories
|
||||
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
| Violence | Content related to violence |
|
||||
| HateUnfairness | Hate speech or unfair content |
|
||||
| Sexual | Sexual content |
|
||||
| SelfHarm | Self-harm related content |
|
||||
|
||||
### Interpreting Results
|
||||
|
||||
- Results are available in the Azure AI Foundry portal (**classic view** — toggle at top-right) under the red teaming section
|
||||
- Lower Attack Success Rate (ASR) is better — target ASR < 5% for production
|
||||
- Review individual attack conversations to understand vulnerabilities
|
||||
|
||||
### Current Limitations
|
||||
|
||||
> [!NOTE]
|
||||
> - The .NET Red Teaming API (`Azure.AI.Projects`) currently supports targeting **model deployments only** via `AzureOpenAIModelConfiguration`. The `AzureAIAgentTarget` type exists in the SDK but is consumed by the **Evaluation Taxonomy** API (`/evaluationtaxonomies`), not by the Red Teaming API (`/redTeams/runs`).
|
||||
> - Agent-targeted red teaming with agent-specific risk categories (Prohibited actions, Sensitive data leakage, Task adherence) is documented in the [concept docs](https://learn.microsoft.com/azure/ai-foundry/concepts/ai-red-teaming-agent) but is not yet available via the public REST API or .NET SDK.
|
||||
> - Results from this API appear in the **classic** Azure AI Foundry portal view. The new Foundry portal uses a separate evaluation-based system with `eval_*` identifiers.
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Azure AI Red Teaming Agent](https://learn.microsoft.com/azure/ai-foundry/concepts/ai-red-teaming-agent)
|
||||
- [RedTeam .NET API Reference](https://learn.microsoft.com/dotnet/api/azure.ai.projects.redteam?view=azure-dotnet-preview)
|
||||
- [Risk and Safety Evaluations](https://learn.microsoft.com/azure/ai-foundry/concepts/evaluation-metrics-built-in#risk-and-safety-evaluators)
|
||||
|
||||
## Next Steps
|
||||
|
||||
After running red teaming:
|
||||
1. Review attack results and strengthen agent guardrails
|
||||
2. Explore the Self-Reflection sample (FoundryAgents_Evaluations_Step02_SelfReflection) for quality assessment
|
||||
3. Set up continuous red teaming in your CI/CD pipeline
|
||||
+6
-2
@@ -6,16 +6,20 @@
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);MAAI001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.OpenAI" />
|
||||
<PackageReference Include="Azure.AI.Projects" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.Evaluation" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.Evaluation.Quality" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.Evaluation.Safety" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to use Microsoft.Extensions.AI.Evaluation.Quality to evaluate
|
||||
// an Agent Framework agent's response quality with a self-reflection loop.
|
||||
//
|
||||
// It uses GroundednessEvaluator, RelevanceEvaluator, and CoherenceEvaluator to score responses,
|
||||
// then iteratively asks the agent to improve based on evaluation feedback.
|
||||
//
|
||||
// Based on: Reflexion: Language Agents with Verbal Reinforcement Learning (NeurIPS 2023)
|
||||
// Reference: https://arxiv.org/abs/2303.11366
|
||||
//
|
||||
// For more details, see:
|
||||
// https://learn.microsoft.com/dotnet/ai/evaluation/libraries
|
||||
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.AI.Projects;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.AI.Evaluation;
|
||||
using Microsoft.Extensions.AI.Evaluation.Quality;
|
||||
using Microsoft.Extensions.AI.Evaluation.Safety;
|
||||
|
||||
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
|
||||
using ChatRole = Microsoft.Extensions.AI.ChatRole;
|
||||
|
||||
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-mini";
|
||||
string openAiEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
|
||||
string evaluatorDeploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? deploymentName;
|
||||
|
||||
Console.WriteLine("=" + new string('=', 79));
|
||||
Console.WriteLine("SELF-REFLECTION EVALUATION SAMPLE");
|
||||
Console.WriteLine("=" + new string('=', 79));
|
||||
Console.WriteLine();
|
||||
|
||||
// Initialize Azure credentials and client
|
||||
// WARNING: 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.
|
||||
DefaultAzureCredential credential = new();
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), credential);
|
||||
|
||||
// Set up the LLM-based chat client for quality evaluators
|
||||
IChatClient chatClient = new AzureOpenAIClient(new Uri(openAiEndpoint), credential)
|
||||
.GetChatClient(evaluatorDeploymentName)
|
||||
.AsIChatClient();
|
||||
|
||||
// Configure evaluation: quality evaluators use the LLM, safety evaluators use Azure AI Foundry
|
||||
ContentSafetyServiceConfiguration safetyConfig = new(
|
||||
credential: credential,
|
||||
endpoint: new Uri(endpoint));
|
||||
|
||||
ChatConfiguration chatConfiguration = safetyConfig.ToChatConfiguration(
|
||||
originalChatConfiguration: new ChatConfiguration(chatClient));
|
||||
|
||||
// Create a test agent
|
||||
AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
|
||||
name: "KnowledgeAgent",
|
||||
model: deploymentName,
|
||||
instructions: "You are a helpful assistant. Answer questions accurately based on the provided context.");
|
||||
Console.WriteLine($"Created agent: {agent.Name}");
|
||||
Console.WriteLine();
|
||||
|
||||
// Example question and grounding context
|
||||
const string Question = """
|
||||
What are the main benefits of using Azure AI Foundry for building AI applications?
|
||||
""";
|
||||
|
||||
const string Context = """
|
||||
Azure AI Foundry is a comprehensive platform for building, deploying, and managing AI applications.
|
||||
Key benefits include:
|
||||
1. Unified development environment with support for multiple AI frameworks and models
|
||||
2. Built-in safety and security features including content filtering and red teaming tools
|
||||
3. Scalable infrastructure that handles deployment and monitoring automatically
|
||||
4. Integration with Azure services like Azure OpenAI, Cognitive Services, and Machine Learning
|
||||
5. Evaluation tools for assessing model quality, safety, and performance
|
||||
6. Support for RAG (Retrieval-Augmented Generation) patterns with vector search
|
||||
7. Enterprise-grade compliance and governance features
|
||||
""";
|
||||
|
||||
Console.WriteLine("Question:");
|
||||
Console.WriteLine(Question);
|
||||
Console.WriteLine();
|
||||
|
||||
// Run evaluations
|
||||
try
|
||||
{
|
||||
await RunSelfReflectionWithGroundedness(agent, Question, Context, chatConfiguration);
|
||||
await RunQualityEvaluation(agent, Question, Context, chatConfiguration);
|
||||
await RunCombinedQualityAndSafetyEvaluation(agent, Question, chatConfiguration);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup
|
||||
await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Cleanup: Agent deleted.");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Implementation Functions
|
||||
// ============================================================================
|
||||
|
||||
static async Task RunSelfReflectionWithGroundedness(
|
||||
AIAgent agent, string question, string context, ChatConfiguration chatConfiguration)
|
||||
{
|
||||
Console.WriteLine("Running Self-Reflection with Groundedness Evaluation...");
|
||||
Console.WriteLine();
|
||||
|
||||
GroundednessEvaluator groundednessEvaluator = new();
|
||||
GroundednessEvaluatorContext groundingContext = new(context);
|
||||
|
||||
const int MaxReflections = 3;
|
||||
double bestScore = 0;
|
||||
|
||||
string currentPrompt = $"Context: {context}\n\nQuestion: {question}";
|
||||
|
||||
for (int i = 0; i < MaxReflections; i++)
|
||||
{
|
||||
Console.WriteLine($"Iteration {i + 1}/{MaxReflections}:");
|
||||
Console.WriteLine(new string('-', 40));
|
||||
|
||||
// Create a new session for each reflection iteration so that
|
||||
// conversation context does not carry over between runs. This keeps
|
||||
// each evaluation independent and avoids biasing groundedness scores.
|
||||
AgentSession session = await agent.CreateSessionAsync();
|
||||
AgentResponse agentResponse = await agent.RunAsync(currentPrompt, session);
|
||||
string responseText = agentResponse.Text;
|
||||
|
||||
Console.WriteLine($"Response: {responseText[..Math.Min(150, responseText.Length)]}...");
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new(ChatRole.User, currentPrompt),
|
||||
];
|
||||
ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, responseText));
|
||||
|
||||
EvaluationResult result = await groundednessEvaluator.EvaluateAsync(
|
||||
messages,
|
||||
chatResponse,
|
||||
chatConfiguration,
|
||||
additionalContext: [groundingContext]);
|
||||
|
||||
NumericMetric groundedness = result.Get<NumericMetric>(GroundednessEvaluator.GroundednessMetricName);
|
||||
double score = groundedness.Value ?? 0;
|
||||
string rating = groundedness.Interpretation?.Rating.ToString() ?? "N/A";
|
||||
|
||||
Console.WriteLine($"Groundedness score: {score:F1}/5 (Rating: {rating})");
|
||||
Console.WriteLine();
|
||||
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
}
|
||||
|
||||
if (score >= 4.0 || i == MaxReflections - 1)
|
||||
{
|
||||
if (score >= 4.0)
|
||||
{
|
||||
Console.WriteLine("Good groundedness achieved!");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Ask for improvement in the next iteration, including the previous response
|
||||
// so the LLM knows what to improve on (each iteration uses a new session).
|
||||
currentPrompt = $"""
|
||||
Context: {context}
|
||||
|
||||
Your previous answer scored {score}/5 on groundedness.
|
||||
Your previous answer was:
|
||||
{responseText}
|
||||
|
||||
Please improve your answer to be more grounded in the provided context.
|
||||
Only include information that is directly supported by the context.
|
||||
|
||||
Question: {question}
|
||||
""";
|
||||
Console.WriteLine("Requesting improvement...");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
Console.WriteLine($"Best groundedness score: {bestScore:F1}/5");
|
||||
Console.WriteLine(new string('=', 80));
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static async Task RunQualityEvaluation(
|
||||
AIAgent agent, string question, string context, ChatConfiguration chatConfiguration)
|
||||
{
|
||||
Console.WriteLine("Running Quality Evaluation (Relevance, Coherence, Groundedness)...");
|
||||
Console.WriteLine();
|
||||
|
||||
IEvaluator[] evaluators =
|
||||
[
|
||||
new RelevanceEvaluator(),
|
||||
new CoherenceEvaluator(),
|
||||
new GroundednessEvaluator(),
|
||||
];
|
||||
|
||||
CompositeEvaluator compositeEvaluator = new(evaluators);
|
||||
GroundednessEvaluatorContext groundingContext = new(context);
|
||||
|
||||
string prompt = $"Context: {context}\n\nQuestion: {question}";
|
||||
|
||||
AgentSession session = await agent.CreateSessionAsync();
|
||||
AgentResponse agentResponse = await agent.RunAsync(prompt, session);
|
||||
string responseText = agentResponse.Text;
|
||||
|
||||
Console.WriteLine($"Response: {responseText[..Math.Min(150, responseText.Length)]}...");
|
||||
Console.WriteLine();
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new(ChatRole.User, prompt),
|
||||
];
|
||||
ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, responseText));
|
||||
|
||||
EvaluationResult result = await compositeEvaluator.EvaluateAsync(
|
||||
messages,
|
||||
chatResponse,
|
||||
chatConfiguration,
|
||||
additionalContext: [groundingContext]);
|
||||
|
||||
foreach (EvaluationMetric metric in result.Metrics.Values)
|
||||
{
|
||||
if (metric is NumericMetric n)
|
||||
{
|
||||
string rating = n.Interpretation?.Rating.ToString() ?? "N/A";
|
||||
Console.WriteLine($" {n.Name,-20} Score: {n.Value:F1}/5 Rating: {rating}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine(new string('=', 80));
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static async Task RunCombinedQualityAndSafetyEvaluation(
|
||||
AIAgent agent, string question, ChatConfiguration chatConfiguration)
|
||||
{
|
||||
Console.WriteLine("Running Combined Quality + Safety Evaluation...");
|
||||
Console.WriteLine();
|
||||
|
||||
IEvaluator[] evaluators =
|
||||
[
|
||||
new RelevanceEvaluator(),
|
||||
new CoherenceEvaluator(),
|
||||
new ContentHarmEvaluator(),
|
||||
new ProtectedMaterialEvaluator(),
|
||||
];
|
||||
|
||||
CompositeEvaluator compositeEvaluator = new(evaluators);
|
||||
|
||||
AgentSession session = await agent.CreateSessionAsync();
|
||||
AgentResponse agentResponse = await agent.RunAsync(question, session);
|
||||
string responseText = agentResponse.Text;
|
||||
|
||||
Console.WriteLine($"Response: {responseText[..Math.Min(150, responseText.Length)]}...");
|
||||
Console.WriteLine();
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new(ChatRole.User, question), // No context in this evaluation — testing quality and safety on raw question
|
||||
];
|
||||
ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, responseText));
|
||||
|
||||
EvaluationResult result = await compositeEvaluator.EvaluateAsync(
|
||||
messages,
|
||||
chatResponse,
|
||||
chatConfiguration);
|
||||
|
||||
Console.WriteLine("Quality Metrics:");
|
||||
foreach (EvaluationMetric metric in result.Metrics.Values)
|
||||
{
|
||||
if (metric is NumericMetric n)
|
||||
{
|
||||
string rating = n.Interpretation?.Rating.ToString() ?? "N/A";
|
||||
bool failed = n.Interpretation?.Failed ?? false;
|
||||
Console.WriteLine($" {n.Name,-25} Score: {n.Value:F1,-6} Rating: {rating,-15} Failed: {failed}");
|
||||
}
|
||||
else if (metric is BooleanMetric b)
|
||||
{
|
||||
string rating = b.Interpretation?.Rating.ToString() ?? "N/A";
|
||||
bool failed = b.Interpretation?.Failed ?? false;
|
||||
Console.WriteLine($" {b.Name,-25} Value: {b.Value,-6} Rating: {rating,-15} Failed: {failed}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine(new string('=', 80));
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
# Self-Reflection Evaluation with Groundedness Assessment
|
||||
|
||||
This sample demonstrates the self-reflection pattern using Agent Framework with `Microsoft.Extensions.AI.Evaluation.Quality` evaluators. The agent iteratively improves its responses based on real groundedness evaluation scores.
|
||||
|
||||
For details on the self-reflection approach, see [Reflexion: Language Agents with Verbal Reinforcement Learning](https://arxiv.org/abs/2303.11366) (NeurIPS 2023).
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Self-reflection loop that improves responses using real `GroundednessEvaluator` scores
|
||||
- Using `RelevanceEvaluator` and `CoherenceEvaluator` for multi-metric quality assessment
|
||||
- Combining quality and safety evaluators with `CompositeEvaluator`
|
||||
- Configuring `ContentSafetyServiceConfiguration` for safety evaluators alongside LLM-based quality evaluators
|
||||
- Tracking improvement across iterations
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Azure AI Foundry project (hub and project created)
|
||||
- Azure OpenAI deployment (e.g., gpt-4o or gpt-4o-mini)
|
||||
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||
|
||||
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
|
||||
|
||||
### Azure Resources Required
|
||||
|
||||
1. **Azure AI Hub and Project**: Create these in the Azure Portal
|
||||
- Follow: https://learn.microsoft.com/azure/ai-foundry/how-to/create-projects
|
||||
2. **Azure OpenAI Deployment**: Deploy a model (e.g., gpt-4o or gpt-4o-mini)
|
||||
- Agent model: Used to generate responses
|
||||
- Evaluator model: Quality evaluators use an LLM; best results with GPT-4o
|
||||
3. **Azure CLI**: Install and authenticate with `az login`
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.api.azureml.ms" # Azure Foundry project endpoint
|
||||
$env:AZURE_OPENAI_ENDPOINT="https://your-openai.openai.azure.com/" # Azure OpenAI endpoint (for quality evaluators)
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Model deployment name
|
||||
```
|
||||
|
||||
**Note**: For best evaluation results, use GPT-4o or GPT-4o-mini as the evaluator model. The groundedness evaluator has been tested and tuned for these models.
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## Expected behavior
|
||||
|
||||
The sample runs three evaluation scenarios:
|
||||
|
||||
### 1. Self-Reflection with Groundedness
|
||||
- Asks a question with grounding context
|
||||
- Evaluates response groundedness using `GroundednessEvaluator`
|
||||
- If score is below 4/5, asks the agent to improve with feedback
|
||||
- Repeats up to 3 iterations
|
||||
- Tracks and reports the best score achieved
|
||||
|
||||
### 2. Quality Evaluation
|
||||
- Evaluates a single response with multiple quality evaluators:
|
||||
- `RelevanceEvaluator` — is the response relevant to the question?
|
||||
- `CoherenceEvaluator` — is the response logically coherent?
|
||||
- `GroundednessEvaluator` — is the response grounded in the provided context?
|
||||
|
||||
### 3. Combined Quality + Safety Evaluation
|
||||
- Runs both quality and safety evaluators together:
|
||||
- `RelevanceEvaluator`, `CoherenceEvaluator` (quality)
|
||||
- `ContentHarmEvaluator` (safety — violence, hate, sexual, self-harm)
|
||||
- `ProtectedMaterialEvaluator` (safety — copyrighted content detection)
|
||||
|
||||
## Understanding the Evaluation
|
||||
|
||||
### Groundedness Score (1-5 scale)
|
||||
|
||||
The `GroundednessEvaluator` measures how well the agent's response is grounded in the provided context:
|
||||
|
||||
- **5** = Excellent - Response is fully grounded in context
|
||||
- **4** = Good - Mostly grounded with minor deviations
|
||||
- **3** = Fair - Partially grounded but includes unsupported claims
|
||||
- **2** = Poor - Significant amount of ungrounded content
|
||||
- **1** = Very Poor - Response is largely unsupported by context
|
||||
|
||||
### Self-Reflection Process
|
||||
|
||||
1. **Initial Response**: Agent generates answer based on question + context
|
||||
2. **Evaluation**: `GroundednessEvaluator` scores the response (1-5)
|
||||
3. **Feedback**: If score < 4, agent receives the score and is asked to improve
|
||||
4. **Iteration**: Process repeats until good score or max iterations
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Provide Complete Context**: Ensure grounding context contains all information needed to answer the question
|
||||
2. **Clear Instructions**: Give the agent clear instructions about staying grounded in context
|
||||
3. **Use Quality Models**: GPT-4o recommended for evaluation tasks
|
||||
4. **Multiple Evaluators**: Use combination of evaluators (groundedness + relevance + coherence)
|
||||
5. **Batch Processing**: For production, process multiple questions in batch
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Reflexion Paper (NeurIPS 2023)](https://arxiv.org/abs/2303.11366)
|
||||
- [Microsoft.Extensions.AI.Evaluation Libraries](https://learn.microsoft.com/dotnet/ai/evaluation/libraries)
|
||||
- [GroundednessEvaluator API Reference](https://learn.microsoft.com/dotnet/api/microsoft.extensions.ai.evaluation.quality.groundednessevaluator)
|
||||
- [Azure AI Foundry Evaluation Service](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/evaluate-sdk)
|
||||
|
||||
## Next Steps
|
||||
|
||||
After running self-reflection evaluation:
|
||||
1. Implement similar patterns for other quality metrics (relevance, coherence, fluency)
|
||||
2. Integrate into CI/CD pipeline for continuous quality assurance
|
||||
3. Explore the Safety Evaluation sample (FoundryAgents_Evaluations_Step01_RedTeaming) for content safety assessment
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);IDE0059</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.Projects" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create and use AI agents with Azure Foundry Agents as the backend.
|
||||
|
||||
using Azure.AI.Projects;
|
||||
using Azure.AI.Projects.Agents;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
|
||||
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-mini";
|
||||
|
||||
const string JokerName = "JokerAgent";
|
||||
|
||||
// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
|
||||
// WARNING: 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.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
|
||||
|
||||
// Define the agent you want to create. (Prompt Agent in this case)
|
||||
AgentVersionCreationOptions options = new(new PromptAgentDefinition(model: deploymentName) { Instructions = "You are good at telling jokes." });
|
||||
|
||||
// Azure.AI.Agents SDK creates and manages agent by name and versions.
|
||||
// You can create a server side agent version with the Azure.AI.Agents SDK client below.
|
||||
AgentVersion createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options);
|
||||
|
||||
// Note:
|
||||
// agentVersion.Id = "<agentName>:<versionNumber>",
|
||||
// agentVersion.Version = <versionNumber>,
|
||||
// agentVersion.Name = <agentName>
|
||||
|
||||
// You can use an AIAgent with an already created server side agent version.
|
||||
AIAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
|
||||
|
||||
// You can also create another AIAgent version by providing the same name with a different definition/instruction.
|
||||
AIAgent newJokerAgent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
|
||||
|
||||
// You can also get the AIAgent latest version by just providing its name.
|
||||
AIAgent jokerAgentLatest = await aiProjectClient.GetAIAgentAsync(name: JokerName);
|
||||
AgentVersion latestAgentVersion = jokerAgentLatest.GetService<AgentVersion>()!;
|
||||
|
||||
// The AIAgent version can be accessed via the GetService method.
|
||||
Console.WriteLine($"Latest agent version id: {latestAgentVersion.Id}");
|
||||
|
||||
// Once you have the AIAgent, you can invoke it like any other AIAgent.
|
||||
Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate."));
|
||||
|
||||
// Cleanup by agent name removes both agent versions created.
|
||||
await aiProjectClient.Agents.DeleteAgentAsync(existingJokerAgent.Name);
|
||||
@@ -0,0 +1,40 @@
|
||||
# Creating and Managing AI Agents with Versioning
|
||||
|
||||
This sample demonstrates how to create and manage AI agents with Azure Foundry Agents, including:
|
||||
- Creating agents with different versions
|
||||
- Retrieving agents by version or latest version
|
||||
- Running multi-turn conversations with agents
|
||||
- Managing agent lifecycle (creation and deletion)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10 SDK or later
|
||||
- Azure Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||
|
||||
**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint
|
||||
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the FoundryAgents sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet/samples/02-agents/FoundryAgents
|
||||
dotnet run --project .\FoundryAgents_Step01.1_Basics
|
||||
```
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
1. **Creating agents with versions**: Shows how to create multiple versions of the same agent with different instructions
|
||||
2. **Retrieving agents**: Demonstrates retrieving agents by specific version or getting the latest version
|
||||
3. **Multi-turn conversations**: Shows how to use threads to maintain conversation context across multiple agent runs
|
||||
4. **Agent cleanup**: Demonstrates proper resource cleanup by deleting agents
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user