Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Microsoft Agent Framework biedt ondersteuning voor het eenvoudig toevoegen van rag-mogelijkheden (Retrieval Augmented Generation) aan agents door AI-contextproviders toe te voegen aan de agent.
Zie gespreks- en geheugenoverzicht voor gespreks-/sessiepatronen naast het ophalen.
TextSearchProvider gebruiken
De TextSearchProvider klasse is een out-of-the-box-implementatie van een RAG-contextprovider.
Het kan eenvoudig worden gekoppeld aan een ChatClientAgent met behulp van de AIContextProviderFactory optie om RAG-mogelijkheden aan de agent te bieden.
De factory is een asynchrone functie die een contextobject en een annuleringstoken ontvangt.
// 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))
});
Hiervoor TextSearchProvider is een functie vereist die de zoekresultaten levert op basis van een query. Dit kan worden geïmplementeerd met behulp van elke zoektechnologie, bijvoorbeeld Azure AI Search of een zoekmachine op internet.
Hier volgt een voorbeeld van een mockzoekfunctie die vooraf gedefinieerde resultaten retourneert op basis van de query.
SourceName en SourceLink zijn optioneel, maar indien opgegeven wordt gebruikt door de agent om de bron van de informatie te citeren bij het beantwoorden van de vraag van de gebruiker.
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);
}
Opties voor TextSearchProvider
De TextSearchProvider kan worden aangepast via de TextSearchProviderOptions klasse. Hier volgt een voorbeeld van het maken van opties voor het uitvoeren van de zoekopdracht vóór elke aanroep van het model en het behouden van een kort lopend gesprekscontext.
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,
};
De TextSearchProvider klasse ondersteunt de volgende opties via de TextSearchProviderOptions klasse.
| Optie | Typologie | Description | Verstek |
|---|---|---|---|
| SearchTime | TextSearchProviderOptions.TextSearchBehavior |
Geeft aan wanneer de zoekopdracht moet worden uitgevoerd. Er zijn twee opties, telkens wanneer de agent wordt aangeroepen, of op aanvraag via functieoproepen. | TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke |
| FunctionToolName | string |
De naam van het weergegeven zoekprogramma wanneer u in de modus op aanvraag werkt. | "Zoeken" |
| FunctionToolDescription | string |
De beschrijving van het weergegeven zoekprogramma wanneer u in de modus op aanvraag werkt. | "Hiermee kunt u zoeken naar aanvullende informatie om de vraag van de gebruiker te beantwoorden." |
| ContextPrompt | string |
De contextprompt voorafgegaan aan resultaten bij gebruik in BeforeAIInvoke de modus. |
"## Aanvullende context\nHoud rekening met de volgende informatie uit brondocumenten bij het reageren op de gebruiker:" |
| CitationsPrompt | string |
De instructie toegevoegd na resultaten om bronvermeldingen aan te vragen wanneer deze in BeforeAIInvoke de modus wordt uitgevoerd. |
"Bronvermeldingen opnemen in het brondocument met documentnaam en koppeling als documentnaam en koppeling beschikbaar is." |
| ContextOpmaak | Func<IList<TextSearchProvider.TextSearchResult>, string> |
Optionele gemachtigde om de opmaak van de lijst met resultaten volledig aan te passen wanneer deze wordt uitgevoerd in BeforeAIInvoke de modus. Indien opgegeven ContextPrompt en CitationsPrompt worden genegeerd. |
null |
| RecentMessageMemoryLimit | int |
Het aantal recente gespreksberichten (zowel gebruiker als assistent) dat in het geheugen moet worden bewaard en moet worden opgenomen bij het samenstellen van de zoekinvoer voor BeforeAIInvoke zoekopdrachten. |
0 (uitgeschakeld) |
| RecentMessageRolesIncluded | List<ChatRole> |
De lijst met ChatRole typen waarop recente berichten moeten worden gefilterd bij het bepalen welke recente berichten moeten worden opgenomen bij het samenstellen van de zoekinvoer. |
ChatRole.User |
Aanbeveling
Zie de .NET-voorbeelden voor volledige runnable voorbeelden.
Agent Framework biedt ondersteuning voor het gebruik van VectorStore-verzamelingen van Semantic Kernel om RAG-mogelijkheden te bieden aan agents. Dit wordt bereikt via de brugfunctionaliteit waarmee Semantische kernelzoekfuncties worden geconverteerd naar Agent Framework-hulpprogramma's.
Een zoekprogramma maken vanuit VectorStore
De create_search_function methode van een Semantische Kernel VectorStore-verzameling retourneert een KernelFunction die kan worden geconverteerd naar een Agent Framework-hulpprogramma met behulp van .as_agent_framework_tool().
Gebruik de documentatie voor connectors voor vectorstores voor meer informatie over het instellen van verschillende vectoropslagverzamelingen.
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)
Belangrijk
Voor deze functie is versie 1.38 of hoger vereist semantic-kernel .
Zoekgedrag aanpassen
U kunt de zoekfunctie aanpassen met verschillende opties:
# 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}",
)
Zie de create_search_function voor meer informatie over de parameters die beschikbaar zijn voor.
Meerdere zoekfuncties gebruiken
U kunt meerdere zoekhulpprogramma's aan een agent opgeven voor verschillende kennisdomeinen:
# 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]
)
U kunt ook meerdere zoekfuncties maken uit dezelfde verzameling met verschillende beschrijvingen en parameters om gespecialiseerde zoekmogelijkheden te bieden:
# 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]
)
Met deze methode kan de agent de meest geschikte zoekstrategie kiezen op basis van de query van de gebruiker.
Volledig voorbeeld
# 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())
Ondersteunde VectorStore-connectors
Dit patroon werkt met elke Semantische Kernel VectorStore-connector, waaronder:
- Azure AI Search (
AzureAISearchCollection) - Qdrant (
QdrantCollection) - Dennenboom (
PineconeCollection) - Redis (
RedisCollection) - Weaviaat (
WeaviateCollection) - In-Memory (
InMemoryVectorStoreCollection) - En meer
Elke connector biedt dezelfde create_search_function methode die kan worden overbrugd naar Agent Framework-hulpprogramma's, zodat u de vectordatabase kunt kiezen die het beste bij uw behoeften past. Bekijk hier de volledige lijst.