Usando plug-ins para RAG (Recuperação de Geração Aumentada)

Geralmente, 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 plug-ins para recuperar dados de fontes externas.

Ao considerar plug-ins para RAG (Recuperação de Geração Aumentada), você deve fazer duas perguntas a si mesmo:

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

Ao desenvolver plug-ins para RAG (Recuperação de Geração Aumentada), você pode usar dois tipos de pesquisa: pesquisa semântica e pesquisa clássica.

A pesquisa semântica utiliza bancos de dados de vetor para entender e recuperar informações com base no significado e no contexto da consulta, em vez de apenas correspondência de palavras-chave. Esse método permite que o mecanismo de pesquisa compreenda as nuances da linguagem, como sinônimos, conceitos relacionados e a intenção geral por trás de uma consulta.

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

Ao fornecer uma LLM com uma função de pesquisa semântica, normalmente você só precisa definir uma função com uma única consulta de pesquisa. Em seguida, o LLM usará essa função para recuperar as informações necessárias. Veja abaixo um exemplo de uma função de pesquisa semântica que usa Pesquisa de IA do Azure  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 com base em critérios, depende da filtragem e da correspondência de termos ou valores exatos em um conjunto de dados. Ele é 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 uma ID de cliente específica ou recuperar produtos dentro de uma faixa de preço e categoria específicas, a pesquisa clássica fornecerá resultados precisos e confiáveis. A pesquisa clássica, no entanto, é limitada por sua incapacidade de entender o contexto ou as variações na linguagem.

Dica

Na maioria dos casos, seus serviços existentes já dão suporte à 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.

Por exemplo, um plug-in que recupera informações do cliente de um sistema CRM usando a pesquisa clássica. Aqui, a IA simplesmente precisa chamar a GetCustomerInfoAsync função com uma ID do 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 pesquisa semântica provavelmente seria impossível ou impraticável devido à natureza não determinística das consultas semânticas.

Quando usar cada

Escolher entre pesquisa semântica e clássica depende da natureza da consulta. É ideal para ambientes com conteúdo pesado, como bases de dados de conhecimento e suporte ao cliente, em que 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 clientes em um repositório de comércio eletrônico pode usar a pesquisa semântica para entender as consultas de usuário e a pesquisa clássica para filtrar produtos com base em atributos específicos, como preço, marca ou disponibilidade.

Veja abaixo um exemplo de um plug-in que combina pesquisa semântica e clássica para recuperar informações do produto 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 dinâmica de dados versus recuperação de dados pré-carregados

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

Recuperação dinâmica de dados

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

Veja abaixo um exemplo de um bate-papo entre um usuário e um agente de IA em que a recuperação dinâmica de dados é necessária.

Função Message
🔵 Utilizador Pode me contar 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 Certeza! Temos Cloud Nine e Best Sleep
🔵 Utilizador Qual é a diferença entre elas?
🔴 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 $1000. Best Sleep é feito de látex e custa $1200.

Recuperação de dados pré-carregados

A recuperação de dados estáticos envolve a busca de dados de fontes externas e sempre o fornece 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 são alterados com frequência.

Por exemplo, um agente que sempre responde 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 chat. Isso permite que o agente gere respostas sobre o clima sem desperdiçar 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);

Mantendo os dados seguros

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

Strategy 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 vetor, verifique se já existe um para o serviço que tem os dados necessários. Reutilizando serviços existentes, você pode evitar duplicar conteúdo confidencial, aproveitar os controles de acesso existentes e usar mecanismos de filtragem existentes que retornam apenas dados aos quais o usuário tem acesso.
Armazenar a referência em bancos de dados vetoriais em vez do conteúdo Em vez de duplicar conteúdo confidencial para bancos de dados 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óximas Etapas 

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