Not
Åtkomst till denna sida kräver auktorisation. Du kan prova att logga in eller byta katalog.
Åtkomst till denna sida kräver auktorisation. Du kan prova att byta katalog.
Mellanprogram i Agent Framework är ett kraftfullt sätt att fånga upp, ändra och förbättra agentinteraktioner i olika skeden av körningen. Du kan använda mellanprogram för att implementera övergripande problem som loggning, säkerhetsverifiering, felhantering och resultattransformering utan att ändra kärnagenten eller funktionslogik.
Agent Framework kan anpassas med tre olika typer av mellanprogram:
- Mellanprogram för agentkörning: Tillåter avlyssning av alla agentkörningar, så att indata och utdata kan inspekteras och/eller ändras efter behov.
- Funktion som anropar mellanprogram: Tillåter avlyssning av alla funktionsanrop som körs av agenten, så att indata och utdata kan inspekteras och ändras efter behov.
-
IChatClient middleware: Tillåter avlyssning av anrop till en
IChatClientimplementering, där en agent använderIChatClientför slutsatsdragningsanrop, till exempel när du använderChatClientAgent.
Alla typer av mellanprogram implementeras via ett funktionsåteranrop, och när flera mellanprogramsinstanser av samma typ registreras bildar de en kedja, där varje mellanprograminstans förväntas anropa nästa i kedjan via en angivet nextFunc.
Agentkörning och funktionsanrop av mellanprogramstyper kan registreras på en agent med hjälp av agentverktyget med ett befintligt agentobjekt.
var middlewareEnabledAgent = originalAgent
.AsBuilder()
.Use(runFunc: CustomAgentRunMiddleware, runStreamingFunc: CustomAgentRunStreamingMiddleware)
.Use(CustomFunctionCallingMiddleware)
.Build();
Viktigt!
Helst både runFunc och runStreamingFunc bör tillhandahållas. När agenten endast tillhandahåller mellanprogram som inte direktuppspelas använder den för både strömnings- och icke-direktuppspelningsanrop. Direktuppspelning körs endast i icke-strömningsläge för att uppfylla förväntningarna på mellanprogram.
Anmärkning
Det finns ytterligare en överlagring, Use(sharedFunc: ...), som gör att du kan tillhandahålla samma mellanprogram för icke-direktuppspelning och strömning utan att blockera strömningen. Det delade mellanprogrammet kan dock inte fånga upp eller åsidosätta utdata. Den här överlagringen bör användas för scenarier där du bara behöver inspektera eller ändra indata innan den når agenten.
IChatClient mellanprogram kan registreras på en IChatClient innan den används med ett ChatClientAgent, med hjälp av chattklientverktygets mönster.
var chatClient = new AzureOpenAIClient(new Uri("https://<myresource>.openai.azure.com"), new DefaultAzureCredential())
.GetChatClient(deploymentName)
.AsIChatClient();
var middlewareEnabledChatClient = chatClient
.AsBuilder()
.Use(getResponseFunc: CustomChatClientMiddleware, getStreamingResponseFunc: null)
.Build();
var agent = new ChatClientAgent(middlewareEnabledChatClient, instructions: "You are a helpful assistant.");
Varning
DefaultAzureCredential är praktiskt för utveckling men kräver noggrant övervägande i produktion. I produktion bör du överväga att använda en specifik autentiseringsuppgift (t.ex. ManagedIdentityCredential) för att undvika problem med svarstid, oavsiktlig avsökning av autentiseringsuppgifter och potentiella säkerhetsrisker från reservmekanismer.
IChatClient mellanprogram kan också registreras med hjälp av en fabriksmetod när du skapar en agent via någon av hjälpmetoderna på SDK-klienter.
var agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetChatClient(deploymentName)
.AsAIAgent("You are a helpful assistant.", clientFactory: (chatClient) => chatClient
.AsBuilder()
.Use(getResponseFunc: CustomChatClientMiddleware, getStreamingResponseFunc: null)
.Build());
Mellanprogram för agentkörning
Här är ett exempel på mellanprogram för agentkörning som kan inspektera och/eller ändra indata och utdata från agentkörningen.
async Task<AgentResponse> CustomAgentRunMiddleware(
IEnumerable<ChatMessage> messages,
AgentSession? session,
AgentRunOptions? options,
AIAgent innerAgent,
CancellationToken cancellationToken)
{
Console.WriteLine(messages.Count());
var response = await innerAgent.RunAsync(messages, session, options, cancellationToken).ConfigureAwait(false);
Console.WriteLine(response.Messages.Count);
return response;
}
Mellanprogram för agentkörningsuppspelning
Här är ett exempel på agent som kör strömmande mellanprogram som kan inspektera och/eller ändra indata och utdata från agentströmningskörningen.
async IAsyncEnumerable<AgentResponseUpdate> CustomAgentRunStreamingMiddleware(
IEnumerable<ChatMessage> messages,
AgentSession? session,
AgentRunOptions? options,
AIAgent innerAgent,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
Console.WriteLine(messages.Count());
List<AgentResponseUpdate> updates = [];
await foreach (var update in innerAgent.RunStreamingAsync(messages, session, options, cancellationToken))
{
updates.Add(update);
yield return update;
}
Console.WriteLine(updates.ToAgentResponse().Messages.Count);
}
Funktion som anropar mellanprogram
Anmärkning
Mellanprogram för funktionsanrop stöds just nu endast med en AIAgent som använder FunctionInvokingChatClient, till exempel ChatClientAgent.
Här är ett exempel på funktion som anropar mellanprogram, som kan inspektera och/eller ändra funktionen som anropas och resultatet från funktionsanropet.
async ValueTask<object?> CustomFunctionCallingMiddleware(
AIAgent agent,
FunctionInvocationContext context,
Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
CancellationToken cancellationToken)
{
Console.WriteLine($"Function Name: {context!.Function.Name}");
var result = await next(context, cancellationToken);
Console.WriteLine($"Function Call Result: {result}");
return result;
}
Det går att avsluta funktionsanropsloopen med funktionen som anropar mellanprogram genom att ställa in den angivna FunctionInvocationContext.Terminate till true.
Detta förhindrar att funktionsanropsloopen skickar en begäran till inferenstjänsten som innehåller funktionsanropsresultatet efter funktionsanropet.
Om det fanns fler än en funktion tillgänglig för anrop under den här iterationen kan det också förhindra att återstående funktioner körs.
Varning
Om du avslutar funktionsanropsloopen kan det leda till att chatthistoriken lämnas i ett inkonsekvent tillstånd, till exempel med funktionsanropsinnehåll utan funktionsresultatinnehåll. Detta kan leda till att chatthistoriken blir oanvändbar för ytterligare körningar.
IChatClient-mellanprogram
Här är ett exempel på mellanprogram för chattklienten som kan inspektera och/eller ändra indata och utdata för begäran till den slutsatsdragningstjänst som chattklienten tillhandahåller.
async Task<ChatResponse> CustomChatClientMiddleware(
IEnumerable<ChatMessage> messages,
ChatOptions? options,
IChatClient innerChatClient,
CancellationToken cancellationToken)
{
Console.WriteLine(messages.Count());
var response = await innerChatClient.GetResponseAsync(messages, options, cancellationToken);
Console.WriteLine(response.Messages.Count);
return response;
}
Tips/Råd
Se .NET-exemplen för fullständiga körbara exempel.
Anmärkning
Mer information om IChatClient mellanprogram finns i Custom IChatClient middleware.
Agent Framework kan anpassas med tre olika typer av mellanprogram:
- Agentmellanprogram: Fångar upp körning av agentkörning så att du kan inspektera och ändra indata, utdata och kontrollflöde.
- Funktionsmellanprogram: Avlyssnar funktionsanrop (verktyg) som görs under agentkörningen, vilket aktiverar validering av indata, resultattransformering och körningskontroll.
- Chattmellanprogram: Fångar upp underliggande chattförfrågningar som skickas till AI-modeller, vilket ger åtkomst till rådata, alternativ och svar.
Alla typer stöder både funktionsbaserade och klassbaserade implementeringar. När flera mellanprogram av samma typ registreras bildar de en kedja där var och en anropar anropsbar för att fortsätta bearbetningen next .
Anmärkning
Mellanprogramsordning med blandade registreringsomfattningar:
- Mellanprogram på agentnivå omsluter mellanprogram på körningsnivå.
- För mellanprogram
[A1, A2]för agent och kör mellanprogram[R1, R2]är körningsordningen:A1 -> A2 -> R1 -> R2 -> Agent -> R2 -> R1 -> A2 -> A1. - Funktions-/chattmellanprogram följer samma omslutningsprincip vid verktygs-/chattsamtalstid.
Mellanprogram för agent
Agentens mellanprogram fångar upp och ändrar körningen av agentkörningen. Den använder som AgentContext innehåller:
-
agent: Agenten som anropas -
messages: Lista över chattmeddelanden i konversationen -
is_streaming: Booleskt värde som anger om svaret strömmas -
metadata: Ordlista för lagring av ytterligare data mellan mellanprogram -
result: Agentens svar (kan ändras) -
terminate: Flagga för att stoppa ytterligare bearbetning -
kwargs: Ytterligare nyckelordsargument som skickas till agentkörningsmetoden
next Anropsbar fortsätter mellanprogramkedjan eller kör agenten om det är det sista mellanprogrammet.
Funktionsbaserad
async def logging_agent_middleware(
context: AgentContext,
next: Callable[[AgentContext], Awaitable[None]],
) -> None:
"""Agent middleware that logs execution timing."""
# Pre-processing: Log before agent execution
print("[Agent] Starting execution")
# Continue to next middleware or agent execution
await next(context)
# Post-processing: Log after agent execution
print("[Agent] Execution completed")
Klassbaserad
Klassbaserad agentmellanprogram använder en process metod som har samma signatur och beteende som funktionsbaserat mellanprogram.
from agent_framework import AgentMiddleware, AgentContext
class LoggingAgentMiddleware(AgentMiddleware):
"""Agent middleware that logs execution."""
async def process(
self,
context: AgentContext,
next: Callable[[AgentContext], Awaitable[None]],
) -> None:
print("[Agent Class] Starting execution")
await next(context)
print("[Agent Class] Execution completed")
Funktionsmellanprogram
Funktionsmellanprogram fångar upp funktionsanrop inom agenter. Den använder som FunctionInvocationContext innehåller:
-
function: Funktionen som anropas -
arguments: De verifierade argumenten för funktionen -
metadata: Ordlista för lagring av ytterligare data mellan mellanprogram -
result: Funktionens returvärde (kan ändras) -
terminate: Flagga för att stoppa ytterligare bearbetning -
kwargs: Ytterligare nyckelordsargument som skickas till chattmetoden som anropade den här funktionen
next Anropsbar fortsätter till nästa mellanprogram eller kör den faktiska funktionen.
Funktionsbaserad
async def logging_function_middleware(
context: FunctionInvocationContext,
next: Callable[[FunctionInvocationContext], Awaitable[None]],
) -> None:
"""Function middleware that logs function execution."""
# Pre-processing: Log before function execution
print(f"[Function] Calling {context.function.name}")
# Continue to next middleware or function execution
await next(context)
# Post-processing: Log after function execution
print(f"[Function] {context.function.name} completed")
Klassbaserad
from agent_framework import FunctionMiddleware, FunctionInvocationContext
class LoggingFunctionMiddleware(FunctionMiddleware):
"""Function middleware that logs function execution."""
async def process(
self,
context: FunctionInvocationContext,
next: Callable[[FunctionInvocationContext], Awaitable[None]],
) -> None:
print(f"[Function Class] Calling {context.function.name}")
await next(context)
print(f"[Function Class] {context.function.name} completed")
Chattmellanprogram
Chattmellanprogram fångar upp chattförfrågningar som skickas till AI-modeller. Den använder som ChatContext innehåller:
-
chat_client: Chattklienten som anropas -
messages: Lista över meddelanden som skickas till AI-tjänsten -
options: Alternativen för chattbegäran -
is_streaming: Booleskt värde som anger om detta är ett direktuppspelningsanrop -
metadata: Ordlista för lagring av ytterligare data mellan mellanprogram -
result: Chattsvaret från AI:n (kan ändras) -
terminate: Flagga för att stoppa ytterligare bearbetning -
kwargs: Ytterligare nyckelordsargument som skickas till chattklienten
Anropsbar next fortsätter till nästa mellanprogram eller skickar begäran till AI-tjänsten.
Funktionsbaserad
async def logging_chat_middleware(
context: ChatContext,
next: Callable[[ChatContext], Awaitable[None]],
) -> None:
"""Chat middleware that logs AI interactions."""
# Pre-processing: Log before AI call
print(f"[Chat] Sending {len(context.messages)} messages to AI")
# Continue to next middleware or AI service
await next(context)
# Post-processing: Log after AI response
print("[Chat] AI response received")
Klassbaserad
from agent_framework import ChatMiddleware, ChatContext
class LoggingChatMiddleware(ChatMiddleware):
"""Chat middleware that logs AI interactions."""
async def process(
self,
context: ChatContext,
next: Callable[[ChatContext], Awaitable[None]],
) -> None:
print(f"[Chat Class] Sending {len(context.messages)} messages to AI")
await next(context)
print("[Chat Class] AI response received")
Mellanprogramsdekoratörer
Dekoratörer tillhandahåller explicit mellanprogramstypdeklaration utan att kräva typanteckningar. De är användbara när du inte använder typanteckningar eller vill förhindra typmatchningar:
from agent_framework import agent_middleware, function_middleware, chat_middleware
@agent_middleware
async def simple_agent_middleware(context, next):
print("Before agent execution")
await next(context)
print("After agent execution")
@function_middleware
async def simple_function_middleware(context, next):
print(f"Calling function: {context.function.name}")
await next(context)
print("Function call completed")
@chat_middleware
async def simple_chat_middleware(context, next):
print(f"Processing {len(context.messages)} chat messages")
await next(context)
print("Chat processing completed")
Registrering av mellanprogram
Mellanprogram kan registreras på två nivåer med olika omfång och beteenden.
Agent-Level vs Run-Level Middleware
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
# Agent-level middleware: Applied to ALL runs of the agent
async with AzureAIAgentClient(credential=credential).as_agent(
name="WeatherAgent",
instructions="You are a helpful weather assistant.",
tools=get_weather,
middleware=[
SecurityAgentMiddleware(), # Applies to all runs
TimingFunctionMiddleware(), # Applies to all runs
],
) as agent:
# This run uses agent-level middleware only
result1 = await agent.run("What's the weather in Seattle?")
# This run uses agent-level + run-level middleware
result2 = await agent.run(
"What's the weather in Portland?",
middleware=[ # Run-level middleware (this run only)
logging_chat_middleware,
]
)
# This run uses agent-level middleware only (no run-level)
result3 = await agent.run("What's the weather in Vancouver?")
Viktiga skillnader:
- Agentnivå: Beständiga för alla körningar, konfigurerade en gång när agenten skapades
- Körningsnivå: Tillämpas endast på specifika körningar, tillåter anpassning per begäran
- Körningsordning: Agentmellanprogram (yttersta) → Kör mellanprogram (innersta) → Agentkörning
Mellanprogramsavslut
Mellanprogram kan avsluta körningen tidigt med .context.terminate Detta är användbart för säkerhetskontroller, hastighetsbegränsning eller valideringsfel.
async def blocking_middleware(
context: AgentContext,
next: Callable[[AgentContext], Awaitable[None]],
) -> None:
"""Middleware that blocks execution based on conditions."""
# Check for blocked content
last_message = context.messages[-1] if context.messages else None
if last_message and last_message.text:
if "blocked" in last_message.text.lower():
print("Request blocked by middleware")
context.terminate = True
return
# If no issues, continue normally
await next(context)
Vad avslutning innebär:
- Ange
context.terminate = Truesignaler om att bearbetningen ska stoppas - Du kan ge ett anpassat resultat innan du avslutar för att ge användarna feedback
- Agentkörningen hoppas över helt när mellanprogrammet avslutas
Åsidosättning av mellanprogramresultat
Mellanprogram kan åsidosätta resultat i både scenarier som inte strömmas och strömning, så att du kan ändra eller helt ersätta agentsvar.
Resultattypen i context.result beror på om agentanropet är direktuppspelning eller inte:
-
Icke-direktuppspelning:
context.resultinnehåller ettAgentResponsemed det fullständiga svaret -
Direktuppspelning:
context.resultinnehåller en asynkron generator som gerAgentResponseUpdatesegment
Du kan använda context.is_streaming för att skilja mellan dessa scenarier och hantera resultat åsidosättningar på lämpligt sätt.
async def weather_override_middleware(
context: AgentContext,
next: Callable[[AgentContext], Awaitable[None]]
) -> None:
"""Middleware that overrides weather results for both streaming and non-streaming."""
# Execute the original agent logic
await next(context)
# Override results if present
if context.result is not None:
custom_message_parts = [
"Weather Override: ",
"Perfect weather everywhere today! ",
"22°C with gentle breezes. ",
"Great day for outdoor activities!"
]
if context.is_streaming:
# Streaming override
async def override_stream() -> AsyncIterable[AgentResponseUpdate]:
for chunk in custom_message_parts:
yield AgentResponseUpdate(contents=[Content.from_text(text=chunk)])
context.result = override_stream()
else:
# Non-streaming override
custom_message = "".join(custom_message_parts)
context.result = AgentResponse(
messages=[Message(role="assistant", contents=[custom_message])]
)
Med den här metoden för mellanprogram kan du implementera avancerad svarstransformering, innehållsfiltrering, resultatförbättring och anpassning av direktuppspelning samtidigt som agentlogik hålls ren och fokuserad.
Kompletta exempel på mellanprogram
Klassbaserat mellanprogram
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import time
from collections.abc import Awaitable, Callable
from random import randint
from typing import Annotated
from agent_framework import (
AgentContext,
AgentMiddleware,
AgentResponse,
FunctionInvocationContext,
FunctionMiddleware,
Message,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
"""
Class-based MiddlewareTypes Example
This sample demonstrates how to implement middleware using class-based approach by inheriting
from AgentMiddleware and FunctionMiddleware base classes. The example includes:
- SecurityAgentMiddleware: Checks for security violations in user queries and blocks requests
containing sensitive information like passwords or secrets
- LoggingFunctionMiddleware: Logs function execution details including timing and parameters
This approach is useful when you need stateful middleware or complex logic that benefits
from object-oriented design patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
"""Get the weather for a given location."""
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
class SecurityAgentMiddleware(AgentMiddleware):
"""Agent middleware that checks for security violations."""
async def process(
self,
context: AgentContext,
call_next: Callable[[], Awaitable[None]],
) -> None:
# Check for potential security violations in the query
# Look at the last user message
last_message = context.messages[-1] if context.messages else None
if last_message and last_message.text:
query = last_message.text
if "password" in query.lower() or "secret" in query.lower():
print("[SecurityAgentMiddleware] Security Warning: Detected sensitive information, blocking request.")
# Override the result with warning message
context.result = AgentResponse(
messages=[Message("assistant", ["Detected sensitive information, the request is blocked."])]
)
# Simply don't call call_next() to prevent execution
return
print("[SecurityAgentMiddleware] Security check passed.")
await call_next()
class LoggingFunctionMiddleware(FunctionMiddleware):
"""Function middleware that logs function calls."""
async def process(
self,
context: FunctionInvocationContext,
call_next: Callable[[], Awaitable[None]],
) -> None:
function_name = context.function.name
print(f"[LoggingFunctionMiddleware] About to call function: {function_name}.")
start_time = time.time()
await call_next()
end_time = time.time()
duration = end_time - start_time
print(f"[LoggingFunctionMiddleware] Function {function_name} completed in {duration:.5f}s.")
async def main() -> None:
"""Example demonstrating class-based middleware."""
print("=== Class-based MiddlewareTypes Example ===")
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
AzureAIAgentClient(credential=credential).as_agent(
name="WeatherAgent",
instructions="You are a helpful weather assistant.",
tools=get_weather,
middleware=[SecurityAgentMiddleware(), LoggingFunctionMiddleware()],
) as agent,
):
# Test with normal query
print("\n--- Normal Query ---")
query = "What's the weather like in Seattle?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result.text}\n")
# Test with security-related query
print("--- Security Test ---")
query = "What's the password for the weather service?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result.text}\n")
if __name__ == "__main__":
asyncio.run(main())
Funktionsbaserat mellanprogram
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import time
from collections.abc import Awaitable, Callable
from random import randint
from typing import Annotated
from agent_framework import (
AgentContext,
AgentMiddleware,
AgentResponse,
FunctionInvocationContext,
FunctionMiddleware,
Message,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
"""
Class-based MiddlewareTypes Example
This sample demonstrates how to implement middleware using class-based approach by inheriting
from AgentMiddleware and FunctionMiddleware base classes. The example includes:
- SecurityAgentMiddleware: Checks for security violations in user queries and blocks requests
containing sensitive information like passwords or secrets
- LoggingFunctionMiddleware: Logs function execution details including timing and parameters
This approach is useful when you need stateful middleware or complex logic that benefits
from object-oriented design patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
"""Get the weather for a given location."""
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
class SecurityAgentMiddleware(AgentMiddleware):
"""Agent middleware that checks for security violations."""
async def process(
self,
context: AgentContext,
call_next: Callable[[], Awaitable[None]],
) -> None:
# Check for potential security violations in the query
# Look at the last user message
last_message = context.messages[-1] if context.messages else None
if last_message and last_message.text:
query = last_message.text
if "password" in query.lower() or "secret" in query.lower():
print("[SecurityAgentMiddleware] Security Warning: Detected sensitive information, blocking request.")
# Override the result with warning message
context.result = AgentResponse(
messages=[Message("assistant", ["Detected sensitive information, the request is blocked."])]
)
# Simply don't call call_next() to prevent execution
return
print("[SecurityAgentMiddleware] Security check passed.")
await call_next()
class LoggingFunctionMiddleware(FunctionMiddleware):
"""Function middleware that logs function calls."""
async def process(
self,
context: FunctionInvocationContext,
call_next: Callable[[], Awaitable[None]],
) -> None:
function_name = context.function.name
print(f"[LoggingFunctionMiddleware] About to call function: {function_name}.")
start_time = time.time()
await call_next()
end_time = time.time()
duration = end_time - start_time
print(f"[LoggingFunctionMiddleware] Function {function_name} completed in {duration:.5f}s.")
async def main() -> None:
"""Example demonstrating class-based middleware."""
print("=== Class-based MiddlewareTypes Example ===")
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
AzureAIAgentClient(credential=credential).as_agent(
name="WeatherAgent",
instructions="You are a helpful weather assistant.",
tools=get_weather,
middleware=[SecurityAgentMiddleware(), LoggingFunctionMiddleware()],
) as agent,
):
# Test with normal query
print("\n--- Normal Query ---")
query = "What's the weather like in Seattle?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result.text}\n")
# Test with security-related query
print("--- Security Test ---")
query = "What's the password for the weather service?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result.text}\n")
if __name__ == "__main__":
asyncio.run(main())
Dekoratörsbaserat mellanprogram
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import time
from collections.abc import Awaitable, Callable
from random import randint
from typing import Annotated
from agent_framework import (
AgentContext,
AgentMiddleware,
AgentResponse,
FunctionInvocationContext,
FunctionMiddleware,
Message,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
"""
Class-based MiddlewareTypes Example
This sample demonstrates how to implement middleware using class-based approach by inheriting
from AgentMiddleware and FunctionMiddleware base classes. The example includes:
- SecurityAgentMiddleware: Checks for security violations in user queries and blocks requests
containing sensitive information like passwords or secrets
- LoggingFunctionMiddleware: Logs function execution details including timing and parameters
This approach is useful when you need stateful middleware or complex logic that benefits
from object-oriented design patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
"""Get the weather for a given location."""
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
class SecurityAgentMiddleware(AgentMiddleware):
"""Agent middleware that checks for security violations."""
async def process(
self,
context: AgentContext,
call_next: Callable[[], Awaitable[None]],
) -> None:
# Check for potential security violations in the query
# Look at the last user message
last_message = context.messages[-1] if context.messages else None
if last_message and last_message.text:
query = last_message.text
if "password" in query.lower() or "secret" in query.lower():
print("[SecurityAgentMiddleware] Security Warning: Detected sensitive information, blocking request.")
# Override the result with warning message
context.result = AgentResponse(
messages=[Message("assistant", ["Detected sensitive information, the request is blocked."])]
)
# Simply don't call call_next() to prevent execution
return
print("[SecurityAgentMiddleware] Security check passed.")
await call_next()
class LoggingFunctionMiddleware(FunctionMiddleware):
"""Function middleware that logs function calls."""
async def process(
self,
context: FunctionInvocationContext,
call_next: Callable[[], Awaitable[None]],
) -> None:
function_name = context.function.name
print(f"[LoggingFunctionMiddleware] About to call function: {function_name}.")
start_time = time.time()
await call_next()
end_time = time.time()
duration = end_time - start_time
print(f"[LoggingFunctionMiddleware] Function {function_name} completed in {duration:.5f}s.")
async def main() -> None:
"""Example demonstrating class-based middleware."""
print("=== Class-based MiddlewareTypes Example ===")
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
AzureAIAgentClient(credential=credential).as_agent(
name="WeatherAgent",
instructions="You are a helpful weather assistant.",
tools=get_weather,
middleware=[SecurityAgentMiddleware(), LoggingFunctionMiddleware()],
) as agent,
):
# Test with normal query
print("\n--- Normal Query ---")
query = "What's the weather like in Seattle?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result.text}\n")
# Test with security-related query
print("--- Security Test ---")
query = "What's the password for the weather service?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result.text}\n")
if __name__ == "__main__":
asyncio.run(main())