Ollama - Extraindo dados estruturados de imagens
![]() |
Neste artigo vamos mostrar como extrair dados estruturados de imagens usando o Ollama. |
Nos últimos anos, os Large Language Models (LLMs) deixaram de ser apenas
ferramentas de geração de texto e passaram a atuar como mecanismos avançados de
interpretação de dados não estruturados — incluindo imagens.
Mas existe
uma diferença enorme entre: Pedir para um modelo descrever uma imagem, e
usar um modelo para extrair dados estruturados confiáveis dessa imagem.
É
aí que a engenharia começa de verdade.
O que é o Ollama?
O Ollama é uma ferramenta que permite
executar modelos de linguagem localmente, na sua própria máquina. Em vez de
depender de APIs externas e enviar dados para a nuvem, você pode baixar um
modelo (como llama3.2-vision) e executá-lo diretamente no seu hardware.
Obs: O llama3.2-vision éum modelo multimodal que entende texto e imagem ao mesmo tempo baseado na familia 3.x da Meta mas com capacidade de visão adicionada. Um modelo “vision” consegue: Interpretar imagens, descrever o que vê , ler texto dentro de imagens (OCR implícito), extrair informações estruturadas e relacionar texto e imagem no mesmo contexto
Isso traz vantagens importantes:
✔ Privacidade de dados
✔ Sem dependência
de API keys
✔ Controle total sobre o ambiente
✔ Custos previsíveis
✔
Possibilidade de uso offline
Para desenvolvedores .NET, o Ollama pode ser
integrado facilmente usando bibliotecas como OllamaSharp e
Microsoft.Extensions.AI, permitindo trabalhar com uma interface padronizada
(IChatClient) independente do provedor do modelo.
O que são Dados Estruturados com IA?
As imagens, textos
livres e documentos escaneados são exemplos clássicos de dados não estruturados.
Eles não possuem um formato diretamente utilizável por sistemas computacionais
tradicionais.
Em nosso exemplo vamos usar a imagem de um recibo que contém:
Titulo, Endereco e Data
Nome dos
produtos
Preços unitários
Valores sub-totais e totais
A seguir temos a figura do recibo do arquivo receipt3.png presente pasta Receipts do projeto. Este arquivo deve ter o valor Copy to Output das propriedades alterado para Copy if Newer.

Mas essas informações estão organizadas
visualmente, não semanticamente.
Quando falamos em extrair dados
estruturados com IA, estamos nos referindo a transformar esse conteúdo não
estruturado em um formato padronizado, como:
{ "items": [ { "name": "Apples", "quantity": 1.500, "unitPrice": 5.99, "totalPrice": 8.99 } ], "subtotal": 8.99 } |
Agora aqui temos:
Campos definidos
Tipos de dados
conhecidos
Estrutura previsível
Validação
possível
Esse
é o ponto central: A IA deixa de ser apenas geração de texto e passa a ser
um mecanismo de extração semântica estruturada.
O Desafio Real
Extrair dados estruturados com LLMs
envolve três desafios principais:
Precisão numérica – o modelo não pode
arredondar valores.
Consistência – a mesma imagem deve gerar resultados
semelhantes.
Validação – precisamos confirmar matematicamente que os dados
fazem sentido.
É exatamente isso que vamos resolver neste artigo:
Transformar um modelo multimodal rodando localmente em uma ferramenta confiável
de extração de dados estruturados.
Extraindo dados estruturados de recibos
Para ilustrar vamos criar um projeto Console no Visual Studio e usar o Ollama e o LLM llama3.2-vision:latest para :
- Extrair itens de um recibo a partir de imagem
- Gerar JSON estruturado
validável
- Desserializar para objetos C# fortemente tipados
- Validar
matematicamente os dados
- Garantir consistência
A arquitetura da solução usada será a seguinte:

Vamos a seguir exibir o código de cada um dos recursos usados no projeto.
1- Receipt
public class Receipt { public string StoreName { get; set; } = string.Empty; public string Address { get; set; } = string.Empty; public DateTime Date { get; set; } public string Manager { get; set; } = string.Empty; public List |
Essa classe representa o modelo de domínio do recibo, ou seja, a estrutura que define como os dados extraídos pela IA devem ser organizados na aplicação.
Ela atua como contrato tipado para o GetResponseAsync<Receipt>, permitindo que o JSON retornado pelo modelo seja convertido automaticamente em objeto C#.
Cada propriedade mapeia um campo esperado no JSON estruturado (loja, itens, subtotal, imposto, total), garantindo tipagem forte e validação posterior. Ela é usada para padronizar os dados, facilitar regras de negócio e permitir serialização consistente dentro do sistema.
2- LineItem
public class LineItem { public string Name { get; set; } = string.Empty; public decimal Price { get; set; } } |
Essa classe representa um item individual do recibo, definindo a estrutura mínima de cada produto extraído pela IA. Ela atua como modelo tipado para mapear cada objeto do array items retornado pelo JSON do modelo multimodal.
As propriedades garantem tipagem forte (nome como texto e preço como decimal), permitindo validações matemáticas confiáveis. Ela é utilizada dentro da classe Receipt para compor a lista de itens e viabilizar cálculos como subtotal e verificação de consistência.
3-PromptBuilder
public static class PromptBuilder { public static ChatMessage BuildSystemPrompt() { return new ChatMessage(ChatRole.System, """ Extract all structured information from this receipt. Respond ONLY in valid JSON with this structure: { "storeName": "", "address": "", "date": "", "manager": "", "items": [ { "name": "item name", "price": 0.00 } ], "subtotal": 0.00, "tax": 0.00, "total": 0.00 } Rules: - Do NOT invent quantities. - Do NOT create unitPrice. - Extract exactly what appears on the receipt. - Read every digit exactly as printed. - Do NOT round values. - Use period as decimal separator. - Ensure subtotal equals sum of item prices. - Ensure subtotal + tax equals total. - Output only valid JSON. """); } } |
Essa classe é responsável por construir o System Prompt, que define o comportamento e as regras que o modelo deve seguir ao interpretar a imagem.
Ela atua configurando o contexto da conversa com ChatRole.System, garantindo prioridade às instruções e reduzindo ambiguidades na saída. O JSON descrito no prompt funciona como um contrato textual, orientando o modelo a retornar dados estruturados no formato esperado.
Ela é usada para controlar a precisão, evitar alucinações e aumentar a consistência da extração de dados.
Obs:
O valor Temperature = 0, reduz a
aleatoriedade da geração de texto, fazendo o modelo escolher sempre a resposta
mais provável estatisticamente. Usamos esse valor para tornar a saída mais
determinística e consistente, especialmente em cenários de extração de dados
estruturados.
4- ReceiptExtractionService
public class ReceiptExtractionService { private readonly IChatClient _chatClient; public ReceiptExtractionService(IChatClient chatClient) { _chatClient = chatClient; } public async Task |
Essa classe é o serviço de aplicação responsável por orquestrar a comunicação com o modelo de IA para extrair dados do recibo. Ela atua enviando a imagem e o prompt ao IChatClient, que encapsula o acesso ao modelo multimodal configurado no Ollama.
O método ExtractAsync lê o arquivo, monta as mensagens (System + User), envia para o modelo e recebe o resultado já desserializado como objeto Receipt. Ela é usada para centralizar a lógica de extração estruturada, mantendo o restante da aplicação desacoplado do provedor de IA.
5- ReceiptValidator
public static class ReceiptValidator { public static bool Validate(Receipt receipt, out List |
Aqui temos a camada de validação determinística do sistema, garantindo que os dados extraídos pela IA sejam consistentes e confiáveis.
Ela atua verificando campos obrigatórios, integridade dos itens e impedindo valores inválidos ou negativos. Também realiza validações matemáticas, assegurando que a soma dos itens corresponda ao subtotal e que subtotal mais imposto resulte no total.
A tolerância decimal evita falsos negativos causados por pequenas diferenças de
arredondamento.
Ela é usada para proteger a
aplicação contra erros probabilísticos do modelo, tornando a IA apenas um
auxiliar e não a fonte final de verdade.
6- Classe Program
using IAStructuredDataFromImage.Application; using IAStructuredDataFromImage.Infrastructure; using IAStructuredDataFromImage.Shared; using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System.Text.Json; CultureNormalization.ApplyInvariantCulture(); var builder = Host.CreateApplicationBuilder(args); builder.Services.AddOllamaVision( "http://localhost:11434", "llama3.2-vision:latest"); builder.Services.AddSingleton |
Esse código é o ponto de entrada da aplicação e atua como orquestrador do fluxo completo de extração com IA. Ele configura a cultura invariável, registra o modelo Ollama e o serviço de extração no container de injeção de dependência.
Em seguida, resolve o ReceiptExtractionService, envia a imagem para o modelo multimodal e recebe os dados estruturados já tipados. Após a extração, executa a validação determinística para garantir consistência matemática e integridade dos campos.
Por fim, exibe os dados no console e serializa o objeto final em JSON formatado para inspeção e depuração.
7- OllamaConfiguration
using Microsoft.Extensions.DependencyInjection; using OllamaSharp; namespace IAStructuredDataFromImage.Infrastructure; public static class OllamaConfiguration { public static IServiceCollection AddOllamaVision( this IServiceCollection services, string endpoint, string model) { var httpClient = new HttpClient { BaseAddress = new Uri(endpoint), Timeout = TimeSpan.FromMinutes(5) }; services.AddChatClient( new OllamaApiClient(httpClient, model)); return services; } } |
Essa classe configura a integração com o modelo Ollama dentro do container de injeção de dependência da aplicação. Ela cria um HttpClient apontando para o endpoint local do Ollama e define um tempo de espera maior para evitar timeout em modelos pesados.
O método AddOllamaVision registra o IChatClient configurado com o modelo especificado, tornando-o disponível para injeção nos serviços.
Ela é usada para isolar a infraestrutura externa, permitindo trocar o provedor de IA sem alterar a lógica da aplicação.
8- CultureNormalization
using System.Globalization; namespace IAStructuredDataFromImage.Shared; public static class CultureNormalization { public static void ApplyInvariantCulture() { CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; } } |
Este código é responsável por padronizar a cultura da aplicação para
InvariantCulture,
evitando variações regionais no processamento de dados. Ela atua
garantindo que números decimais utilizem sempre ponto como separador,
independentemente da configuração regional da máquina.
Isso é essencial para evitar erros na leitura e validação de valores monetários
retornados pelo modelo.
Ela é usada para assegurar consistência na serialização, desserialização e cálculos numéricos dentro do sistema.
Agora é só alegria...![]()
Executando o projeto iremos obter:

Pegue o projeto completo em : https://github.com/macoratti/IAStructuredDataFromImage
E estamos
conversados...
"Assim que, se alguém está em Cristo, nova criatura é;
as coisas velhas já passaram; eis que tudo se fez novo."
2 Coríntios 5:17
Referências:
NET - Unit of Work - Padrão Unidade de ...