mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Foundry Agent Completeness (#954)
* foundry completeness * tests + openapi sample * bing grounding sample * options integration tests * merge conflict fix * fix failing test * add mcp approval handling
This commit is contained in:
committed by
GitHub
Unverified
parent
74e2e2e21d
commit
b0971fdec6
@@ -7,12 +7,17 @@ This folder contains examples demonstrating different ways to create and use age
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`azure_ai_basic.py`](azure_ai_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureAIAgentClient`. It automatically handles all configuration using environment variables. |
|
||||
| [`azure_ai_with_explicit_settings.py`](azure_ai_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `AzureAIAgentClient` settings, including project endpoint, model deployment, credentials, and agent name. |
|
||||
| [`azure_ai_with_bing_grounding.py`](azure_ai_with_bing_grounding.py) | Shows how to use Bing Grounding search with Azure AI agents to find real-time information from the web. Demonstrates web search capabilities with proper source citations and comprehensive error handling. |
|
||||
| [`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. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`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 ID to the Azure AI chat client. This example also demonstrates proper cleanup of manually created agents. |
|
||||
| [`azure_ai_with_existing_thread.py`](azure_ai_with_existing_thread.py) | Shows how to work with a pre-existing thread by providing the thread ID to the Azure AI chat client. This example also demonstrates proper cleanup of manually created threads. |
|
||||
| [`azure_ai_with_explicit_settings.py`](azure_ai_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `AzureAIAgentClient` settings, including project endpoint, model deployment, credentials, and agent name. |
|
||||
| [`azure_ai_with_file_search.py`](azure_ai_with_file_search.py) | Demonstrates how to use the HostedFileSearchTool with Azure AI agents to search through uploaded documents. Shows file upload, vector store creation, and querying document content. Includes both streaming and non-streaming examples. |
|
||||
| [`azure_ai_with_function_tools.py`](azure_ai_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`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. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_ai_with_local_mcp.py`](azure_ai_with_local_mcp.py) | Shows how to integrate Azure AI agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. |
|
||||
| [`azure_ai_with_hosted_mcp.py`](azure_ai_with_hosted_mcp.py) | Shows how to integrate Azure AI agents with hosted Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates remote MCP server connections and tool discovery. |
|
||||
| [`azure_ai_with_local_mcp.py`](azure_ai_with_local_mcp.py) | Shows how to integrate Azure AI agents with local Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. |
|
||||
| [`azure_ai_with_multiple_tools.py`](azure_ai_with_multiple_tools.py) | Demonstrates how to use multiple tools together with Azure AI agents, including web search, MCP servers, and function tools. Shows coordinated multi-tool interactions and approval workflows. |
|
||||
| [`azure_ai_with_openapi_tools.py`](azure_ai_with_openapi_tools.py) | Demonstrates how to use OpenAPI tools with Azure AI agents to integrate external REST APIs. Shows OpenAPI specification loading, anonymous authentication, thread context management, and coordinated multi-API conversations using weather and countries APIs. |
|
||||
| [`azure_ai_with_thread.py`](azure_ai_with_thread.py) | Demonstrates thread management with Azure AI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import ChatAgent, HostedWebSearchTool
|
||||
from agent_framework_azure_ai import AzureAIAgentClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
"""
|
||||
The following sample demonstrates how to create an Azure AI agent that
|
||||
uses Bing Grounding search to find real-time information from the web.
|
||||
|
||||
Prerequisites:
|
||||
1. A connected Grounding with Bing Search resource in your Azure AI project
|
||||
2. Set the BING_CONNECTION_ID environment variable to your Bing connection ID
|
||||
Example: BING_CONNECTION_ID="/subscriptions/{subscription-id}/resourceGroups/{resource-group}/
|
||||
providers/Microsoft.CognitiveServices/accounts/{ai-service-name}/projects/{project-name}/
|
||||
connections/{connection-name}"
|
||||
|
||||
To set up Bing Grounding:
|
||||
1. Go to Azure AI Foundry portal (https://ai.azure.com)
|
||||
2. Navigate to your project's "Connected resources" section
|
||||
3. Add a new connection for "Grounding with Bing Search"
|
||||
4. Copy the connection ID and set it as the BING_CONNECTION_ID environment variable
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Main function demonstrating Azure AI agent with Bing Grounding search."""
|
||||
# 1. Create Bing Grounding search tool using HostedWebSearchTool
|
||||
# The connection_id will be automatically picked up from BING_CONNECTION_ID environment variable
|
||||
bing_search_tool = HostedWebSearchTool(
|
||||
name="Bing Grounding Search",
|
||||
description="Search the web for current information using Bing",
|
||||
)
|
||||
|
||||
# 2. Use AzureAIAgentClient as async context manager for automatic cleanup
|
||||
async with (
|
||||
AzureAIAgentClient(async_credential=AzureCliCredential()) as client,
|
||||
ChatAgent(
|
||||
chat_client=client,
|
||||
name="BingSearchAgent",
|
||||
instructions=(
|
||||
"You are a helpful assistant that can search the web for current information. "
|
||||
"Use the Bing search tool to find up-to-date information and provide accurate, "
|
||||
"well-sourced answers. Always cite your sources when possible."
|
||||
),
|
||||
tools=bing_search_tool,
|
||||
) as agent,
|
||||
):
|
||||
# 4. Demonstrate agent capabilities with web search
|
||||
print("=== Azure AI Agent with Bing Grounding Search ===\n")
|
||||
|
||||
user_input = "What is the most popular programming language?"
|
||||
print(f"User: {user_input}")
|
||||
response = await agent.run(user_input)
|
||||
print(f"Agent: {response.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,81 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
from agent_framework import ChatAgent, HostedFileSearchTool, HostedVectorStoreContent
|
||||
from agent_framework_azure_ai import AzureAIAgentClient
|
||||
from azure.ai.agents.models import FileInfo, VectorStore
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
"""
|
||||
The following sample demonstrates how to create a simple, Azure AI agent that
|
||||
uses a file search tool to answer user questions.
|
||||
"""
|
||||
|
||||
|
||||
# Simulate a conversation with the agent
|
||||
USER_INPUTS = [
|
||||
"Who is the youngest employee?",
|
||||
"Who works in sales?",
|
||||
"I have a customer request, who can help me?",
|
||||
]
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Main function demonstrating Azure AI agent with file search capabilities."""
|
||||
client = AzureAIAgentClient(async_credential=AzureCliCredential())
|
||||
file: FileInfo | None = None
|
||||
vector_store: VectorStore | None = None
|
||||
|
||||
try:
|
||||
# 1. Upload file and create vector store
|
||||
pdf_file_path = Path(__file__).parent.parent / "resources" / "employees.pdf"
|
||||
print(f"Uploading file from: {pdf_file_path}")
|
||||
|
||||
file = await client.project_client.agents.files.upload_and_poll(
|
||||
file_path=str(pdf_file_path), purpose="assistants"
|
||||
)
|
||||
print(f"Uploaded file, file ID: {file.id}")
|
||||
|
||||
vector_store = await client.project_client.agents.vector_stores.create_and_poll(
|
||||
file_ids=[file.id], name="my_vectorstore"
|
||||
)
|
||||
print(f"Created vector store, vector store ID: {vector_store.id}")
|
||||
|
||||
# 2. Create file search tool with uploaded resources
|
||||
file_search_tool = HostedFileSearchTool(inputs=[HostedVectorStoreContent(vector_store_id=vector_store.id)])
|
||||
|
||||
# 3. Create an agent with file search capabilities
|
||||
# The tool_resources are automatically extracted from HostedFileSearchTool
|
||||
async with ChatAgent(
|
||||
chat_client=client,
|
||||
name="EmployeeSearchAgent",
|
||||
instructions=(
|
||||
"You are a helpful assistant that can search through uploaded employee files "
|
||||
"to answer questions about employees."
|
||||
),
|
||||
tools=file_search_tool,
|
||||
) as agent:
|
||||
# 4. Simulate conversation with the agent
|
||||
for user_input in USER_INPUTS:
|
||||
print(f"# User: '{user_input}'")
|
||||
response = await agent.run(user_input)
|
||||
print(f"# Agent: {response.text}")
|
||||
|
||||
finally:
|
||||
# 5. Cleanup: Delete the vector store and file
|
||||
try:
|
||||
if vector_store is not None:
|
||||
await client.project_client.agents.vector_stores.delete(vector_store.id)
|
||||
if file is not None:
|
||||
await client.project_client.agents.files.delete(file.id)
|
||||
except Exception:
|
||||
# Ignore cleanup errors to avoid masking issues
|
||||
pass
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import ChatAgent
|
||||
from agent_framework_azure_ai import AzureAIAgentClient
|
||||
from azure.ai.agents.models import OpenApiAnonymousAuthDetails, OpenApiTool
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
"""
|
||||
The following sample demonstrates how to create a simple, Azure AI agent that
|
||||
uses OpenAPI tools to answer user questions.
|
||||
"""
|
||||
|
||||
# Simulate a conversation with the agent
|
||||
USER_INPUTS = [
|
||||
"What is the name and population of the country that uses currency with abbreviation THB?",
|
||||
"What is the current weather in the capital city of that country?",
|
||||
]
|
||||
|
||||
|
||||
def load_openapi_specs() -> tuple[dict[str, Any], dict[str, Any]]:
|
||||
"""Load OpenAPI specification files."""
|
||||
resources_path = Path(__file__).parent.parent / "resources"
|
||||
|
||||
with open(resources_path / "weather.json") as weather_file:
|
||||
weather_spec = json.load(weather_file)
|
||||
|
||||
with open(resources_path / "countries.json") as countries_file:
|
||||
countries_spec = json.load(countries_file)
|
||||
|
||||
return weather_spec, countries_spec
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Main function demonstrating Azure AI agent with OpenAPI tools."""
|
||||
# 1. Load OpenAPI specifications (synchronous operation)
|
||||
weather_openapi_spec, countries_openapi_spec = load_openapi_specs()
|
||||
|
||||
# 2. Use AzureAIAgentClient as async context manager for automatic cleanup
|
||||
async with AzureAIAgentClient(async_credential=AzureCliCredential()) as client:
|
||||
# 3. Create OpenAPI tools using Azure AI's OpenApiTool
|
||||
auth = OpenApiAnonymousAuthDetails()
|
||||
|
||||
openapi_weather = OpenApiTool(
|
||||
name="get_weather",
|
||||
spec=weather_openapi_spec,
|
||||
description="Retrieve weather information for a location using wttr.in service",
|
||||
auth=auth,
|
||||
)
|
||||
|
||||
openapi_countries = OpenApiTool(
|
||||
name="get_country_info",
|
||||
spec=countries_openapi_spec,
|
||||
description="Retrieve country information including population and capital city",
|
||||
auth=auth,
|
||||
)
|
||||
|
||||
# 4. Create an agent with OpenAPI tools
|
||||
# Note: We need to pass the Azure AI native OpenApiTool definitions directly
|
||||
# since the agent framework doesn't have a HostedOpenApiTool wrapper yet
|
||||
async with ChatAgent(
|
||||
chat_client=client,
|
||||
name="OpenAPIAgent",
|
||||
instructions=(
|
||||
"You are a helpful assistant that can search for country information "
|
||||
"and weather data using APIs. When asked about countries, use the country "
|
||||
"API to find information. When asked about weather, use the weather API. "
|
||||
"Provide clear, informative answers based on the API results."
|
||||
),
|
||||
# Pass the raw tool definitions from Azure AI's OpenApiTool
|
||||
tools=[*openapi_countries.definitions, *openapi_weather.definitions],
|
||||
) as agent:
|
||||
# 5. Simulate conversation with the agent maintaining thread context
|
||||
print("=== Azure AI Agent with OpenAPI Tools ===\n")
|
||||
|
||||
# Create a thread to maintain conversation context across multiple runs
|
||||
thread = agent.get_new_thread()
|
||||
|
||||
for user_input in USER_INPUTS:
|
||||
print(f"User: {user_input}")
|
||||
# Pass the thread to maintain context across multiple agent.run() calls
|
||||
response = await agent.run(user_input, thread=thread)
|
||||
print(f"Agent: {response.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"openapi": "3.0.1",
|
||||
"info": {
|
||||
"title": "REST Countries API",
|
||||
"description": "Get information about countries of the world",
|
||||
"version": "3.1"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://restcountries.com/v3.1"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/currency/{currency}": {
|
||||
"get": {
|
||||
"operationId": "getCountriesByCurrency",
|
||||
"summary": "Get countries by currency",
|
||||
"description": "Search for countries by currency code",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "currency",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Currency code (e.g., THB, USD, EUR)"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"common": {"type": "string"},
|
||||
"official": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"population": {"type": "integer"},
|
||||
"region": {"type": "string"},
|
||||
"subregion": {"type": "string"},
|
||||
"capital": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
"currencies": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"symbol": {"type": "string"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"languages": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"type": "string"}
|
||||
},
|
||||
"latlng": {
|
||||
"type": "array",
|
||||
"items": {"type": "number"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/name/{name}": {
|
||||
"get": {
|
||||
"operationId": "getCountryByName",
|
||||
"summary": "Get country by name",
|
||||
"description": "Search for countries by name",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Country name"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"common": {"type": "string"},
|
||||
"official": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"population": {"type": "integer"},
|
||||
"region": {"type": "string"},
|
||||
"subregion": {"type": "string"},
|
||||
"capital": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
"currencies": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"symbol": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
%PDF-1.7
|
||||
%����
|
||||
1 0 obj
|
||||
<</Type/Catalog/Pages 2 0 R/Lang(en) /StructTreeRoot 22 0 R/MarkInfo<</Marked true>>/Metadata 132 0 R/ViewerPreferences 133 0 R>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<</Type/Pages/Count 1/Kids[ 4 0 R] >>
|
||||
endobj
|
||||
3 0 obj
|
||||
<</Author(Test Author) /Creator(Test Creator) /Title(Employee Directory) >>
|
||||
endobj
|
||||
4 0 obj
|
||||
<</Type/Page/Parent 2 0 R/MediaBox[0 0 612 792]/Resources<</Font<</F1 5 0 R>>>>/Contents 6 0 R>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<</Type/Font/Subtype/Type1/BaseFont/Times-Roman>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<</Length 200>>
|
||||
stream
|
||||
BT
|
||||
/F1 12 Tf
|
||||
50 750 Td
|
||||
(Employee Directory) Tj
|
||||
0 -30 Td
|
||||
(Name: John Smith) Tj
|
||||
0 -15 Td
|
||||
(Department: Engineering) Tj
|
||||
0 -15 Td
|
||||
(Age: 28) Tj
|
||||
0 -30 Td
|
||||
(Name: Alice Johnson) Tj
|
||||
0 -15 Td
|
||||
(Department: Sales) Tj
|
||||
0 -15 Td
|
||||
(Age: 24) Tj
|
||||
0 -30 Td
|
||||
(Name: Bob Wilson) Tj
|
||||
0 -15 Td
|
||||
(Department: Marketing) Tj
|
||||
0 -15 Td
|
||||
(Age: 35) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
22 0 obj
|
||||
<</Type/StructTreeRoot>>
|
||||
endobj
|
||||
132 0 obj
|
||||
<</Type/Metadata/Subtype/XML>>
|
||||
endobj
|
||||
133 0 obj
|
||||
<</DisplayDocTitle true>>
|
||||
endobj
|
||||
xref
|
||||
0 10
|
||||
0000000000 65535 f
|
||||
0000000015 00000 n
|
||||
0000000152 00000 n
|
||||
0000000209 00000 n
|
||||
0000000300 00000 n
|
||||
0000000420 00000 n
|
||||
0000000490 00000 n
|
||||
0000000000 65535 f
|
||||
0000000000 65535 f
|
||||
0000000000 65535 f
|
||||
22 1
|
||||
0000000740 00000 n
|
||||
132 2
|
||||
0000000780 00000 n
|
||||
0000000820 00000 n
|
||||
trailer
|
||||
<</Size 134/Root 1 0 R/Info 3 0 R>>
|
||||
startxref
|
||||
860
|
||||
%%EOF
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "wttr.in Weather API",
|
||||
"description": "Retrieves current weather data for a location using wttr.in service",
|
||||
"version": "v1.0.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://wttr.in"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/{location}": {
|
||||
"get": {
|
||||
"operationId": "GetCurrentWeather",
|
||||
"summary": "Get weather information for a specific location",
|
||||
"description": "Get weather information for a specific location",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "location",
|
||||
"in": "path",
|
||||
"description": "City or location to retrieve the weather for",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"in": "query",
|
||||
"description": "Format in which to return data. Always use 3.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful response",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Location not found"
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user