mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Azure AI Search Support Update + Refactored Samples & Unit Tests (#1683)
* azure ai search sample update * azure ai search update * small fix
This commit is contained in:
committed by
GitHub
Unverified
parent
943d92674e
commit
2059e7b3e8
@@ -51,8 +51,6 @@ from azure.ai.agents.models import (
|
||||
AgentStreamEvent,
|
||||
AsyncAgentEventHandler,
|
||||
AsyncAgentRunStream,
|
||||
AzureAISearchQueryType,
|
||||
AzureAISearchTool,
|
||||
BingCustomSearchTool,
|
||||
BingGroundingTool,
|
||||
CodeInterpreterToolDefinition,
|
||||
@@ -88,7 +86,6 @@ from azure.ai.agents.models import (
|
||||
ToolOutput,
|
||||
)
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.ai.projects.models import ConnectionType
|
||||
from azure.core.credentials_async import AsyncTokenCredential
|
||||
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError
|
||||
from pydantic import ValidationError
|
||||
@@ -988,49 +985,6 @@ class AzureAIAgentClient(BaseChatClient):
|
||||
# Set tool_resources for file search to work properly with Azure AI
|
||||
if run_options is not None and "tool_resources" not in run_options:
|
||||
run_options["tool_resources"] = file_search.resources
|
||||
else:
|
||||
additional_props = tool.additional_properties or {}
|
||||
index_name = additional_props.get("index_name") or os.getenv("AZURE_AI_SEARCH_INDEX_NAME")
|
||||
if not index_name:
|
||||
raise ServiceInitializationError(
|
||||
"File search tool requires at least one vector store input, "
|
||||
"for file search in the Azure AI Project "
|
||||
"or an 'index_name' to use Azure AI Search, "
|
||||
"in additional_properties or environment variable 'AZURE_AI_SEARCH_INDEX_NAME'."
|
||||
)
|
||||
try:
|
||||
azs_conn_id = await self.project_client.connections.get_default(
|
||||
ConnectionType.AZURE_AI_SEARCH
|
||||
)
|
||||
except ValueError as err:
|
||||
raise ServiceInitializationError(
|
||||
"No default Azure AI Search connection found in the Azure AI Project. "
|
||||
"Please create one or provide vector store inputs for the file search tool.",
|
||||
err,
|
||||
) from err
|
||||
else:
|
||||
query_type_enum = AzureAISearchQueryType.SIMPLE
|
||||
if query_type := additional_props.get("query_type"):
|
||||
try:
|
||||
query_type_enum = AzureAISearchQueryType(query_type)
|
||||
except ValueError as ex:
|
||||
raise ServiceInitializationError(
|
||||
f"Invalid query_type '{query_type}' for Azure AI Search. "
|
||||
f"Valid values are: {[qt.value for qt in AzureAISearchQueryType]}",
|
||||
ex,
|
||||
) from ex
|
||||
ai_search = AzureAISearchTool(
|
||||
index_connection_id=azs_conn_id.id,
|
||||
index_name=index_name,
|
||||
query_type=query_type_enum,
|
||||
top_k=additional_props.get("top_k", 3),
|
||||
filter=additional_props.get("filter", ""),
|
||||
)
|
||||
tool_definitions.extend(ai_search.definitions)
|
||||
# Add tool resources for Azure AI Search
|
||||
if run_options is not None:
|
||||
run_options.setdefault("tool_resources", {})
|
||||
run_options["tool_resources"].update(ai_search.resources)
|
||||
case ToolDefinition():
|
||||
tool_definitions.append(tool)
|
||||
case dict():
|
||||
|
||||
@@ -48,12 +48,10 @@ from azure.ai.agents.models import (
|
||||
ThreadRun,
|
||||
VectorStore,
|
||||
)
|
||||
from azure.ai.projects.models import ConnectionType
|
||||
from azure.core.credentials_async import AsyncTokenCredential
|
||||
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
from pytest import MonkeyPatch
|
||||
|
||||
from agent_framework_azure_ai import AzureAIAgentClient, AzureAISettings
|
||||
|
||||
@@ -141,8 +139,8 @@ def test_azure_ai_chat_client_init_auto_create_client(
|
||||
chat_client.project_client = mock_ai_project_client
|
||||
chat_client.agent_id = None
|
||||
chat_client.thread_id = None
|
||||
chat_client._should_delete_agent = False
|
||||
chat_client._should_close_client = False
|
||||
chat_client._should_delete_agent = False # type: ignore
|
||||
chat_client._should_close_client = False # type: ignore
|
||||
chat_client.credential = None
|
||||
chat_client.model_id = azure_ai_settings.model_deployment_name
|
||||
chat_client.agent_name = None
|
||||
@@ -697,7 +695,7 @@ async def test_azure_ai_chat_client_create_run_options_with_none_tool_choice(
|
||||
chat_options = ChatOptions()
|
||||
chat_options.tool_choice = "none"
|
||||
|
||||
run_options, _ = await chat_client._create_run_options([], chat_options)
|
||||
run_options, _ = await chat_client._create_run_options([], chat_options) # type: ignore
|
||||
|
||||
from azure.ai.agents.models import AgentsToolChoiceOptionMode
|
||||
|
||||
@@ -713,7 +711,7 @@ async def test_azure_ai_chat_client_create_run_options_with_auto_tool_choice(
|
||||
chat_options = ChatOptions()
|
||||
chat_options.tool_choice = "auto"
|
||||
|
||||
run_options, _ = await chat_client._create_run_options([], chat_options)
|
||||
run_options, _ = await chat_client._create_run_options([], chat_options) # type: ignore
|
||||
|
||||
from azure.ai.agents.models import AgentsToolChoiceOptionMode
|
||||
|
||||
@@ -732,7 +730,7 @@ async def test_azure_ai_chat_client_create_run_options_with_response_format(
|
||||
chat_options = ChatOptions()
|
||||
chat_options.response_format = TestResponseModel
|
||||
|
||||
run_options, _ = await chat_client._create_run_options([], chat_options)
|
||||
run_options, _ = await chat_client._create_run_options([], chat_options) # type: ignore
|
||||
|
||||
assert "response_format" in run_options
|
||||
response_format = run_options["response_format"]
|
||||
@@ -1028,101 +1026,67 @@ async def test_azure_ai_chat_client_prep_tools_file_search_with_vector_stores(
|
||||
mock_file_search.assert_called_once_with(vector_store_ids=["vs-123"])
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prep_tools_file_search_with_ai_search(mock_ai_project_client: MagicMock) -> None:
|
||||
"""Test _prep_tools with HostedFileSearchTool using Azure AI Search."""
|
||||
|
||||
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
|
||||
|
||||
file_search_tool = HostedFileSearchTool(
|
||||
additional_properties={
|
||||
"index_name": "test-index",
|
||||
"query_type": "simple",
|
||||
"top_k": 5,
|
||||
"filter": "category eq 'docs'",
|
||||
}
|
||||
)
|
||||
|
||||
# Mock connections.get_default
|
||||
mock_connection = MagicMock()
|
||||
mock_connection.id = "search-connection-id"
|
||||
mock_ai_project_client.connections.get_default = AsyncMock(return_value=mock_connection)
|
||||
|
||||
# Mock AzureAISearchTool
|
||||
with patch("agent_framework_azure_ai._chat_client.AzureAISearchTool") as mock_ai_search:
|
||||
mock_search_tool = MagicMock()
|
||||
mock_search_tool.definitions = [{"type": "azure_ai_search"}]
|
||||
mock_ai_search.return_value = mock_search_tool
|
||||
|
||||
# Mock AzureAISearchQueryType
|
||||
with patch("agent_framework_azure_ai._chat_client.AzureAISearchQueryType") as mock_query_type:
|
||||
mock_query_type.SIMPLE = "simple"
|
||||
mock_query_type.return_value = "simple"
|
||||
|
||||
result = await chat_client._prep_tools([file_search_tool]) # type: ignore
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == {"type": "azure_ai_search"}
|
||||
mock_ai_project_client.connections.get_default.assert_called_once_with(ConnectionType.AZURE_AI_SEARCH)
|
||||
mock_ai_search.assert_called_once_with(
|
||||
index_connection_id="search-connection-id",
|
||||
index_name="test-index",
|
||||
query_type="simple",
|
||||
top_k=5,
|
||||
filter="category eq 'docs'",
|
||||
)
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prep_tools_file_search_invalid_query_type(
|
||||
async def test_azure_ai_chat_client_setup_azure_ai_observability_success(
|
||||
mock_ai_project_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test _prep_tools with HostedFileSearchTool using invalid query_type."""
|
||||
|
||||
"""Test setup_azure_ai_observability success case with connection string."""
|
||||
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
|
||||
|
||||
file_search_tool = HostedFileSearchTool(
|
||||
additional_properties={"index_name": "test-index", "query_type": "invalid_type"}
|
||||
# Mock successful connection string retrieval
|
||||
mock_ai_project_client.telemetry.get_application_insights_connection_string = AsyncMock(
|
||||
return_value="InstrumentationKey=test-key;IngestionEndpoint=https://test.com"
|
||||
)
|
||||
|
||||
# Mock connections.get_default
|
||||
mock_connection = MagicMock()
|
||||
mock_connection.id = "search-connection-id"
|
||||
mock_ai_project_client.connections.get_default = AsyncMock(return_value=mock_connection)
|
||||
# Mock setup_observability function (it's imported inside the method)
|
||||
with patch("agent_framework.observability.setup_observability") as mock_setup:
|
||||
await chat_client.setup_azure_ai_observability(enable_sensitive_data=True)
|
||||
|
||||
# Mock AzureAISearchQueryType to raise ValueError
|
||||
with patch("agent_framework_azure_ai._chat_client.AzureAISearchQueryType") as mock_query_type:
|
||||
mock_query_type.side_effect = ValueError("Invalid query type")
|
||||
|
||||
with pytest.raises(ServiceInitializationError, match="Invalid query_type 'invalid_type'"):
|
||||
await chat_client._prep_tools([file_search_tool]) # type: ignore
|
||||
# Verify setup_observability was called with the correct parameters
|
||||
mock_setup.assert_called_once_with(
|
||||
applicationinsights_connection_string="InstrumentationKey=test-key;IngestionEndpoint=https://test.com",
|
||||
enable_sensitive_data=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prep_tools_file_search_no_connection(mock_ai_project_client: MagicMock) -> None:
|
||||
"""Test _prep_tools with HostedFileSearchTool when no AI Search connection exists."""
|
||||
|
||||
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
|
||||
|
||||
file_search_tool = HostedFileSearchTool(additional_properties={"index_name": "test-index"})
|
||||
|
||||
# Mock connections.get_default to raise ValueError
|
||||
mock_ai_project_client.connections.get_default = AsyncMock(side_effect=ValueError("No connection found"))
|
||||
|
||||
with pytest.raises(ServiceInitializationError, match="No default Azure AI Search connection found"):
|
||||
await chat_client._prep_tools([file_search_tool]) # type: ignore
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prep_tools_file_search_no_index_name(
|
||||
mock_ai_project_client: MagicMock, monkeypatch: MonkeyPatch
|
||||
async def test_azure_ai_chat_client_create_agent_stream_submit_tool_approvals(
|
||||
mock_ai_project_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test _prep_tools with HostedFileSearchTool missing index_name and vector stores."""
|
||||
monkeypatch.delenv("AZURE_AI_SEARCH_INDEX_NAME", raising=False)
|
||||
|
||||
"""Test _create_agent_stream with tool approvals submission path."""
|
||||
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
|
||||
|
||||
# File search tool with no vector stores and no index_name
|
||||
file_search_tool = HostedFileSearchTool()
|
||||
# Mock active thread run that matches the tool run ID
|
||||
mock_thread_run = MagicMock()
|
||||
mock_thread_run.thread_id = "test-thread"
|
||||
mock_thread_run.id = "test-run-id"
|
||||
chat_client._get_active_thread_run = AsyncMock(return_value=mock_thread_run) # type: ignore
|
||||
|
||||
with pytest.raises(ServiceInitializationError, match="File search tool requires at least one vector store input"):
|
||||
await chat_client._prep_tools([file_search_tool]) # type: ignore
|
||||
# Mock required action results with approval response that matches run ID
|
||||
approval_response = FunctionApprovalResponseContent(
|
||||
id='["test-run-id", "test-call-id"]',
|
||||
function_call=FunctionCallContent(
|
||||
call_id='["test-run-id", "test-call-id"]', name="test_function", arguments="{}"
|
||||
),
|
||||
approved=True,
|
||||
)
|
||||
|
||||
# Mock submit_tool_outputs_stream
|
||||
mock_handler = MagicMock()
|
||||
mock_ai_project_client.agents.runs.submit_tool_outputs_stream = AsyncMock()
|
||||
|
||||
with patch("azure.ai.agents.models.AsyncAgentEventHandler", return_value=mock_handler):
|
||||
stream, final_thread_id = await chat_client._create_agent_stream( # type: ignore
|
||||
"test-thread", "test-agent", {}, [approval_response]
|
||||
)
|
||||
|
||||
# Verify the approvals path was taken
|
||||
assert final_thread_id == "test-thread"
|
||||
|
||||
# Verify submit_tool_outputs_stream was called with approvals
|
||||
mock_ai_project_client.agents.runs.submit_tool_outputs_stream.assert_called_once()
|
||||
call_args = mock_ai_project_client.agents.runs.submit_tool_outputs_stream.call_args[1]
|
||||
assert "tool_approvals" in call_args
|
||||
assert call_args["tool_approvals"][0].tool_call_id == "test-call-id"
|
||||
assert call_args["tool_approvals"][0].approve is True
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prep_tools_dict_tool(mock_ai_project_client: MagicMock) -> None:
|
||||
@@ -1156,7 +1120,7 @@ async def test_azure_ai_chat_client_get_active_thread_run_with_active_run(mock_a
|
||||
mock_run = MagicMock()
|
||||
mock_run.status = RunStatus.IN_PROGRESS
|
||||
|
||||
async def mock_list_runs(*args, **kwargs):
|
||||
async def mock_list_runs(*args, **kwargs): # type: ignore
|
||||
yield mock_run
|
||||
|
||||
mock_ai_project_client.agents.runs.list = mock_list_runs
|
||||
@@ -1175,7 +1139,7 @@ async def test_azure_ai_chat_client_get_active_thread_run_no_active_run(mock_ai_
|
||||
mock_run = MagicMock()
|
||||
mock_run.status = RunStatus.COMPLETED
|
||||
|
||||
async def mock_list_runs(*args, **kwargs):
|
||||
async def mock_list_runs(*args, **kwargs): # type: ignore
|
||||
yield mock_run
|
||||
|
||||
mock_ai_project_client.agents.runs.list = mock_list_runs
|
||||
@@ -1432,7 +1396,7 @@ async def test_azure_ai_chat_client_get_agent_id_or_create_with_tool_resources(
|
||||
async def test_azure_ai_chat_client_close_method(mock_ai_project_client: MagicMock) -> None:
|
||||
"""Test close method."""
|
||||
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, should_delete_agent=True)
|
||||
chat_client._should_close_client = True
|
||||
chat_client._should_close_client = True # type: ignore
|
||||
chat_client.agent_id = "test-agent"
|
||||
|
||||
# Mock cleanup methods
|
||||
@@ -1456,7 +1420,7 @@ async def test_azure_ai_chat_client_create_agent_stream_submit_tool_outputs(
|
||||
mock_thread_run = MagicMock()
|
||||
mock_thread_run.thread_id = "test-thread"
|
||||
mock_thread_run.id = "test-run-id"
|
||||
chat_client._get_active_thread_run = AsyncMock(return_value=mock_thread_run)
|
||||
chat_client._get_active_thread_run = AsyncMock(return_value=mock_thread_run) # type: ignore
|
||||
|
||||
# Mock required action results with matching run ID
|
||||
function_result = FunctionResultContent(call_id='["test-run-id", "test-call-id"]', result="test result")
|
||||
@@ -1466,7 +1430,7 @@ async def test_azure_ai_chat_client_create_agent_stream_submit_tool_outputs(
|
||||
mock_ai_project_client.agents.runs.submit_tool_outputs_stream = AsyncMock()
|
||||
|
||||
with patch("azure.ai.agents.models.AsyncAgentEventHandler", return_value=mock_handler):
|
||||
stream, final_thread_id = await chat_client._create_agent_stream(
|
||||
stream, final_thread_id = await chat_client._create_agent_stream( # type: ignore
|
||||
thread_id="test-thread", agent_id="test-agent", run_options={}, required_action_results=[function_result]
|
||||
)
|
||||
|
||||
@@ -1520,87 +1484,6 @@ def test_azure_ai_chat_client_extract_url_citations_with_citations(mock_ai_proje
|
||||
assert citation.annotated_regions[0].end_index == 20
|
||||
|
||||
|
||||
def test_azure_ai_chat_client_extract_url_citations_no_citations(mock_ai_project_client: MagicMock) -> None:
|
||||
"""Test _extract_url_citations with MessageDeltaChunk containing no citations."""
|
||||
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
|
||||
|
||||
# Create mock text content without annotations
|
||||
mock_text_content = MagicMock(spec=MessageDeltaTextContent)
|
||||
mock_text_content.text = None # No text, so no annotations
|
||||
|
||||
# Create mock delta
|
||||
mock_delta = MagicMock()
|
||||
mock_delta.content = [mock_text_content]
|
||||
|
||||
# Create mock MessageDeltaChunk
|
||||
mock_chunk = MagicMock(spec=MessageDeltaChunk)
|
||||
mock_chunk.delta = mock_delta
|
||||
|
||||
# Call the method
|
||||
citations = chat_client._extract_url_citations(mock_chunk) # type: ignore
|
||||
|
||||
# Verify no citations returned
|
||||
assert len(citations) == 0
|
||||
|
||||
|
||||
def test_azure_ai_chat_client_extract_url_citations_empty_delta(mock_ai_project_client: MagicMock) -> None:
|
||||
"""Test _extract_url_citations with empty delta content."""
|
||||
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
|
||||
|
||||
# Create mock delta with empty content
|
||||
mock_delta = MagicMock()
|
||||
mock_delta.content = []
|
||||
|
||||
# Create mock MessageDeltaChunk
|
||||
mock_chunk = MagicMock(spec=MessageDeltaChunk)
|
||||
mock_chunk.delta = mock_delta
|
||||
|
||||
# Call the method
|
||||
citations = chat_client._extract_url_citations(mock_chunk) # type: ignore
|
||||
|
||||
# Verify no citations returned
|
||||
assert len(citations) == 0
|
||||
|
||||
|
||||
def test_azure_ai_chat_client_extract_url_citations_without_indices(mock_ai_project_client: MagicMock) -> None:
|
||||
"""Test _extract_url_citations with URL citations that don't have start/end indices."""
|
||||
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
|
||||
|
||||
# Create mock URL citation annotation without indices
|
||||
mock_url_citation = MagicMock()
|
||||
mock_url_citation.url = "https://example.com/no-indices"
|
||||
|
||||
mock_annotation = MagicMock(spec=MessageDeltaTextUrlCitationAnnotation)
|
||||
mock_annotation.url_citation = mock_url_citation
|
||||
mock_annotation.start_index = None
|
||||
mock_annotation.end_index = None
|
||||
|
||||
# Create mock text content with annotations
|
||||
mock_text = MagicMock()
|
||||
mock_text.annotations = [mock_annotation]
|
||||
|
||||
mock_text_content = MagicMock(spec=MessageDeltaTextContent)
|
||||
mock_text_content.text = mock_text
|
||||
|
||||
# Create mock delta
|
||||
mock_delta = MagicMock()
|
||||
mock_delta.content = [mock_text_content]
|
||||
|
||||
# Create mock MessageDeltaChunk
|
||||
mock_chunk = MagicMock(spec=MessageDeltaChunk)
|
||||
mock_chunk.delta = mock_delta
|
||||
|
||||
# Call the method
|
||||
citations = chat_client._extract_url_citations(mock_chunk) # type: ignore
|
||||
|
||||
# Verify results
|
||||
assert len(citations) == 1
|
||||
citation = citations[0]
|
||||
assert citation.url == "https://example.com/no-indices"
|
||||
assert citation.annotated_regions is not None
|
||||
assert len(citation.annotated_regions) == 0 # No regions when indices are None
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_setup_azure_ai_observability_resource_not_found(
|
||||
mock_ai_project_client: MagicMock,
|
||||
) -> None:
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from agent_framework import ChatAgent, HostedFileSearchTool
|
||||
from agent_framework import ChatAgent, CitationAnnotation
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.ai.projects.models import ConnectionType
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
"""
|
||||
@@ -18,55 +21,98 @@ Prerequisites:
|
||||
3. The search index "hotels-sample-index" should exist in your Azure AI Search service
|
||||
(you can create this using the Azure portal with sample hotel data)
|
||||
|
||||
Environment variables:
|
||||
- AZURE_AI_PROJECT_ENDPOINT: Your Azure AI project endpoint
|
||||
- AZURE_AI_MODEL_DEPLOYMENT_NAME: The name of your model deployment
|
||||
"""
|
||||
NOTE: To ensure consistent search tool usage:
|
||||
- Include explicit instructions for the agent to use the search tool
|
||||
- Mention the search requirement in your queries
|
||||
- Use `tool_choice="required"` to force tool usage
|
||||
|
||||
# Test queries to verify Azure AI Search is working with the hotels-sample-index
|
||||
USER_INPUTS = [
|
||||
"Search the hotel database for Stay-Kay City Hotel and give me detailed information.",
|
||||
]
|
||||
More info on `query type` can be found here:
|
||||
https://learn.microsoft.com/en-us/python/api/azure-ai-agents/azure.ai.agents.models.aisearchindexresource?view=azure-python-preview
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Main function demonstrating Azure AI agent with Azure AI Search capabilities."""
|
||||
"""Main function demonstrating Azure AI agent with raw Azure AI Search tool."""
|
||||
print("=== Azure AI Agent with Raw Azure AI Search Tool ===")
|
||||
|
||||
# 1. Create Azure AI Search tool using HostedFileSearchTool
|
||||
# The tool will automatically use the default Azure AI Search connection from your project
|
||||
azure_ai_search_tool = HostedFileSearchTool(
|
||||
additional_properties={
|
||||
"index_name": "hotels-sample-index", # Name of your search index
|
||||
"query_type": "simple", # Use simple search
|
||||
"top_k": 10, # Get more comprehensive results
|
||||
},
|
||||
)
|
||||
|
||||
# 2. Use AzureAIAgentClient as async context manager for automatic cleanup
|
||||
# Create the client and manually create an agent with Azure AI Search tool
|
||||
async with (
|
||||
AzureAIAgentClient(async_credential=AzureCliCredential()) as client,
|
||||
ChatAgent(
|
||||
chat_client=client,
|
||||
name="HotelSearchAgent",
|
||||
instructions=("You are a helpful travel assistant that searches hotel information."),
|
||||
tools=azure_ai_search_tool,
|
||||
) as agent,
|
||||
AzureCliCredential() as credential,
|
||||
AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as client,
|
||||
):
|
||||
print("=== Azure AI Agent with Azure AI Search ===")
|
||||
print("This agent can search through hotel data to help you find accommodations.\n")
|
||||
ai_search_conn_id = ""
|
||||
async for connection in client.connections.list():
|
||||
if connection.type == ConnectionType.AZURE_AI_SEARCH:
|
||||
ai_search_conn_id = connection.id
|
||||
break
|
||||
|
||||
# 3. Simulate conversation with the agent
|
||||
for user_input in USER_INPUTS:
|
||||
print(f"User: {user_input}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
# 1. Create Azure AI agent with the search tool
|
||||
azure_ai_agent = await client.agents.create_agent(
|
||||
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
|
||||
name="HotelSearchAgent",
|
||||
instructions=(
|
||||
"You are a helpful agent that searches hotel information using Azure AI Search. "
|
||||
"Always use the search tool and index to find hotel data and provide accurate information."
|
||||
),
|
||||
tools=[{"type": "azure_ai_search"}],
|
||||
tool_resources={
|
||||
"azure_ai_search": {
|
||||
"indexes": [
|
||||
{
|
||||
"index_connection_id": ai_search_conn_id,
|
||||
"index_name": "hotels-sample-index",
|
||||
"query_type": "vector",
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Stream the response for better user experience
|
||||
async for chunk in agent.run_stream(user_input):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print("\n" + "=" * 50 + "\n")
|
||||
# 2. Create chat client with the existing agent
|
||||
chat_client = AzureAIAgentClient(project_client=client, agent_id=azure_ai_agent.id)
|
||||
|
||||
print("Hotel search conversation completed!")
|
||||
try:
|
||||
async with ChatAgent(
|
||||
chat_client=chat_client,
|
||||
# Additional instructions for this specific conversation
|
||||
instructions=("You are a helpful agent that uses the search tool and index to find hotel information."),
|
||||
) as agent:
|
||||
print("This agent uses raw Azure AI Search tool to search hotel data.\n")
|
||||
|
||||
# 3. Simulate conversation with the agent
|
||||
user_input = (
|
||||
"Use Azure AI search knowledge tool to find detailed information about a winter hotel."
|
||||
" Use the search tool and index." # You can modify prompt to force tool usage
|
||||
)
|
||||
print(f"User: {user_input}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
|
||||
# Stream the response and collect citations
|
||||
citations: list[CitationAnnotation] = []
|
||||
async for chunk in agent.run_stream(user_input):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
|
||||
# Collect citations from Azure AI Search responses
|
||||
for content in getattr(chunk, "contents", []):
|
||||
annotations = getattr(content, "annotations", [])
|
||||
if annotations:
|
||||
citations.extend(annotations)
|
||||
|
||||
print()
|
||||
|
||||
# Display collected citations
|
||||
if citations:
|
||||
print("\n\nCitations:")
|
||||
for i, citation in enumerate(citations, 1):
|
||||
print(f"[{i}] Reference: {citation.url}")
|
||||
|
||||
print("\n" + "=" * 50 + "\n")
|
||||
print("Hotel search conversation completed!")
|
||||
|
||||
finally:
|
||||
# Clean up the agent manually
|
||||
await client.agents.delete_agent(azure_ai_agent.id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -7,21 +7,23 @@ This folder contains examples demonstrating different ways to create and use age
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
|
||||
| [`azure_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_assistants_with_existing_assistant.py`](azure_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the Azure Assistants client. Demonstrates proper cleanup of manually created assistants. |
|
||||
| [`azure_assistants_with_explicit_settings.py`](azure_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_assistants_with_function_tools.py`](azure_assistants_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_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_assistants_with_thread.py`](azure_assistants_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. |
|
||||
| [`azure_chat_client_with_explicit_settings.py`](azure_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_chat_client_with_function_tools.py`](azure_chat_client_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_chat_client_with_thread.py`](azure_chat_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. |
|
||||
| [`azure_responses_client_code_interpreter_files.py`](azure_responses_client_code_interpreter_files.py) | Demonstrates using HostedCodeInterpreterTool with file uploads for data analysis. Shows how to create, upload, and analyze CSV files using Python code execution with Azure OpenAI Responses. |
|
||||
| [`azure_responses_client_image_analysis.py`](azure_responses_client_image_analysis.py) | Shows how to use Azure OpenAI Responses for image analysis and vision tasks. Demonstrates multi-modal messages combining text and image content using remote URLs. |
|
||||
| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_responses_client_with_explicit_settings.py`](azure_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_responses_client_with_function_tools.py`](azure_responses_client_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_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_responses_client_with_local_mcp.py`](azure_responses_client_with_local_mcp.py) | Shows how to integrate Azure OpenAI Responses Client with local Model Context Protocol (MCP) servers using MCPStreamableHTTPTool for extended functionality. |
|
||||
| [`azure_responses_client_with_thread.py`](azure_responses_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`azure_responses_client_image_analysis.py`](azure_responses_client_image_analysis.py) | Shows how to use Azure OpenAI Responses for image analysis and vision tasks. Demonstrates multi-modal messages combining text and image content using remote URLs. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ This folder contains examples demonstrating different ways to create and use age
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`openai_assistants_basic.py`](openai_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
|
||||
| [`openai_assistants_with_code_interpreter.py`](openai_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with OpenAI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`openai_assistants_with_existing_assistant.py`](openai_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the OpenAI Assistants client. Demonstrates proper cleanup of manually created assistants. |
|
||||
| [`openai_assistants_with_explicit_settings.py`](openai_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including API key and model ID. |
|
||||
| [`openai_assistants_with_function_tools.py`](openai_assistants_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). |
|
||||
| [`openai_assistants_with_code_interpreter.py`](openai_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with OpenAI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`openai_assistants_with_file_search.py`](openai_assistants_with_file_search.py) | Demonstrates how to use file search capabilities with OpenAI agents, allowing the agent to search through uploaded files to answer questions. |
|
||||
| [`openai_assistants_with_function_tools.py`](openai_assistants_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). |
|
||||
| [`openai_assistants_with_thread.py`](openai_assistants_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`openai_chat_client_basic.py`](openai_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with OpenAI models. |
|
||||
| [`openai_chat_client_with_explicit_settings.py`](openai_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including API key and model ID. |
|
||||
@@ -21,6 +21,7 @@ This folder contains examples demonstrating different ways to create and use age
|
||||
| [`openai_chat_client_with_web_search.py`](openai_chat_client_with_web_search.py) | Shows how to use web search capabilities with OpenAI agents to retrieve and use information from the internet in responses. |
|
||||
| [`openai_responses_client_basic.py`](openai_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with OpenAI models. |
|
||||
| [`openai_responses_client_image_analysis.py`](openai_responses_client_image_analysis.py) | Demonstrates how to use vision capabilities with agents to analyze images. |
|
||||
| [`openai_responses_client_image_generation.py`](openai_responses_client_image_generation.py) | Demonstrates how to use image generation capabilities with OpenAI agents to create images based on text descriptions. Requires PIL (Pillow) for image display. |
|
||||
| [`openai_responses_client_reasoning.py`](openai_responses_client_reasoning.py) | Demonstrates how to use reasoning capabilities with OpenAI agents, showing how the agent can provide detailed reasoning for its responses. |
|
||||
| [`openai_responses_client_with_code_interpreter.py`](openai_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with OpenAI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`openai_responses_client_with_explicit_settings.py`](openai_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including API key and model ID. |
|
||||
|
||||
Reference in New Issue
Block a user