Partilhar via


Usando plugins para Geração Aumentada de Recuperação (RAG)

Muitas vezes, seus agentes de IA devem recuperar dados de fontes externas para gerar respostas fundamentadas. Sem esse contexto adicional, seus agentes de IA podem alucinar ou fornecer informações incorretas. Para resolver isso, você pode usar plugins para recuperar dados de fontes externas.

Ao considerar plugins para Retrieval Augmented Generation (RAG), você deve se fazer duas perguntas:

  1. Como você (ou seu agente de IA) "pesquisará" os dados necessários? Precisa de pesquisa semântica ou pesquisa clássica?
  2. Você já conhece os dados que o agente de IA precisa com antecedência (dados pré-buscados) ou o agente de IA precisa recuperar os dados dinamicamente?
  3. Como você manterá seus dados seguros e evitará o compartilhamento excessivo de informações confidenciais?

Ao desenvolver plugins para Retrieval Augmented Generation (RAG), você pode usar dois tipos de pesquisa: pesquisa semântica e pesquisa clássica.

A pesquisa semântica utiliza bancos de dados vetoriais para entender e recuperar informações com base no significado e no contexto da consulta, em vez de apenas combinar palavras-chave. Este método permite que o motor de busca compreenda as nuances da linguagem, tais como sinónimos, conceitos relacionados e a intenção geral por detrás de uma consulta.

A pesquisa semântica se destaca em ambientes onde as consultas do usuário são complexas, abertas ou exigem uma compreensão mais profunda do conteúdo. Por exemplo, pesquisar por "melhores smartphones para fotografia" produziria resultados que consideram o contexto dos recursos de fotografia em smartphones, em vez de apenas corresponder às palavras "melhor", "smartphones" e "fotografia".

Ao fornecer um LLM com uma função de pesquisa semântica, normalmente você só precisa definir uma função com uma única consulta de pesquisa. O LLM usará essa função para recuperar as informações necessárias. Abaixo está um exemplo de uma função de pesquisa semântica que usa o Azure AI Search para localizar documentos semelhantes a uma determinada consulta.

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; }
    }
}

A pesquisa clássica, também conhecida como pesquisa baseada em atributos ou critérios, depende da filtragem e da correspondência de termos ou valores exatos dentro de um conjunto de dados. É particularmente eficaz para consultas de banco de dados, pesquisas de inventário e qualquer situação em que a filtragem por atributos específicos seja necessária.

Por exemplo, se um usuário quiser encontrar todos os pedidos feitos por um ID de cliente específico ou recuperar produtos dentro de uma faixa de preço e categoria específica, a pesquisa clássica fornece resultados precisos e confiáveis. A pesquisa clássica, no entanto, é limitada por sua incapacidade de entender o contexto ou variações na linguagem.

Gorjeta

Na maioria dos casos, os seus serviços existentes já suportam a pesquisa clássica. Antes de implementar uma pesquisa semântica, considere se seus serviços existentes podem fornecer o contexto necessário para seus agentes de IA.

Tomemos, por exemplo, um plugin que recupera informações do cliente de um sistema CRM usando a pesquisa clássica. Aqui, a IA simplesmente precisa chamar a função com um ID de GetCustomerInfoAsync cliente para recuperar as informações necessárias.

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);
    }
}

Alcançar a mesma funcionalidade de pesquisa com a pesquisa semântica provavelmente seria impossível ou impraticável devido à natureza não determinística das consultas semânticas.

Quando usar cada

A escolha entre pesquisa semântica e clássica depende da natureza da consulta. É ideal para ambientes com muito conteúdo, como bases de conhecimento e suporte ao cliente, onde os usuários podem fazer perguntas ou procurar produtos usando linguagem natural. A pesquisa clássica, por outro lado, deve ser empregada quando a precisão e as correspondências exatas são importantes.

Em alguns cenários, talvez seja necessário combinar ambas as abordagens para fornecer recursos de pesquisa abrangentes. Por exemplo, um chatbot que auxilia os clientes em uma loja de comércio eletrônico pode usar a pesquisa semântica para entender as consultas do usuário e a pesquisa clássica para filtrar produtos com base em atributos específicos, como preço, marca ou disponibilidade.

Abaixo está um exemplo de um plugin que combina pesquisa semântica e clássica para recuperar informações de produtos de um banco de dados de comércio eletrônico.

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
    }
}

Recuperação de dados dinâmica vs pré-buscada

Ao desenvolver plugins para Geração Aumentada de Recuperação (RAG), você também deve considerar se o processo de recuperação de dados é estático ou dinâmico. Isso permite que você otimize o desempenho de seus agentes de IA, recuperando dados apenas quando necessário.

Recuperação dinâmica de dados

Na maioria dos casos, a consulta do usuário determinará os dados que o agente de IA precisa recuperar. Por exemplo, um usuário pode perguntar a diferença entre dois produtos diferentes. O agente de IA precisaria então recuperar dinamicamente as informações do produto de um banco de dados ou API para gerar uma resposta usando chamada de função. Seria impraticável pré-buscar todas as informações possíveis do produto com antecedência e entregá-las ao agente de IA.

Abaixo está um exemplo de um bate-papo de ida e volta entre um usuário e um agente de IA onde a recuperação dinâmica de dados é necessária.

Role Mensagem
🔵Utilizador Você pode me falar sobre os melhores colchões?
🔴Assistente (chamada de função) Products.Search("mattresses")
🟢Ferramenta [{"id": 25323, "name": "Cloud Nine"},{"id": 63633, "name": "Best Sleep"}]
🔴Assistente Claro! Temos Cloud Nine e Best Sleep
🔵Utilizador Qual é a diferença entre ambas?
🔴Assistente (chamada de função) Products.GetDetails(25323) Products.GetDetails(63633)
🟢Ferramenta { "id": 25323, "name": "Cloud Nine", "price": 1000, "material": "Memory foam" }
🟢Ferramenta { "id": 63633, "name": "Best Sleep", "price": 1200, "material": "Latex" }
🔴Assistente Cloud Nine é feito de espuma de memória e custa US $ 1000. Best Sleep é feito de látex e custa US $ 1200.

Recuperação de dados pré-buscados

A recuperação de dados estáticos envolve buscar dados de fontes externas e sempre fornecê-los ao agente de IA. Isso é útil quando os dados são necessários para cada solicitação ou quando os dados são relativamente estáveis e não mudam com frequência.

Tomemos como exemplo um agente que sempre responde a perguntas sobre o clima local. Supondo que você tenha um WeatherPlugin, você pode pré-buscar dados meteorológicos de uma API meteorológica e fornecê-los no histórico de bate-papo. Isso permite que o agente gere respostas sobre o clima sem perder tempo solicitando os dados da 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);

Manter os dados seguros

Ao recuperar dados de fontes externas, é importante garantir que os dados estejam seguros e que as informações sensíveis não sejam expostas. Para evitar o compartilhamento excessivo de informações confidenciais, você pode usar as seguintes estratégias:

Estratégia Description
Usar o token de autenticação do usuário Evite criar entidades de serviço usadas pelo agente de IA para recuperar informações para os usuários. Isso dificulta a verificação de que um usuário tem acesso às informações recuperadas.
Evite recriar serviços de pesquisa Antes de criar um novo serviço de pesquisa com um banco de dados vetorial, verifique se já existe um para o serviço que possui os dados necessários. Ao reutilizar serviços existentes, você pode evitar a duplicação de conteúdo confidencial, aproveitar os controles de acesso existentes e usar mecanismos de filtragem existentes que retornam apenas os dados aos quais o usuário tem acesso.
Armazenar referência em DBs vetoriais em vez de conteúdo Em vez de duplicar conteúdo confidencial para DBs vetoriais, você pode armazenar referências aos dados reais. Para que um usuário acesse essas informações, seu token de autenticação deve primeiro ser usado para recuperar os dados reais.

Próximos passos

Agora que você já sabe como fundamentar seus agentes de IA com dados de fontes externas, agora você pode aprender como usar agentes de IA para automatizar processos de negócios. Para saber mais, consulte Usando funções de automação de tarefas.