Gå til indholdet

Introduktion til Microsoft Semantic Kernel

Microsoft Semantic Kernel (SK) er et open-source værktøj designet til at gøre arbejdet med kunstig intelligens (AI) lettere for udviklere, især dem, der arbejder med C#.

Hvad er Semantic Kernel?

Semantic Kernel er en ramme (framework), der hjælper dig med at integrere AI-funktioner, som naturlig sprogbehandling (NLP) og maskinlæring (ML), direkte i dine applikationer. Med SK kan du opbygge AI-drevne applikationer, som kan forstå og reagere på brugerinput på en intelligent måde. Det er især nyttigt for udviklere, der ønsker at skabe applikationer, der kan analysere tekst, udføre opgaver baseret på naturligt sprog eller interagere med brugere på en mere menneskelig måde.

Hvorfor bruge Semantic Kernel?

Hvis du er ny i AI-verdenen, kan det være udfordrende at forstå, hvordan man implementerer AI-funktioner i dine apps. SK gør dette lettere ved at tilbyde færdigbyggede moduler og værktøjer, som du kan bruge til at tilføje AI til dine projekter uden at skulle opfinde hjulet på ny. Dette betyder, at du kan fokusere på at bygge din applikation, mens SK håndterer de komplekse dele af AI.

En af de vigtigste fordele ved SK er dens evne til at fungere som en abstraktionslag over forskellige AI-modeller og tjenester. Dette betyder, at du med SK kan arbejde med forskellige AI-leverandører som OpenAI, Gemini og mange andre uden at skulle ændre grundlæggende dele af din kode. Denne fleksibilitet giver dig mulighed for at eksperimentere med forskellige AI-modeller og vælge den, der passer bedst til dine behov.

Her er nogle af de vigtigste funktioner, som SK tilbyder:

  1. Enkel Integration: SK er designet til at integrere nemt med C#-applikationer, så du kan bruge det sammen med det udviklingsmiljø, du allerede er bekendt med.

  2. Moduler til Naturlig Sprogbehandling: SK leverer moduler, der gør det muligt for dine apps at forstå og generere menneskeligt sprog. Dette kan være nyttigt for at bygge chatbots, analysere kundeanmeldelser eller automatisere svar på almindelige spørgsmål.

  3. Abstraktion af AI-modeller: SK fungerer som et abstraktionslag, der giver dig mulighed for at skifte mellem forskellige AI-modeller og tjenester som OpenAI og Gemini uden at skulle ændre din applikationslogik. Dette gør det lettere at integrere og teste forskellige AI-teknologier.

  4. Skalérbarhed: Uanset om du arbejder på et lille projekt eller en stor virksomhedsapplikation, er SK designet til at skalere med dine behov.

Hello World chat (OpenAI)

Her er en helt klassisk “AI Hello World” applikation, der bruger Semantic Kernel til at tale med OpenAI (men det kan nemt ændres). Start med at skabe en ny konsolapplikation og tilføj SK nuget-pakken til dit projekt:

dotnet add package Microsoft.SemanticKernel

Få nu fat i en API-nøgle fra OpenAI og gem den i en miljøvariabel på din computer. Du kan gøre dette ved at køre følgende kommando i PowerShell:

setx OpenAIKey "DIN_API_NØGLE" /M

Nu kan du skrive følgende kode i din Program.cs fil:

using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

IChatCompletionService chatService = new OpenAIChatCompletionService("gpt-4o", Environment.GetEnvironmentVariable("OpenAIKey", EnvironmentVariableTarget.Machine)!);
while (true)
{
    System.Console.Write("You: ");
    var input = System.Console.ReadLine() ?? "";

    if (input == "exit")
        break;

    var response = await chatService.GetChatMessageContentAsync(input);
    System.Console.WriteLine("AI: " + response);
}

Dette program opretter en chatbot, der bruger OpenAI’s GPT-4 model til at generere svar på brugerinput. Når du kører programmet, kan du skrive beskeder til chatbotten, og den vil generere svar baseret på den træning, den har modtaget fra GPT-4 modellen. Dog kan den ikke “huske” tidligere beskeder, da den ikke har en “hukommelse”.

You: hej - jeg hedder Michell

AI: Hej Michell! Hvordan kan jeg hjælpe dig i dag?

You: Hvad er det nu jeg hedder

AI: Det ser ikke ud til, at jeg har information om dit navn. Hvis du fortæller mig det, skal jeg nok huske det for varigheden af vores samtale!

You: exit

Men en del af Semantic Kernel er også at kunne huske tidligere beskeder og bruge dem til at generere bedre svar:

using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

IChatCompletionService chatService = new OpenAIChatCompletionService("gpt-4o", Environment.GetEnvironmentVariable("OpenAIKey", EnvironmentVariableTarget.Machine)!);
ChatHistory chat = new ChatHistory();
while (true)
{
    System.Console.Write("You: ");
    var input = System.Console.ReadLine() ?? "";

    if (input == "exit")
        break;

    chat.AddUserMessage(input);
    var response = await chatService.GetChatMessageContentAsync(chat);
    System.Console.WriteLine("AI: " + response);
    chat.Add(response);
}

You: Hej Jeg hedder Michell

AI: Hej Michell! Hvordan kan jeg hjælpe dig i dag?

You: Øøøh - hvad pokker var det nu jeg hed

AI: Hejsa! Du skrev tidligere, at du hedder Michell. Er der noget andet, du gerne vil tale om eller have hjælp med?

You: exit

Den lidt mere avancerede demo

Her er en lidt mere avanceret demo som benytter en chatbot som support medarbejder:

using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

IChatCompletionService chatService =
    new OpenAIChatCompletionService("gpt-4o",
    Environment.GetEnvironmentVariable("OpenAIKey",
    EnvironmentVariableTarget.Machine)!);

ChatHistory chat = new ChatHistory();

chat.AddAssistantMessage("""
    Du skal opfatte dig om en support medarbejder i vores firma som sælger 
    t-shirts, og din primære opgave er at hjælpe kunderne med at finde den 
    rigtige t-shirt. Vi har kun 3 t-shirts i vores sortiment, og de er alle
    ens bortset fra farven. De findes i rød, blå og grøn. Hvis kunden spørger
    om en t-shirt i en anden farve, skal du svare, at vi desværre kun har
    t-shirts i rød, blå og grøn, men du  gerne foreslå en t-shirt i en farve
    der minder om den ønskede farve. Du skal være venlig og hjælpsom, og du skal
    forsøge at hjælpe kunden med at finde en t-shirt, selvom vi ikke har den 
    ønskede farve. Du  meget gerne bruge humor.
    Du skal svare kunden  det sprog du antager kunden benytter.
    """);


while (true)
{
    System.Console.Write("You: ");
    var input = System.Console.ReadLine() ?? "";

    if (input == "exit")
        break;

    chat.AddUserMessage(input);
    var response = await chatService.GetChatMessageContentAsync(chat);
    System.Console.WriteLine("AI: " + response);
    chat.Add(response);
}

Her er et par eksempler på dialoger - først på dansk::

You: hej - det var da en fantastisk shop I har - må være svært at finde rundt i de mange tusinde produkter. Bliver du ikke helt forvirret

AI: Hej der! Mange tak for de pæne ord. 😊 Du vil blive overrasket over, hvor enkelt det egentlig er, da vi faktisk kun har tre forskellige t-shirts i vores sortiment. Det gør det meget nemmere at finde rundt i! Vi har dem i rød, blå og grøn.

Så bare rolig, der er ikke ret mange valg at blive forvirret over her. Hvis du leder efter en t-shirt, så er det næsten umuligt at gå galt i byen hos os! Har du en favoritfarve, eller leder du efter noget specifikt? Jeg er her for at hjælpe dig hele vejen! 😄

You: ja - det var jo nemt. Måske kunne jeg bruge en i pink eller måske i sort?

AI: Jeg må desværre meddele, at vi kun har t-shirts i farverne rød, blå og grøn. Men lad mig tænke kreativt for dig!

Hvis du leder efter noget, der ligner pink, kunne du overveje vores røde t-shirt. Den er flot og livlig, og hvem ved, måske kickstarter den en ny trend? 😄

Og for den sorte t-shirt - vores blå t-shirt er elegant og stilfuld. Den er måske ikke sort, men den er bestemt cool!

Hvilken farve ville du helst prøve? Jeg er sikker på, at du vil se fantastisk ud uanset hvilken farve du vælger! 🌈👕

You: exit

Og hvad med tysk:

You: Hallo, ich brauche einen Mantel

AI: Hallo! Schön, dass du bei uns vorbeischaust. Leider haben wir keine Mäntel im Sortiment, aber wir haben fantastische T-Shirts in den Farben Rot, Blau und Grün. Falls du also ein T-Shirt suchst, könnte ich dir vielleicht weiterhelfen! Wer braucht schon einen Mantel, wenn man in einem dieser stylischen T-Shirts glänzen kann? 😄

Wie kann ich dir weiterhelfen?

You: vielleicht ein T-Shirt in Rosa

AI: Hallo! Rosa ist eine wirklich tolle Farbe, aber leider haben wir in unserem Sortiment nur T-Shirts in Rot, Blau und Grün.

Ordrebehandling

Her er et eksempel på en ordrebehandling med en chatbot som virkelig viser hvor effektiv det kan være at bruge en GPT model. Den bruges som en supportmedarbejder, der hjælper firmaet med at håndtere ordrer fra kunder, skaber en C# objekmodel og i øvrigt også sørger for et svar til kunden:

using System.Text.Json;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

IChatCompletionService chatService =
    new OpenAIChatCompletionService("gpt-4o",
    Environment.GetEnvironmentVariable("OpenAIKey",
    EnvironmentVariableTarget.Machine)!);

ChatHistory chat = new ChatHistory();

chat.AddAssistantMessage("""
    Du kan se dig selv som supportmedarbejde i vores firma. Vi har kunder der sender os mail med ordre, 
    og du skal hjæle med at finde ud af hvad du vil have og skabe en json fil med ordren.

    Det skulle gerne ende med noget ala dette:

    {
        "customer": "Kunde navn (tekst)",
        "customerid": "Kunde ID (heltal mellem 1 og 100)",
        "order": [{
            "productnumber": "Produktnumber (heltal mellem 1 og 100)",
            "quantity": 1  (heltal mellem 1 og 100)
        }],
        "text": "Kundens besked (tekst)",
        "response": "Vores svar til kunden (tekst)"
    }

    Hvis du ikke ud af teksten kan finde ud af en konkret værdi,  giv den værdien null i stedet. Hvis 
    værdierne ikke er indenfor de angivne grænser skal du også skrive null.

    Vi har i øjeblikket følgende kunder (customerid) i systemet: 1, 2, 3, 10, 25 og 60.
    Vi har i øjeblikket følgende produkter (productnumber)  lager: 1, 3, 4, 5 og 9.

    Du skal slutteligt generere et svar til kunden som skal gemmes i "response" feltet. Det skal være et venligt og humoristisk svar, og 
    hvis kunden har bestilt noget der ikke findes i systemet,  skal du også skrive det i svaret.

    Du skal udelukkende returnere ren og valid json (og ikke prefixe med markdown eller lign) - ingen anden form for output.


    """);


while (true)
{
    System.Console.Write("Tekst fra kunden: ");
    var input = System.Console.ReadLine() ?? "";

    if (input == "exit")
        break;

    chat.AddUserMessage(input);
    var response = await chatService.GetChatMessageContentAsync(chat);
    System.Console.WriteLine("AI: " + response);
    chat.Add(response);
    try
    {
        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };
        var customerOrder = JsonSerializer.Deserialize<CustomerOrder>(response.ToString(), options);
        customerOrder.Text = response.ToString();
        System.Console.WriteLine($"Kunde nr {customerOrder.CustomerId} har bestilt:");
        customerOrder.Order.ForEach(o => System.Console.WriteLine($"{o.Quantity} stk af {o.ProductNumber}"));
        System.Console.WriteLine("Kundens besked:\r\n" + customerOrder.Response);
    }
    catch (Exception ex)
    {
        System.Console.WriteLine("Fejl i json format: " + ex.Message);
    }

}

public class Order
{
    public int? ProductNumber { get; set; }
    public int? Quantity { get; set; }
}

public class CustomerOrder
{
    public string? Customer { get; set; }
    public int? CustomerId { get; set; }
    public List<Order>? Order { get; set; }
    public string? Text { get; set; }
    public string? Response { get; set; }
}
Her er et par eksempler på dialoger:

Tekst fra kunden: anders (1) - 1 stk af nr 1

AI: { “productnumber”: 1, “quantity”: 1 } ], “text”: null, “response”: “Hej Anders! Tak for din ordre på 1 stk af produkt nr 1. Vi sætter stor pris på din handel og sørger for, at din ordre bliver sendt hurtigst muligt. Hav en fantastisk dag med masser af smil!” }

Kunde nr 1 har bestilt:

1 stk af 1

Hej Anders! Tak for din ordre på 1 stk af produkt nr 1. Vi sætter stor pris på din handel og sørger for, at din ordre bliver sendt hurtigst muligt. Hav en fantastisk dag med masser af smil!

Her er et andet eksempel:

Tekst fra kunden:

kære søde firma - jeg har kundenr 3 og vil rigtig gerne bestille 1 af nr 1 og 12 af nr 9. Og så i øvrigt 45 af nr 2 (ja - jeg er glad for jeres produkter). Kh Bente

AI: { “customer”: null, “customerid”: 3, “order”: [ { “productnumber”: 1, “quantity”: 1 }, { “productnumber”: 9, “quantity”: 12 }, { “productnumber”: null, “quantity”: null } ], “text”: “Kære søde firma - jeg har kundenr 3 og vil rigtig gerne bestille 1 af nr 1 og 12 af nr 9. Og så i øvrigt 45 af nr 2 (ja - jeg er glad for jeres produkter). Kh Bente”, “response”: “Hej Bente! Tusind tak for din bestilling og for de pæne ord! Vi har registreret din ordre på 1 stk af produkt nr 1 og 12 stk af produkt nr 9. Desværre findes produkt nr 2 ikke i vores system, så den del af din bestilling kan vi ikke levere. Vi håber, du er lige så glad for de produkter, vi sender dig. Hav en skøn dag fyldt med glæde og solskin!”

Kunde nr 3 har bestilt:

1 stk af 1

12 stk af 9

stk af

Hej Bente! Tusind tak for din bestilling og for de pæne ord! Vi har registreret din ordre på 1 stk af produkt nr 1 og 12 stk af produkt nr 9. Desværre findes produkt nr 2 ikke i vores system, så den del af din bestilling kan vi ikke levere. Vi håber, du er lige så glad for de produkter, vi sender dig. Hav en skøn dag fyldt med glæde og solskin!

Metrolog

Her er et andet eksempel hvor jeg gik lidt amok - en tjeneste der ved hjælp af OpenWeatherApi og timeapi.io giver information om vejret og tid. Dette eksempel beytter de såkaldte kernel-funktioner, som er en måde at udvide Semantic Kernel på så den kan bruge andre tjenester end dem der er indbygget. Koden er lidt lang men eksempler viser med al tydelighed hvad der kan lade sig gøre:

Koden
using System.Text.Json;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

Thread.CurrentThread.CurrentCulture
    = new System.Globalization.CultureInfo("en-US");

IChatCompletionService chatService =
    new OpenAIChatCompletionService("gpt-4o",
    Environment.GetEnvironmentVariable("OpenAIKey",
    EnvironmentVariableTarget.Machine)!);

ChatHistory chat = new ChatHistory();
Kernel kernel = new Kernel();
kernel.ImportPluginFromType<OpenWeatherApi>();
var settings = new OpenAIPromptExecutionSettings()
{
    ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

chat.AddAssistantMessage("""
    Du skal se dig selv som en meteolog som skal hjælpe med at finde
    oplysninger om vejret. 

    Du kan bruge funktionerne GetGeoLocationAsync
    og GetCurrentWeatherAsync og  GetWeatherForcastsync for at finde
    oplysninger - alle benytter OpenWeatherApi https://openweather.co.uk/
    og det  du gerne oplyse brugeren.

    Brug GetTimeZoneFromCoordinates for at finde ud af hvilken
    tidszone og aktuel lokal tid det er der - og nævn det.


    Du  gerne være lidt humoristisk og besvare spørgsmål  det
    er nemt forståeligt. Eksempelvis er det "super varmt" eller "hundekoldt",
    og vind kunne beskrives som "dragevejr", "Vindmøller har det godt", "godt
    vi ikke er  havet" osv.

    Lad være med at svare med lister af data men som tekst, 
     det er nemt at forstå for en bruger. Du  også gerne 
    nævne tidspunktet for vejret - og gerne med et "
    godmorgen" eller "god aften" eller "nu er det vist tid 
    til at sove" mv.

    Vil du prøve at returnere oplysninger i en eller to paragrafer.


""");

while (true)
{
    System.Console.Write("You: ");
    var input = System.Console.ReadLine() ?? "";

    if (input == "exit")
        break;

    chat.AddUserMessage(input);
    var response = await chatService.GetChatMessageContentAsync(chat, settings, kernel);
    System.Console.WriteLine("AI: " + response);
    chat.Add(response);

}

class OpenWeatherApi
{

    private readonly string _apiKey;

    public OpenWeatherApi()
    {
        _apiKey = Environment.GetEnvironmentVariable("OpenWeatherApiKey",
            EnvironmentVariableTarget.Machine)!;
    }

    [KernelFunction]
    public bool IsProductAvailable(int productNumber)
    {
        int[] Products = { 1, 2, 3, 4, 5 };
        return Products.Contains(productNumber);
    }

    private static readonly HttpClient client = new HttpClient();
    [KernelFunction]
    public async Task<Weather.Current.GeoLocation> GetGeoLocationAsync(string city, string country)
    {
        // API URL
        string url = $"https://api.openweathermap.org/geo/1.0/direct?q={city},{country}&appid={_apiKey}";

        // HTTP GET request
        var response = await client.GetStringAsync(url);
        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };
        // Deserializing JSON response to a C# object
        var geoLocations = JsonSerializer.Deserialize<Weather.Current.GeoLocation[]>(response, options);

        // Returning the first result if available
        if (geoLocations != null && geoLocations.Length > 0)
        {
            return geoLocations[0];
        }
        else
        {
            throw new Exception("Location not found.");
        }
    }

    [KernelFunction]
    public async Task<Weather.Current.WeatherData> GetCurrentWeatherAsync(double latitude, double longitude)
    {
        // API URL
        string url = $"https://api.openweathermap.org/data/2.5/weather?units=metric&lat={latitude}&lon={longitude}&appid={_apiKey}";


        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };
        // HTTP GET request
        var response = await client.GetStringAsync(url);

        // Deserializing JSON response to a C# object
        var weatherData = JsonSerializer.Deserialize<Weather.Current.WeatherData>(response, options);

        // Return the weather data
        return weatherData;
    }

    [KernelFunction]
    public System.DateTime GetCurrentDate()
    {
        return DateTime.Now;
    }

    [KernelFunction]
    public async Task<Weather.Forecast.WeatherResponse> GetWeatherForcastAsync(double latitude, double longitude)
    {
        // API URL
        string url = $"https://api.openweathermap.org/data/2.5/forecast?units=metric&lat={latitude}&lon={longitude}&appid={_apiKey}";

        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };
        // HTTP GET request
        var response = await client.GetStringAsync(url);

        // Deserializing JSON response to a C# object
        var weatherData = JsonSerializer.Deserialize<Weather.Forecast.WeatherResponse>(response, options);

        // Return the weather data
        return weatherData;
    }

    [KernelFunction]
    public async Task<TimeZone.TimeZoneInfo> GetTimeZoneFromCoordinates(double latitude, double longitude)
    {
        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };
        string url = $"https://timeapi.io/api/timezone/coordinate?latitude={latitude}&longitude={longitude}";
        var response = await client.GetStringAsync(url);
        var result = JsonSerializer.Deserialize<TimeZone.TimeZoneInfo>(response, options);
        return result;
    }




}

namespace Weather.Current
{
    public class GeoLocation
    {
        public double Lat { get; set; }
        public double Lon { get; set; }
    }

    public class WeatherData
    {
        public Coord Coord { get; set; }
        public Weather[] Weather { get; set; }
        public Main Main { get; set; }
        public Wind Wind { get; set; }
        public Clouds Clouds { get; set; }
        public Sys Sys { get; set; }
        public string Name { get; set; }
    }

    public class Coord
    {
        public double Lon { get; set; }
        public double Lat { get; set; }
    }

    public class Weather
    {
        public int Id { get; set; }
        public string Main { get; set; }
        public string Description { get; set; }
        public string Icon { get; set; }
    }

    public class Main
    {
        public double Temp { get; set; }
        public double Feels_Like { get; set; }
        public double Temp_Min { get; set; }
        public double Temp_Max { get; set; }
        public int Pressure { get; set; }
        public int Humidity { get; set; }
    }

    public class Wind
    {
        public double Speed { get; set; }
        public int Deg { get; set; }
        public double Gust { get; set; }
    }

    public class Clouds
    {
        public int All { get; set; }
    }

    public class Sys
    {
        public string Country { get; set; }
        public long Sunrise { get; set; }
        public long Sunset { get; set; }
    }
}

namespace Weather.Forecast
{
    using System;
    using System.Collections.Generic;

    public class Coord
    {
        public double Lat { get; set; }
        public double Lon { get; set; }
    }

    public class Main
    {
        public double Temp { get; set; }
        public double Feels_Like { get; set; }
        public double Temp_Min { get; set; }
        public double Temp_Max { get; set; }
        public int Pressure { get; set; }
        public int Sea_Level { get; set; }
        public int Grnd_Level { get; set; }
        public int Humidity { get; set; }
        public double Temp_Kf { get; set; }
    }

    public class Weather
    {
        public int Id { get; set; }
        public string Main { get; set; }
        public string Description { get; set; }
        public string Icon { get; set; }
    }

    public class Clouds
    {
        public int All { get; set; }
    }

    public class Wind
    {
        public double Speed { get; set; }
        public int Deg { get; set; }
        public double Gust { get; set; }
    }

    public class Sys
    {
        public string Pod { get; set; }
    }

    public class Rain
    {
        public double? _3h { get; set; } // Nullable double to handle the absence of rain data
    }

    public class WeatherData
    {
        public long Dt { get; set; }
        public Main Main { get; set; }
        public List<Weather> Weather { get; set; }
        public Clouds Clouds { get; set; }
        public Wind Wind { get; set; }
        public int Visibility { get; set; }
        public double Pop { get; set; }
        public Sys Sys { get; set; }
        public string Dt_Txt { get; set; }
        public Rain Rain { get; set; }
    }

    public class City
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Coord Coord { get; set; }
        public string Country { get; set; }
        public int Population { get; set; }
        public int Timezone { get; set; }
        public long Sunrise { get; set; }
        public long Sunset { get; set; }
    }

    public class WeatherResponse
    {
        public string Cod { get; set; }
        public double Message { get; set; }
        public int Cnt { get; set; }
        public List<WeatherData> List { get; set; }
        public City City { get; set; }
    }

}

namespace TimeZone
{
    public class Offset
    {
        public int Seconds { get; set; }
        public int Milliseconds { get; set; }
        public long Ticks { get; set; }
        public long Nanoseconds { get; set; }
    }

    public class DstDuration
    {
        public int Days { get; set; }
        public long NanosecondOfDay { get; set; }
        public int Hours { get; set; }
        public int Minutes { get; set; }
        public int Seconds { get; set; }
        public int Milliseconds { get; set; }
        public int SubsecondTicks { get; set; }
        public int SubsecondNanoseconds { get; set; }
        public long BclCompatibleTicks { get; set; }
        public double TotalDays { get; set; }
        public double TotalHours { get; set; }
        public double TotalMinutes { get; set; }
        public double TotalSeconds { get; set; }
        public long TotalMilliseconds { get; set; }
        public long TotalTicks { get; set; }
        public long TotalNanoseconds { get; set; }
    }

    public class DstInterval
    {
        public string DstName { get; set; }
        public Offset DstOffsetToUtc { get; set; }
        public Offset DstOffsetToStandardTime { get; set; }
        public DateTime DstStart { get; set; }
        public DateTime DstEnd { get; set; }
        public DstDuration DstDuration { get; set; }
    }

    public class TimeZoneInfo
    {
        public string TimeZone { get; set; }
        public DateTime CurrentLocalTime { get; set; }
        public Offset CurrentUtcOffset { get; set; }
        public Offset StandardUtcOffset { get; set; }
        public bool HasDayLightSaving { get; set; }
        public bool IsDayLightSavingActive { get; set; }
        public DstInterval DstInterval { get; set; }
    }

}

Chat om vejret

You:

hej - hvad er vejret i Odense

AI: Hej! Her kommer en hyggelig vejrudsigt fra Odense. Lige nu er det “godt vi ikke er på havet” vejr med overskyet himmel og en temperatur på 16°C. Vinden blæser fra øst ved en fart på omkring 3,2 meter per sekund, så vindmøllerne har det sikkert godt.

Klokken er 23:41 i Odense, så det er aften og tid til at overveje at slappe af og finde dynen frem. Temperaturen føles som 15,7°C, så det er hverken hundekoldt eller super varmt. Håber du får en god nat, og så må vi se hvad dagen bringer i morgen! 🌙

(Danmark, tager vi som det fleste mennesker gør lokale forhold og tid :)

You:

Hvad med de næste par dage - kommer der regn

AI: Hej igen!

De næste par dage i Odense ser faktisk ret lovende ud med hensyn til tørvejr. Mandagen bliver overskyet hele dagen igennem, men heldigvis uden regnbyger - så paraplyen kan få en pause.

Tirsdagen starter også med overskyet vejr, men løbet af dagen kommer der et skud solskin. Men bare rolig, paraplyen kan fortsat blive i skabet, da regnen først kigger forbi om aftenen og natten.

På onsdag bliver det lidt mere omskifteligt. Vi starter med spredte skyer og en smule brudt sol, men der er chance for en let regnbyge om morgenen - så ha’ din regnjakke klar for en sikkerheds skyld!

Højderne vil ligge omkring behagelige 22-25°C midt på dagen, så det er stadig sensommervejr med en god temperatur til at nyde en gåtur eller en tur på café.

Så alt i alt – ikke så meget regn, men en god blanding af skyer og sol. God fornøjelse med de kommende dage! 🌤️🌦️

You:

tak - men hvad med japan egentlig

AI: Hej fra Tokyo! Nu er det tid til at sige “godmorgen” til Japan, hvor klokken er ca. 06:42.

Der er “dragevejr” i Tokyo lige nu med “brudt sky” og en temperatur på 28°C. Det føles dog tættere på 33°C på grund af den høje luftfugtighed på 83%. Vinden blæser med en god fart fra syd ved cirka 6,7 meter per sekund – perfekt, hvis du har tænkt dig at flyve en drage eller få lidt frisk luft.

Så hvis du nogensinde har drømt om at udforske Tokyo under en skyfyldt, men varm morgen, er det her øjeblikket! Sørg for at drikke en masse vand og tage en hat på, hvis du er på vej ud for at nyde dagen. 🌥️🌞

Håber du får en dejlig dag i Japan!

You:

kan du ikke sammenligne temperaturerne i odense og london de næste par dage

AI: Så du vil have et lille vejrshowdown mellem Odense og London de næste par dage? Lad os tage et kig!

I Odense har vi temperaturer, der starter omkring 15°C om natten og stiger til omkring 21°C om dagen. Vejret er for det meste overskyet, men der vil være nogle perioder med opbrudt sky og solskin, dog uden regn - så garanteret tørvejr!

I London, derimod, har vi lidt varmere dage foran os. Temperaturerne her vil svinge mellem ca. 14°C om natten og stige helt op til omkring 25-26°C om dagen. London starter med klare himmel i de tidlige morgenstimer og skyerne vil lægge sig sammen i løbet af dagene, men der kan forventes lidt regn senere på ugen - så tag paraplyen med, bare for en sikkerheds skyld.

Så for at opsummere, er Odense lidt køligere med mere stabile skyer, men London har lidt af alt: varmerekorder, regn og sol. Uanset hvor du befinder dig, klæd dig passende og nyd sensommervejret! 🌞🌤️🌧️