Python: renamed ai search and cleanup of samples and unified import logic (#2369)

* renamed ai search and cleanup of samples and unified import logic

* fixed error messages

* fixed folder name

* remove old samples from readme
This commit is contained in:
Eduard van Valkenburg
2025-11-24 18:06:22 +01:00
committed by GitHub
Unverified
parent db424d56f3
commit 9f43108ef1
36 changed files with 4132 additions and 3823 deletions
+1
View File
@@ -27,6 +27,7 @@
"aiplatform",
"azuredocindex",
"azuredocs",
"azurefunctions",
"boto",
"contentvector",
"contoso",
@@ -3,7 +3,7 @@
Please install this package via pip:
```bash
pip install agent-framework-aisearch --pre
pip install agent-framework-azure-ai-search --pre
```
## Azure AI Search Integration
@@ -1,15 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
"""Azure AI Search Context Provider for Agent Framework.
This module provides context providers for Azure AI Search integration with two modes:
- Agentic: Recommended for most scenarios. Uses Knowledge Bases for query planning and
multi-hop reasoning. Slightly slower with more token consumption, but more accurate.
- Semantic: Fast hybrid search (vector + keyword) with semantic ranker. Best for simple
queries where speed is critical.
See: https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/foundry-iq-boost-response-relevance-by-36-with-agentic-retrieval/4470720
"""
import sys
from collections.abc import Awaitable, Callable, MutableSequence
@@ -111,6 +101,18 @@ if sys.version_info >= (3, 12):
else:
from typing_extensions import override # type: ignore[import] # pragma: no cover
"""Azure AI Search Context Provider for Agent Framework.
This module provides context providers for Azure AI Search integration with two modes:
- Agentic: Recommended for most scenarios. Uses Knowledge Bases for query planning and
multi-hop reasoning. Slightly slower with more token consumption, but more accurate.
- Semantic: Fast hybrid search (vector + keyword) with semantic ranker. Best for simple
queries where speed is critical.
See: https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/foundry-iq-boost-response-relevance-by-36-with-agentic-retrieval/4470720
"""
# Module-level constants
logger = get_logger("agent_framework.azure")
_DEFAULT_AGENTIC_MESSAGE_HISTORY_COUNT = 10
@@ -1,5 +1,5 @@
[project]
name = "agent-framework-aisearch"
name = "agent-framework-azure-ai-search"
description = "Azure AI Search integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
@@ -76,15 +76,15 @@ disallow_incomplete_defs = true
disallow_untyped_decorators = true
[tool.bandit]
targets = ["agent_framework_aisearch"]
targets = ["agent_framework_azure_ai_search"]
exclude_dirs = ["tests"]
[tool.poe]
executor.type = "uv"
include = "../../shared_tasks.toml"
[tool.poe.tasks]
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_aisearch"
test = "pytest --cov=agent_framework_aisearch --cov-report=term-missing:skip-covered tests"
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure_ai_search"
test = "pytest --cov=agent_framework_azure_ai_search --cov-report=term-missing:skip-covered tests"
[build-system]
requires = ["flit-core >= 3.11,<4.0"]
@@ -6,13 +6,11 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from agent_framework import ChatMessage, Context, Role
from agent_framework.azure import AzureAISearchContextProvider
from agent_framework.azure import AzureAISearchContextProvider, AzureAISearchSettings
from agent_framework.exceptions import ServiceInitializationError
from azure.core.credentials import AzureKeyCredential
from azure.core.exceptions import ResourceNotFoundError
from agent_framework_aisearch import AzureAISearchSettings
@pytest.fixture
def mock_search_client() -> AsyncMock:
@@ -246,7 +244,7 @@ class TestSemanticSearch:
"""Test semantic search functionality."""
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_semantic_search_basic(
self, mock_search_class: MagicMock, sample_messages: list[ChatMessage]
) -> None:
@@ -275,7 +273,7 @@ class TestSemanticSearch:
assert "Test document content" in context.messages[1].text
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_semantic_search_empty_query(self, mock_search_class: MagicMock) -> None:
"""Test that empty queries return empty context."""
mock_search_client = AsyncMock()
@@ -295,7 +293,7 @@ class TestSemanticSearch:
assert len(context.messages) == 0
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_semantic_search_with_vector_query(
self, mock_search_class: MagicMock, sample_messages: list[ChatMessage]
) -> None:
@@ -332,8 +330,8 @@ class TestKnowledgeBaseSetup:
"""Test Knowledge Base setup for agentic mode."""
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_ensure_knowledge_base_creates_when_not_exists(
self, mock_search_class: MagicMock, mock_index_class: MagicMock
) -> None:
@@ -369,8 +367,8 @@ class TestKnowledgeBaseSetup:
mock_index_client.create_or_update_knowledge_base.assert_called_once()
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_ensure_knowledge_base_skips_when_exists(
self, mock_search_class: MagicMock, mock_index_class: MagicMock
) -> None:
@@ -406,7 +404,7 @@ class TestContextProviderLifecycle:
"""Test context provider lifecycle methods."""
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_context_manager(self, mock_search_class: MagicMock) -> None:
"""Test that provider can be used as async context manager."""
mock_search_client = AsyncMock()
@@ -422,9 +420,9 @@ class TestContextProviderLifecycle:
assert isinstance(provider, AzureAISearchContextProvider)
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.KnowledgeBaseRetrievalClient")
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.KnowledgeBaseRetrievalClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_context_manager_agentic_cleanup(
self, mock_search_class: MagicMock, mock_index_class: MagicMock, mock_retrieval_class: MagicMock
) -> None:
@@ -470,7 +468,7 @@ class TestMessageFiltering:
"""Test message filtering functionality."""
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_filters_non_user_assistant_messages(self, mock_search_class: MagicMock) -> None:
"""Test that only USER and ASSISTANT messages are processed."""
# Setup mock
@@ -502,7 +500,7 @@ class TestMessageFiltering:
mock_search_client.search.assert_called_once()
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_filters_empty_messages(self, mock_search_class: MagicMock) -> None:
"""Test that empty/whitespace messages are filtered out."""
mock_search_client = AsyncMock()
@@ -532,7 +530,7 @@ class TestCitations:
"""Test citation functionality."""
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_citations_included_in_semantic_search(self, mock_search_class: MagicMock) -> None:
"""Test that citations are included in semantic search results."""
# Setup mock with document ID
@@ -564,9 +562,9 @@ class TestAgenticSearch:
"""Test agentic search functionality."""
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.KnowledgeBaseRetrievalClient")
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.KnowledgeBaseRetrievalClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_agentic_search_basic(
self,
mock_search_class: MagicMock,
@@ -593,7 +591,7 @@ class TestAgenticSearch:
mock_content = MagicMock()
mock_content.text = "Agentic search result"
# Make it pass isinstance check
from agent_framework_aisearch._search_provider import _agentic_retrieval_available
from agent_framework_azure_ai_search._search_provider import _agentic_retrieval_available
if _agentic_retrieval_available:
from azure.search.documents.knowledgebases.models import KnowledgeBaseMessageTextContent
@@ -623,9 +621,9 @@ class TestAgenticSearch:
assert len(context.messages) >= 1
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.KnowledgeBaseRetrievalClient")
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.KnowledgeBaseRetrievalClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_agentic_search_no_results(
self,
mock_search_class: MagicMock,
@@ -670,9 +668,9 @@ class TestAgenticSearch:
assert len(context.messages) >= 1
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.KnowledgeBaseRetrievalClient")
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.KnowledgeBaseRetrievalClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_agentic_search_with_medium_reasoning(
self,
mock_search_class: MagicMock,
@@ -696,7 +694,7 @@ class TestAgenticSearch:
mock_message = MagicMock()
mock_content = MagicMock()
mock_content.text = "Medium reasoning result"
from agent_framework_aisearch._search_provider import _agentic_retrieval_available
from agent_framework_azure_ai_search._search_provider import _agentic_retrieval_available
if _agentic_retrieval_available:
from azure.search.documents.knowledgebases.models import KnowledgeBaseMessageTextContent
@@ -730,8 +728,8 @@ class TestVectorFieldAutoDiscovery:
"""Test vector field auto-discovery functionality."""
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_auto_discovers_single_vector_field(
self, mock_search_class: MagicMock, mock_index_class: MagicMock
) -> None:
@@ -795,8 +793,8 @@ class TestVectorFieldAutoDiscovery:
assert is_vector_3 is False
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_no_false_positives_on_string_fields(
self, mock_search_class: MagicMock, mock_index_class: MagicMock
) -> None:
@@ -839,8 +837,8 @@ class TestVectorFieldAutoDiscovery:
assert provider._auto_discovered_vector_field is True
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_multiple_vector_fields_without_vectorizer(
self, mock_search_class: MagicMock, mock_index_class: MagicMock
) -> None:
@@ -884,8 +882,8 @@ class TestVectorFieldAutoDiscovery:
assert provider._auto_discovered_vector_field is True
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_multiple_vectorizable_fields(
self, mock_search_class: MagicMock, mock_index_class: MagicMock
) -> None:
@@ -941,8 +939,8 @@ class TestVectorFieldAutoDiscovery:
assert provider._auto_discovered_vector_field is True
@pytest.mark.asyncio
@patch("agent_framework_aisearch._search_provider.SearchIndexClient")
@patch("agent_framework_aisearch._search_provider.SearchClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient")
@patch("agent_framework_azure_ai_search._search_provider.SearchClient")
async def test_single_vectorizable_field_detected(
self, mock_search_class: MagicMock, mock_index_class: MagicMock
) -> None:
@@ -1,185 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
from ._agent import WorkflowAgent
from ._agent_executor import (
AgentExecutor,
AgentExecutorRequest,
AgentExecutorResponse,
)
from ._checkpoint import (
CheckpointStorage,
FileCheckpointStorage,
InMemoryCheckpointStorage,
WorkflowCheckpoint,
)
from ._checkpoint_summary import WorkflowCheckpointSummary, get_checkpoint_summary
from ._concurrent import ConcurrentBuilder
from ._const import DEFAULT_MAX_ITERATIONS
from ._edge import (
Case,
Default,
Edge,
FanInEdgeGroup,
FanOutEdgeGroup,
SingleEdgeGroup,
SwitchCaseEdgeGroup,
SwitchCaseEdgeGroupCase,
SwitchCaseEdgeGroupDefault,
)
from ._edge_runner import create_edge_runner
from ._events import (
AgentRunEvent,
AgentRunUpdateEvent,
ExecutorCompletedEvent,
ExecutorEvent,
ExecutorFailedEvent,
ExecutorInvokedEvent,
RequestInfoEvent,
SuperStepCompletedEvent,
SuperStepStartedEvent,
WorkflowErrorDetails,
WorkflowEvent,
WorkflowEventSource,
WorkflowFailedEvent,
WorkflowLifecycleEvent,
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStartedEvent,
WorkflowStatusEvent,
)
from ._executor import (
Executor,
handler,
)
from ._function_executor import FunctionExecutor, executor
from ._group_chat import (
DEFAULT_MANAGER_INSTRUCTIONS,
DEFAULT_MANAGER_STRUCTURED_OUTPUT_PROMPT,
GroupChatBuilder,
GroupChatDirective,
GroupChatStateSnapshot,
)
from ._handoff import HandoffBuilder, HandoffUserInputRequest
from ._magentic import (
MagenticAgentDeltaEvent,
MagenticAgentMessageEvent,
MagenticBuilder,
MagenticContext,
MagenticFinalResultEvent,
MagenticManagerBase,
MagenticOrchestratorMessageEvent,
MagenticPlanReviewDecision,
MagenticPlanReviewReply,
MagenticPlanReviewRequest,
StandardMagenticManager,
)
from ._orchestration_state import OrchestrationState
from ._request_info_mixin import response_handler
from ._runner import Runner
from ._runner_context import (
InProcRunnerContext,
Message,
RunnerContext,
)
from ._sequential import SequentialBuilder
from ._shared_state import SharedState
from ._validation import (
EdgeDuplicationError,
GraphConnectivityError,
TypeCompatibilityError,
ValidationTypeEnum,
WorkflowValidationError,
validate_workflow_graph,
)
from ._viz import WorkflowViz
from ._workflow import Workflow, WorkflowRunResult
from ._workflow_builder import WorkflowBuilder
from ._workflow_context import WorkflowContext
from ._workflow_executor import SubWorkflowRequestMessage, SubWorkflowResponseMessage, WorkflowExecutor
__all__ = [
"DEFAULT_MANAGER_INSTRUCTIONS",
"DEFAULT_MANAGER_STRUCTURED_OUTPUT_PROMPT",
"DEFAULT_MAX_ITERATIONS",
"AgentExecutor",
"AgentExecutorRequest",
"AgentExecutorResponse",
"AgentRunEvent",
"AgentRunUpdateEvent",
"Case",
"CheckpointStorage",
"ConcurrentBuilder",
"Default",
"Edge",
"EdgeDuplicationError",
"Executor",
"ExecutorCompletedEvent",
"ExecutorEvent",
"ExecutorFailedEvent",
"ExecutorInvokedEvent",
"FanInEdgeGroup",
"FanOutEdgeGroup",
"FileCheckpointStorage",
"FunctionExecutor",
"GraphConnectivityError",
"GroupChatBuilder",
"GroupChatDirective",
"GroupChatStateSnapshot",
"HandoffBuilder",
"HandoffUserInputRequest",
"InMemoryCheckpointStorage",
"InProcRunnerContext",
"MagenticAgentDeltaEvent",
"MagenticAgentMessageEvent",
"MagenticBuilder",
"MagenticContext",
"MagenticFinalResultEvent",
"MagenticManagerBase",
"MagenticOrchestratorMessageEvent",
"MagenticPlanReviewDecision",
"MagenticPlanReviewReply",
"MagenticPlanReviewRequest",
"Message",
"OrchestrationState",
"RequestInfoEvent",
"Runner",
"RunnerContext",
"SequentialBuilder",
"SharedState",
"SingleEdgeGroup",
"StandardMagenticManager",
"SubWorkflowRequestMessage",
"SubWorkflowResponseMessage",
"SuperStepCompletedEvent",
"SuperStepStartedEvent",
"SwitchCaseEdgeGroup",
"SwitchCaseEdgeGroupCase",
"SwitchCaseEdgeGroupDefault",
"TypeCompatibilityError",
"ValidationTypeEnum",
"Workflow",
"WorkflowAgent",
"WorkflowBuilder",
"WorkflowCheckpoint",
"WorkflowCheckpointSummary",
"WorkflowContext",
"WorkflowErrorDetails",
"WorkflowEvent",
"WorkflowEventSource",
"WorkflowExecutor",
"WorkflowFailedEvent",
"WorkflowLifecycleEvent",
"WorkflowOutputEvent",
"WorkflowRunResult",
"WorkflowRunState",
"WorkflowStartedEvent",
"WorkflowStatusEvent",
"WorkflowValidationError",
"WorkflowViz",
"create_edge_runner",
"executor",
"get_checkpoint_summary",
"handler",
"response_handler",
"validate_workflow_graph",
]
@@ -3,20 +3,20 @@
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_a2a"
PACKAGE_EXTRA = "a2a"
IMPORT_PATH = "agent_framework_a2a"
PACKAGE_NAME = "agent-framework-a2a"
_IMPORTS = ["__version__", "A2AAgent"]
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
return getattr(importlib.import_module(IMPORT_PATH), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, please do `pip install agent-framework-{PACKAGE_EXTRA}`"
f"The '{PACKAGE_NAME}' package is not installed, please do `pip install {PACKAGE_NAME}`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
raise AttributeError(f"Module {IMPORT_PATH} has no attribute {name}.")
def __dir__() -> list[str]:
@@ -1,5 +1,11 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_a2a import A2AAgent, __version__
from agent_framework_a2a import (
A2AAgent,
__version__,
)
__all__ = ["A2AAgent", "__version__"]
__all__ = [
"A2AAgent",
"__version__",
]
@@ -3,8 +3,8 @@
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_ag_ui"
PACKAGE_EXTRA = "ag-ui"
IMPORT_PATH = "agent_framework_ag_ui"
PACKAGE_NAME = "agent-framework-ag-ui"
_IMPORTS = [
"__version__",
"AgentFrameworkAgent",
@@ -23,12 +23,12 @@ _IMPORTS = [
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
return getattr(importlib.import_module(IMPORT_PATH), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, please do `pip install agent-framework-{PACKAGE_EXTRA}`"
f"The '{PACKAGE_NAME}' package is not installed, please do `pip install {PACKAGE_NAME}`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
raise AttributeError(f"Module {IMPORT_PATH} has no attribute {name}.")
def __dir__() -> list[str]:
@@ -3,20 +3,20 @@
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_anthropic"
PACKAGE_EXTRA = "anthropic"
IMPORT_PATH = "agent_framework_anthropic"
PACKAGE_NAME = "agent-framework-anthropic"
_IMPORTS = ["__version__", "AnthropicClient"]
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
return getattr(importlib.import_module(IMPORT_PATH), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, please do `pip install agent-framework-{PACKAGE_EXTRA}`"
f"The '{PACKAGE_NAME}' package is not installed, please do `pip install {PACKAGE_NAME}`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
raise AttributeError(f"Module {IMPORT_PATH} has no attribute {name}.")
def __dir__() -> list[str]:
@@ -1,5 +1,11 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_anthropic import AnthropicClient, __version__
from agent_framework_anthropic import (
AnthropicClient,
__version__,
)
__all__ = ["AnthropicClient", "__version__"]
__all__ = [
"AnthropicClient",
"__version__",
]
@@ -1,36 +1,35 @@
# Copyright (c) Microsoft. All rights reserved.
import importlib
from typing import Any
_IMPORTS: dict[str, tuple[str, str]] = {
"AgentCallbackContext": ("agent_framework_azurefunctions", "azurefunctions"),
"AgentFunctionApp": ("agent_framework_azurefunctions", "azurefunctions"),
"AgentResponseCallbackProtocol": ("agent_framework_azurefunctions", "azurefunctions"),
"AzureAIAgentClient": ("agent_framework_azure_ai", "azure-ai"),
"AzureAIClient": ("agent_framework_azure_ai", "azure-ai"),
"AzureAISearchContextProvider": ("agent_framework_aisearch", "aisearch"),
"AzureAISearchSettings": ("agent_framework_aisearch", "aisearch"),
"AzureOpenAIAssistantsClient": ("agent_framework.azure._assistants_client", "core"),
"AzureOpenAIChatClient": ("agent_framework.azure._chat_client", "core"),
"AzureAISettings": ("agent_framework_azure_ai", "azure-ai"),
"AzureOpenAISettings": ("agent_framework.azure._shared", "core"),
"AzureOpenAIResponsesClient": ("agent_framework.azure._responses_client", "core"),
"DurableAIAgent": ("agent_framework_azurefunctions", "azurefunctions"),
"get_entra_auth_token": ("agent_framework.azure._entra_id_authentication", "core"),
"AgentCallbackContext": ("agent_framework_azurefunctions", "agent-framework-azurefunctions"),
"AgentFunctionApp": ("agent_framework_azurefunctions", "agent-framework-azurefunctions"),
"AgentResponseCallbackProtocol": ("agent_framework_azurefunctions", "agent-framework-azurefunctions"),
"AzureAIAgentClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"),
"AzureAIClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"),
"AzureAISearchContextProvider": ("agent_framework_azure_ai_search", "agent-framework-azure-ai-search"),
"AzureAISearchSettings": ("agent_framework_azure_ai_search", "agent-framework-azure-ai-search"),
"AzureAISettings": ("agent_framework_azure_ai", "agent-framework-azure-ai"),
"AzureOpenAIAssistantsClient": ("agent_framework.azure._assistants_client", "agent-framework-core"),
"AzureOpenAIChatClient": ("agent_framework.azure._chat_client", "agent-framework-core"),
"AzureOpenAIResponsesClient": ("agent_framework.azure._responses_client", "agent-framework-core"),
"AzureOpenAISettings": ("agent_framework.azure._shared", "agent-framework-core"),
"DurableAIAgent": ("agent_framework_azurefunctions", "agent-framework-azurefunctions"),
"get_entra_auth_token": ("agent_framework.azure._entra_id_authentication", "agent-framework-core"),
}
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
package_name, package_extra = _IMPORTS[name]
import_path, package_name = _IMPORTS[name]
try:
return getattr(importlib.import_module(package_name), name)
return getattr(importlib.import_module(import_path), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"please use `pip install agent-framework-{package_extra}`, "
"or update your requirements.txt or pyproject.toml file."
f"The package {package_name} is required to use `{name}`. "
f"Please use `pip install {package_name}`, or update your requirements.txt or pyproject.toml file."
) from exc
raise AttributeError(f"Module `azure` has no attribute {name}.")
@@ -1,6 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_azure_ai import AzureAIAgentClient, AzureAIClient, AzureAISettings
from agent_framework_azure_ai_search import AzureAISearchContextProvider, AzureAISearchSettings
from agent_framework_azurefunctions import (
AgentCallbackContext,
AgentFunctionApp,
@@ -20,6 +21,8 @@ __all__ = [
"AgentResponseCallbackProtocol",
"AzureAIAgentClient",
"AzureAIClient",
"AzureAISearchContextProvider",
"AzureAISearchSettings",
"AzureAISettings",
"AzureOpenAIAssistantsClient",
"AzureOpenAIChatClient",
@@ -3,20 +3,20 @@
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_chatkit"
PACKAGE_EXTRA = "chatkit"
IMPORT_PATH = "agent_framework_chatkit"
PACKAGE_NAME = "agent-framework-chatkit"
_IMPORTS = ["__version__", "ThreadItemConverter", "simple_to_agent_input", "stream_agent_response"]
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
return getattr(importlib.import_module(IMPORT_PATH), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, please do `pip install agent-framework-{PACKAGE_EXTRA}`"
f"The '{PACKAGE_NAME}' package is not installed, please do `pip install {PACKAGE_NAME}`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
raise AttributeError(f"Module {IMPORT_PATH} has no attribute {name}.")
def __dir__() -> list[str]:
@@ -7,4 +7,9 @@ from agent_framework_chatkit import (
stream_agent_response,
)
__all__ = ["ThreadItemConverter", "__version__", "simple_to_agent_input", "stream_agent_response"]
__all__ = [
"ThreadItemConverter",
"__version__",
"simple_to_agent_input",
"stream_agent_response",
]
@@ -3,8 +3,8 @@
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_devui"
PACKAGE_EXTRA = "devui"
IMPORT_PATH = "agent_framework_devui"
PACKAGE_NAME = "agent-framework-devui"
_IMPORTS = [
"AgentFrameworkRequest",
"DevServer",
@@ -22,12 +22,12 @@ _IMPORTS = [
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
return getattr(importlib.import_module(IMPORT_PATH), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, please do `pip install agent-framework-{PACKAGE_EXTRA}`"
f"The '{PACKAGE_NAME}' package is not installed, please do `pip install {PACKAGE_NAME}`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
raise AttributeError(f"Module {IMPORT_PATH} has no attribute {name}.")
def __dir__() -> list[str]:
@@ -1,4 +1,5 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_devui import (
AgentFrameworkRequest,
DevServer,
@@ -3,20 +3,20 @@
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_mem0"
PACKAGE_EXTRA = "mem0"
IMPORT_PATH = "agent_framework_mem0"
PACKAGE_NAME = "agent-framework-mem0"
_IMPORTS = ["__version__", "Mem0Provider"]
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
return getattr(importlib.import_module(IMPORT_PATH), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, please do `pip install agent-framework-{PACKAGE_EXTRA}`"
f"The '{PACKAGE_NAME}' package is not installed, please do `pip install {PACKAGE_NAME}`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
raise AttributeError(f"Module {IMPORT_PATH} has no attribute {name}.")
def __dir__() -> list[str]:
@@ -1,5 +1,11 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_mem0 import Mem0Provider, __version__
from agent_framework_mem0 import (
Mem0Provider,
__version__,
)
__all__ = ["Mem0Provider", "__version__"]
__all__ = [
"Mem0Provider",
"__version__",
]
@@ -3,35 +3,33 @@
import importlib
from typing import Any
_IMPORTS: dict[str, tuple[str, list[str]]] = {
"CopilotStudioAgent": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]),
"__version__": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]),
"acquire_token": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]),
# Purview (Graph Data Security & Governance) integration exports
"PurviewPolicyMiddleware": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewChatPolicyMiddleware": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewSettings": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewAppLocation": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewLocationType": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewAuthenticationError": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewPaymentRequiredError": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewRateLimitError": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewRequestError": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"PurviewServiceError": ("agent_framework_purview", ["microsoft-purview", "purview"]),
"CacheProvider": ("agent_framework_purview", ["microsoft-purview", "purview"]),
_IMPORTS: dict[str, tuple[str, str]] = {
"CopilotStudioAgent": ("agent_framework_copilotstudio", "agent-framework-copilotstudio"),
"__version__": ("agent_framework_copilotstudio", "agent-framework-copilotstudio"),
"acquire_token": ("agent_framework_copilotstudio", "agent-framework-copilotstudio"),
"PurviewPolicyMiddleware": ("agent_framework_purview", "agent-framework-purview"),
"PurviewChatPolicyMiddleware": ("agent_framework_purview", "agent-framework-purview"),
"PurviewSettings": ("agent_framework_purview", "agent-framework-purview"),
"PurviewAppLocation": ("agent_framework_purview", "agent-framework-purview"),
"PurviewLocationType": ("agent_framework_purview", "agent-framework-purview"),
"PurviewAuthenticationError": ("agent_framework_purview", "agent-framework-purview"),
"PurviewPaymentRequiredError": ("agent_framework_purview", "agent-framework-purview"),
"PurviewRateLimitError": ("agent_framework_purview", "agent-framework-purview"),
"PurviewRequestError": ("agent_framework_purview", "agent-framework-purview"),
"PurviewServiceError": ("agent_framework_purview", "agent-framework-purview"),
"CacheProvider": ("agent_framework_purview", "agent-framework-purview"),
}
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
package_name, package_extra = _IMPORTS[name]
import_path, package_name = _IMPORTS[name]
try:
return getattr(importlib.import_module(package_name), name)
return getattr(importlib.import_module(import_path), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The {' or '.join(package_extra)} extra is not installed, "
f"please use `pip install agent-framework-{package_extra[0]}`, "
"or update your requirements.txt or pyproject.toml file."
f"The package {package_name} is required to use `{name}`. "
f"Please use `pip install {package_name}`, or update your requirements.txt or pyproject.toml file."
) from exc
raise AttributeError(f"Module `microsoft` has no attribute {name}.")
@@ -1,6 +1,10 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_copilotstudio import CopilotStudioAgent, __version__, acquire_token
from agent_framework_copilotstudio import (
CopilotStudioAgent,
__version__,
acquire_token,
)
from agent_framework_purview import (
CacheProvider,
PurviewAppLocation,
@@ -46,9 +46,7 @@ RESPONSE_TYPE = Union[
OPTION_TYPE = Union[ChatOptions, dict[str, Any]]
__all__ = [
"OpenAISettings",
]
__all__ = ["OpenAISettings"]
def _check_openai_version_for_callable_api_key() -> None:
@@ -3,20 +3,20 @@
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_redis"
PACKAGE_EXTRA = "redis"
IMPORT_PATH = "agent_framework_redis"
PACKAGE_NAME = "agent-framework-redis"
_IMPORTS = ["__version__", "RedisProvider", "RedisChatMessageStore"]
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
return getattr(importlib.import_module(IMPORT_PATH), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, please do `pip install agent-framework-{PACKAGE_EXTRA}`"
f"The '{PACKAGE_NAME}' package is not installed, please do `pip install {PACKAGE_NAME}`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
raise AttributeError(f"Module {IMPORT_PATH} has no attribute {name}.")
def __dir__() -> list[str]:
@@ -1,5 +1,13 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_redis import RedisChatMessageStore, RedisProvider, __version__
from agent_framework_redis import (
RedisChatMessageStore,
RedisProvider,
__version__,
)
__all__ = ["RedisChatMessageStore", "RedisProvider", "__version__"]
__all__ = [
"RedisChatMessageStore",
"RedisProvider",
"__version__",
]
+1 -1
View File
@@ -43,7 +43,7 @@ dependencies = [
all = [
"agent-framework-a2a",
"agent-framework-ag-ui",
"agent-framework-aisearch",
"agent-framework-azure-ai-search",
"agent-framework-anthropic",
"agent-framework-azure-ai",
"agent-framework-azurefunctions",
+1 -1
View File
@@ -81,7 +81,7 @@ agent-framework = { workspace = true }
agent-framework-core = { workspace = true }
agent-framework-a2a = { workspace = true }
agent-framework-ag-ui = { workspace = true }
agent-framework-aisearch = { workspace = true }
agent-framework-azure-ai-search = { workspace = true }
agent-framework-anthropic = { workspace = true }
agent-framework-azure-ai = { workspace = true }
agent-framework-azurefunctions = { workspace = true }
@@ -20,6 +20,8 @@ This folder contains examples demonstrating different ways to create and use age
| [`azure_ai_with_file_search.py`](azure_ai_with_file_search.py) | Shows how to use the `HostedFileSearchTool` with Azure AI agents to upload files, create vector stores, and enable agents to search through uploaded documents to answer user questions. |
| [`azure_ai_with_hosted_mcp.py`](azure_ai_with_hosted_mcp.py) | Shows how to integrate hosted Model Context Protocol (MCP) tools with Azure AI Agent. |
| [`azure_ai_with_response_format.py`](azure_ai_with_response_format.py) | Shows how to use structured outputs (response format) with Azure AI agents using Pydantic models to enforce specific response schemas. |
| [`azure_ai_with_search_context_agentic.py`](../../context_providers/azure_ai_search/azure_ai_with_search_context_agentic.py) | Shows how to use AzureAISearchContextProvider with agentic mode. Uses Knowledge Bases for multi-hop reasoning across documents with query planning. Recommended for most scenarios - slightly slower with more token consumption for query planning, but more accurate results. |
| [`azure_ai_with_search_context_semantic.py`](../../context_providers/azure_ai_search/azure_ai_with_search_context_semantic.py) | Shows how to use AzureAISearchContextProvider with semantic mode. Fast hybrid search with vector + keyword search and semantic ranking for RAG. Best for simple queries where speed is critical. |
| [`azure_ai_with_sharepoint.py`](azure_ai_with_sharepoint.py) | Shows how to use SharePoint grounding with Azure AI agents to search through SharePoint content and answer user questions with proper citations. Requires a SharePoint connection configured in your Azure AI project. |
| [`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. |
| [`azure_ai_with_image_generation.py`](azure_ai_with_image_generation.py) | Shows how to use the `ImageGenTool` with Azure AI agents to generate images based on text prompts. |
@@ -20,8 +20,6 @@ This folder contains examples demonstrating different ways to create and use age
| [`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_search_context_agentic.py`](azure_ai_with_search_context_agentic.py) | Shows how to use AzureAISearchContextProvider with agentic mode. Uses Knowledge Bases for multi-hop reasoning across documents with query planning. Recommended for most scenarios - slightly slower with more token consumption for query planning, but more accurate results. |
| [`azure_ai_with_search_context_semantic.py`](azure_ai_with_search_context_semantic.py) | Shows how to use AzureAISearchContextProvider with semantic mode. Fast hybrid search with vector + keyword search and semantic ranking for RAG. Best for simple queries where speed is critical. |
| [`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,179 @@
# Context Provider Examples
Context providers enable agents to maintain memory, retrieve relevant information, and enhance conversations with external context. The Agent Framework supports various context providers for different use cases, from simple in-memory storage to advanced persistent solutions with search capabilities.
This folder contains examples demonstrating how to use different context providers with the Agent Framework.
## Overview
Context providers implement two key methods:
- **`invoking`**: Called before the agent processes a request. Provides additional context, instructions, or retrieved information to enhance the agent's response.
- **`invoked`**: Called after the agent generates a response. Allows for storing information, updating memory, or performing post-processing.
## Examples
### Simple Context Provider
| File | Description | Installation |
|------|-------------|--------------|
| [`simple_context_provider.py`](simple_context_provider.py) | Demonstrates building a custom context provider that extracts and stores user information (name and age) from conversations. Shows how to use structured output to extract data and provide dynamic instructions based on stored context. | No additional package required - uses core `agent-framework` |
**Install:**
```bash
pip install agent-framework-azure-ai
```
### Azure AI Search
| File | Description |
|------|-------------|
| [`azure_ai_search/azure_ai_with_search_context_agentic.py`](azure_ai_search/azure_ai_with_search_context_agentic.py) | **Agentic mode** (recommended for most scenarios): Uses Knowledge Bases in Azure AI Search for query planning and multi-hop reasoning. Provides more accurate results through intelligent retrieval. Slightly slower with more token consumption. |
| [`azure_ai_search/azure_ai_with_search_context_semantic.py`](azure_ai_search/azure_ai_with_search_context_semantic.py) | **Semantic mode** (fast queries): Fast hybrid search combining vector and keyword search with semantic ranking. Best for scenarios where speed is critical. |
**Install:**
```bash
pip install agent-framework-azure-ai-search agent-framework-azure-ai
```
**Prerequisites:**
- Azure AI Search service with a search index
- Azure AI Foundry project with a model deployment
- For agentic mode: Azure OpenAI resource for Knowledge Base model calls
- Environment variables: `AZURE_SEARCH_ENDPOINT`, `AZURE_SEARCH_INDEX_NAME`, `AZURE_AI_PROJECT_ENDPOINT`
**Key Concepts:**
- **Agentic mode**: Intelligent retrieval with multi-hop reasoning, better for complex queries
- **Semantic mode**: Fast hybrid search with semantic ranking, better for simple queries and speed
### Mem0
The [mem0](mem0/) folder contains examples using Mem0, a self-improving memory layer that enables applications to have long-term memory capabilities.
| File | Description |
|------|-------------|
| [`mem0/mem0_basic.py`](mem0/mem0_basic.py) | Basic example storing and retrieving user preferences across different conversation threads. |
| [`mem0/mem0_threads.py`](mem0/mem0_threads.py) | Advanced thread scoping strategies: global scope (memories shared), per-operation scope (memories isolated), and multiple agents with different memory configurations. |
| [`mem0/mem0_oss.py`](mem0/mem0_oss.py) | Using Mem0 Open Source self-hosted version as the context provider. |
**Install:**
```bash
pip install agent-framework-mem0
```
**Prerequisites:**
- Mem0 API key from [app.mem0.ai](https://app.mem0.ai/) OR self-host [Mem0 Open Source](https://docs.mem0.ai/open-source/overview)
- For Mem0 Platform: `MEM0_API_KEY` environment variable
- For Mem0 OSS: `OPENAI_API_KEY` for embedding generation
**Key Concepts:**
- **Global Scope**: Memories shared across all conversation threads
- **Thread Scope**: Memories isolated per conversation thread
- **Memory Association**: Records can be associated with `user_id`, `agent_id`, `thread_id`, or `application_id`
See the [mem0 README](mem0/README.md) for detailed documentation.
### Redis
The [redis](redis/) folder contains examples using Redis (RediSearch) for persistent, searchable memory with full-text and optional hybrid vector search.
| File | Description |
|------|-------------|
| [`redis/redis_basics.py`](redis/redis_basics.py) | Standalone provider usage and agent integration. Demonstrates writing messages, full-text/hybrid search, persisting preferences, and tool output memory. |
| [`redis/redis_conversation.py`](redis/redis_conversation.py) | Conversational examples showing memory persistence across sessions. |
| [`redis/redis_threads.py`](redis/redis_threads.py) | Thread scoping: global scope, per-operation scope, and multiple agents with isolated memory via different `agent_id` values. |
**Install:**
```bash
pip install agent-framework-redis
```
**Prerequisites:**
- Running Redis with RediSearch (Redis Stack or managed service)
- **Docker**: `docker run --name redis -p 6379:6379 -d redis:8.0.3`
- **Redis Cloud**: [redis.io/cloud](https://redis.io/cloud/)
- **Azure Managed Redis**: [Azure quickstart](https://learn.microsoft.com/azure/redis/quickstart-create-managed-redis)
- Optional: `OPENAI_API_KEY` for vector embeddings (hybrid search)
**Key Concepts:**
- **Full-text search**: Fast keyword-based retrieval
- **Hybrid vector search**: Optional embeddings for semantic search (`vectorizer_choice="openai"` or `"hf"`)
- **Memory scoping**: Partition by `application_id`, `agent_id`, `user_id`, or `thread_id`
- **Thread scoping**: `scope_to_per_operation_thread_id=True` isolates memory per operation
See the [redis README](redis/README.md) for detailed documentation.
## Choosing a Context Provider
| Provider | Use Case | Persistence | Search | Complexity |
|----------|----------|-------------|--------|------------|
| **Simple/Custom** | Learning, prototyping, simple memory needs | No (in-memory) | No | Low |
| **Azure AI Search** | RAG, document search, enterprise knowledge bases | Yes | Hybrid + Semantic | Medium |
| **Mem0** | Long-term user memory, preferences, personalization | Yes (cloud/self-hosted) | Semantic | Low-Medium |
| **Redis** | Fast retrieval, session memory, full-text + vector search | Yes | Full-text + Hybrid | Medium |
## Common Patterns
### 1. User Preference Memory
Store and retrieve user preferences, settings, or personal information across sessions.
- **Examples**: `simple_context_provider.py`, `mem0/mem0_basic.py`, `redis/redis_basics.py`
### 2. Document Retrieval (RAG)
Retrieve relevant documents or knowledge base articles to answer questions.
- **Examples**: `azure_ai_search/azure_ai_with_search_context_*.py`
### 3. Conversation History
Maintain conversation context across multiple turns and sessions.
- **Examples**: `redis/redis_conversation.py`, `mem0/mem0_threads.py`
### 4. Thread Scoping
Isolate memory per conversation thread or share globally across threads.
- **Examples**: `mem0/mem0_threads.py`, `redis/redis_threads.py`
### 5. Multi-Agent Memory
Different agents with isolated or shared memory configurations.
- **Examples**: `mem0/mem0_threads.py`, `redis/redis_threads.py`
## Building Custom Context Providers
To create a custom context provider, implement the `ContextProvider` protocol:
```python
from agent_framework import ContextProvider, Context, ChatMessage
from collections.abc import MutableSequence, Sequence
from typing import Any
class MyContextProvider(ContextProvider):
async def invoking(
self,
messages: ChatMessage | MutableSequence[ChatMessage],
**kwargs: Any
) -> Context:
"""Provide context before the agent processes the request."""
# Return additional instructions, messages, or context
return Context(instructions="Additional instructions here")
async def invoked(
self,
request_messages: ChatMessage | Sequence[ChatMessage],
response_messages: ChatMessage | Sequence[ChatMessage] | None = None,
invoke_exception: Exception | None = None,
**kwargs: Any,
) -> None:
"""Process the response after the agent generates it."""
# Store information, update memory, etc.
pass
def serialize(self) -> str:
"""Serialize the provider state for persistence."""
return "{}"
```
See `simple_context_provider.py` for a complete example.
## Additional Resources
- [Agent Framework Documentation](https://github.com/microsoft/agent-framework)
- [Azure AI Search Documentation](https://learn.microsoft.com/azure/search/)
- [Mem0 Documentation](https://docs.mem0.ai/)
- [Redis Documentation](https://redis.io/docs/)
@@ -0,0 +1,264 @@
# Azure AI Search Context Provider Examples
Azure AI Search context provider enables Retrieval Augmented Generation (RAG) with your agents by retrieving relevant documents from Azure AI Search indexes. It supports two search modes optimized for different use cases.
This folder contains examples demonstrating how to use the Azure AI Search context provider with the Agent Framework.
## Examples
| File | Description |
|------|-------------|
| [`azure_ai_with_search_context_agentic.py`](azure_ai_with_search_context_agentic.py) | **Agentic mode** (recommended for most scenarios): Uses Knowledge Bases in Azure AI Search for query planning and multi-hop reasoning. Provides more accurate results through intelligent retrieval with automatic query reformulation. Slightly slower with more token consumption for query planning. [Learn more](https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/foundry-iq-boost-response-relevance-by-36-with-agentic-retrieval/4470720) |
| [`azure_ai_with_search_context_semantic.py`](azure_ai_with_search_context_semantic.py) | **Semantic mode** (fast queries): Fast hybrid search combining vector and keyword search with semantic ranking. Returns raw search results as context. Best for scenarios where speed is critical and simple retrieval is sufficient. |
## Installation
```bash
pip install agent-framework-azure-ai-search agent-framework-azure-ai
```
## Prerequisites
### Required Resources
1. **Azure AI Search service** with a search index containing your documents
- [Create Azure AI Search service](https://learn.microsoft.com/azure/search/search-create-service-portal)
- [Create and populate a search index](https://learn.microsoft.com/azure/search/search-what-is-an-index)
2. **Azure AI Foundry project** with a model deployment
- [Create Azure AI Foundry project](https://learn.microsoft.com/azure/ai-studio/how-to/create-projects)
- Deploy a model (e.g., GPT-4o)
3. **For Agentic mode only**: Azure OpenAI resource for Knowledge Base model calls
- [Create Azure OpenAI resource](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource)
- Note: This is separate from your Azure AI Foundry project endpoint
### Authentication
Both examples support two authentication methods:
- **API Key**: Set `AZURE_SEARCH_API_KEY` environment variable
- **Entra ID (Managed Identity)**: Uses `DefaultAzureCredential` when API key is not provided
Run `az login` if using Entra ID authentication.
## Configuration
### Environment Variables
**Common (both modes):**
- `AZURE_SEARCH_ENDPOINT`: Your Azure AI Search endpoint (e.g., `https://myservice.search.windows.net`)
- `AZURE_SEARCH_INDEX_NAME`: Name of your search index
- `AZURE_AI_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
- `AZURE_AI_MODEL_DEPLOYMENT_NAME`: Model deployment name (e.g., `gpt-4o`, defaults to `gpt-4o`)
- `AZURE_SEARCH_API_KEY`: _(Optional)_ Your search API key - if not provided, uses DefaultAzureCredential
**Agentic mode only:**
- `AZURE_SEARCH_KNOWLEDGE_BASE_NAME`: Name of your Knowledge Base in Azure AI Search
- `AZURE_OPENAI_RESOURCE_URL`: Your Azure OpenAI resource URL (e.g., `https://myresource.openai.azure.com`)
- **Important**: This is different from `AZURE_AI_PROJECT_ENDPOINT` - Knowledge Base needs the OpenAI endpoint for model calls
### Example .env file
**For Semantic Mode:**
```env
AZURE_SEARCH_ENDPOINT=https://myservice.search.windows.net
AZURE_SEARCH_INDEX_NAME=my-index
AZURE_AI_PROJECT_ENDPOINT=https://myproject.api.azureml.ms
AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o
# Optional - omit to use Entra ID
AZURE_SEARCH_API_KEY=your-search-key
```
**For Agentic Mode (add these to semantic mode variables):**
```env
AZURE_SEARCH_KNOWLEDGE_BASE_NAME=my-knowledge-base
AZURE_OPENAI_RESOURCE_URL=https://myresource.openai.azure.com
```
## Search Modes Comparison
| Feature | Semantic Mode | Agentic Mode |
|---------|--------------|--------------|
| **Speed** | Fast | Slower (query planning overhead) |
| **Token Usage** | Lower | Higher (query reformulation) |
| **Retrieval Strategy** | Hybrid search + semantic ranking | Multi-hop reasoning with Knowledge Base |
| **Query Handling** | Direct search | Automatic query reformulation |
| **Best For** | Simple queries, speed-critical apps | Complex queries, multi-document reasoning |
| **Additional Setup** | None | Requires Knowledge Base + OpenAI resource |
### When to Use Semantic Mode
- **Simple queries** where direct keyword/vector search is sufficient
- **Speed is critical** and you need low latency
- **Straightforward retrieval** from single documents
- **Lower token costs** are important
### When to Use Agentic Mode
- **Complex queries** requiring multi-hop reasoning
- **Cross-document analysis** where information spans multiple sources
- **Ambiguous queries** that benefit from automatic reformulation
- **Higher accuracy** is more important than speed
- You need **intelligent query planning** and document synthesis
## How the Examples Work
### Semantic Mode Flow
1. User query is sent to Azure AI Search
2. Hybrid search (vector + keyword) retrieves relevant documents
3. Semantic ranking reorders results for relevance
4. Top-k documents are returned as context
5. Agent generates response using retrieved context
### Agentic Mode Flow
1. User query is sent to the Knowledge Base
2. Knowledge Base plans the retrieval strategy
3. Multiple search queries may be executed (multi-hop)
4. Retrieved information is synthesized
5. Enhanced context is provided to the agent
6. Agent generates response with comprehensive context
## Code Example
### Semantic Mode
```python
from agent_framework import ChatAgent
from agent_framework.azure import AzureAIAgentClient, AzureAISearchContextProvider
from azure.identity.aio import DefaultAzureCredential
# Create search provider with semantic mode (default)
search_provider = AzureAISearchContextProvider(
endpoint=search_endpoint,
index_name=index_name,
api_key=search_key, # Or use credential for Entra ID
mode="semantic", # Default mode
top_k=3, # Number of documents to retrieve
)
# Create agent with search context
async with AzureAIAgentClient(async_credential=DefaultAzureCredential()) as client:
async with ChatAgent(
chat_client=client,
model=model_deployment,
context_providers=search_provider,
) as agent:
response = await agent.run("What information is in the knowledge base?")
```
### Agentic Mode
```python
from agent_framework.azure import AzureAISearchContextProvider
# Create search provider with agentic mode
search_provider = AzureAISearchContextProvider(
endpoint=search_endpoint,
index_name=index_name,
api_key=search_key,
mode="agentic", # Enable agentic retrieval
knowledge_base_name=knowledge_base_name,
azure_openai_resource_url=azure_openai_resource_url,
top_k=5,
)
# Use with agent (same as semantic mode)
async with ChatAgent(
chat_client=client,
model=model_deployment,
context_providers=search_provider,
) as agent:
response = await agent.run("Analyze and compare topics across documents")
```
## Running the Examples
1. **Set up environment variables** (see Configuration section above)
2. **Ensure you have an Azure AI Search index** with documents:
```bash
# Verify your index exists
curl -X GET "https://myservice.search.windows.net/indexes/my-index?api-version=2024-07-01" \
-H "api-key: YOUR_API_KEY"
```
3. **For agentic mode**: Create a Knowledge Base in Azure AI Search
- [Knowledge Base documentation](https://learn.microsoft.com/azure/search/knowledge-store-create-portal)
4. **Run the examples**:
```bash
# Semantic mode (fast, simple)
python azure_ai_with_search_context_semantic.py
# Agentic mode (intelligent, complex)
python azure_ai_with_search_context_agentic.py
```
## Key Parameters
### Common Parameters
- `endpoint`: Azure AI Search service endpoint
- `index_name`: Name of the search index
- `api_key`: API key for authentication (optional, can use credential instead)
- `credential`: Azure credential for Entra ID auth (e.g., `DefaultAzureCredential()`)
- `mode`: Search mode - `"semantic"` (default) or `"agentic"`
- `top_k`: Number of documents to retrieve (default: 3 for semantic, 5 for agentic)
### Semantic Mode Parameters
- `semantic_configuration`: Name of semantic configuration in your index (optional)
- `query_type`: Query type - `"semantic"` for semantic search (default)
### Agentic Mode Parameters
- `knowledge_base_name`: Name of your Knowledge Base (required)
- `azure_openai_resource_url`: Azure OpenAI resource URL (required)
- `max_search_queries`: Maximum number of search queries to generate (default: 3)
## Troubleshooting
### Common Issues
1. **Authentication errors**
- Ensure `AZURE_SEARCH_API_KEY` is set, or run `az login` for Entra ID auth
- Verify your credentials have search permissions
2. **Index not found**
- Verify `AZURE_SEARCH_INDEX_NAME` matches your index name exactly
- Check that the index exists and contains documents
3. **Agentic mode errors**
- Ensure `AZURE_SEARCH_KNOWLEDGE_BASE_NAME` is correctly configured
- Verify `AZURE_OPENAI_RESOURCE_URL` points to your Azure OpenAI resource (not AI Foundry endpoint)
- Check that your OpenAI resource has the necessary model deployments
4. **No results returned**
- Verify your index has documents with vector embeddings (for semantic/hybrid search)
- Check that your queries match the content in your index
- Try increasing `top_k` parameter
5. **Slow responses in agentic mode**
- This is expected - agentic mode trades speed for accuracy
- Reduce `max_search_queries` if needed
- Consider semantic mode for speed-critical applications
## Performance Tips
- **Use semantic mode** as the default for most scenarios - it's fast and effective
- **Switch to agentic mode** when you need multi-hop reasoning or complex queries
- **Adjust `top_k`** based on your needs - higher values provide more context but increase token usage
- **Enable semantic configuration** in your index for better semantic ranking
- **Use Entra ID authentication** in production for better security
## Additional Resources
- [Azure AI Search Documentation](https://learn.microsoft.com/azure/search/)
- [Azure AI Foundry Documentation](https://learn.microsoft.com/azure/ai-studio/)
- [RAG with Azure AI Search](https://learn.microsoft.com/azure/search/retrieval-augmented-generation-overview)
- [Semantic Search in Azure AI Search](https://learn.microsoft.com/azure/search/semantic-search-overview)
- [Knowledge Bases in Azure AI Search](https://learn.microsoft.com/azure/search/knowledge-store-concept-intro)
- [Agentic Retrieval Blog Post](https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/foundry-iq-boost-response-relevance-by-36-with-agentic-retrieval/4470720)
@@ -4,8 +4,7 @@ import asyncio
import os
from agent_framework import ChatAgent
from agent_framework_aisearch import AzureAISearchContextProvider
from agent_framework_azure_ai import AzureAIAgentClient
from agent_framework.azure import AzureAIAgentClient, AzureAISearchContextProvider
from azure.identity.aio import AzureCliCredential
from dotenv import load_dotenv
@@ -4,8 +4,7 @@ import asyncio
import os
from agent_framework import ChatAgent
from agent_framework_aisearch import AzureAISearchContextProvider
from agent_framework_azure_ai import AzureAIAgentClient
from agent_framework.azure import AzureAIAgentClient, AzureAISearchContextProvider
from azure.identity.aio import AzureCliCredential
from dotenv import load_dotenv
@@ -5,7 +5,7 @@ from collections.abc import MutableSequence, Sequence
from typing import Any
from agent_framework import ChatAgent, ChatClientProtocol, ChatMessage, ChatOptions, Context, ContextProvider
from agent_framework.azure import AzureAIAgentClient
from agent_framework.azure import AzureAIClient
from azure.identity.aio import AzureCliCredential
from pydantic import BaseModel
@@ -47,7 +47,8 @@ class UserInfoMemory(ContextProvider):
result = await self._chat_client.get_response(
messages=request_messages, # type: ignore
chat_options=ChatOptions(
instructions="Extract the user's name and age from the message if present. If not present return nulls.",
instructions="Extract the user's name and age from the message if present. "
"If not present return nulls.",
response_format=UserInfo,
),
)
@@ -90,7 +91,7 @@ class UserInfoMemory(ContextProvider):
async def main():
async with AzureCliCredential() as credential:
chat_client = AzureAIAgentClient(async_credential=credential)
chat_client = AzureAIClient(async_credential=credential)
# Create the memory provider
memory_provider = UserInfoMemory(chat_client)
+3503 -3486
View File
File diff suppressed because it is too large Load Diff