التحكم في إنفاق عيد الميلاد باستخدام Semantic Kernel
## مقدمة
مع اقتراب موسم العطلات، يمكن أن تصبح إدارة النفقات تحديًا، خاصة مع موجة التسوق وشراء الهدايا. في منشور المدونة هذا، سنستكشف كيفية الاستفادة من الذكاء الاصطناعي للمساعدة في تتبع إنفاقك في عيد الميلاد باستخدام تقنيات .NET. من خلال تحليل الإيصالات بقوة Semantic Kernel والذكاء الاصطناعي، يمكننا استخراج التفاصيل الأساسية بكفاءة مثل أسماء المتاجر والتواريخ وقوائم العناصر والمبالغ الإجمالية. يتيح لك هذا الحل مراقبة إنفاقك في عيد الميلاد وإدارته بسهولة، مما يضمن لك البقاء على اطلاع بميزانيتك دون الحاجة إلى مراجعة الإيصالات يدويًا.
Calendario de Adviento de Inteligencia Artificial 2024 en Español

استلهم هذا المشروع من مشاركتي في Calendario de Adviento de Inteligencia Artificial 2024 en Español، وهو حدث عبر الإنترنت مخصص للذكاء الاصطناعي. يمكنك العثور على المزيد حول الحدث على رابط Dev.to هذا.
##المشروع
في هذا المشروع، سنستخدم Azure OpenAI، وهي خدمة تتيح لنا استخدام نماذج الذكاء الاصطناعي القوية مثل GPT-4 لمعالجة الصور وتحليلها. تتضمن العملية عدة خطوات، بدءًا من إعداد خدمة واجهة برمجة التطبيقات الخلفية وحتى التكامل مع الواجهة الأمامية لـ Blazor لتحميل الصور. سنستخدم أيضًا .NET Aspire، وهو مكون يساعد على ربط كل شيء بسلاسة.
المتطلبات الأساسية
قبل أن نتعمق في الكود، تأكد من أن لديك المتطلبات الأساسية التالية:
-.نت 9
- الوصول إلى Azure OpenAI (مفتاح API)
- Visual Studio أو Visual Studio Code
- المعرفة الأساسية بـ Blazor وعملاء HTTP وتطوير واجهة برمجة التطبيقات
حل Visual Studio
سينتهي بنا الأمر بالحصول على شيء كهذا، أحب أن أبقي الأشياء منفصلة وبأسماء رائعة، فإليك كيف يبدو الأمر:

ولكن دعونا نمضي قدمًا في إنشاء الأشياء خطوة بخطوة!
الخطوة 0: النماذج
يعتمد جوهر تطبيق Receipt Scanner على العديد من النماذج الرئيسية التي تسهل التفاعل بين خدمات الواجهة الأمامية وواجهة برمجة التطبيقات (API) وخدمات الذكاء الاصطناعي (AI). وفيما يلي النماذج الرئيسية المستخدمة في هذا المشروع:
طلب تحليل الإيصال
يمثل هذا النموذج بنية الطلب لتحليل الإيصال. يحتوي على خاصيةImageBytes، التي تحتوي على مصفوفة البايت لصورة الإيصال التي ستتم معالجتها.public class AnalyzeReceiptRequest { public byte[] ImageBytes { get; set; } }نتيجة تحليل الإيصال
يلتقط هذا النموذج النتيجة بعد معالجة الإيصال. ويحتفظ بالبيانات المنظمة المستخرجة من الإيصال، مثل اسم المتجر والتاريخ والأصناف والمبلغ الإجمالي.public class ReceiptAnalyzeResult { public DateTime CreatedAt { get; set; } public ReceiptData Result { get; set; } }بيانات الاستلام
هذا هو النموذج الذي يحمل بيانات الاستلام المنظمة. ويتضمن خصائص اسم المتجر، والتاريخ، وقائمة العناصر (مع اسم كل عنصر وسعره)، والمبلغ الإجمالي في الإيصال.public class ReceiptData { public string Store { get; set; } public DateTime? Date { get; set; } public List<ReceiptItem> Items { get; set; } public decimal? Total { get; set; } }عنصر الإيصال
يتم تمثيل كل عنصر في الإيصال بواسطة هذا النموذج. يحمل اسم السلعة وسعرها.public class ReceiptItem { public string Name { get; set; } public decimal? Price { get; set; } } ```تعمل هذه النماذج كأساس لتمرير البيانات بين العميل والخادم، مما يضمن التدفق السلس للمعلومات. تتلقى واجهة برمجة التطبيقات (API) صورة الإيصال، وفي المقابل، تقوم بمعالجة وإرجاع كائن JSON منظم يمكن استهلاكه بسهولة بواسطة الواجهة الأمامية.
الخطوة 1: إعداد خدمة Backend API
الخطوة الأولى في بناء هذا التطبيق هي إعداد خدمة API لتحليل صور الإيصالات. سنستخدم Azure OpenAI API لاستخراج المعلومات من صور الإيصال. فيما يلي تفصيل لكيفية تناسب كل شيء معًا:
خدمة الذكاء الاصطناعي - نظرة عميقة
تقع خدمة الذكاء الاصطناعي في قلب نظام تحليل الإيصالات الخاص بنا. إنها مسؤولة عن التواصل مع واجهة برمجة تطبيقات Azure OpenAI لمعالجة بيانات الصورة وإرجاع رؤى ذات معنى. فئة AiApiClient هي العميل الذي سيتعامل مع كافة التفاعلات مع Azure OpenAI API.
تنفيذ عميل الذكاء الاصطناعي
AiApiClient هو المكون الرئيسي المسؤول عن إرسال صورة الإيصال (بتنسيق صفيف البايت) إلى Azure OpenAI API. يتعامل مع الاتصالات وتسجيل الأخطاء وتحليل البيانات:
public class AiApiClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<AiApiClient> _logger;
public AiApiClient(HttpClient httpClient, ILogger<AiApiClient> logger)
{
_httpClient = httpClient;
_logger = logger;
}
public async Task<ReceiptAnalyzeResult?> AnalyzeAsync(byte[] imageBytes, CancellationToken cancellationToken = default)
{
if (imageBytes == null || imageBytes.Length == 0)
{
_logger.LogWarning("ImageBytes is null or empty.");
return null;
}
_logger.LogInformation("Sending analyze request with image bytes of length: {Length}", imageBytes.Length);
var request = new AnalyzeReceiptRequest
{
ImageBytes = imageBytes
};
try
{
var response = await _httpClient.PostAsJsonAsync("/analyze-receipt", request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
_logger.LogWarning("Failed to analyze receipt. StatusCode: {StatusCode}", response.StatusCode);
return null;
}
var analyzeResult = await response.Content.ReadFromJsonAsync<ReceiptAnalyzeResult>(cancellationToken: cancellationToken);
if (analyzeResult == null)
{
_logger.LogWarning("No content received from AI API service.");
return null;
}
_logger.LogInformation("Analysis result received: {AnalyzeResult}", analyzeResult);
return analyzeResult;
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while analyzing the receipt.");
return null;
}
}
}
في هذا الجزء من الكود، قمنا بتعريف الطريقة AnalyzeAsync، وهي المسؤولة عن:
- إرسال مصفوفة بايت الصورة إلى Azure OpenAI API.
- التعامل مع أي أخطاء أو استجابات غير ناجحة من API.
- تحليل بيانات JSON التي تم إرجاعها إلى نتيجة منظمة (
ReceiptAnalyzeResult).
تتضمن فوائد فصل هذه الوظيفة إلى خدمة مخصصة (AiApiClient) ما يلي:
- معالجة الأخطاء: معالجة مركزية للأخطاء مثل مشكلات الشبكة أو الاستجابات غير الصالحة.
- التسجيل: التسجيل الصحيح للطلبات والاستجابات لمراقبة سلوك النظام.

خدمة API - التعامل مع الطلبات والردود
تعمل خدمة API كوسيط بين تطبيق Blazor للواجهة الأمامية وخدمة الذكاء الاصطناعي. هذه الخدمة مسؤولة عن قبول بيانات الصورة وتمريرها إلى خدمة الذكاء الاصطناعي وإرجاع نتائج التحليل إلى العميل.
نقطة نهاية واجهة برمجة التطبيقات
في هذه الخطوة، نحدد نقطة نهاية بسيطة لواجهة برمجة التطبيقات (API) لقبول صور الإيصالات، وإعادة توجيهها إلى خدمة الذكاء الاصطناعي للمعالجة، وإرجاع النتائج إلى العميل:
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();
نقطة النهاية هذه:
- قبول صورة الإيصال كجزء من نص الطلب.
- يستدعي أسلوب النقطة الداخلية
AiServiceلإرسال الصورة إلى Azure OpenAI للمعالجة. - إرجاع نتيجة التحليل إلى العميل.

الخطوة الثانية: إعداد واجهة 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 align="center">
<img src="https://imgur.com/BLswKhm.png">
</ص>
## الخطوة 3: .NET Aspire
<p align="center">
<img src="https://imgur.com/ja56RWN.png">
</ص>
### ما هو .NET Aspire؟
.NET Aspire عبارة عن مجموعة من الأدوات والقوالب والحزم القوية لإنشاء تطبيقات يمكن ملاحظتها وجاهزة للإنتاج. يتم تسليم .NET Aspire من خلال مجموعة من حزم NuGet التي تتعامل مع اهتمامات محددة تتعلق بالسحابة الأصلية. غالبًا ما تتكون التطبيقات السحابية الأصلية من أجزاء صغيرة مترابطة أو خدمات صغيرة بدلاً من قاعدة تعليمات برمجية واحدة متجانسة. تستهلك التطبيقات السحابية بشكل عام عددًا كبيرًا من الخدمات، مثل قواعد البيانات والمراسلة والتخزين المؤقت. للحصول على معلومات حول الدعم، راجع سياسة دعم .NET Aspire.
التطبيق الموزع هو الذي يستخدم الموارد الحسابية عبر عقد متعددة، مثل الحاويات التي تعمل على مضيفين مختلفين. يجب أن تتواصل هذه العقد عبر حدود الشبكة لتقديم الاستجابات للمستخدمين. التطبيق السحابي الأصلي هو نوع محدد من التطبيقات الموزعة التي تستفيد بشكل كامل من قابلية التوسع والمرونة وسهولة الإدارة للبنى التحتية السحابية.
يوفر استخدام **.NET Aspire** لهذا المشروع العديد من الفوائد التي تعمل على تحسين جودة النظام بشكل عام، مثل:
### 1. **التسجيل المركزي**
يقوم .NET Aspire بدمج التسجيل تلقائيًا عبر التطبيق بأكمله، مما يعني أنك لن تضطر إلى تكوين التسجيل يدويًا لكل خدمة. ويضمن ذلك اتساق السجلات وتخزينها في موقع مركزي، مما يجعل تصحيح الأخطاء والمراقبة أسهل بكثير.
على سبيل المثال، تستخدم فئة `AiApiClient` التسجيل لتسجيل بايتات الصورة المرسلة إلى خدمة الذكاء الاصطناعي، واستجابات واجهة برمجة التطبيقات، وأي أخطاء تحدث أثناء عملية التحليل.
```csharp
_logger.LogInformation("Sending analyze request with image bytes of length: {Length}",
imageBytes.Length);

2. جمع المقاييس تلقائيًا
يقوم .NET Aspire أيضًا بتتبع مقاييس التطبيق المهمة والإبلاغ عنها تلقائيًا مثل أوقات الاستجابة وعدد الطلبات ومعدلات الخطأ. يساعدك هذا على فهم كيفية أداء التطبيق والكشف بسرعة عن أي اختناقات أو مشكلات.

3. تحسين الأداء
يعمل .NET Aspire على تحسين مكالمات HTTP، مما يساعد على إبقاء أوقات الاستجابة منخفضة وتقليل استهلاك الموارد غير الضرورية. فهو يوفر ميزات مثل تجميع الاتصالات وإعادة محاولة الطلب والتوجيه الذكي.
4. التكامل السلس
يعمل .NET Aspire على تبسيط تكامل الخدمات المتنوعة (مثل خدمات الذكاء الاصطناعي وواجهة برمجة التطبيقات في هذا المشروع) وتبسيط عملية النشر. لا داعي للقلق بشأن التكوينات ذات المستوى المنخفض، حيث تتولى Aspire المهام المتعلقة بالبنية التحتية نيابةً عنك.

الخلاصةلم يعد الذكاء الاصطناعي مجرد كلمة طنانة أو شيء نراه في أفلام الخيال العلمي. إنها تعمل بنشاط على حل مشكلات العالم الحقيقي اليوم، مثل تلك التي عالجناها في هذا المشروع، وهي استخراج البيانات المنظمة من الإيصالات. بمساعدة Azure OpenAI و**.NET Aspire** وBlazor، يمكننا أتمتة المهمة اليدوية التي قد تستغرق وقتًا طويلاً وعرضة للأخطاء. لا يقوم الذكاء الاصطناعي فقط بالدردشة أو الاستجابة للمطالبات مثل ChatGPT؛ فهو يفسر الصور ويستخرج معلومات قيمة ويعطينا رؤى قابلة للتنفيذ في ثوانٍ.
باستخدام Azure OpenAI لتحليل الإيصالات و**.NET Aspire** للتكامل السلس مع التسجيل والمقاييس، قمنا بإنشاء حل قوي وقابل للتطوير. إن إمكانات الذكاء الاصطناعي في تبسيط العمليات التجارية، وأتمتة المهام الشاقة، وتحسين الدقة هائلة، وهذا مجرد مثال واحد لكيفية تطبيقه.
يعد هذا المنشور جزءًا من Calendario de Adviento de Inteligencia Artificial 2024 en Español، وهو حدث يعرض تطبيقات الذكاء الاصطناعي في العالم الحقيقي ويقوم بتثقيف مجتمع التكنولوجيا الناطق بالإسبانية حول أحدث الاتجاهات. إذا كنت تتطلع إلى التعمق أكثر في الذكاء الاصطناعي وإمكانياته، فإن هذا الحدث يعد مكانًا رائعًا للبدء.
يعمل الذكاء الاصطناعي على تغيير طريقة عملنا، وهذا المشروع هو مجرد لمحة عما هو ممكن. تكمن القوة الحقيقية للذكاء الاصطناعي في قدرته على حل المشكلات الحقيقية، سواء كانت معالجة الإيصالات، أو تحليل الصور، أو التنبؤ بالاتجاهات. نحن مجرد خدش السطح.
كود المصدر
كود المصدر الكامل لهذا المشروع متاح على GitHub. لا تتردد في تنزيله، واستكشاف كيفية عمل خدمات الذكاء الاصطناعي وواجهة برمجة التطبيقات معًا، وتكييفه ليناسب حالات الاستخدام الخاصة بك. إذا واجهت أية مشكلات، أو إذا كانت لديك أفكار للتحسينات، فلا تتردد في إنشاء مشكلة أو إرسال طلب سحب. المساهمات مرحب بها دائمًا، وستساعد تعليقاتك في جعل هذا المشروع أفضل!
ترميز سعيد!