Compartilhar via


Seleção de função contextual com agentes

Importante

Esse recurso está em estágio experimental. Os recursos estão em fase de desenvolvimento ativo e podem mudar significativamente antes de avançar para o estágio de pré-visualização ou de candidato a lançamento.

Visão geral

A Seleção de Função Contextual é uma funcionalidade avançada no Framework de Agentes de Kernel Semântico que permite que os agentes selecionem e anunciem dinamicamente apenas as funções mais relevantes com base no contexto atual da conversa. Em vez de expor todas as funções disponíveis ao modelo de IA, esse recurso usa Retrieval-Augmented Geração (RAG) para filtrar e apresentar apenas essas funções mais pertinentes à solicitação do usuário.

Essa abordagem aborda o desafio da seleção de função ao lidar com um grande número de funções disponíveis, em que os modelos de IA podem ter dificuldades para escolher a função apropriada, levando a confusão e desempenho abaixo do ideal.

Como funciona a seleção de função contextual

Quando um agente é configurado com a seleção de função contextual, ele aproveita um repositório de vetores e um gerador de inserção para corresponder semanticamente ao contexto de conversa atual (incluindo mensagens anteriores e entrada do usuário) com as descrições e nomes de funções disponíveis. As funções mais relevantes, até o limite especificado, são anunciadas para o modelo de IA para invocação.

Esse mecanismo é especialmente útil para agentes que têm acesso a um amplo conjunto de plug-ins ou ferramentas, garantindo que apenas ações contextualmente apropriadas sejam consideradas em cada etapa.

Exemplo de uso

O exemplo a seguir demonstra como um agente pode ser configurado para usar a seleção de função contextual. O agente é configurado para resumir as revisões do cliente, mas apenas as funções mais relevantes são anunciadas para o modelo de IA para cada invocação. O GetAvailableFunctions método inclui intencionalmente funções relevantes e irrelevantes para realçar os benefícios da seleção contextual.

// Create an embedding generator for function vectorization
var embeddingGenerator = new AzureOpenAIClient(new Uri("<endpoint>"), new ApiKeyCredential("<api-key>"))
    .GetEmbeddingClient("<deployment-name>")
    .AsIEmbeddingGenerator();

// Create kernel and register AzureOpenAI chat completion service
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion("<deployment-name>", "<endpoint>", "<api-key>");
    .Build();

// Create a chat completion agent
ChatCompletionAgent agent = new()
{
    Name = "ReviewGuru",
    Instructions = "You are a friendly assistant that summarizes key points and sentiments from customer reviews. For each response, list available functions.",
    Kernel = kernel,
    Arguments = new(new PromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new FunctionChoiceBehaviorOptions { RetainArgumentTypes = true }) })
};

// Create the agent thread and register the contextual function provider
ChatHistoryAgentThread agentThread = new();

agentThread.AIContextProviders.Add(
    new ContextualFunctionProvider(
        vectorStore: new InMemoryVectorStore(new InMemoryVectorStoreOptions() { EmbeddingGenerator = embeddingGenerator }),
        vectorDimensions: 1536,
        functions: AvailableFunctions(),
        maxNumberOfFunctions: 3, // Only the top 3 relevant functions are advertised
        loggerFactory: LoggerFactory
    )
);


// Invoke the agent
ChatMessageContent message = await agent.InvokeAsync("Get and summarize customer review.", agentThread).FirstAsync();
Console.WriteLine(message.Content);

// Output
/*
    Customer Reviews:
    -----------------
    1. John D. - ★★★★★
       Comment: Great product and fast shipping!
       Date: 2023-10-01

    Summary:
    --------
    The reviews indicate high customer satisfaction,
    highlighting product quality and shipping speed.

    Available functions:
    --------------------
    - Tools-GetCustomerReviews
    - Tools-Summarize
    - Tools-CollectSentiments
*/

IReadOnlyList<AIFunction> GetAvailableFunctions()
{
    // Only a few functions are directly related to the prompt; the majority are unrelated to demonstrate the benefits of contextual filtering.
    return new List<AIFunction>
    {
        // Relevant functions
        AIFunctionFactory.Create(() => "[ { 'reviewer': 'John D.', 'date': '2023-10-01', 'rating': 5, 'comment': 'Great product and fast shipping!' } ]", "GetCustomerReviews"),
        AIFunctionFactory.Create((string text) => "Summary generated based on input data: key points include customer satisfaction.", "Summarize"),
        AIFunctionFactory.Create((string text) => "The collected sentiment is mostly positive.", "CollectSentiments"),

        // Irrelevant functions
        AIFunctionFactory.Create(() => "Current weather is sunny.", "GetWeather"),
        AIFunctionFactory.Create(() => "Email sent.", "SendEmail"),
        AIFunctionFactory.Create(() => "The current stock price is $123.45.", "GetStockPrice"),
        AIFunctionFactory.Create(() => "The time is 12:00 PM.", "GetCurrentTime")
    };
}

Armazenamento Vetorial

O provedor foi projetado principalmente para trabalhar com repositórios de vetores na memória, que oferecem simplicidade. No entanto, se outros tipos de repositórios de vetores forem usados, é importante observar que a responsabilidade de lidar com a sincronização e consistência de dados recai sobre o aplicativo de hospedagem.

A sincronização é necessária sempre que a lista de funções é alterada ou quando a origem das inserções de função é modificada. Por exemplo, se um agente tiver inicialmente três funções (f1, f2, f3) vetorizadas e armazenadas em um repositório de vetores de nuvem e, posteriormente, f3 for removida da lista de funções do agente, o repositório de vetores deverá ser atualizado para refletir apenas as funções atuais que o agente tem (f1 e f2). A falha ao atualizar o repositório de vetores pode resultar em funções irrelevantes retornadas como resultados. Da mesma forma, se os dados usados para vetorização, como nomes de função, descrições etc. forem alterados, o repositório de vetores deverá ser limpo e repovoado com novas inserções com base nas informações atualizadas.

O gerenciamento da sincronização de dados em repositórios de vetores externos ou distribuídos pode ser complexo e propenso a erros, especialmente em aplicativos distribuídos em que diferentes serviços ou instâncias podem operar de forma independente e exigir acesso consistente aos mesmos dados. Por outro lado, o uso de um repositório na memória simplifica esse processo: quando a lista de funções ou a fonte de vetorização é alterada, o repositório na memória pode ser facilmente recriado com o novo conjunto de funções e suas inserções, garantindo consistência com esforço mínimo.

Especificando funções

O provedor de funções contextuais deve ser fornecido com uma lista de funções das quais pode selecionar as mais relevantes com base no contexto atual. Isso é feito fornecendo uma lista de funções para o functions parâmetro do ContextualFunctionProvider construtor.

Além das funções, você também deve especificar o número máximo de funções relevantes a serem retornadas usando o maxNumberOfFunctions parâmetro. Esse parâmetro determina quantas funções o provedor considerará ao selecionar as mais relevantes para o contexto atual. O número especificado não deve ser preciso; em vez disso, ele serve como um limite superior que depende do cenário específico.

Definir esse valor muito baixo pode impedir que o agente acesse todas as funções necessárias para um cenário, potencialmente levando à falha do cenário. Por outro lado, defini-lo muito alto pode sobrecarregar o agente com muitas funções, o que pode resultar em alucinações, consumo excessivo de token de entrada e desempenho abaixo do ideal.

// Create the provider with a list of functions and a maximum number of functions to return
ContextualFunctionProvider provider = new (
    vectorStore: new InMemoryVectorStore(new InMemoryVectorStoreOptions { EmbeddingGenerator = embeddingGenerator }),
    vectorDimensions: 1536,
    functions: [AIFunctionFactory.Create((string text) => $"Echo: {text}", "Echo"), <other functions>]
    maxNumberOfFunctions: 3 // Only the top 3 relevant functions are advertised
);

Opções de provedor de funções contextuais

O provedor pode ser configurado usando a classe, o ContextualFunctionProviderOptions que permite personalizar vários aspectos de como o provedor opera:

// Create options for the contextual function provider
ContextualFunctionProviderOptions options = new ()
{
    ...
};

// Create the provider with options
ContextualFunctionProvider provider = new (
    ...
    options: options // Pass the options
);

Tamanho do contexto

O tamanho do contexto determina quantas mensagens recentes de invocações de agente anteriores são incluídas ao formar o contexto para uma nova invocação. O provedor coleta todas as mensagens de invocações anteriores, até o número especificado e as prepara para as novas mensagens para formar o contexto.

Usar mensagens recentes junto com novas mensagens é especialmente útil para tarefas que exigem informações de etapas anteriores em uma conversa. Por exemplo, se um agente provisionar um recurso em uma invocação e implantá-lo na próxima, a etapa de implantação poderá acessar detalhes da etapa de provisionamento para obter informações de recursos provisionadas para a implantação.

O valor padrão para o número de mensagens recentes no contexto é 2, mas isso pode ser configurado conforme necessário especificando a NumberOfRecentMessagesInContext propriedade no ContextualFunctionProviderOptions:

ContextualFunctionProviderOptions options = new ()
{
    NumberOfRecentMessagesInContext = 1 // Only the last message will be included in the context
};

Valor de origem da inserção de contexto

Para executar a seleção de função contextual, o provedor precisa vetorizar o contexto atual para que ele possa ser comparado com as funções disponíveis no repositório de vetores. Por padrão, o provedor cria essa inserção de contexto concatenando todas as mensagens recentes e novas não vazias em uma única cadeia de caracteres, que é então vetorizada e usada para pesquisar funções relevantes.

Em alguns cenários, talvez você queira personalizar esse comportamento para:

  • Concentre-se em tipos de mensagem específicos (por exemplo, somente mensagens de usuário)
  • Excluir determinadas informações do contexto
  • Pré-processar ou resumir o contexto antes da vetorização (por exemplo, reescrever o prompt)

Para fazer isso, você pode atribuir um delegado personalizado a ContextEmbeddingValueProvider. Esse delegado recebe as mensagens recentes e novas e retorna um valor de cadeia de caracteres a ser usado como uma fonte para a inserção de contexto:

ContextualFunctionProviderOptions options = new()
{
    ContextEmbeddingValueProvider = async (recentMessages, newMessages, cancellationToken) =>
    {
        // Example: Only include user messages in the embedding
        var allUserMessages = recentMessages.Concat(newMessages)
            .Where(m => m.Role == "user")
            .Select(m => m.Content)
            .Where(content => !string.IsNullOrWhiteSpace(content));
        return string.Join("\n", allUserMessages);
    }
};

Personalizar a inserção de contexto pode melhorar a relevância da seleção de função, especialmente em cenários de agente complexos ou altamente especializados.

Valor da origem da inserção de função

O provedor precisa vetorizar cada função disponível para compará-la com o contexto e selecionar as mais relevantes. Por padrão, o provedor cria uma inserção de função concatenando o nome e a descrição da função em uma única cadeia de caracteres, que é então vetorizada e armazenada no repositório de vetores.

Você pode personalizar esse comportamento usando a EmbeddingValueProvider propriedade de ContextualFunctionProviderOptions. Essa propriedade permite que você especifique um retorno de chamada que recebe a função e um token de cancelamento e retorna uma cadeia de caracteres a ser usada como a origem da inserção de função. Isso é útil se você quiser:

  • Adicionar metadados de função adicionais à fonte de inserção
  • Pré-processar, filtrar ou reformatar as informações da função antes da vetorização
ContextualFunctionProviderOptions options = new()
{
    EmbeddingValueProvider = async (function, cancellationToken) =>
    {
        // Example: Use only the function name for embedding
        return function.Name;
    }
};

Personalizar o valor de origem da inserção de função pode melhorar a precisão da seleção de função, especialmente quando suas funções têm metadados avançados e relevantes para contexto ou quando você deseja concentrar a pesquisa em aspectos específicos de cada função.

Próximas etapas

Explorar os exemplos de seleção de função contextual

Em breve

Mais informações em breve.

Em breve

Mais informações em breve.