RAG

Microsoft代理框架支持通过上下文提供程序检索扩充生成(RAG),这些上下文提供程序在模型调用和搜索工具之前添加检索的内容,以便模型按需检索基于数据。

有关对话/会话模式以及检索,请参阅 对话和内存概述。 有关特定于服务的设置,请参阅 Azure AI 搜索、Microsoft Foundry 和 Neo4j。

使用 TextSearchProvider

该 TextSearchProvider 类是 RAG 上下文提供程序的现用实现。 它支持不同的操作模式,例如,使用聊天历史记录对每个代理执行搜索,或播发用于执行搜索的广告功能工具。

它可以轻松地附加到 ChatClientAgent 使用 AIContextProviders 此选项。

// Configure the options for the TextSearchProvider.
TextSearchProviderOptions textSearchOptions = new()
{
    SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke,
};

// Create the AI agent with the TextSearchProvider.
AIAgent agent = azureOpenAIClient
    .GetChatClient(deploymentName)
    .AsAIAgent(new ChatClientAgentOptions
    {
        ChatOptions = new() { Instructions = "You are a helpful support specialist. Answer questions using the provided context and cite the source document when available." },
        AIContextProviders = [new TextSearchProvider(SearchAdapter, textSearchOptions)]
    });

需要 TextSearchProvider 一个提供给定查询的搜索结果的函数。 这可以使用任何搜索技术来实现,例如Azure AI 搜索或 Web 搜索引擎。

Tip

有关如何将矢量存储用于搜索结果的详细信息,请参阅 矢量存储集成 。

下面是基于查询返回预定义结果的模拟搜索函数的示例。 SourceName 并且 SourceLink 是可选的,但是如果代理将在回答用户的问题时引用信息的来源,则为可选。

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 选项

TextSearchProvider可以通过类自定义TextSearchProviderOptions该类。 下面是创建选项以在每个模型调用之前运行搜索的选项,并保留一个简短的聊天历史记录滚动窗口进行搜索的示例。

TextSearchProviderOptions textSearchOptions = new()
{
    // Run the search prior to every model invocation and keep a short rolling window of chat history for searches.
    SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke,
    RecentMessageMemoryLimit = 6,
};

该 TextSearchProvider 类通过 TextSearchProviderOptions 类支持以下选项。

选项 类型 说明 Default
SearchTime TextSearchProviderOptions.TextSearchBehavior 指示何时应执行搜索。 每次运行代理或通过函数调用按需运行时,都有两个选项。 TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke
FunctionToolName string 在按需模式下运行时公开的搜索工具的名称。 “搜索”
FunctionToolDescription string 在按需模式下运行时公开的搜索工具的说明。 “允许搜索其他信息来帮助回答用户问题。
ContextPrompt string 前缀为结果的上下文提示。 “## 其他上下文\n响应用户时,请考虑源文档中的以下信息:
CitationsPrompt string 结果后追加的说明以请求引文。 “如果文档名称和链接可用,请包含指向源文档的引文和链接。
ContextFormatter Func<IList<TextSearchProvider.TextSearchResult>, string> 用于完全自定义结果列表格式的可选委托。 如果提供, ContextPrompt 则 CitationsPrompt 忽略。 null
RecentMessageMemoryLimit int 要保留在内存中的最近聊天消息(用户和助手)的数量,并在构造搜索输入 BeforeAIInvoke 时包括。 0 (已禁用)
RecentMessageRolesIncluded List<ChatRole> 在决定构造搜索输入时要包含哪些最近消息时筛选最近消息的类型列表 ChatRole 。 ChatRole.User

Tip

有关完整的可运行示例,请参阅 .NET 示例 。

代理框架提供本机矢量存储协定和 create_vector_search_tool()。 帮助程序将任何 SupportsVectorSearch 实现转换为函数工具,以便模型可以在其回答之前检索地面数据。

创建本机矢量搜索工具

首先,定义矢量存储模型、创建集合并加载其记录。 以下示例与 一 起使用 ,但可以提供实现 的任何本机 Agent Framework 集合。 然后,它会向模型公开可选的类别和分级筛选器,将每个结果映射到地面文本,并指示代理在回答之前进行搜索:

import asyncio
import json
import os
from typing import Annotated, Any, Literal
from urllib.request import urlopen

from agent_framework import (
    Agent,
    Filter,
    FilterGroup,
    InMemoryCollection,
    Param,
    VectorStoreField,
    create_vector_search_tool,
    vectorstoremodel,
)
from agent_framework.openai import OpenAIChatClient, OpenAIEmbeddingClient
from dotenv import load_dotenv
async def main() -> None:
    """Create an in-memory hotel search tool and give it to an agent."""
    api_key = os.environ["OPENAI_API_KEY"]
    collection: InMemoryCollection[str, Hotel] = InMemoryCollection(
        Hotel,
        embedding_generator=OpenAIEmbeddingClient(
            model="text-embedding-3-small",
            api_key=api_key,
        ),
    )
    await collection.ensure_collection_exists()

    # 1. Load the hotel records.
    hotels = await asyncio.to_thread(load_hotels)
    await collection.upsert(hotels)

    # 2. Param values become optional model-visible filter arguments.
    # When the allowed values are known, use Literal so the tool schema exposes
    # them as an enum.
    category = Param(
        "category",
        Literal["Boutique", "Budget", "Extended-Stay", "Luxury", "Resort and Spa", "Suite"],
        description="Only return hotels in this category.",
    )
    min_rating = Param(
        "min_rating",
        float,
        description="The minimum guest rating.",
        minimum=0,
        maximum=5,
    )
    tool = create_vector_search_tool(
        collection,
        description="Search the hotel dataset, optionally filtering by category and minimum rating.",
        filter=FilterGroup(
            "and",
            (
                Filter("category", "eq", category),
                Filter("rating", "gte", min_rating),
            ),
        ),
        result_mapper=lambda result: (
            f"(hotel_id: {result['record'].hotel_id}) {result['record'].hotel_name} "
            f"(rating {result['record'].rating}) - {result['record'].description}. "
            f"Address: {result['record'].address.city}, {result['record'].address.country}."
        ),
    )

    # 3. The agent chooses whether to supply the exposed category and minimum-rating filters.
    async with Agent(
        client=OpenAIChatClient(
            model="gpt-5.4-nano",
            api_key=api_key,
        ),
        name="HotelAgent",
        instructions=(
            "Always use the search tool to answer hotel questions. "
            "Use category and minimum rating filters when the request provides them. "
            "Include the hotel_id in the answer."
        ),
        tools=[tool],
    ) as agent:
        result = await agent.run("Find a resort and spa with a rating of at least 4.")
        print(result)

完整示例定义模型, Hotel 并在显示集合设置之前加载源记录。 在运行之前进行设置 OPENAI_API_KEY 。

自定义搜索行为

使用以下选项进行配置 create_vector_search_tool() :

选项 Purpose
name 设置向模型公开的函数名称。 添加多个搜索工具时,请使用唯一名称。
description 说明模型何时和为何应使用该工具。
approval_mode 将工具审批设置为 always_require 或 never_require。
search_type vector选择或keyword_hybrid搜索。 集合必须支持所选模式。
top 和 skip 设置固定分页值或使用模型提供的类型化 Param 值。
filter 应用可移植 Filter 或 FilterGroup。 筛选器可以包含 Param 工具架构中公开的类型化值。
result_mapper 将每个 SearchResponse 模型转换为文本或多模式 Content 。

生成的工具始终包含一个 query 字符串。 Param筛选器topskip或设置中的任何值都将成为其他已验证的工具参数。 使用 Literal 和数值约束使模型提供的值保持在应用程序接受的范围内。

可以为不同的集合或搜索模式创建多个工具。 为每个工具指定一个不同的 name 工具, description 以便模型可以选择适当的知识源。

选择本机向量存储

本机Python实现可用于内存中搜索、Azure AI 搜索、具有 pgvector、Qdrant 和 Redis 的 PostgreSQL。 它们的搜索模式、包生命周期、安装命令和限制有所不同。 请参阅 Vector 存储集成 以选择和配置实现。 该页还标识当前只有单独的语义内核连接器的数据库。

注释

Go 语言对该功能的支持即将推出。 有关最新状态,请参阅 Agent Framework Go 存储库 。

图谱 RAG

有关使用图形遍历扩充搜索和 Cypher 查询的 GraphRAG,请参阅 Neo4j GraphRAG 提供程序。

后续步骤