Criando ferramentas em C# com Microsoft.Extensions.AI, Ollama e injeção de dependência
![]() |
Este artigo apresenta o funcionamento do Tool Calling em aplicações .NET, utilizando C#, Microsoft.Extensions.AI, Ollama e injeção de dependência. O exemplo demonstra como disponibilizar ferramentas de consulta de pedidos ao modelo de IA, mantendo a execução e as regras de segurança sob responsabilidade da aplicação. (Gerado com ajuda da IA, ajustado e revisado por Macoratti) |
Este artigo não pretende construir um agente de IA completo, com todos os recursos que uma solução de agentes pode oferecer. Não vamos implementar planejamento autônomo sofisticado, memória persistente, coordenação de múltiplos agentes, workflows complexos ou mecanismos avançados de recuperação de conhecimento.
O objetivo é didático: demonstrar como o Tool Calling funciona no contexto de uma aplicação que utiliza um modelo de IA como parte de um agente. Vamos mostrar como uma aplicação .NET disponibiliza ferramentas ao modelo, como o modelo solicita sua execução e como o resultado é devolvido para que ele produza uma resposta. Também vamos organizar o exemplo com Generic Host e injeção de dependência, aproximando-o de uma aplicação .NET real.
Imagine um assistente de atendimento. Um cliente pergunta: “Onde está meu pedido?”. Um chatbot que apenas gera texto não consegue consultar o pedido por conta própria. Para responder com informações reais, ele precisa solicitar uma operação que consulte o sistema, verificar o status e utilizar o resultado na resposta.
Um modelo de linguagem não deve receber acesso irrestrito ao banco de dados nem executar qualquer método C# escolhido pelo usuário. A aplicação precisa disponibilizar capacidades específicas, controlar sua execução e aplicar as regras de segurança.
É nesse cenário que entra o Tool Calling, também chamado de chamada de ferramentas. O modelo identifica quando precisa de uma capacidade externa e solicita sua execução por meio de uma chamada estruturada. A aplicação é responsável por localizar e executar a ferramenta permitida, validar os dados e devolver o resultado ao modelo.
Neste artigo, utilizaremos C#, Microsoft.Extensions.AI e Ollama. O exemplo consulta o status de um pedido. A mesma abordagem pode ser aplicada a consultas de estoque, sistemas corporativos, APIs, documentos e outros serviços.
Um Large Language Model (LLM) é um modelo treinado para compreender e gerar linguagem. Ele produz respostas com base no contexto recebido e no conhecimento adquirido durante o treinamento. Por padrão, ele não conhece os dados atuais de um sistema empresarial nem executa diretamente as operações internas da aplicação.
O Tool Calling é a capacidade de um modelo solicitar a execução de uma ferramenta previamente disponibilizada pela aplicação. Uma ferramenta pode representar um método C#, uma consulta encapsulada em um serviço, uma chamada a uma API ou uma capacidade fornecida por um servidor MCP.
O modelo recebe a descrição das ferramentas disponíveis. Quando considera uma delas adequada, retorna uma solicitação estruturada com o nome da ferramenta e os argumentos. A aplicação executa a operação autorizada e devolve o resultado ao modelo.
A solicitação do modelo não é, por si só, a execução do método C#. A invocação é responsabilidade da aplicação. Em Microsoft.Extensions.AI, AIFunction representa uma função que pode ser descrita ao modelo e invocada; AIFunctionFactory cria essas representações a partir de métodos .NET; e FunctionInvokingChatClient automatiza o ciclo de invocação para os clientes compatíveis.
Um agente de IA é uma solução que utiliza um modelo e pode combinar ferramentas com outros recursos para alcançar um objetivo. Dependendo do caso, pode manter estado e histórico, planejar etapas, selecionar ferramentas, avaliar resultados, recuperar conhecimento ou colaborar com outros agentes.
Portanto, Tool Calling não é sinônimo de agente de IA. É uma capacidade importante que um agente pode utilizar. O exemplo deste artigo é um agente didático e intencionalmente limitado: ele recebe uma solicitação, pode consultar uma ferramenta e formula uma resposta com o resultado.
Um fluxo típico pode ser descrito nas seguintes etapas:
• Registro das ferramentas: a aplicação define quais funções podem ser solicitadas pelo modelo.
• Envio da solicitação: a aplicação envia a conversa e as descrições das ferramentas ao modelo.
• Decisão do modelo: o modelo decide se responde diretamente ou solicita uma ferramenta.
• Solicitação estruturada: se precisar de uma ferramenta, o modelo retorna seu nome e os argumentos.
• Execução controlada: a aplicação valida e executa a ferramenta correspondente.
• Retorno do resultado: a aplicação inclui o resultado na conversa e envia uma nova solicitação ao modelo.
• Resposta final: o modelo utiliza o resultado da ferramenta para produzir a resposta ao usuário.
Esse ciclo pode se repetir. O modelo pode solicitar uma segunda ferramenta depois de receber o primeiro resultado. Alguns modelos e provedores também oferecem suporte a chamadas paralelas. O comportamento disponível depende do modelo e da implementação do provedor.
Com Microsoft.Extensions.AI, FunctionInvokingChatClient pode gerenciar automaticamente as etapas de invocação, retorno do resultado e novas interações com o modelo. Assim, a aplicação não precisa implementar manualmente todo o loop de chamadas.
• .NET 10 /11 SDK (ou uma versão compatível com os pacotes utilizados).
• Conhecimentos básicos de C# e programação assíncrona.
• Ollama instalado e em execução localmente.
• Um modelo Ollama que ofereça suporte a chamadas de ferramentas.
• Os pacotes Microsoft.Extensions.AI, OllamaSharp e Microsoft.Extensions.Hosting.
Na data desta revisão, as versões de referência consultadas são Microsoft.Extensions.AI 10.11.0 e OllamaSharp 5.5.0. As versões evoluem; confira a compatibilidade e as versões disponíveis no NuGet antes de reproduzir o exemplo.
dotnet new console -n AssistentePedidos
|
Baixe um modelo com suporte a ferramentas. Para manter a relação com o exemplo original, utilizaremoso modelo Qwen 2.5 7B:
ollama pull qwen2.5:7b
O suporte a Tool Calling depende da combinação entre modelo, versão do Ollama e biblioteca de integração. O fato de o modelo responder a prompts normalmente não significa que ele ofereça suporte adequado à chamada de ferramentas. Teste a combinação que será utilizada no ambiente de destino.
Vamos começar pela capacidade que desejamos disponibilizar ao modelo: consultar o status de um pedido. Em um sistema real, essa operação não deve acessar diretamente o banco de dados a partir do método exposto ao modelo. Ela deve delegar a consulta a um serviço de aplicação ou repositório que aplique as regras de negócio e autorização.
Para manter o exemplo pequeno e executável sem banco de dados, utilizaremos uma entidade Pedido com dados suficientes para representar um pedido real de forma didática. Ela terá uma identidade interna (Id), um número público usado pelo cliente (Numero), data, nome do cliente, valor total e status tipado por enum. A coleção em memória conterá dados fictícios.
public enum PedidoStatus
|
public sealed class RepositorioDePedidos
|
A classe RepositorioDePedidos representa apenas uma fonte de dados didática. O Id é a identidade interna da entidade, enquanto Numero é o identificador público informado pelo cliente e utilizado pela ferramenta. Em uma aplicação real, a implementação do repositório poderia utilizar Entity Framework Core, Dapper ou uma API externa.
public sealed class ServicoDePedidos(RepositorioDePedidos repositorio)
|
A consulta fica separada da ferramenta. Isso permite reutilizar a regra de negócio por outros pontos da aplicação e testá-la sem depender do modelo de IA. Neste exemplo, o serviço é simples; em produção, também seria o local para aplicar regras de acesso ao pedido com base no usuário autenticado.
using System.Globalization;
|
Neste exemplo as ferramentas funcionam sem utilizar nenhum atributo, mas isso nãosignifica que seja a melhor prática. Veja como seria usando os atributos:
using System.ComponentModel; public sealed class FerramentasDePedidos
{
[Description("Consulta o status atual de um pedido.")]
public string ConsultarStatusPedido(
[Description("Número do pedido. Exemplo: PED-1042.")]
string numeroPedido)
{
//...
return "Pedido enviado.";
}
[Description("Consulta os detalhes de um pedido.")] public string ConsultarDetalhesPedido( [Description("Número do pedido.")] string numeroPedido) { //...
return "Pedido PED-1042 - Valor: R$ 350,00";
}
}
|
O AIFunctionFactory.Create()
do
Microsoft.Extensions.AI consegue criar uma ferramenta a
partir de um método C# e utilizar informações como o nome do método, seus
parâmetros e seus tipos para construir a definição da função.
A ferramenta ConsultarStatusPedido retorna somente o número e o status. Já a ferramenta ConsultarDetalhesPedido retorna também data, cliente e valor total. Essa separação é intencional: a entidade pode conter vários dados, mas cada ferramenta deve expor apenas as informações necessárias para sua finalidade. Em produção, a consulta de detalhes deve validar se o usuário autenticado tem autorização para visualizar esses dados.
Agora vamos criar o componente que coordena a conversa com o modelo. Ele receberá IChatClient e FerramentasDePedidos por injeção de dependência. Os métodos ConsultarStatusPedido e ConsultarDetalhesPedido serão convertidos em AIFunction e disponibilizados nas opções de chat.
Também utilizaremos o Generic Host. Ele fornece ao aplicativo um contêiner de injeção de dependência, configuração, logging e gerenciamento do ciclo de vida. Embora seja comum em Worker Services, o Generic Host também pode ser utilizado em aplicações Console.
using Microsoft.Extensions.AI;
|
O AIFunctionFactory.Create transforma cada método em uma ferramenta que pode ser descrita ao modelo. Os argumentos dos métodos são expostos como parâmetros das ferramentas. As descrições ajudam o modelo a escolher entre uma consulta simples de status e uma consulta de detalhes. A escolha do modelo não substitui a autorização: a aplicação deve validar o acesso antes de devolver dados.
O System Prompt orienta o comportamento do assistente, mas não é um mecanismo de segurança. Não substitui validação, autorização ou regras de negócio no serviço que executa a operação.
No Program.cs, criaremos o HostApplicationBuilder, registraremos o cliente Ollama e adicionaremos o middleware de invocação de funções ao pipeline com AddChatClient(...).UseFunctionInvocation(). Essa é a abordagem de integração por DI documentada para IChatClient.
using Microsoft.Extensions.AI;
|
O registro AddChatClient recebe a implementação de IChatClient do OllamaSharp. A chamada UseFunctionInvocation configura o pipeline para executar automaticamente as funções solicitadas pelo modelo. O restante dos serviços é registrado no contêiner e resolvido por construtor.
Para experimentar, execute a aplicação e pergunte: “Qual é o status do pedido PED-1042?”.
O modelo deverá solicitar consultar_status_pedido.
Em seguida, pergunte: “Mostre os detalhes do pedido PED-1043”. Nesse caso, deverá solicitar consultar_detalhes_pedido. A aplicação executará o método correspondente e devolverá o resultado ao modelo para compor a resposta.
Também é possível perguntar por um número de pedido inexistente. Nesse caso, a ferramenta retorna uma mensagem informando que o pedido não foi encontrado. O modelo deve utilizar esse resultado, em vez de inventar um status ou detalhes.
Quando ResponderAsync chama GetResponseAsync, o cliente configurado recebe a conversa e as ferramentas registradas. O modelo pode retornar uma resposta textual ou uma solicitação de chamada de ferramenta.
Quando há uma solicitação de ferramenta, FunctionInvokingChatClient identifica a AIFunction correspondente, faz o binding dos argumentos e invoca o método. O resultado é incorporado à conversa e uma nova chamada ao modelo é realizada. Esse processo continua até que o modelo produza uma resposta final ou até que os limites configurados sejam atingidos.
Uma solicitação conceitual do modelo poderia se parecer com esta:
{
|
Esse JSON é ilustrativo. O formato real da chamada depende do protocolo e do provedor. Não é a aplicação que deve confiar cegamente nesse conteúdo: a camada de invocação e, principalmente, o código da ferramenta precisam validar os argumentos e controlar a operação.
O exemplo poderia instanciar OllamaApiClient diretamente e construir um ChatClientBuilder manualmente. Entretanto, registrar o cliente com AddChatClient oferece integração com o contêiner de DI e permite compor o pipeline de funcionalidades por meio de métodos de extensão.
O Generic Host também evita que a aplicação Console concentre toda a criação de dependências no método Main. A configuração do cliente, os serviços de negócio e o agente ficam registrados de maneira explícita e podem ser substituídos ou ampliados.
| Componente | Responsabilidade |
| IChatClient | Abstração para conversar com o provedor de IA. |
| OllamaApiClient | Implementação que conecta a aplicação ao Ollama. |
| AddChatClient | Registra o cliente de chat no contêiner de DI. |
| UseFunctionInvocation | Adiciona invocação automática de ferramentas ao pipeline. |
| AIFunctionFactory | Cria uma AIFunction a partir de um método .NET. |
| AssistenteDePedidos | Coordena a conversa e disponibiliza as ferramentas. |
| FerramentasDePedidos | Expõe operações específicas de consulta de status e detalhes que o modelo pode solicitar. |
| ServicoDePedidos | Executa a consulta de negócio. |
| RepositorioDePedidos | Fornece os dados de demonstração. |
A implementação com Generic Host apresentada anteriormente é uma aplicação Console. Uma aplicação ASP.NET Core também utiliza o Generic Host internamente; portanto, a ideia de registro de dependências é a mesma. A diferença é que o endpoint HTTP recebe a pergunta e chama o serviço do agente.
Em uma Web API, os registros podem ser feitos em Program.cs da aplicação ASP.NET Core. Os serviços de negócio que dependem de contexto por requisição normalmente devem ter lifetime Scoped. O cliente de chat pode ser registrado conforme o lifetime recomendado pelo provedor e pela integração utilizada.
using Microsoft.Extensions.AI;
|
Esse trecho ilustra a integração com Minimal APIs. Em uma aplicação real, o serviço do agente deve receber o contexto de usuário autenticado por meios confiáveis. Não permita que o modelo escolha ou informe livremente a identidade do usuário usada para autorizar o acesso aos dados.
Depois que a primeira ferramenta estiver funcionando, podemos disponibilizar outras capacidades específicas. Por exemplo:
| Ferramenta | Finalidade | Controle necessário |
| ConsultarStatusPedido | Consultar o andamento de um pedido. | Validar acesso do usuário ao pedido. |
| PesquisarProdutos | Pesquisar produtos por nome ou categoria. | Aplicar regras de visibilidade e disponibilidade. |
| ConsultarFatura | Consultar valores de uma fatura. | Restringir acesso às faturas autorizadas. |
| PesquisarBaseConhecimento | Localizar documentos internos. | Aplicar permissões por documento. |
| ConsultarPerfilCliente | Consultar dados do cliente. | Retornar somente os dados necessários. |
Não registre indiscriminadamente todos os métodos da aplicação como ferramentas. Disponibilize somente as operações necessárias para o objetivo do agente. Um agente de suporte pode precisar consultar pedidos e entregas, mas não precisa necessariamente emitir reembolsos, alterar endereços ou excluir registros. Da mesma forma, mantenha separadas ferramentas de status e de detalhes: a segunda pode revelar informações pessoais e financeiras que não são necessárias em toda consulta.
Ferramentas menores e bem descritas facilitam testes, manutenção, controle de permissões e compreensão pelo modelo. Também reduzem a quantidade de definições que precisam ser enviadas ao provedor a cada interação.
Trate os argumentos produzidos pelo modelo como entrada não confiável. Verifique valores obrigatórios, limites de tamanho, formatos permitidos e regras de negócio. Um JSON sintaticamente válido não significa que a solicitação seja válida ou autorizada.
Não delegue ao modelo a decisão sobre o usuário que pode visualizar um pedido, fatura ou documento. A autorização deve ser aplicada no serviço que executa a operação, usando a identidade autenticada e confiável fornecida pela aplicação.
Funções de consulta são mais fáceis de controlar do que ferramentas que alteram dados. Para ações como emitir reembolsos, enviar mensagens externas, excluir registros ou alterar contas, implemente permissões específicas, auditoria e confirmação explícita quando apropriado.
Não disponibilize ao modelo uma conexão genérica capaz de executar SQL arbitrário. Prefira ferramentas de aplicação com finalidade bem definida. Se houver um cenário legítimo de linguagem natural para SQL, utilize permissões mínimas, esquemas permitidos, validação de consultas, limites de recursos e controles adequados ao risco.
Defina limites de chamadas, duração das requisições, tamanho das respostas e consumo de serviços externos. Previna loops de invocação, registre informações suficientes para diagnóstico e evite gravar segredos, tokens ou dados pessoais desnecessários em prompts e logs.
Documentos, páginas e respostas de APIs podem conter instruções maliciosas destinadas a manipular o modelo. Conteúdo recuperado deve ser tratado como dado, não como uma instrução capaz de substituir as regras da aplicação ou ampliar as permissões das ferramentas.
Esses controles precisam estar na arquitetura e no código da aplicação. Um System Prompt pode orientar o modelo, mas não é uma fronteira de segurança.
O modelo responde sem chamar a ferramenta. Verifique se o modelo e o provedor suportam Tool Calling, se a ferramenta foi registrada e se a descrição deixa claro quando deve ser utilizada. A seleção da ferramenta é uma decisão do modelo e pode variar.
A ferramenta não é invocada. Confirme o registro em ChatOptions.Tools e a configuração de UseFunctionInvocation no pipeline. Verifique também se o modelo e a integração do Ollama suportam chamadas de ferramentas.
O modelo fornece argumentos incorretos. Valide o número do pedido e os demais argumentos no método da ferramenta e no serviço. Nunca presuma que os valores gerados pelo modelo são corretos.
A ferramenta retorna dados incorretos. Teste isoladamente o serviço e o repositório. Tool Calling não corrige dados desatualizados nem erros de regra de negócio.
O agente chama ferramentas demais. Reduza o conjunto de ferramentas, melhore as descrições e configure limites de invocação adequados.
A aplicação está lenta ou cara. Meça a latência do modelo, tempo de execução das ferramentas, uso de tokens e quantidade de interações com o modelo. Utilize cache somente quando for seguro para os dados.
O Tool Calling é uma forma prática de conectar modelos de linguagem a capacidades reais de uma aplicação sem conceder ao modelo controle irrestrito sobre o sistema.
Com .NET, Microsoft.Extensions.AI e um provedor compatível, podemos disponibilizar métodos C# como ferramentas, permitir que o modelo solicite sua execução, executar as operações dentro da aplicação e devolver os resultados para a elaboração da resposta.
Neste exemplo, utilizamos Ollama, Generic Host, injeção de dependência e AddChatClient para demonstrar uma composição simples, mas organizada. O exemplo não implementa todos os recursos de um agente completo; seu propósito é apresentar o mecanismo de Tool Calling e a separação entre modelo, ferramenta e lógica de negócio.
Em produção, o desafio não é apenas fazer uma chamada funcionar. É definir responsabilidades claras, proteger dados, validar entradas, aplicar autorização, tratar falhas e monitorar a execução. Comece com ferramentas pequenas e somente de leitura, teste o fluxo completo e amplie as capacidades conforme a necessidade.
E estamos conversados..![]()
Pegue o projeto aqui :
AssistentePedidos.zip
"Eu me deitei e dormi; acordei, porque o Senhor me
sustentou."
Salmos 3:5
• Microsoft Learn — Chamada de ferramenta de IA: https://learn.microsoft.com/pt-br/dotnet/ai/conceptual/calling-tools
• Microsoft Learn — Usar a interface IChatClient: https://learn.microsoft.com/pt-br/dotnet/ai/ichatclient
• Microsoft Learn — Host Genérico .NET: https://learn.microsoft.com/pt-br/dotnet/core/extensions/generic-host
• NuGet — Microsoft.Extensions.AI: https://www.nuget.org/packages/Microsoft.Extensions.AI
• NuGet — OllamaSharp: https://www.nuget.org/packages/OllamaSharp
• Ollama — Biblioteca de modelos: https://ollama.com/library