Gå til indholdet

O295 Semantic Kernel - Farvekategorisering

I denne opgave skal du skabe en konsolapplikation der bruger Semantic Kernel og OpenRouter til at kategorisere farver til standard farver.

Applikationen skal kunne:

  • Tage imod en farve som input (f.eks. “lyseblå”, “pink”, “mørkerød”)
  • Bruge en LLM til at kategorisere farven til en af de 7 grundfarver: rød, gul, grøn, blå, lilla, orange eller hvid
  • Returnere svaret som JSON med felterne input, category og confidence

Eksempel på kørsel:

Indtast en farve (eller 'exit'): lyseblå
{"input":"lyseblå","category":"blå","confidence":"høj"}

Indtast en farve (eller 'exit'): pink  
{"input":"pink","category":"rød","confidence":"medium"}

Indtast en farve (eller 'exit'): himmelblå
{"input":"himmelblå","category":"blå","confidence":"høj"}

Indtast en farve (eller 'exit'): exit

Krav:

  1. Brug OpenRouter med en valgfri model (f.eks. mistralai/mistral-small-3.2-24b-instruct eller openai/gpt-4o-mini) - få API nøgle fra instruktøren.
  2. Brug en system message til at instruere LLM’en om at returnere JSON
  3. JSON skal være valid og kunne deserialiseres til en C# record
  4. Confidence skal være enten “høj”, “medium” eller “lav”

Tips:

  • Husk at instruere LLM’en om IKKE at inkludere markdown formatering (```json) i outputtet
  • Brug JsonSerializer.Deserialize<ColorResponse>() til at parse svaret
  • Hvis farven er ukendt eller svær at kategorisere, kan LLM’en vælge den nærmeste farve med lav confidence

Ekstra udfordring:

Udvid programmet til også at returnere en kort forklaring på hvorfor farven blev kategoriseret sådan (tilføj et explanation felt i JSON’en).

Klik for at se et forslag til en løsning
using System.Text.Json;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;

// Konfiguration
const int MAX_RETRY_ATTEMPTS = 2;

/// <summary>
/// Farve kategorisering program der bruger AI til at klassificere farver.
/// Programmet tager brugerens farve input og kategoriserer det til en af 7 grundfarver
/// med retry-logik for robust JSON parsing.
/// </summary>

// Opret Semantic Kernel med OpenRouter AI service
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(
    modelId: "mistralai/mistral-small-3.2-24b-instruct",
    apiKey: Environment.GetEnvironmentVariable("OpenRouterKey", EnvironmentVariableTarget.Machine)!,
    endpoint: new Uri("https://openrouter.ai/api/v1"));

Kernel kernel = builder.Build();
var chatService = kernel.GetRequiredService<IChatCompletionService>();
ChatHistory chat = new ChatHistory();

// System prompt der definerer AI'ens rolle og outputformat
chat.AddSystemMessage("""
Du er en farveekspert der kategoriserer farver til en af følgende 7 grundfarver:
rød, gul, grøn, blå, lilla, orange eller hvid.

Du skal returnere et JSON objekt med følgende struktur:
{
"input": "den originale farve brugeren skrev",
"category": "en af de 7 grundfarver",
"confidence": "høj, medium eller lav"
}

Regler:
- Lyse/mørke varianter kategoriseres til grundfarven (lyseblå -> blå, mørkerød -> rød)
- Pastelfarver kategoriseres til deres grundfarve (pink -> rød, mint -> grøn)
- Brug "høj" confidence for klare farver, "medium" for pastelfarver, "lav" for sjældne/uklare farver
- Returner UDELUKKENDE valid JSON uden markdown formatering (ingen ```json tags)

Eksempler:
Input: "lyseblå" -> {"input":"lyseblå","category":"blå","confidence":"høj"}
Input: "pink" -> {"input":"pink","category":"rød","confidence":"medium"}
Input: "turkis" -> {"input":"turkis","category":"blå","confidence":"medium"}
""");

// JSON deserialisering indstillinger
var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};

// Hovedloop for brugerinteraktion
while (true)
{
    Console.Write("Indtast en farve (eller 'exit'): ");
    var input = Console.ReadLine() ?? "";
    if (input.ToLower() == "exit")
    break;

    // Tilføj brugerens input til chat historik
    chat.AddUserMessage(input);

    // Forsøg at få et gyldigt farve-svar med retry-logik
    var colorResponse = await TryGetColorResponseAsync(chatService, chat, options);

    // Vis det kategoriserede resultat
    Console.WriteLine($"Kategoriseret: {colorResponse.Input} -> {colorResponse.Category} (confidence: {colorResponse.Confidence})");

    // Nulstil chat historik for næste farve (behold kun system message)
    while (chat.Count > 1)
    {
        chat.RemoveAt(chat.Count - 1);
    }
}

/// <summary>
/// Forsøger at få et gyldigt ColorResponse fra AI service med retry-logik.
/// Hvis JSON parsing fejler, prøves igen op til MAX_RETRY_ATTEMPTS gange.
/// Hvis alle forsøg fejler, returneres en ColorResponse med null værdier.
/// </summary>
/// <param name="chatService">Chat completion service til AI kommunikation</param>
/// <param name="chat">Chat historik med system message og bruger input</param>
/// <param name="options">JSON serialisering indstillinger</param>
/// <returns>ColorResponse objekt - enten parsed fra AI eller med null værdier</returns>
static async Task<ColorResponse> TryGetColorResponseAsync(IChatCompletionService chatService, ChatHistory chat, JsonSerializerOptions options)
{
    for (int attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++)
    {
        // Få svar fra AI service
        var response = await chatService.GetChatMessageContentAsync(chat);
        var responseString = response.ToString();

        try
        {
            // Forsøg at parse JSON response
    var colorResponse = JsonSerializer.Deserialize<ColorResponse>(responseString, options);

            // Vis succesfuldt svar med attempt nummer hvis relevant
Console.WriteLine($"Svar{(attempt > 1 ? $" (forsøg {attempt})" : "")}: {responseString}");

    return colorResponse!;
}
        catch (Exception ex)
{
        // Log parsing fejlt med attempt nummer
Console.WriteLine($"Forsøg {attempt} fejlede med JSON parsing: {ex.Message}");

// Hvis dette var sidste forsøg, returner fallback
    if (attempt == MAX_RETRY_ATTEMPTS)
            {
Console.WriteLine("Returnerer JSON med null værdier");
            return new ColorResponse(null, null, null);
}
    }
    }

    // Denne linje nås aldrig, men compiler kræver return statement
    return new ColorResponse(null, null, null);
}

/// <summary>
/// Repræsenterer et farve kategorisering svar fra AI systemet.
/// Alle felter er nullable for at håndtere parsing fejl.
/// </summary>
/// <param name="Input">Den originale farve som brugeren indtastede</param>
/// <param name="Category">Kategoriseret grundfarve (rød, gul, grøn, blå, lilla, orange, hvid)</param>
/// <param name="Confidence">Confidence niveau (høj, medium, lav)</param>
public record ColorResponse(string? Input, string? Category, string? Confidence);