mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Added rai_config to Azure AI agent creation (#3265)
* Add kwargs to create_agent method * Added test for kwargs * Addressed comment * Added doc string
This commit is contained in:
committed by
GitHub
Unverified
parent
f87e55ba33
commit
915df3b404
@@ -4,7 +4,7 @@ import importlib.metadata
|
||||
|
||||
from ._agent_provider import AzureAIAgentsProvider
|
||||
from ._chat_client import AzureAIAgentClient, AzureAIAgentOptions
|
||||
from ._client import AzureAIClient
|
||||
from ._client import AzureAIClient, AzureAIProjectAgentOptions
|
||||
from ._project_provider import AzureAIProjectAgentProvider
|
||||
from ._shared import AzureAISettings
|
||||
|
||||
@@ -18,6 +18,7 @@ __all__ = [
|
||||
"AzureAIAgentOptions",
|
||||
"AzureAIAgentsProvider",
|
||||
"AzureAIClient",
|
||||
"AzureAIProjectAgentOptions",
|
||||
"AzureAIProjectAgentProvider",
|
||||
"AzureAISettings",
|
||||
"__version__",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import sys
|
||||
from collections.abc import Callable, Mapping, MutableMapping, MutableSequence, Sequence
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypedDict, TypeVar, cast
|
||||
from typing import Any, ClassVar, Generic, TypedDict, TypeVar, cast
|
||||
|
||||
from agent_framework import (
|
||||
AGENT_FRAMEWORK_USER_AGENT,
|
||||
@@ -20,12 +20,14 @@ from agent_framework import (
|
||||
)
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
from agent_framework.observability import use_instrumentation
|
||||
from agent_framework.openai import OpenAIResponsesOptions
|
||||
from agent_framework.openai._responses_client import OpenAIBaseResponsesClient
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.ai.projects.models import (
|
||||
MCPTool,
|
||||
PromptAgentDefinition,
|
||||
PromptAgentDefinitionText,
|
||||
RaiConfig,
|
||||
)
|
||||
from azure.core.credentials_async import AsyncTokenCredential
|
||||
from azure.core.exceptions import ResourceNotFoundError
|
||||
@@ -33,9 +35,6 @@ from pydantic import ValidationError
|
||||
|
||||
from ._shared import AzureAISettings, create_text_format_config
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from agent_framework.openai import OpenAIResponsesOptions
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from typing import TypeVar # type: ignore # pragma: no cover
|
||||
else:
|
||||
@@ -52,10 +51,18 @@ else:
|
||||
|
||||
logger = get_logger("agent_framework.azure")
|
||||
|
||||
|
||||
class AzureAIProjectAgentOptions(OpenAIResponsesOptions):
|
||||
"""Azure AI Project Agent options."""
|
||||
|
||||
rai_config: RaiConfig
|
||||
"""Configuration for Responsible AI (RAI) content filtering and safety features."""
|
||||
|
||||
|
||||
TAzureAIClientOptions = TypeVar(
|
||||
"TAzureAIClientOptions",
|
||||
bound=TypedDict, # type: ignore[valid-type]
|
||||
default="OpenAIResponsesOptions",
|
||||
default="AzureAIProjectAgentOptions",
|
||||
covariant=True,
|
||||
)
|
||||
|
||||
@@ -397,6 +404,7 @@ class AzureAIClient(OpenAIBaseResponsesClient[TAzureAIClientOptions], Generic[TA
|
||||
"model",
|
||||
"tools",
|
||||
"response_format",
|
||||
"rai_config",
|
||||
"temperature",
|
||||
"top_p",
|
||||
"text",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import sys
|
||||
from collections.abc import Callable, MutableMapping, Sequence
|
||||
from typing import TYPE_CHECKING, Any, Generic, TypedDict
|
||||
from typing import Any, Generic, TypedDict
|
||||
|
||||
from agent_framework import (
|
||||
AGENT_FRAMEWORK_USER_AGENT,
|
||||
@@ -26,12 +26,9 @@ from azure.ai.projects.models import (
|
||||
from azure.core.credentials_async import AsyncTokenCredential
|
||||
from pydantic import ValidationError
|
||||
|
||||
from ._client import AzureAIClient
|
||||
from ._client import AzureAIClient, AzureAIProjectAgentOptions
|
||||
from ._shared import AzureAISettings, create_text_format_config, from_azure_ai_tools, to_azure_ai_tools
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from agent_framework.openai import OpenAIResponsesOptions
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from typing import Self, TypeVar # pragma: no cover
|
||||
else:
|
||||
@@ -46,7 +43,7 @@ logger = get_logger("agent_framework.azure")
|
||||
TOptions_co = TypeVar(
|
||||
"TOptions_co",
|
||||
bound=TypedDict, # type: ignore[valid-type]
|
||||
default="OpenAIResponsesOptions",
|
||||
default="AzureAIProjectAgentOptions",
|
||||
covariant=True,
|
||||
)
|
||||
|
||||
@@ -193,9 +190,10 @@ class AzureAIProjectAgentProvider(Generic[TOptions_co]):
|
||||
"or set 'AZURE_AI_MODEL_DEPLOYMENT_NAME' environment variable."
|
||||
)
|
||||
|
||||
# Extract response_format from default_options if present
|
||||
# Extract options from default_options if present
|
||||
opts = dict(default_options) if default_options else {}
|
||||
response_format = opts.get("response_format")
|
||||
rai_config = opts.get("rai_config")
|
||||
|
||||
args: dict[str, Any] = {"model": resolved_model}
|
||||
|
||||
@@ -205,6 +203,8 @@ class AzureAIProjectAgentProvider(Generic[TOptions_co]):
|
||||
args["text"] = PromptAgentDefinitionText(
|
||||
format=create_text_format_config(response_format) # type: ignore[arg-type]
|
||||
)
|
||||
if rai_config:
|
||||
args["rai_config"] = rai_config
|
||||
|
||||
# Normalize tools once and reuse for both Azure AI API and ChatAgent
|
||||
normalized_tools = normalize_tools(tools)
|
||||
|
||||
@@ -207,6 +207,49 @@ async def test_provider_create_agent_missing_model(mock_project_client: MagicMoc
|
||||
await provider.create_agent(name="test-agent")
|
||||
|
||||
|
||||
async def test_provider_create_agent_with_rai_config(
|
||||
mock_project_client: MagicMock,
|
||||
azure_ai_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test AzureAIProjectAgentProvider.create_agent passes rai_config from default_options."""
|
||||
with patch("agent_framework_azure_ai._project_provider.AzureAISettings") as mock_settings:
|
||||
mock_settings.return_value.project_endpoint = azure_ai_unit_test_env["AZURE_AI_PROJECT_ENDPOINT"]
|
||||
mock_settings.return_value.model_deployment_name = azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"]
|
||||
|
||||
provider = AzureAIProjectAgentProvider(project_client=mock_project_client)
|
||||
|
||||
# Mock agent creation response
|
||||
mock_agent_version = MagicMock(spec=AgentVersionDetails)
|
||||
mock_agent_version.id = "agent-id"
|
||||
mock_agent_version.name = "test-agent"
|
||||
mock_agent_version.version = "1.0"
|
||||
mock_agent_version.description = None
|
||||
mock_agent_version.definition = MagicMock(spec=PromptAgentDefinition)
|
||||
mock_agent_version.definition.model = "gpt-4"
|
||||
mock_agent_version.definition.instructions = None
|
||||
mock_agent_version.definition.temperature = None
|
||||
mock_agent_version.definition.top_p = None
|
||||
mock_agent_version.definition.tools = []
|
||||
|
||||
mock_project_client.agents.create_version = AsyncMock(return_value=mock_agent_version)
|
||||
|
||||
# Create a mock RaiConfig-like object
|
||||
mock_rai_config = MagicMock()
|
||||
mock_rai_config.rai_policy_name = "policy-name"
|
||||
|
||||
# Call create_agent with rai_config in default_options
|
||||
await provider.create_agent(
|
||||
name="test-agent",
|
||||
model="gpt-4",
|
||||
default_options={"rai_config": mock_rai_config},
|
||||
)
|
||||
|
||||
# Verify rai_config was passed to PromptAgentDefinition
|
||||
call_args = mock_project_client.agents.create_version.call_args
|
||||
definition = call_args[1]["definition"]
|
||||
assert definition.rai_config is mock_rai_config
|
||||
|
||||
|
||||
async def test_provider_get_agent_with_name(mock_project_client: MagicMock) -> None:
|
||||
"""Test AzureAIProjectAgentProvider.get_agent with name parameter."""
|
||||
provider = AzureAIProjectAgentProvider(project_client=mock_project_client)
|
||||
|
||||
@@ -9,6 +9,7 @@ _IMPORTS: dict[str, tuple[str, str]] = {
|
||||
"AgentResponseCallbackProtocol": ("agent_framework_azurefunctions", "agent-framework-azurefunctions"),
|
||||
"AzureAIAgentClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"),
|
||||
"AzureAIAgentOptions": ("agent_framework_azure_ai", "agent-framework-azure-ai"),
|
||||
"AzureAIProjectAgentOptions": ("agent_framework_azure_ai", "agent-framework-azure-ai"),
|
||||
"AzureAIClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"),
|
||||
"AzureAIProjectAgentProvider": ("agent_framework_azure_ai", "agent-framework-azure-ai"),
|
||||
"AzureAISearchContextProvider": ("agent_framework_azure_ai_search", "agent-framework-azure-ai-search"),
|
||||
|
||||
@@ -4,6 +4,7 @@ from agent_framework_azure_ai import (
|
||||
AzureAIAgentClient,
|
||||
AzureAIAgentsProvider,
|
||||
AzureAIClient,
|
||||
AzureAIProjectAgentOptions,
|
||||
AzureAIProjectAgentProvider,
|
||||
AzureAISettings,
|
||||
)
|
||||
@@ -28,6 +29,7 @@ __all__ = [
|
||||
"AzureAIAgentClient",
|
||||
"AzureAIAgentsProvider",
|
||||
"AzureAIClient",
|
||||
"AzureAIProjectAgentOptions",
|
||||
"AzureAIProjectAgentProvider",
|
||||
"AzureAISearchContextProvider",
|
||||
"AzureAISearchSettings",
|
||||
|
||||
@@ -17,6 +17,7 @@ This folder contains examples demonstrating different ways to create and use age
|
||||
| [`azure_ai_with_code_interpreter.py`](azure_ai_with_code_interpreter.py) | Shows how to use the `HostedCodeInterpreterTool` with Azure AI agents to write and execute Python code for mathematical problem solving and data analysis. |
|
||||
| [`azure_ai_with_code_interpreter_file_generation.py`](azure_ai_with_code_interpreter_file_generation.py) | Shows how to retrieve file IDs from code interpreter generated files using both streaming and non-streaming approaches. |
|
||||
| [`azure_ai_with_code_interpreter_file_download.py`](azure_ai_with_code_interpreter_file_download.py) | Shows how to download files generated by code interpreter using the OpenAI containers API. |
|
||||
| [`azure_ai_with_content_filtering.py`](azure_ai_with_content_filtering.py) | Shows how to enable content filtering (RAI policy) on Azure AI agents using `RaiConfig`. Requires creating an RAI policy in Azure AI Foundry portal first. |
|
||||
| [`azure_ai_with_existing_agent.py`](azure_ai_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent name and version to the Azure AI client. Demonstrates agent reuse patterns for production scenarios. |
|
||||
| [`azure_ai_with_existing_conversation.py`](azure_ai_with_existing_conversation.py) | Demonstrates how to use an existing conversation created on the service side with Azure AI agents. Shows two approaches: specifying conversation ID at the client level and using AgentThread with an existing conversation ID. |
|
||||
| [`azure_ai_with_application_endpoint.py`](azure_ai_with_application_endpoint.py) | Demonstrates calling the Azure AI application-scoped endpoint. |
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.azure import AzureAIProjectAgentProvider
|
||||
from azure.ai.projects.models import RaiConfig
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
"""
|
||||
Azure AI Agent with Content Filtering (RAI Policy) Example
|
||||
|
||||
This sample demonstrates how to enable content filtering on Azure AI agents using RaiConfig.
|
||||
|
||||
Prerequisites:
|
||||
1. Create an RAI Policy in Azure AI Foundry portal:
|
||||
- Go to Azure AI Foundry > Your Project > Guardrails + Controls > Content Filters
|
||||
- Create a new content filter or use an existing one
|
||||
- Note the policy name
|
||||
|
||||
2. Set environment variables:
|
||||
- AZURE_AI_PROJECT_ENDPOINT: Your Azure AI Foundry project endpoint
|
||||
- AZURE_AI_MODEL_DEPLOYMENT_NAME: Your model deployment name
|
||||
|
||||
3. Run `az login` to authenticate
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Azure AI Agent with Content Filtering ===\n")
|
||||
|
||||
# Replace with your RAI policy from Azure AI Foundry portal
|
||||
rai_policy_name = (
|
||||
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/"
|
||||
"Microsoft.CognitiveServices/accounts/{accountName}/raiPolicies/{policyName}"
|
||||
)
|
||||
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIProjectAgentProvider(credential=credential) as provider,
|
||||
):
|
||||
# Create agent with content filtering enabled via default_options
|
||||
agent = await provider.create_agent(
|
||||
name="ContentFilteredAgent",
|
||||
instructions="You are a helpful assistant.",
|
||||
default_options={"rai_config": RaiConfig(rai_policy_name=rai_policy_name)},
|
||||
)
|
||||
|
||||
# Test with a normal query
|
||||
query = "What is the capital of France?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Test with a query that might trigger content filtering
|
||||
# (depending on your RAI policy configuration)
|
||||
query2 = "Tell me something inappropriate."
|
||||
print(f"User: {query2}")
|
||||
try:
|
||||
result2 = await agent.run(query2)
|
||||
print(f"Agent: {result2}\n")
|
||||
except Exception as e:
|
||||
print(f"Content filter triggered: {e}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user