Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Microsoft Agent Framework prend en charge l’ajout de fonctionnalités rag (Recovery Augmented Generation) aux agents facilement en ajoutant des fournisseurs de contexte IA à l’agent.
Pour connaître les modèles de conversation/session en même temps que la récupération, consultez La vue d’ensemble des conversations et de la mémoire.
Utilisation de TextSearchProvider
La TextSearchProvider classe est une implémentation prête à l’emploi d’un fournisseur de contexte RAG.
Il peut facilement être attaché à une ChatClientAgent option utilisant la AIContextProviderFactory possibilité de fournir des fonctionnalités RAG à l’agent.
Dans le domaine technique, "factory" est couramment utilisée. Une "factory" est une fonction asynchrone qui reçoit un context object et un jeton d'annulation.
// Create the AI agent with the TextSearchProvider as the AI context provider.
AIAgent agent = azureOpenAIClient
.GetChatClient(deploymentName)
.AsAIAgent(new ChatClientAgentOptions
{
ChatOptions = new() { Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available." },
AIContextProviderFactory = (ctx, ct) => new ValueTask<AIContextProvider>(
new TextSearchProvider(SearchAdapter, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions))
});
La TextSearchProvider fonction nécessite une fonction qui fournit les résultats de la recherche en fonction d’une requête. Cela peut être implémenté à l’aide de n’importe quelle technologie de recherche, par exemple Azure AI Search ou d’un moteur de recherche web.
Voici un exemple de fonction de recherche fictif qui retourne des résultats prédéfinis en fonction de la requête.
SourceName et SourceLink sont facultatifs, mais si fourni sera utilisé par l’agent pour citer la source des informations lors de la réponse à la question de l’utilisateur.
static Task<IEnumerable<TextSearchProvider.TextSearchResult>> SearchAdapter(string query, CancellationToken cancellationToken)
{
// The mock search inspects the user's question and returns pre-defined snippets
// that resemble documents stored in an external knowledge source.
List<TextSearchProvider.TextSearchResult> results = new();
if (query.Contains("return", StringComparison.OrdinalIgnoreCase) || query.Contains("refund", StringComparison.OrdinalIgnoreCase))
{
results.Add(new()
{
SourceName = "Contoso Outdoors Return Policy",
SourceLink = "https://contoso.com/policies/returns",
Text = "Customers may return any item within 30 days of delivery. Items should be unused and include original packaging. Refunds are issued to the original payment method within 5 business days of inspection."
});
}
return Task.FromResult<IEnumerable<TextSearchProvider.TextSearchResult>>(results);
}
TextSearchProvider Options
Il TextSearchProvider peut être personnalisé par le biais de la TextSearchProviderOptions classe. Voici un exemple de création d’options permettant d’exécuter la recherche avant chaque appel de modèle et de conserver une courte fenêtre propagée de contexte de conversation.
TextSearchProviderOptions textSearchOptions = new()
{
// Run the search prior to every model invocation and keep a short rolling window of conversation context.
SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke,
RecentMessageMemoryLimit = 6,
};
La TextSearchProvider classe prend en charge les options suivantes via la TextSearchProviderOptions classe.
| Choix | Type | Descriptif | Par défaut |
|---|---|---|---|
| SearchTime | TextSearchProviderOptions.TextSearchBehavior |
Indique quand la recherche doit être exécutée. Il existe deux options, chaque fois que l’agent est appelé ou à la demande via l’appel de fonction. | TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke |
| FunctionToolName | string |
Nom de l’outil de recherche exposé lors de l’utilisation en mode à la demande. | « Rechercher » |
| FunctionToolDescription | string |
Description de l’outil de recherche exposé lors de l’utilisation en mode à la demande. | « Permet de rechercher des informations supplémentaires pour répondre à la question de l’utilisateur. » |
| ContextPrompt | string |
Le contexte est précédé de résultats lors de l’utilisation en BeforeAIInvoke mode. |
« ## Contexte supplémentaire\nprendre en compte les informations suivantes des documents sources lors de la réponse à l’utilisateur : » |
| CitationsPrompt | string |
L’instruction ajoutée après les résultats pour demander des citations lors de l’utilisation en BeforeAIInvoke mode. |
« Incluez des citations au document source avec le nom du document et le lien si le nom et le lien du document sont disponibles. » |
| ContextFormatter | Func<IList<TextSearchProvider.TextSearchResult>, string> |
Délégué facultatif pour personnaliser entièrement la mise en forme de la liste de résultats lors de l’utilisation en BeforeAIInvoke mode. Si elles sont fournies et ContextPromptCitationsPrompt sont ignorées. |
null |
| RecentMessageMemoryLimit | int |
Nombre de messages de conversation récents (utilisateur et Assistant) à conserver en mémoire et à inclure lors de la construction de l’entrée de recherche pour BeforeAIInvoke les recherches. |
0 (désactivé) |
| RecentMessageRolesIncluded | List<ChatRole> |
Liste des ChatRole types à filtrer pour filtrer les messages récents lors du choix des messages récents à inclure lors de la construction de l’entrée de recherche. |
ChatRole.User |
Conseil / Astuce
Consultez les exemples .NET pour obtenir des exemples exécutables complets.
Agent Framework prend en charge l’utilisation des collections VectorStore du noyau sémantique pour fournir des fonctionnalités RAG aux agents. Pour ce faire, vous pouvez utiliser la fonctionnalité de pont qui convertit les fonctions de recherche de noyau sémantique en outils Agent Framework.
Création d’un outil de recherche à partir de VectorStore
La create_search_function méthode d’une collection Semantic Kernel VectorStore retourne une KernelFunction valeur qui peut être convertie en outil Agent Framework à l’aide .as_agent_framework_tool()de .
Utilisez la documentation des connecteurs de magasin de vecteurs pour apprendre à configurer différentes collections de magasins vectoriels.
from semantic_kernel.connectors.ai.open_ai import OpenAITextEmbedding
from semantic_kernel.connectors.azure_ai_search import AzureAISearchCollection
from semantic_kernel.functions import KernelParameterMetadata
from agent_framework.openai import OpenAIResponsesClient
# Define your data model
class SupportArticle:
article_id: str
title: str
content: str
category: str
# ... other fields
# Create an Azure AI Search collection
collection = AzureAISearchCollection[str, SupportArticle](
record_type=SupportArticle,
embedding_generator=OpenAITextEmbedding()
)
async with collection:
await collection.ensure_collection_exists()
# Load your knowledge base articles into the collection
# await collection.upsert(articles)
# Create a search function from the collection
search_function = collection.create_search_function(
function_name="search_knowledge_base",
description="Search the knowledge base for support articles and product information.",
search_type="keyword_hybrid",
parameters=[
KernelParameterMetadata(
name="query",
description="The search query to find relevant information.",
type="str",
is_required=True,
type_object=str,
),
KernelParameterMetadata(
name="top",
description="Number of results to return.",
type="int",
default_value=3,
type_object=int,
),
],
string_mapper=lambda x: f"[{x.record.category}] {x.record.title}: {x.record.content}",
)
# Convert the search function to an Agent Framework tool
search_tool = search_function.as_agent_framework_tool()
# Create an agent with the search tool
agent = OpenAIResponsesClient(model_id="gpt-4o").as_agent(
instructions="You are a helpful support specialist. Use the search tool to find relevant information before answering questions. Always cite your sources.",
tools=search_tool
)
# Use the agent with RAG capabilities
response = await agent.run("How do I return a product?")
print(response.text)
Important
Cette fonctionnalité nécessite semantic-kernel la version 1.38 ou ultérieure.
Personnalisation du comportement de recherche
Vous pouvez personnaliser la fonction de recherche avec différentes options :
# Create a search function with filtering and custom formatting
search_function = collection.create_search_function(
function_name="search_support_articles",
description="Search for support articles in specific categories.",
search_type="keyword_hybrid",
# Apply filters to restrict search scope
filter=lambda x: x.is_published == True,
parameters=[
KernelParameterMetadata(
name="query",
description="What to search for in the knowledge base.",
type="str",
is_required=True,
type_object=str,
),
KernelParameterMetadata(
name="category",
description="Filter by category: returns, shipping, products, or billing.",
type="str",
type_object=str,
),
KernelParameterMetadata(
name="top",
description="Maximum number of results to return.",
type="int",
default_value=5,
type_object=int,
),
],
# Customize how results are formatted for the agent
string_mapper=lambda x: f"Article: {x.record.title}\nCategory: {x.record.category}\nContent: {x.record.content}\nSource: {x.record.article_id}",
)
Pour plus d’informations sur les paramètres disponibles, create_search_functionconsultez la documentation du noyau sémantique.
Utilisation de plusieurs fonctions de recherche
Vous pouvez fournir plusieurs outils de recherche à un agent pour différents domaines de connaissances :
# Create search functions for different knowledge bases
product_search = product_collection.create_search_function(
function_name="search_products",
description="Search for product information and specifications.",
search_type="semantic_hybrid",
string_mapper=lambda x: f"{x.record.name}: {x.record.description}",
).as_agent_framework_tool()
policy_search = policy_collection.create_search_function(
function_name="search_policies",
description="Search for company policies and procedures.",
search_type="keyword_hybrid",
string_mapper=lambda x: f"Policy: {x.record.title}\n{x.record.content}",
).as_agent_framework_tool()
# Create an agent with multiple search tools
agent = chat_client.as_agent(
instructions="You are a support agent. Use the appropriate search tool to find information before answering. Cite your sources.",
tools=[product_search, policy_search]
)
Vous pouvez également créer plusieurs fonctions de recherche à partir de la même collection avec différentes descriptions et paramètres pour fournir des fonctionnalités de recherche spécialisées :
# Create multiple search functions from the same collection
# Generic search for broad queries
general_search = support_collection.create_search_function(
function_name="search_all_articles",
description="Search all support articles for general information.",
search_type="semantic_hybrid",
parameters=[
KernelParameterMetadata(
name="query",
description="The search query.",
type="str",
is_required=True,
type_object=str,
),
],
string_mapper=lambda x: f"{x.record.title}: {x.record.content}",
).as_agent_framework_tool()
# Detailed lookup for specific article IDs
detail_lookup = support_collection.create_search_function(
function_name="get_article_details",
description="Get detailed information for a specific article by its ID.",
search_type="keyword",
top=1,
parameters=[
KernelParameterMetadata(
name="article_id",
description="The specific article ID to retrieve.",
type="str",
is_required=True,
type_object=str,
),
],
string_mapper=lambda x: f"Title: {x.record.title}\nFull Content: {x.record.content}\nLast Updated: {x.record.updated_date}",
).as_agent_framework_tool()
# Create an agent with both search functions
agent = chat_client.as_agent(
instructions="You are a support agent. Use search_all_articles for general queries and get_article_details when you need full details about a specific article.",
tools=[general_search, detail_lookup]
)
Cette approche permet à l’agent de choisir la stratégie de recherche la plus appropriée en fonction de la requête de l’utilisateur.
Exemple complet
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from collections.abc import MutableSequence, Sequence
from typing import Any
from agent_framework import Agent, BaseContextProvider, Context, Message, SupportsChatGetResponse
from agent_framework.azure import AzureAIClient
from azure.identity.aio import AzureCliCredential
from pydantic import BaseModel
class UserInfo(BaseModel):
name: str | None = None
age: int | None = None
class UserInfoMemory(BaseContextProvider):
def __init__(self, client: SupportsChatGetResponse, user_info: UserInfo | None = None, **kwargs: Any):
"""Create the memory.
If you pass in kwargs, they will be attempted to be used to create a UserInfo object.
"""
self._chat_client = client
if user_info:
self.user_info = user_info
elif kwargs:
self.user_info = UserInfo.model_validate(kwargs)
else:
self.user_info = UserInfo()
async def invoked(
self,
request_messages: Message | Sequence[Message],
response_messages: Message | Sequence[Message] | None = None,
invoke_exception: Exception | None = None,
**kwargs: Any,
) -> None:
"""Extract user information from messages after each agent call."""
# Check if we need to extract user info from user messages
user_messages = [msg for msg in request_messages if hasattr(msg, "role") and msg.role == "user"] # type: ignore
if (self.user_info.name is None or self.user_info.age is None) and user_messages:
try:
# Use the chat client to extract structured information
result = await self._chat_client.get_response(
messages=request_messages, # type: ignore
instructions="Extract the user's name and age from the message if present. "
"If not present return nulls.",
options={"response_format": UserInfo},
)
# Update user info with extracted data
try:
extracted = result.value
if self.user_info.name is None and extracted.name:
self.user_info.name = extracted.name
if self.user_info.age is None and extracted.age:
self.user_info.age = extracted.age
except Exception:
pass # Failed to extract, continue without updating
except Exception:
pass # Failed to extract, continue without updating
async def invoking(self, messages: Message | MutableSequence[Message], **kwargs: Any) -> Context:
"""Provide user information context before each agent call."""
instructions: list[str] = []
if self.user_info.name is None:
instructions.append(
"Ask the user for their name and politely decline to answer any questions until they provide it."
)
else:
instructions.append(f"The user's name is {self.user_info.name}.")
if self.user_info.age is None:
instructions.append(
"Ask the user for their age and politely decline to answer any questions until they provide it."
)
else:
instructions.append(f"The user's age is {self.user_info.age}.")
# Return context with additional instructions
return Context(instructions=" ".join(instructions))
def serialize(self) -> str:
"""Serialize the user info for thread persistence."""
return self.user_info.model_dump_json()
async def main():
async with AzureCliCredential() as credential:
client = AzureAIClient(credential=credential)
# Create the memory provider
memory_provider = UserInfoMemory(client)
# Create the agent with memory
async with Agent(
client=client,
instructions="You are a friendly assistant. Always address the user by their name.",
context_providers=[memory_provider],
) as agent:
# Create a new thread for the conversation
thread = agent.create_session()
print(await agent.run("Hello, what is the square root of 9?", session=thread))
print(await agent.run("My name is Ruaidhrí", session=thread))
print(await agent.run("I am 20 years old", session=thread))
# Access the memory component and inspect the memories
user_info_memory = memory_provider
if user_info_memory:
print()
print(f"MEMORY - User Name: {user_info_memory.user_info.name}") # type: ignore
print(f"MEMORY - User Age: {user_info_memory.user_info.age}") # type: ignore
if __name__ == "__main__":
asyncio.run(main())
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from collections.abc import MutableSequence, Sequence
from typing import Any
from agent_framework import Agent, BaseContextProvider, Context, Message, SupportsChatGetResponse
from agent_framework.azure import AzureAIClient
from azure.identity.aio import AzureCliCredential
from pydantic import BaseModel
class UserInfo(BaseModel):
name: str | None = None
age: int | None = None
class UserInfoMemory(BaseContextProvider):
def __init__(self, client: SupportsChatGetResponse, user_info: UserInfo | None = None, **kwargs: Any):
"""Create the memory.
If you pass in kwargs, they will be attempted to be used to create a UserInfo object.
"""
self._chat_client = client
if user_info:
self.user_info = user_info
elif kwargs:
self.user_info = UserInfo.model_validate(kwargs)
else:
self.user_info = UserInfo()
async def invoked(
self,
request_messages: Message | Sequence[Message],
response_messages: Message | Sequence[Message] | None = None,
invoke_exception: Exception | None = None,
**kwargs: Any,
) -> None:
"""Extract user information from messages after each agent call."""
# Check if we need to extract user info from user messages
user_messages = [msg for msg in request_messages if hasattr(msg, "role") and msg.role == "user"] # type: ignore
if (self.user_info.name is None or self.user_info.age is None) and user_messages:
try:
# Use the chat client to extract structured information
result = await self._chat_client.get_response(
messages=request_messages, # type: ignore
instructions="Extract the user's name and age from the message if present. "
"If not present return nulls.",
options={"response_format": UserInfo},
)
# Update user info with extracted data
try:
extracted = result.value
if self.user_info.name is None and extracted.name:
self.user_info.name = extracted.name
if self.user_info.age is None and extracted.age:
self.user_info.age = extracted.age
except Exception:
pass # Failed to extract, continue without updating
except Exception:
pass # Failed to extract, continue without updating
async def invoking(self, messages: Message | MutableSequence[Message], **kwargs: Any) -> Context:
"""Provide user information context before each agent call."""
instructions: list[str] = []
if self.user_info.name is None:
instructions.append(
"Ask the user for their name and politely decline to answer any questions until they provide it."
)
else:
instructions.append(f"The user's name is {self.user_info.name}.")
if self.user_info.age is None:
instructions.append(
"Ask the user for their age and politely decline to answer any questions until they provide it."
)
else:
instructions.append(f"The user's age is {self.user_info.age}.")
# Return context with additional instructions
return Context(instructions=" ".join(instructions))
def serialize(self) -> str:
"""Serialize the user info for thread persistence."""
return self.user_info.model_dump_json()
async def main():
async with AzureCliCredential() as credential:
client = AzureAIClient(credential=credential)
# Create the memory provider
memory_provider = UserInfoMemory(client)
# Create the agent with memory
async with Agent(
client=client,
instructions="You are a friendly assistant. Always address the user by their name.",
context_providers=[memory_provider],
) as agent:
# Create a new thread for the conversation
thread = agent.create_session()
print(await agent.run("Hello, what is the square root of 9?", session=thread))
print(await agent.run("My name is Ruaidhrí", session=thread))
print(await agent.run("I am 20 years old", session=thread))
# Access the memory component and inspect the memories
user_info_memory = memory_provider
if user_info_memory:
print()
print(f"MEMORY - User Name: {user_info_memory.user_info.name}") # type: ignore
print(f"MEMORY - User Age: {user_info_memory.user_info.age}") # type: ignore
if __name__ == "__main__":
asyncio.run(main())
Connecteurs VectorStore pris en charge
Ce modèle fonctionne avec n’importe quel connecteur VectorStore du noyau sémantique, notamment :
- Recherche Azure AI (
AzureAISearchCollection) - Qdrant (
QdrantCollection) - Pincone (
PineconeCollection) - Redis (
RedisCollection) - Weaviate (
WeaviateCollection) - In-Memory (
InMemoryVectorStoreCollection) - Et bien plus encore
Chaque connecteur fournit la même create_search_function méthode que les outils Agent Framework, ce qui vous permet de choisir la base de données vectorielle qui répond le mieux à vos besoins. Consultez la liste complète ici.