Контроль рождественских расходов с помощью Semantic Kernel
## Введение
По мере приближения курортного сезона управление расходами может стать проблемой, особенно с учетом большого количества покупок и покупок подарков. В этой записи блога мы рассмотрим, как использовать искусственный интеллект, чтобы отслеживать ваши рождественские расходы с помощью технологий .NET. Анализируя квитанции с помощью семантического ядра и искусственного интеллекта, мы можем эффективно извлекать ключевые детали, такие как названия магазинов, даты, списки товаров и общие суммы. Это решение позволяет вам легко отслеживать и управлять своими рождественскими расходами, гарантируя, что вы будете оставаться в рамках своего бюджета без необходимости вручную проверять квитанции.
Calendario de Adviento de Inteligencia Artificial 2024 на испанском языке

Этот проект был вдохновлен моим участием в Calendario de Adviento de Inteligencia Artificial 2024 en Español, онлайн-мероприятии, посвященном искусственному интеллекту. Дополнительную информацию о мероприятии можно найти по ссылке Dev.to.
Проект
В этом проекте мы будем использовать Azure OpenAI, сервис, который позволяет нам использовать мощные модели искусственного интеллекта, такие как GPT-4, для обработки и анализа изображений. Этот процесс включает в себя несколько этапов: от настройки внутренней службы API до интеграции с внешним интерфейсом Blazor для загрузки изображений. Мы также будем использовать .NET Aspire, компонент, который помогает легко соединить все.
Предварительные условия
Прежде чем мы углубимся в код, убедитесь, что у вас есть следующие предварительные условия:
- .NET 9
- Доступ к Azure OpenAI (ключ API)
- Visual Studio или код Visual Studio
- Базовые знания Blazor, HTTP-клиентов и разработки API.
Решение Visual Studio
В конечном итоге у нас получится что-то вроде этого. Мне нравится, когда все вещи разделены и имеют классные названия, вот как это выглядит:

Но давайте шаг за шагом создавать вещи!
Шаг 0: Модели
Ядро приложения Receipt Scanner опирается на несколько ключевых моделей, которые облегчают взаимодействие между внешним интерфейсом, API и службами искусственного интеллекта. Ниже приведены основные модели, использованные в этом проекте:
АнализReceiptRequest
Эта модель представляет собой структуру запроса для анализа чека. Он содержит свойствоImageBytes, которое содержит массив байтов изображения чека, который будет обработан.[[[ТОК_4]]]
ReceiptAnalyzeResult
Эта модель фиксирует результат после обработки квитанции. Он содержит структурированные данные, извлеченные из квитанции, такие как название магазина, дата, товары и общая сумма.public class ReceiptAnalyzeResult { public DateTime CreatedAt { get; set; } public ReceiptData Result { get; set; } }Данные квитанции
Это модель, содержащая структурированные данные квитанции. Он включает в себя свойства названия магазина, даты, списка товаров (с названием и ценой каждого товара) и общую сумму в чеке.[[[ТОК_6]]]
Элемент получения
Каждый товар в чеке представлен этой моделью. Он содержит название товара и его цену.[[[ТОК_7]]]Эти модели служат основой для передачи данных между клиентом и сервером, обеспечивая плавный поток информации. API получает изображение квитанции и взамен обрабатывает и возвращает структурированный объект JSON, который может быть легко использован внешним интерфейсом.
Шаг 1. Настройка внутренней службы API
Первым шагом в создании этого приложения является настройка службы API для анализа изображений квитанций. Мы будем использовать API Azure OpenAI для извлечения информации из изображений квитанций. Вот разбивка того, как все сочетается друг с другом:
Служба искусственного интеллекта – глубокое погружение
Служба искусственного интеллекта лежит в основе нашей системы анализа квитанций. Он отвечает за взаимодействие с API Azure OpenAI для обработки данных изображения и получения значимой информации. Класс AiApiClient — это клиент, который будет обрабатывать все взаимодействия с API Azure OpenAI.
Реализация AI-клиента
AiApiClient — это ключевой компонент, отвечающий за отправку изображения квитанции (в формате массива байтов) в API Azure OpenAI. Он управляет связью, регистрирует ошибки и анализирует данные:
[[[ТОК_9]]]
В этой части кода мы определяем метод AnalyzeAsync, который отвечает за:
- Отправка массива байтов изображения в API Azure OpenAI.
- Обработка любых ошибок или неудачных ответов API.
- Анализ возвращенных данных JSON в структурированный результат (
ReceiptAnalyzeResult).
Преимущества выделения этой функции в выделенную службу (AiApiClient) включают в себя:
- Обработка ошибок. Централизованная обработка ошибок, таких как проблемы с сетью или неверные ответы.
- Журналирование: Надлежащее протоколирование запросов и ответов для мониторинга поведения системы.

Служба API — обработка запросов и ответов
Служба API выступает в качестве посредника между интерфейсным приложением Blazor и службой AI. Эта служба отвечает за прием данных изображения, передачу их службе AI и возврат результатов анализа клиенту.
Конечная точка API
На этом этапе мы определяем простую конечную точку API, которая будет принимать изображения квитанций, пересылать их в службу AI для обработки и возвращать результаты клиенту:
using ReceiptScanner.Shared.Clients;
using ReceiptScanner.Shared.Models;
var builder = WebApplication.CreateBuilder(args);
// Add service defaults & Aspire client integrations.
builder.AddServiceDefaults();
// Add services to the container.
builder.Services.AddProblemDetails();
// Register AiApiClient with HttpClient
builder.Services.AddHttpClient<AiApiClient>(client =>
{
client.BaseAddress = new Uri("https+http://aiservice");
});
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseExceptionHandler();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
// POST endpoint to analyze receipt
app.MapPost("/api/analyze-receipt", async (AnalyzeReceiptRequest request, AiApiClient aiApiClient, ILogger<Program> logger) =>
{
if (request.ImageBytes == null || request.ImageBytes.Length == 0)
{
logger.LogWarning("ImageBytes is null or empty.");
return Results.BadRequest("ImageBytes is required.");
}
logger.LogInformation("Received analyze receipt request with image bytes of length: {Length}", request.ImageBytes.Length);
try
{
var result = await aiApiClient.AnalyzeAsync(request.ImageBytes);
if (result == null)
{
logger.LogWarning("Failed to analyze the receipt.");
return Results.Problem("Unable to process the receipt at this time. Please try again later.");
}
logger.LogInformation("Analysis completed successfully. Result: {Result}", result);
return Results.Ok(result);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while processing the receipt.");
return Results.Problem("An error occurred while processing the receipt. Please try again later.");
}
});
app.MapDefaultEndpoints();
app.Run();
Эта конечная точка:
- Принимает изображение квитанции как часть тела запроса.
- Вызывает метод endopint
AiServiceдля отправки изображения в Azure OpenAI для обработки. - Возвращает результат анализа обратно клиенту.

Шаг 2. Настройка внешнего интерфейса Blazor
Теперь, когда у нас настроена серверная часть, давайте обратим внимание на интерфейс Blazor. Здесь пользователи могут загружать изображения своих квитанций для анализа и видеть результаты.
Реализация страницы Blazor
Страница Blazor предоставляет простой интерфейс, в котором пользователи могут загружать несколько изображений квитанций, а затем просматривать результаты анализа, отображаемые в таблице. Вот код страницы:
@page "/analyzer"
@using ReceiptScanner.Shared.Clients
@using ReceiptScanner.Shared.Models
@using System.Globalization
@inject ApiServiceClient ApiClient
@inject ILogger<Program> Logger
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Receipt Analyzer</PageTitle>
<h1 class="text-center my-4">Receipt Analyzer</h1>
<div class="container">
<p class="lead text-center mb-4">Upload receipt images below to extract their data.</p>
<!-- File Upload Section -->
<div class="card mb-4">
<div class="card-body">
<InputFile OnChange="HandleFileSelected" multiple class="form-control mb-3" />
<button class="btn btn-primary w-100" @onclick="ProcessReceipts" disabled="@(!hasFiles)" type="button">
<span class="@(!processing ? "d-none" : "") spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
@if (processing)
{
<span>Processing...</span>
}
else
{
<span>Process Receipts</span>
}
</button>
</div>
</div>
<!-- Uploaded Images Preview -->
@if (fileBytesList.Any())
{
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Uploaded Receipt Images</h5>
</div>
<div class="card-body">
<div class="row">
@foreach (var fileBytes in fileBytesList)
{
<div class="col-12 col-md-4 mb-3">
<img src="@($"data:image/jpeg;base64,{Convert.ToBase64String(fileBytes)}")" class="img-fluid rounded" alt="Uploaded receipt" />
</div>
}
</div>
</div>
</div>
}
<!-- Processing Indicator -->
@if (processing)
{
<div class="alert alert-info text-center" role="alert">
<strong>Processing receipts...</strong> Please wait while we analyze the uploaded files.
</div>
}
<!-- Analysis Results Section -->
@if (analyzedReceipts != null && analyzedReceipts.Any())
{
<div class="card">
<div class="card-header">
<h5 class="mb-0">Analysis Results</h5>
</div>
<div class="card-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Store</th>
<th>Date</th>
<th>Total</th>
<th>Items</th>
</tr>
</thead>
<tbody>
@foreach (var receipt in analyzedReceipts)
{
<tr>
<td>@(receipt.Result?.Store ?? "Unknown")</td>
<td>@(receipt.Result?.Date?.ToString() ?? "Unknown")</td>
<td>@(receipt.Result?.Total?.ToString("C", ci) ?? "Unknown")</td>
<td>
<ul class="list-unstyled">
@if (receipt.Result?.Items is not null)
{
@foreach (var item in receipt.Result?.Items!)
{
<li><strong>@item.Name</strong> - @item.Price?.ToString("C", ci)</li>
}
}
</ul>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
else if (processed && (analyzedReceipts == null || !analyzedReceipts.Any()))
{
<div class="alert alert-warning text-center" role="alert">
<strong>No results found.</strong> Please try again with different images or ensure they are clear and legible.
</div>
}
</div>
@code {
private bool hasFiles;
private bool processing;
private bool processed;
private List<byte[]> fileBytesList = new();
private List<ReceiptAnalyzeResult> analyzedReceipts = new();
CultureInfo ci = new CultureInfo("es-es");
private async Task HandleFileSelected(InputFileChangeEventArgs e)
{
try
{
fileBytesList.Clear();
foreach (var file in e.GetMultipleFiles())
{
var memoryStream = new MemoryStream();
await file.OpenReadStream().CopyToAsync(memoryStream);
fileBytesList.Add(memoryStream.ToArray());
}
hasFiles = fileBytesList.Any();
}
catch (Exception ex)
{
Logger.LogError(ex, "Error while handling file upload.");
}
}
private async Task ProcessReceipts()
{
if (!hasFiles)
return;
processing = true;
analyzedReceipts.Clear();
try
{
foreach (var fileBytes in fileBytesList)
{
var result = await ApiClient.AnalyzeReceiptAsync(fileBytes);
if (result != null)
{
analyzedReceipts.Add(result);
}
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error while processing receipts.");
}
finally
{
processing = false;
processed = true;
}
}
}
```Эта страница позволяет пользователям загружать квитанции и отображает результаты анализа в таблице с названиями магазинов, датами, общими суммами и списком товаров.
<p выравнивание="центр">
<img src="https://imgur.com/BLswKhm.png">
</p>
## Шаг 3: .NET Aspire
<p выравнивание="центр">
<img src="https://imgur.com/ja56RWN.png">
</p>
### Что такое .NET Aspire?
.NET Aspire — это набор мощных инструментов, шаблонов и пакетов для создания наблюдаемых, готовых к использованию приложений. .NET Aspire поставляется в виде набора пакетов NuGet, которые решают специфические проблемы облачной среды. Облачные приложения часто состоят из небольших взаимосвязанных частей или микросервисов, а не из единой монолитной базы кода. Облачные приложения обычно используют большое количество сервисов, таких как базы данных, обмен сообщениями и кэширование. Информацию о поддержке см. в Политике поддержки .NET Aspire.
Распределенное приложение — это приложение, которое использует вычислительные ресурсы на нескольких узлах, например контейнерах, работающих на разных хостах. Такие узлы должны обмениваться данными через границы сети, чтобы доставлять ответы пользователям. Облачное приложение — это особый тип распределенного приложения, которое в полной мере использует преимущества масштабируемости, устойчивости и управляемости облачных инфраструктур.
Использование **.NET Aspire** для этого проекта дает ряд преимуществ, улучшающих общее качество системы, например:
### 1. **Централизованное ведение журнала**
.NET Aspire автоматически интегрирует ведение журнала во всем приложении, что означает, что вам не придется вручную настраивать ведение журнала для каждой службы. Это гарантирует согласованность журналов и их централизованное хранение, что значительно упрощает отладку и мониторинг.
Например, класс `AiApiClient` использует журналирование для записи байтов изображения, отправленных в службу AI, ответов API и любых ошибок, возникающих в процессе анализа.
```csharp
_logger.LogInformation("Sending analyze request with image bytes of length: {Length}",
imageBytes.Length);

2. Автоматический сбор показателей
.NET Aspire также автоматически отслеживает и сообщает важные показатели приложения, такие как время ответа, количество запросов и частота ошибок. Это поможет вам понять, как работает приложение, и быстро обнаружить любые узкие места или проблемы.

3. Улучшенная производительность
.NET Aspire оптимизирует HTTP-вызовы, что помогает сократить время ответа и сократить ненужное потребление ресурсов. Он предоставляет такие функции, как объединение пулов соединений, повторные попытки запросов и интеллектуальную маршрутизацию.
4. Бесшовная интеграция
.NET Aspire упрощает интеграцию различных сервисов (например, сервисов искусственного интеллекта и API в этом проекте) и оптимизирует процесс развертывания. Вам не нужно беспокоиться о низкоуровневых конфигурациях, поскольку Aspire позаботится о задачах, связанных с инфраструктурой, за вас.

ЗаключениеИИ больше не является просто модным словечком или чем-то, что мы видим в научно-фантастических фильмах. Сегодня он активно решает реальные проблемы, подобные той, которую мы решали в этом проекте — извлечение структурированных данных из квитанций. С помощью Azure OpenAI, .NET Aspire и Blazor мы можем автоматизировать то, что в противном случае было бы трудоемким и подверженным ошибкам ручным заданием. ИИ не просто общается или отвечает на запросы, такие как ChatGPT; он интерпретирует изображения, извлекает ценную информацию и дает нам ценную информацию за считанные секунды.
Используя Azure OpenAI для анализа квитанций и .NET Aspire для плавной интеграции с журналами и метриками, мы создали мощное и масштабируемое решение. Потенциал искусственного интеллекта для оптимизации бизнес-процессов, автоматизации утомительных задач и повышения точности огромен, и это лишь один пример того, как его можно применить.
Этот пост является частью Calendario de Adviento de Inteligencia Artificial 2024 en Español — мероприятия, которое демонстрирует реальные приложения искусственного интеллекта и знакомит испаноязычное технологическое сообщество с последними тенденциями. Если вы хотите глубже погрузиться в искусственный интеллект и его возможности, это мероприятие — отличное место для начала.
ИИ меняет то, как мы работаем, и этот проект — лишь проблеск того, что возможно. Настоящая сила ИИ заключается в его способности решать реальные проблемы — будь то обработка квитанций, анализ изображений или прогнозирование тенденций. Мы только царапаем поверхность.
Исходный код
Полный исходный код этого проекта доступен на GitHub. Не стесняйтесь загружать его, изучать, как службы искусственного интеллекта и API работают вместе, и адаптировать его для своих собственных сценариев использования. Если у вас возникнут какие-либо проблемы или у вас есть идеи по улучшению, не стесняйтесь создавать проблему или отправлять запрос на включение. Вклад всегда приветствуется, а ваши отзывы помогут сделать этот проект еще лучше!
Приятного кодирования!