Использование подключаемых модулей для извлечения дополненного поколения (RAG)

Часто агенты ИИ должны получать данные из внешних источников, чтобы создать заземленные ответы. Без этого дополнительного контекста агенты ИИ могут галлюцинировать или предоставлять неверные сведения. Для решения этой проблемы можно использовать подключаемые модули для получения данных из внешних источников.

При рассмотрении подключаемых модулей для получения дополненного поколения (RAG) следует задать себе два вопроса:

  1. Как вы (или ваш агент ИИ) будете искать необходимые данные? Вам нужен семантический поиск или классический поиск?
  2. Вы уже знаете, что данные, необходимые агенту ИИ, требуются заранее (предварительно полученные данные) или агент ИИ должен динамически извлекать данные?
  3. Как защитить данные и предотвратить перенахлажение конфиденциальной информации?

При разработке подключаемых модулей для получения дополненного поколения (RAG) можно использовать два типа поиска: семантический поиск и классический поиск.

Семантический поиск использует векторные базы данных для понимания и извлечения информации на основе смысла и контекста запроса, а не просто соответствующих ключевых слов. Этот метод позволяет поисковой системе понять нюансы языка, такие как синонимы, связанные понятия и общее намерение запроса.

Семантический поиск в средах, где запросы пользователей являются сложными, открытыми или требуют более глубокого понимания содержимого. Например, поиск "лучших смартфонов для фотографии" даст результаты, которые рассматривают контекст функций фотографии на смартфонах, а не просто совпадают со словами "лучшие", "смартфоны" и "фотография".

При предоставлении LLM с семантической функции поиска обычно необходимо определить функцию с одним поисковым запросом. Затем LLM будет использовать эту функцию для получения необходимых сведений. Ниже приведен пример семантической функции поиска, которая использует поиск ИИ Azure для поиска документов, аналогичных заданному запросу.

using System.ComponentModel;
using System.Text.Json.Serialization;
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Models;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Embeddings;

public class InternalDocumentsPlugin
{
    private readonly ITextEmbeddingGenerationService _textEmbeddingGenerationService;
    private readonly SearchIndexClient _indexClient;

    public AzureAISearchPlugin(ITextEmbeddingGenerationService textEmbeddingGenerationService, SearchIndexClient indexClient)
    {
        _textEmbeddingGenerationService = textEmbeddingGenerationService;
        _indexClient = indexClient;
    }

    [KernelFunction("Search")]
    [Description("Search for a document similar to the given query.")]
    public async Task<string> SearchAsync(string query)
    {
        // Convert string query to vector
        ReadOnlyMemory<float> embedding = await _textEmbeddingGenerationService.GenerateEmbeddingAsync(query);

        // Get client for search operations
        SearchClient searchClient = _indexClient.GetSearchClient("default-collection");

        // Configure request parameters
        VectorizedQuery vectorQuery = new(embedding);
        vectorQuery.Fields.Add("vector");

        SearchOptions searchOptions = new() { VectorSearch = new() { Queries = { vectorQuery } } };

        // Perform search request
        Response<SearchResults<IndexSchema>> response = await searchClient.SearchAsync<IndexSchema>(searchOptions);

        // Collect search results
        await foreach (SearchResult<IndexSchema> result in response.Value.GetResultsAsync())
        {
            return result.Document.Chunk; // Return text from first result
        }

        return string.Empty;
    }

    private sealed class IndexSchema
    {
        [JsonPropertyName("chunk")]
        public string Chunk { get; set; }

        [JsonPropertyName("vector")]
        public ReadOnlyMemory<float> Vector { get; set; }
    }
}

Классический поиск, также известный как поиск на основе атрибутов или критериев, зависит от фильтрации и сопоставления точных терминов или значений в наборе данных. Это особенно эффективно для запросов базы данных, поиска инвентаризации и любой ситуации, когда требуется фильтрация по определенным атрибутам.

Например, если пользователь хочет найти все заказы, размещенные определенным идентификатором клиента или получить продукты в определенном ценовом диапазоне и категории, классический поиск предоставляет точные и надежные результаты. Однако классический поиск ограничен его неспособностью понять контекст или вариации языка.

Совет

В большинстве случаев существующие службы уже поддерживают классический поиск. Прежде чем реализовать семантический поиск, рассмотрите, могут ли существующие службы предоставить необходимый контекст для агентов ИИ.

Например, подключаемый модуль, который извлекает сведения о клиентах из системы CRM с помощью классического поиска. Здесь ИИ просто должен вызвать GetCustomerInfoAsync функцию с идентификатором клиента, чтобы получить необходимые сведения.

using System.ComponentModel;
using Microsoft.SemanticKernel;

public class CRMPlugin
{
    private readonly CRMService _crmService;

    public CRMPlugin(CRMService crmService)
    {
        _crmService = crmService;
    }

    [KernelFunction("GetCustomerInfo")]
    [Description("Retrieve customer information based on the given customer ID.")]
    public async Task<Customer> GetCustomerInfoAsync(string customerId)
    {
        return await _crmService.GetCustomerInfoAsync(customerId);
    }
}

Достижение того же функциональных возможностей поиска семантического поиска, скорее всего, было бы невозможным или непрактичным из-за недетерминированного характера семантических запросов.

Когда следует использовать каждый

Выбор между семантической и классической поиск зависит от характера запроса. Это идеально подходит для сред с большим количеством содержимого, таких как база знаний и поддержка клиентов, где пользователи могут задавать вопросы или искать продукты с использованием естественного языка. Классический поиск, с другой стороны, следует использовать, когда точность и точные совпадения важны.

В некоторых сценариях может потребоваться объединить оба подхода для предоставления комплексных возможностей поиска. Например, чат-бот, помогающий клиентам в магазине электронной коммерции, может использовать семантический поиск для понимания запросов пользователей и классического поиска для фильтрации продуктов на основе определенных атрибутов, таких как цена, бренд или доступность.

Ниже приведен пример подключаемого модуля, объединяющего семантический и классический поиск для получения сведений о продукте из базы данных электронной коммерции.

using System.ComponentModel;
using Microsoft.SemanticKernel;

public class ECommercePlugin
{
    [KernelFunction("search_products")]
    [Description("Search for products based on the given query.")]
    public async Task<IEnumerable<Product>> SearchProductsAsync(string query, ProductCategories category = null, decimal? minPrice = null, decimal? maxPrice = null)
    {
        // Perform semantic and classic search with the given parameters
    }
}

Динамическое и предварительное получение данных

При разработке подключаемых модулей для получения дополненного поколения (RAG) необходимо также учитывать, является ли процесс извлечения данных статическим или динамическим. Это позволяет оптимизировать производительность агентов ИИ, извлекая данные только при необходимости.

Динамическое извлечение данных

В большинстве случаев запрос пользователя определяет данные, необходимые агенту ИИ. Например, пользователь может запросить разницу между двумя различными продуктами. Затем агент ИИ должен динамически извлекать сведения о продукте из базы данных или API, чтобы создать ответ с помощью вызова функции. Это было бы нецелесообразно, чтобы предварительно получить все возможные сведения о продукте заранее и дать ему агенту ИИ.

Ниже приведен пример обратного и вперед чата между пользователем и агентом ИИ, где требуется динамическое получение данных.

Роль Сообщение
🔵Пользователь Можете ли вы рассказать мне о лучших матрасах?
🔴Помощник (вызов функции) Products.Search("mattresses")
🟢Инструмент [{"id": 25323, "name": "Cloud Nine"},{"id": 63633, "name": "Best Sleep"}]
🔴Помощник Конечно. У нас есть облако девять и лучший сон
🔵Пользователь В чем разница между ними?
🔴Помощник (вызов функции) Products.GetDetails(25323) Products.GetDetails(63633)
🟢Инструмент { "id": 25323, "name": "Cloud Nine", "price": 1000, "material": "Memory foam" }
🟢Инструмент { "id": 63633, "name": "Best Sleep", "price": 1200, "material": "Latex" }
🔴Помощник Cloud Nine состоит из пены памяти и стоит $ 1000. Лучший сон сделан из латекса и стоит $ 1200.

Получение предварительно подготовленных данных

Получение статических данных включает получение данных из внешних источников и всегда предоставляет их агенту ИИ. Это полезно, если данные требуются для каждого запроса или когда данные относительно стабильны и часто не изменяются.

Например, агент, который всегда отвечает на вопросы о локальной погоде. Предположим, что у вас есть WeatherPlugin, вы можете предварительно получить данные о погоде из API погоды и предоставить его в журнале чата. Это позволяет агенту создавать ответы о погоде, не запрашивая время, запрашивая данные из API.

using System.Text.Json;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;

IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(deploymentName, endpoint, apiKey);
builder.Plugins.AddFromType<WeatherPlugin>();
Kernel kernel = builder.Build();

// Get the weather
var weather = await kernel.Plugins.GetFunction("WeatherPlugin", "get_weather").InvokeAsync(kernel);

// Initialize the chat history with the weather
ChatHistory chatHistory = new ChatHistory("The weather is:\n" + JsonSerializer.Serialize(weather));

// Simulate a user message
chatHistory.AddUserMessage("What is the weather like today?");

// Get the answer from the AI agent
IChatCompletionService chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
var result = await chatCompletionService.GetChatMessageContentAsync(chatHistory);

Обеспечение безопасности данных

При получении данных из внешних источников важно убедиться, что данные защищены и что конфиденциальная информация не предоставляется. Чтобы предотвратить чрезмерное использование конфиденциальной информации, можно использовать следующие стратегии:

Стратегия Description
Использование маркера проверки подлинности пользователя Избегайте создания субъектов-служб, используемых агентом ИИ для получения сведений для пользователей. Это затрудняет проверку наличия у пользователя доступа к полученной информации.
Избегайте повторного восстановления служб поиска Прежде чем создавать новую службу поиска с помощью векторной базы данных, проверьте, существует ли служба с необходимыми данными. Повторно используя существующие службы, можно избежать дедупликации конфиденциального содержимого, использовать существующие элементы управления доступом и использовать существующие механизмы фильтрации, которые возвращают только данные, к которым пользователь имеет доступ.
Хранение ссылок в векторных DBS вместо содержимого Вместо дедупликации конфиденциального содержимого в векторные DBS можно хранить ссылки на фактические данные. Для доступа к этой информации пользователю сначала необходимо использовать маркер проверки подлинности для получения реальных данных.

Следующие шаги

Теперь, когда вы научитесь создавать агенты ИИ с данными из внешних источников, теперь можно узнать, как использовать агенты ИИ для автоматизации бизнес-процессов. Дополнительные сведения см. в статье об использовании функций автоматизации задач.