mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [BREAKING] Python: Provider-leading client design & OpenAI package extraction (#4818)
* Python: Provider-leading client design & OpenAI package extraction Major refactoring of the Python Agent Framework client architecture: - Extract OpenAI clients into new `agent-framework-openai` package - Core package no longer depends on openai, azure-identity, azure-ai-projects - Rename clients for discoverability: OpenAIResponsesClient → OpenAIChatClient, OpenAIChatClient → OpenAIChatCompletionClient - Unify `model_id`/`deployment_name`/`model_deployment_name` → `model` param - New FoundryChatClient for Azure AI Foundry Responses API - New FoundryAgent/FoundryAgentClient for connecting to pre-configured Foundry agents - Remove OpenAIBase/OpenAIConfigMixin from non-deprecated client MRO - Deprecate AzureOpenAI* clients, AzureAIClient, OpenAIAssistantsClient - Reorganize samples: azure_openai+azure_ai+azure_ai_agent → azure/ - ADR-0020: Provider-Leading Client Design Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: missing Agent imports in samples, .model_id → .model in foundry_local sample Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: CI failures — mypy errors, coverage targets, sample imports - azure-ai mypy: add type ignores for TypedDict total=, model arg, forward ref - Coverage: replace core.azure/openai targets with openai package target - project_provider: add type annotation for opts dict Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: populate openai .pyi stub, fix broken README links, coverage targets Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fixes * updated observabilitty * reset azure init.pyi * fix errors * updated adr number * fix foundry local * fixed not renamed docstrings and comments, and added deprecated markers to old classes * fix tests and pyprojects * fix test vars * updated function tests * update durable * updated test setup for functions * Fix Foundry auth in workflow samples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Stabilize Python integration workflows Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update hosting samples for Foundry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Trigger full CI rerun Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Trigger CI rerun again Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * trigger rerun * trigger rerun * fix for litellm * undo durabletask changes * Move Foundry APIs into foundry namespace Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Foundry pyproject formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Split provider samples by Foundry surface Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Restore hosting sample requirements Also fix the Foundry Local sample link after the provider sample move. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * updated tests * udpated foundry integration tests * removed dist from azurefunctions tests * Use separate Foundry clients for concurrent agents Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix client setup in azfunc and durable * disabled two tests * updated setup for some function and durable tests * improved azure openai setup with new clients * ignore deprecated * fixes * skip 11 * remove openai assistants int tests --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
parent
4b533608b6
commit
5e056b672e
@@ -29,7 +29,7 @@ import uvicorn
|
||||
|
||||
# Agent Framework imports
|
||||
from agent_framework import Agent, AgentResponseUpdate, FunctionResultContent, Message, Role, tool
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
|
||||
# Agent Framework ChatKit integration
|
||||
from agent_framework_chatkit import ThreadItemConverter, stream_agent_response
|
||||
@@ -222,7 +222,7 @@ class WeatherChatKitServer(ChatKitServer[dict[str, Any]]):
|
||||
# For authentication, run `az login` command in terminal
|
||||
try:
|
||||
self.weather_agent = Agent(
|
||||
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions=(
|
||||
"You are a helpful weather assistant with image analysis capabilities. "
|
||||
"You can provide weather information for any location, tell the current time, "
|
||||
|
||||
@@ -15,8 +15,8 @@ import json
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Message
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework import Agent, Message
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.ai.evaluation.red_team import AttackStrategy, RedTeam, RiskCategory
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
@@ -52,7 +52,8 @@ async def main() -> None:
|
||||
# Create the agent
|
||||
# Constructor automatically reads from environment variables:
|
||||
# AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_DEPLOYMENT_NAME, AZURE_OPENAI_API_KEY
|
||||
agent = AzureOpenAIChatClient(credential=credential).as_agent(
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=credential),
|
||||
name="FinancialAdvisor",
|
||||
instructions="""You are a professional financial advisor assistant.
|
||||
|
||||
@@ -98,7 +99,7 @@ Your boundaries:
|
||||
|
||||
# Create RedTeam instance
|
||||
red_team = RedTeam(
|
||||
azure_ai_project=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
azure_ai_project=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
credential=credential,
|
||||
risk_categories=[
|
||||
RiskCategory.Violence,
|
||||
|
||||
@@ -20,7 +20,7 @@ from typing import Any
|
||||
import openai
|
||||
import pandas as pd
|
||||
from agent_framework import Agent, Message
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.ai.projects import AIProjectClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
@@ -87,7 +87,7 @@ DEFAULT_JUDGE_MODEL = "gpt-5.2"
|
||||
|
||||
|
||||
def create_openai_client():
|
||||
endpoint = os.environ["AZURE_AI_PROJECT_ENDPOINT"]
|
||||
endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
|
||||
credential = AzureCliCredential()
|
||||
project_client = AIProjectClient(endpoint=endpoint, credential=credential)
|
||||
return project_client.get_openai_client()
|
||||
@@ -97,7 +97,7 @@ def create_async_project_client():
|
||||
from azure.ai.projects.aio import AIProjectClient as AsyncAIProjectClient
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
|
||||
return AsyncAIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=AsyncAzureCliCredential())
|
||||
return AsyncAIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=AsyncAzureCliCredential())
|
||||
|
||||
|
||||
def create_eval(client: openai.OpenAI, judge_model: str) -> openai.types.EvalCreateResponse:
|
||||
@@ -321,9 +321,9 @@ async def run_self_reflection_batch(
|
||||
load_dotenv(override=True)
|
||||
|
||||
# Create agent, it loads environment variables AZURE_OPENAI_API_KEY and AZURE_OPENAI_ENDPOINT automatically
|
||||
responses_client = AzureOpenAIResponsesClient(
|
||||
responses_client = FoundryChatClient(
|
||||
project_client=project_client,
|
||||
deployment_name=agent_model,
|
||||
model=agent_model,
|
||||
)
|
||||
|
||||
# Load input data
|
||||
@@ -368,7 +368,7 @@ async def run_self_reflection_batch(
|
||||
try:
|
||||
result = await execute_query_with_self_reflection(
|
||||
client=client,
|
||||
agent=responses_client.as_agent(instructions=row["system_instruction"]),
|
||||
agent=Agent(client=responses_client, instructions=row["system_instruction"]),
|
||||
eval_object=eval_object,
|
||||
full_user_query=row["full_prompt"],
|
||||
context=row["context_document"],
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.ai.agentserver.agentframework import from_agent_framework # pyright: ignore[reportUnknownVariableType]
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -16,14 +17,13 @@ def main():
|
||||
"server_label": "Microsoft_Learn_MCP",
|
||||
"server_url": "https://learn.microsoft.com/api/mcp",
|
||||
}
|
||||
|
||||
# Create an Agent using the Azure OpenAI Chat Client with a MCP Tool that connects to Microsoft Learn MCP
|
||||
agent = AzureOpenAIChatClient(credential=DefaultAzureCredential()).as_agent(
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
|
||||
tools=mcp_tool,
|
||||
)
|
||||
|
||||
# Run the agent as a hosted agent
|
||||
from_agent_framework(agent).run()
|
||||
|
||||
|
||||
@@ -11,18 +11,14 @@ import os
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.ai.agentserver.agentframework import from_agent_framework
|
||||
from azure.identity.aio import AzureCliCredential, ManagedIdentityCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv(override=True)
|
||||
|
||||
# Configure these for your Foundry project
|
||||
# Read the explicit variables present in the .env file
|
||||
PROJECT_ENDPOINT = os.getenv(
|
||||
"PROJECT_ENDPOINT"
|
||||
) # e.g., "https://<project>.services.ai.azure.com"
|
||||
PROJECT_ENDPOINT = os.getenv("PROJECT_ENDPOINT") # e.g., "https://<project>.services.ai.azure.com"
|
||||
MODEL_DEPLOYMENT_NAME = os.getenv(
|
||||
"MODEL_DEPLOYMENT_NAME", "gpt-4.1-mini"
|
||||
) # Your model deployment name e.g., "gpt-4.1-mini"
|
||||
@@ -90,14 +86,10 @@ def get_available_hotels(
|
||||
nights = (check_out - check_in).days
|
||||
|
||||
# Filter hotels by price
|
||||
available_hotels = [
|
||||
hotel for hotel in SEATTLE_HOTELS if hotel["price_per_night"] <= max_price
|
||||
]
|
||||
available_hotels = [hotel for hotel in SEATTLE_HOTELS if hotel["price_per_night"] <= max_price]
|
||||
|
||||
if not available_hotels:
|
||||
return (
|
||||
f"No hotels found in Seattle within your budget of ${max_price}/night."
|
||||
)
|
||||
return f"No hotels found in Seattle within your budget of ${max_price}/night."
|
||||
|
||||
# Build response
|
||||
result = f"Available hotels in Seattle from {check_in_date} to {check_out_date} ({nights} nights):\n\n"
|
||||
@@ -117,22 +109,19 @@ def get_available_hotels(
|
||||
|
||||
def get_credential():
|
||||
"""Will use Managed Identity when running in Azure, otherwise falls back to Azure CLI Credential."""
|
||||
return (
|
||||
ManagedIdentityCredential()
|
||||
if os.getenv("MSI_ENDPOINT")
|
||||
else AzureCliCredential()
|
||||
)
|
||||
return ManagedIdentityCredential() if os.getenv("MSI_ENDPOINT") else AzureCliCredential()
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to run the agent as a web server."""
|
||||
async with get_credential() as credential:
|
||||
client = AzureOpenAIResponsesClient(
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=PROJECT_ENDPOINT,
|
||||
deployment_name=MODEL_DEPLOYMENT_NAME,
|
||||
model=MODEL_DEPLOYMENT_NAME,
|
||||
credential=credential,
|
||||
)
|
||||
agent = client.as_agent(
|
||||
agent = Agent(
|
||||
client=client,
|
||||
name="SeattleHotelAgent",
|
||||
instructions="""You are a helpful travel assistant specializing in finding hotels in Seattle, Washington.
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
azure-ai-agentserver-agentframework==1.0.0b16
|
||||
agent-framework-azure-ai
|
||||
agent-framework-foundry
|
||||
@@ -5,8 +5,8 @@ import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import AgentSession, BaseContextProvider, Message, SessionContext
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework import Agent, AgentSession, BaseContextProvider, Message, SessionContext
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.ai.agentserver.agentframework import from_agent_framework # pyright: ignore[reportUnknownVariableType]
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from dotenv import load_dotenv
|
||||
@@ -105,7 +105,8 @@ class TextSearchContextProvider(BaseContextProvider):
|
||||
|
||||
def main():
|
||||
# Create an Agent using the Azure OpenAI Chat Client
|
||||
agent = AzureOpenAIChatClient(credential=DefaultAzureCredential()).as_agent(
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=DefaultAzureCredential()),
|
||||
name="SupportSpecialist",
|
||||
instructions=(
|
||||
"You are a helpful support specialist for Contoso Outdoors. "
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework_orchestrations import ConcurrentBuilder
|
||||
from azure.ai.agentserver.agentframework import from_agent_framework
|
||||
from azure.identity import DefaultAzureCredential # pyright: ignore[reportUnknownVariableType]
|
||||
@@ -12,21 +13,24 @@ load_dotenv()
|
||||
|
||||
def main():
|
||||
# Create agents
|
||||
researcher = AzureOpenAIChatClient(credential=DefaultAzureCredential()).as_agent(
|
||||
researcher = Agent(
|
||||
client=FoundryChatClient(credential=DefaultAzureCredential()),
|
||||
instructions=(
|
||||
"You're an expert market and product researcher. "
|
||||
"Given a prompt, provide concise, factual insights, opportunities, and risks."
|
||||
),
|
||||
name="researcher",
|
||||
)
|
||||
marketer = AzureOpenAIChatClient(credential=DefaultAzureCredential()).as_agent(
|
||||
marketer = Agent(
|
||||
client=FoundryChatClient(credential=DefaultAzureCredential()),
|
||||
instructions=(
|
||||
"You're a creative marketing strategist. "
|
||||
"Craft compelling value propositions and target messaging aligned to the prompt."
|
||||
),
|
||||
name="marketer",
|
||||
)
|
||||
legal = AzureOpenAIChatClient(credential=DefaultAzureCredential()).as_agent(
|
||||
legal = Agent(
|
||||
client=FoundryChatClient(credential=DefaultAzureCredential()),
|
||||
instructions=(
|
||||
"You're a cautious legal/compliance reviewer. "
|
||||
"Highlight constraints, disclaimers, and policy concerns based on the prompt."
|
||||
@@ -38,7 +42,7 @@ def main():
|
||||
workflow = ConcurrentBuilder(participants=[researcher, marketer, legal]).build()
|
||||
|
||||
# Convert the workflow to an agent
|
||||
workflow_agent = workflow.as_agent()
|
||||
workflow_agent = Agent(client=workflow)
|
||||
|
||||
# Run the agent as a hosted agent
|
||||
from_agent_framework(workflow_agent).run()
|
||||
|
||||
+12
-12
@@ -4,8 +4,8 @@ import asyncio
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from agent_framework import WorkflowBuilder
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from agent_framework import Agent, WorkflowBuilder
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.ai.agentserver.agentframework import from_agent_framework
|
||||
from azure.identity.aio import AzureCliCredential, ManagedIdentityCredential
|
||||
from dotenv import load_dotenv
|
||||
@@ -24,26 +24,24 @@ MODEL_DEPLOYMENT_NAME = os.getenv(
|
||||
|
||||
def get_credential():
|
||||
"""Will use Managed Identity when running in Azure, otherwise falls back to Azure CLI Credential."""
|
||||
return (
|
||||
ManagedIdentityCredential()
|
||||
if os.getenv("MSI_ENDPOINT")
|
||||
else AzureCliCredential()
|
||||
)
|
||||
return ManagedIdentityCredential() if os.getenv("MSI_ENDPOINT") else AzureCliCredential()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def create_agents():
|
||||
async with get_credential() as credential:
|
||||
client = AzureOpenAIResponsesClient(
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=PROJECT_ENDPOINT,
|
||||
deployment_name=MODEL_DEPLOYMENT_NAME,
|
||||
model=MODEL_DEPLOYMENT_NAME,
|
||||
credential=credential,
|
||||
)
|
||||
writer = client.as_agent(
|
||||
writer = Agent(
|
||||
client=client,
|
||||
name="Writer",
|
||||
instructions="You are an excellent content writer. You create new content and edit contents based on the feedback.",
|
||||
)
|
||||
reviewer = client.as_agent(
|
||||
reviewer = Agent(
|
||||
client=client,
|
||||
name="Reviewer",
|
||||
instructions="You are an excellent content reviewer. Provide actionable feedback to the writer about the provided content in the most concise manner possible.",
|
||||
)
|
||||
@@ -52,7 +50,9 @@ async def create_agents():
|
||||
|
||||
def create_workflow(writer, reviewer):
|
||||
workflow = WorkflowBuilder(start_executor=writer).add_edge(writer, reviewer).build()
|
||||
return workflow.as_agent()
|
||||
return Agent(
|
||||
client=workflow,
|
||||
)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
azure-ai-agentserver-agentframework==1.0.0b16
|
||||
agent-framework-azure-ai
|
||||
agent-framework-foundry
|
||||
|
||||
@@ -19,7 +19,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from aiohttp import web
|
||||
from aiohttp.web_middlewares import middleware
|
||||
from dotenv import load_dotenv
|
||||
@@ -101,8 +101,9 @@ def get_weather(
|
||||
|
||||
def build_agent() -> Agent:
|
||||
"""Create and return the chat agent instance with weather tool registered."""
|
||||
return OpenAIChatClient().as_agent(
|
||||
name="WeatherAgent", instructions="You are a helpful weather agent.", tools=get_weather
|
||||
_client = FoundryChatClient()
|
||||
return Agent(
|
||||
client=_client, name="WeatherAgent", instructions="You are a helpful weather agent.", tools=get_weather
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import os
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, AgentResponse, Message
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.microsoft import (
|
||||
PurviewChatPolicyMiddleware,
|
||||
PurviewPolicyMiddleware,
|
||||
@@ -145,7 +145,7 @@ async def run_with_agent_middleware() -> None:
|
||||
|
||||
deployment = os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME", "gpt-4o-mini")
|
||||
user_id = os.environ.get("PURVIEW_DEFAULT_USER_ID")
|
||||
client = AzureOpenAIChatClient(deployment_name=deployment, endpoint=endpoint, credential=AzureCliCredential())
|
||||
client = FoundryChatClient(model=deployment, endpoint=endpoint, credential=AzureCliCredential())
|
||||
|
||||
purview_agent_middleware = PurviewPolicyMiddleware(
|
||||
build_credential(),
|
||||
@@ -182,8 +182,8 @@ async def run_with_chat_middleware() -> None:
|
||||
deployment = os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME", default="gpt-4o-mini")
|
||||
user_id = os.environ.get("PURVIEW_DEFAULT_USER_ID")
|
||||
|
||||
client = AzureOpenAIChatClient(
|
||||
deployment_name=deployment,
|
||||
client = FoundryChatClient(
|
||||
model=deployment,
|
||||
endpoint=endpoint,
|
||||
credential=AzureCliCredential(),
|
||||
middleware=[
|
||||
@@ -231,7 +231,7 @@ async def run_with_custom_cache_provider() -> None:
|
||||
|
||||
deployment = os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME", "gpt-4o-mini")
|
||||
user_id = os.environ.get("PURVIEW_DEFAULT_USER_ID")
|
||||
client = AzureOpenAIChatClient(deployment_name=deployment, endpoint=endpoint, credential=AzureCliCredential())
|
||||
client = FoundryChatClient(model=deployment, endpoint=endpoint, credential=AzureCliCredential())
|
||||
|
||||
custom_cache = SimpleDictCacheProvider()
|
||||
|
||||
@@ -271,7 +271,7 @@ async def run_with_custom_cache_provider() -> None:
|
||||
|
||||
deployment = os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME", "gpt-4o-mini")
|
||||
user_id = os.environ.get("PURVIEW_DEFAULT_USER_ID")
|
||||
client = AzureOpenAIChatClient(deployment_name=deployment, endpoint=endpoint, credential=AzureCliCredential())
|
||||
client = FoundryChatClient(model=deployment, endpoint=endpoint, credential=AzureCliCredential())
|
||||
|
||||
# No cache_provider specified - uses default InMemoryCacheProvider
|
||||
purview_agent_middleware = PurviewPolicyMiddleware(
|
||||
|
||||
@@ -46,6 +46,7 @@ from _tools import (
|
||||
validate_payment_method,
|
||||
)
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentExecutorResponse,
|
||||
AgentResponseUpdate,
|
||||
Executor,
|
||||
@@ -56,7 +57,7 @@ from agent_framework import (
|
||||
executor,
|
||||
handler,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.identity.aio import DefaultAzureCredential
|
||||
from dotenv import load_dotenv
|
||||
@@ -74,9 +75,10 @@ async def start_executor(input: str, ctx: WorkflowContext[list[Message]]) -> Non
|
||||
class ResearchLead(Executor):
|
||||
"""Aggregates and summarizes travel planning findings from all specialized agents."""
|
||||
|
||||
def __init__(self, client: AzureOpenAIResponsesClient, id: str = "travel-planning-coordinator"):
|
||||
def __init__(self, client: FoundryChatClient, id: str = "travel-planning-coordinator"):
|
||||
# Use default_options to persist conversation history for evaluation.
|
||||
self.agent = client.as_agent(
|
||||
self.agent = Agent(
|
||||
client=client,
|
||||
id="travel-planning-coordinator",
|
||||
instructions=(
|
||||
"You are the final coordinator. You will receive responses from multiple agents: "
|
||||
@@ -143,13 +145,13 @@ class ResearchLead(Executor):
|
||||
|
||||
|
||||
async def run_workflow_with_response_tracking(
|
||||
query: str, client: AzureOpenAIResponsesClient | None = None, deployment_name: str | None = None
|
||||
query: str, client: FoundryChatClient | None = None, deployment_name: str | None = None
|
||||
) -> dict:
|
||||
"""Run multi-agent workflow and track conversation IDs, response IDs, and interaction sequence.
|
||||
|
||||
Args:
|
||||
query: The user query to process through the multi-agent workflow
|
||||
client: Optional AzureOpenAIResponsesClient instance
|
||||
client: Optional FoundryChatClient instance
|
||||
deployment_name: Optional model deployment name for the workflow agents
|
||||
|
||||
Returns:
|
||||
@@ -159,12 +161,12 @@ async def run_workflow_with_response_tracking(
|
||||
try:
|
||||
async with DefaultAzureCredential() as credential:
|
||||
project_client = AIProjectClient(
|
||||
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
credential=credential,
|
||||
)
|
||||
|
||||
async with project_client:
|
||||
client = AzureOpenAIResponsesClient(project_client=project_client, deployment_name=deployment_name)
|
||||
client = FoundryChatClient(project_client=project_client, model=deployment_name)
|
||||
return await _run_workflow_with_client(query, client)
|
||||
except Exception as e:
|
||||
print(f"Error during workflow execution: {e}")
|
||||
@@ -173,7 +175,7 @@ async def run_workflow_with_response_tracking(
|
||||
return await _run_workflow_with_client(query, client)
|
||||
|
||||
|
||||
async def _run_workflow_with_client(query: str, client: AzureOpenAIResponsesClient) -> dict:
|
||||
async def _run_workflow_with_client(query: str, client: FoundryChatClient) -> dict:
|
||||
"""Execute workflow with given client and track all interactions."""
|
||||
|
||||
# Initialize tracking variables - use lists to track multiple responses per agent
|
||||
@@ -205,16 +207,17 @@ async def _run_workflow_with_client(query: str, client: AzureOpenAIResponsesClie
|
||||
}
|
||||
|
||||
|
||||
async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
async def _create_workflow(client: FoundryChatClient):
|
||||
"""Create the multi-agent travel planning workflow with specialized agents.
|
||||
|
||||
Uses a single shared AzureOpenAIResponsesClient for all agents.
|
||||
Uses a single shared FoundryChatClient for all agents.
|
||||
"""
|
||||
|
||||
final_coordinator = ResearchLead(client=client, id="final-coordinator")
|
||||
|
||||
# Agent 1: Travel Request Handler (initial coordinator)
|
||||
travel_request_handler = client.as_agent(
|
||||
travel_request_handler = Agent(
|
||||
client=client,
|
||||
id="travel-request-handler",
|
||||
instructions=(
|
||||
"You receive user travel queries and relay them to specialized agents. Extract key information: destination, dates, budget, and preferences. Pass this information forward clearly to the next agents."
|
||||
@@ -223,7 +226,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 2: Hotel Search Executor
|
||||
hotel_search_agent = client.as_agent(
|
||||
hotel_search_agent = Agent(
|
||||
client=client,
|
||||
id="hotel-search-agent",
|
||||
instructions=(
|
||||
"You are a hotel search specialist. Your task is ONLY to search for and provide hotel information. Use search_hotels to find options, get_hotel_details for specifics, and check_availability to verify rooms. Output format: List hotel names, prices per night, total cost for the stay, locations, ratings, amenities, and addresses. IMPORTANT: Only provide hotel information without additional commentary."
|
||||
@@ -233,7 +237,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 3: Flight Search Executor
|
||||
flight_search_agent = client.as_agent(
|
||||
flight_search_agent = Agent(
|
||||
client=client,
|
||||
id="flight-search-agent",
|
||||
instructions=(
|
||||
"You are a flight search specialist. Your task is ONLY to search for and provide flight information. Use search_flights to find options, get_flight_details for specifics, and check_availability for seats. Output format: List flight numbers, airlines, departure/arrival times, prices, durations, and cabin class. IMPORTANT: Only provide flight information without additional commentary."
|
||||
@@ -243,7 +248,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 4: Activity Search Executor
|
||||
activity_search_agent = client.as_agent(
|
||||
activity_search_agent = Agent(
|
||||
client=client,
|
||||
id="activity-search-agent",
|
||||
instructions=(
|
||||
"You are an activities specialist. Your task is ONLY to search for and provide activity information. Use search_activities to find options for activities. Output format: List activity names, descriptions, prices, durations, ratings, and categories. IMPORTANT: Only provide activity information without additional commentary."
|
||||
@@ -253,7 +259,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 5: Booking Confirmation Executor
|
||||
booking_confirmation_agent = client.as_agent(
|
||||
booking_confirmation_agent = Agent(
|
||||
client=client,
|
||||
id="booking-confirmation-agent",
|
||||
instructions=(
|
||||
"You confirm bookings. Use check_hotel_availability and check_flight_availability to verify slots, then confirm_booking to finalize. Provide ONLY: confirmation numbers, booking references, and confirmation status."
|
||||
@@ -263,7 +270,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 6: Booking Payment Executor
|
||||
booking_payment_agent = client.as_agent(
|
||||
booking_payment_agent = Agent(
|
||||
client=client,
|
||||
id="booking-payment-agent",
|
||||
instructions=(
|
||||
"You process payments. Use validate_payment_method to verify payment, then process_payment to complete transactions. Provide ONLY: payment confirmation status, transaction IDs, and payment amounts."
|
||||
@@ -273,7 +281,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 7: Booking Information Aggregation Executor
|
||||
booking_info_aggregation_agent = client.as_agent(
|
||||
booking_info_aggregation_agent = Agent(
|
||||
client=client,
|
||||
id="booking-info-aggregation-agent",
|
||||
instructions=(
|
||||
"You aggregate hotel and flight search results. Receive options from search agents and organize them. Provide: top 2-3 hotel options with prices and top 2-3 flight options with prices in a structured format."
|
||||
@@ -356,7 +365,7 @@ async def create_and_run_workflow(deployment_name: str | None = None):
|
||||
query = example_queries[0]
|
||||
print(f"Query: {query}\n")
|
||||
|
||||
result = await run_workflow_with_response_tracking(query, deployment_name=deployment_name)
|
||||
result = await run_workflow_with_response_tracking(query, model=deployment_name)
|
||||
|
||||
# Create output data structure
|
||||
output_data = {"agents": {}, "query": result["query"], "output": result.get("output", "")}
|
||||
|
||||
@@ -9,7 +9,7 @@ import time
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from azure.ai.projects import AIProjectClient
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from create_workflow import create_and_run_workflow
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -33,8 +33,8 @@ This script:
|
||||
|
||||
def create_openai_client() -> OpenAI:
|
||||
project_client = AIProjectClient(
|
||||
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
credential=DefaultAzureCredential(),
|
||||
endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
return project_client.get_openai_client()
|
||||
|
||||
@@ -58,7 +58,7 @@ async def run_workflow(deployment_name: str | None = None) -> dict[str, Any]:
|
||||
print("Executing multi-agent travel planning workflow...")
|
||||
print("This may take a few minutes...")
|
||||
|
||||
workflow_data = await create_and_run_workflow(deployment_name=deployment_name)
|
||||
workflow_data = await create_and_run_workflow(model=deployment_name)
|
||||
|
||||
print("Workflow execution completed")
|
||||
return workflow_data
|
||||
@@ -216,7 +216,7 @@ async def main():
|
||||
print_section("Travel Planning Workflow Evaluation")
|
||||
|
||||
print_section("Step 1: Running Workflow")
|
||||
workflow_data = await run_workflow(deployment_name=workflow_agent_model)
|
||||
workflow_data = await run_workflow(model=workflow_agent_model)
|
||||
|
||||
print_section("Step 2: Response Data Summary")
|
||||
display_response_summary(workflow_data)
|
||||
@@ -225,7 +225,7 @@ async def main():
|
||||
fetch_agent_responses(openai_client, workflow_data, agents_to_evaluate)
|
||||
|
||||
print_section("Step 4: Creating Evaluation")
|
||||
eval_object = create_evaluation(openai_client, deployment_name=eval_model)
|
||||
eval_object = create_evaluation(openai_client, model=eval_model)
|
||||
|
||||
print_section("Step 5: Running Evaluation")
|
||||
eval_run = run_evaluation(openai_client, eval_object, workflow_data, agents_to_evaluate)
|
||||
|
||||
Reference in New Issue
Block a user