Large Language Models (LLMs) sind in aller Munde und ich dachte mir, dass es Zeit wird ein
wenig mehr mit diesen zu experimentieren und abseits der täglichen Nutzung in meinen
Arbeitsalltag herauszufinden, wie diese eingesetzt werden können.
Hierbei springe ich bewusst nicht auf dem Zug auf, der uns allen erzählt, wie bunt und toll
die Welt wird, wenn uns autonome KI-Agenten in „nicht absehbarer“ Zeit ersetzen und unsere
Arbeit übernehmen, sondern habe mir ein Anwendungsszenario überlegt, bei dem ein LLM eine
Benutzer:in einer Geschäftsanwendung unterstützt. Wichtig war es mir hierbei auch
herauszufinden, inwieweit man bei dem Einsatz von LLMs auch gewisse datenschutzrelevante
Spannungsfelder berücksichtigen kann. Genau gesagt wollte ich herausfinden, ob man mit selbst
gehosteten LLMs die Modelle der großen Technologieanbieter wie ChatGPT, Microsoft Copilot
etc., ersetzen kann.
Zusammengefasst: In diesem Blogbeitrag beschreibe ich, wie ein Large Language Model mit Hilfe
von Ollama als Docker Container „lokal“ in Form einer Azure Container Instance gehostet und
mit einer Azure Function aufgerufen wird, um eine Serviceanfrage aus Microsoft Dynamics 365
Customer Service mit Schlagworten zu versehen, so dass die Anfrage nachgelagert z.B. in eine
entsprechende Queue vorsortiert werden kann. Hierbei gehe ich kurz auf Large Language Models
ein, beschreibe was Ollama ist und zeige Schritt für Schritt auf, wie ich mein
Anwendungsszenario umgesetzt habe.
Was sind Large Language Model (LLM)?
Im Zusammenhang mit LLMs wird auch oft der Begriff der generativen KI genutzt. LLMs sind nicht gleichzusetzen mit generativer KI, sondern sind ein Bauteil dieser. Generative KI umfasst alle Technologien, die Inhalte (Text, Bild, Musik) eigenständig erzeugen können. Microsoft Copilot, ChatGPT und andere sind generative und vortrainierte Modelle, die als cloudbasierte Services laufen und Daten an externe Server zur Verarbeitung schicken können. Um die externe Verarbeitung von kritischen Daten zu verhindern, kann man auf Dienste zurückgreifen, die LLMs lokal auf einem Rechner ausführen, ohne hierbei Daten an Dritte zu verschicken. Ollama ist solch ein Dienst. Mithilfe von Ollama können eine Vielzahl von LLMs wie Llama oder Phi genutzt werden, ohne hierbei die Komplexität der Installation und Konfiguration der Modelle zu haben. Die Modelle können einfach als Docker-Container lokal ausgeführt werden.
Das Anwendungsszenario: Automatisierte Verschlagwortung einer Serviceanfrage in Dynamics 365 Customer Service mithilfe von Large Language Models
Bei der Nutzung von Microsoft Dynamics 365 Customer Service helfen Warteschlangen bei der schnellen Bearbeitung von Serviceanfragen, indem Anfragen unter anderem thematisch vorsortiert werden können. Das Vorsortieren kann je nach Reifegrad eines Serviceprozesses entweder manuell durch eine Mitarbeiter:in vorgenommen werden oder automatisch durch Routing-Regelsätze, die nach einfachen Suchkriterien („Betreff enthält Wort „X“) eine Anfrage automatisch zuordnen. Wir möchten nun mithilfe eines LLMs das automatische Routing verbessern, indem wir den Anfragentext mithilfe des LLMs nach Schlagwörtern durchsuchen, um diese dann nachgelagert für ein Routing zu nutzen.
- Microsoft Dynamics 365 Customer Service: Die Serviceanfrage ist Teil der First-Party App zur Verwaltung von Serviceprozessen. In unserem Szenario werden wir auf diese Tabelle zurückgreifen. Damit eine Verschlagwortung der Serviceanfrage möglich ist, legen wir die neue Tabelle „Kategorie“ an. Diese Tabelle hat nur ein relevantes Feld „Name“ und eine N:M-Beziehung zu der Serviceanfrage. Wir möchten es ermöglichen, dass eine Serviceanfrage zu mehreren Kategorien zugeordnet werden kann und eine Kategorie mit mehreren Serviceanfragen.
- Azure Functions: Die Kommunikation mit dem Large Language Model wird über eine Azure Function abgebildet. Die Azure Function wird mithilfe eines http-Triggers aus dem CRM aufgerufen. Dies passiert nach der Anlage der Serviceanfrage. Sobald diese erstellt wurde, wird die Azure Function mit dem Query-Parameter caseId aufgerufen. Die caseId enthält die Guid der Serviceanfrage. Innerhalb der Azure Function erstellen wir einen Prompt und schicken diesen an unser selbst gehostetes LLM.
- Azure Container Registry: Hier legen wir unser Docker Image ab, um daraus einen Container zu erstellen.
- Azure Container Instances: Dies ist ein Service von Microsoft Azure, der es ermöglicht, Container-Anwendungen direkt in der Cloud auszuführen. Wir werden das Docker-Image von Ollama hier hosten, sodass die Azure Function die Prompts gegen diese Container Instance schickt.
Der generelle Ablauf ist also wie folgt: In Dynamics 365 Customer Service pflegen wir Kategorien, zu denen Serviceanfragen zugeordnet werden können. Wenn eine Serviceanfrage angelegt wird, rufen wir automatisch eine Azure Function auf. Diese Azure Function holt sich die Beschreibung der Serviceanfrage sowie alle Kategorien und erstellt hieraus einen Prompt. Dieser Prompt wird an ein LLM geschickt, welches wir mithilfe von Ollama in einer Azure Container Instance laufen lassen. Zu guter Letzt schickt die Azure Function identifizierte Kategorien an das CRM zurück, sodass ein Routing der Anfrage vorgenommen werden kann.
Hands on: Dynamics 365 Customer Service erweitern und neue Tabelle anlegen
Nachfolgend zeige ich die Schritte auf, die ich durchgefüht habe, um das oben beschriebene
Szenario umzusetzen. Zunächst erweitern wir Dynamics 365 Customer Service.
Wir legen unsere neue Entität „Kategorie“ an. Hierfür öffnen wir make.powerapps.com und
klicken in der Navigation auf „Solutions/Lösungen“. Nachdem wir eine neue Lösung
„LLMPlayground“ angelegt haben, fügen wir dieser eine neue Tabelle hinzu. Die Tabelle nennen
wir „Kategorie“ und stellen sicher, dass auf dem Hauptformular das Namensfeld angezeigt wird.
Nach der Anlage der Tabelle erstellen wir eine neue N:M-Beziehung zwischen der Kategorie und
der Anfrage. Als letzten Schritt passen wir das Anfragenformular an und fügen ein Subgrid auf
die Kategorien hinzu. Hierdurch können nun Kategorien, die durch unsere Azure Function
identifiziert worden sind, direkt mit der Anfrage verknüpft werden.
Azure Container Instances aufsetzen und Ollama-Image mit dem LLM Phi3.5 deployen
Unser Docker Image liegt nun in der Container Registry und wir können eine Azure Container Instance auf Grundlage dessen erzeugen. Hierfür öffnen wir portal.azure.com, navigieren in unsere Ressourcengruppe und erstellen eine neue Container Instances. Bei der Anlage der Container Instances wählen wir bei der Image Source "Azure Container Registry" aus und nutzen das soeben hochgeladene Image. Bei der Size belassen wir die CPU so wie die Default-Einstellungen sind, jedoch müssen wir die Memory auf 8 GiB erhöhen, da ansonsten nicht genug Platz für das LLM vorhanden ist. In den Netzwerkeinstellungen geben wir den Port 11434 frei.
Nach erfolgreichen Deployment der Container Instance, könnt ihr die Container Instance öffnen. Für einen späteren Schritt benötigen wir die öffentliche IP-Adresse, da wir so das LLM ansprechen können.
Anlegen unseres Azure Functions Projekt und Aufruf des LLMs mit Microsoft.Extensions.Ai
Nachdem wir nun unseren Docker Container mit dem Image von Ollama in der Azure Cloud gehostet
haben, widmen wir uns dem Schreiben unserer Azure Function, die eine Anfragen von Dynamics 365
Customer Service entgegennimmt, die Beschreibung der Anfrage sowie alle Kategorien abruft und
diese an das LLM schickt.
Als erstes legen wir und Azure Functions Projekt mithilfe von Visual Studio Code und der den Azure Functions Core Tools an. Da ich bereits in vorherigen Blog Posts im Detail darauf eingangen bin, wie wir mit den Core
Tools ein Functions Project anlegen und wie wir uns zu Dataverse verbinden, gehe ich in diese Post
nicht mehr im Detail darauf ein. Nachfolgend die Links zum Nachlesen:
- Azure Functions Projekt mithilfe der Core Tools anlegen
- Mithilfe einer Managed Identity zu Dataverse verbinden
Um loslegen zu können, müssen wir die relvanten Nuget-Packages installieren:
Unser appsettings-Eintrag "OllamaURl" enthält die URL, die wir bei der Erstellung der Container Instance erhalten haben. Diese URL enthält die öffentliche IP-Adresse der Container Instance und den Port 11434. Die local.settings.json sehen wie folgt aus:
Sobald die Azure Function in die Cloud gepushed wurde, müssen die von uns angelegten Einträge in der local.settings.json noch im Bereich der Umgebungsvariablen in der Azure Function App hinzugefügt werden.
Unsere eigentliche Function benennen wir CaseTagging.cs und in einem ersten Schritt reichen wir über den Konstruktor unsere relevanten Abhängigkeiten hinein. Dies sind der IOrganizationService, um mit dem Dynamics 365 zu kommunizieren sowie der IChatClient, um ein Gespräch mit unserem LLM zu führen.
Die Azure Function wird über einen HTTP-Trigger aufgerufen und bekommt als Query-Parameter die Id der Serviceanfrage aus dem CRM mitgegeben. Diese Id speichern wir in der Variablen "caseId". Anschließend holen wir uns die Serviceanfrage sowie die Kategorien mithilfe des IOrganizationService und bauen unseren Prompt.
Damit das LLM jedoch auch immer eine Antwort zurückgibt, die in unseren Type CategoryList konvertiert werden kann, muss unser Prompt richtig entworfen werden. Dies tun wir in der Methode GeneratePrompt. Unser Prompt setzt sich zum Teil aus standardisiertem Text und zum Teil aus dynamischem Inhalt zusammen, den wir aus der Anfrage laden. In dem Prompt lassen wir das LLM auch wissen, in welchem Format wir die Antwort erwarten.
Nachdem die Azure Function erfolgreich durchgelaufen ist, sollte die Serviceanfrage in Dynamics 365 Customer Service nun mit relevanten Kategorien versehen sein. Diese Kategorien können dann für ein Routing oder andere nachgelagerte Prozessschritte genutzt werden.
Recap: Wie Large Language Models den Kundenservice in Dynamics 365 unterstützen können
Das Deployen des LLMs in einer Azure Container Instance ist relativ einfach und schnell. Ollama bietet uns eine Alternative zu den LLMs der großen Technologieanbieter. Somit haben wir deutlich mehr Kontrolle über die Weiterverarbeitung unternehmensrelevanter Daten.
Die Bibliothek Microsoft.Extensions.Ai bietet uns eine einfache Möglichkeit, LLMs in .Net Anwendungen zu integrieren. Ohne viel Code zu ändern, könnte man das genutzte LLM mithilfer dieser Bibliothek einfach austauschen.
Ohne die nachfolgenden zwei Blogartikel wäre es mir nicht so einfach gefallen, dieses Thema zu behandeln.
- Working with LLMs in .NET using Microsoft.Extensions.AI - Milan Jovanovic
- Azure Container Apps with Ollama for general AI inference - Batsirai Tambo
Ollama-Agent for Dynamics 365 Customer Service
Wie immer freue ich mich über Feedback von eurer Seite. Wenn ihr Fragen zu dem Thema habt, dann schreibt mir einfach!