adesso Blog

Künstliche Intelligenz ist längst kein reines Backend-Thema mehr, das nur auf riesigen Serverfarmen stattfindet. Immer häufiger entstehen Anwendungen, die direkt auf dem Endgerät mit Large Language Models (LLMs) arbeiten oder diese gezielt orchestrieren. Doch wer schon einmal versucht hat, die kreativen Ergüsse einer KI in eine starre App-Logik zu pressen, kennt das Problem: Ein Prompt ist noch lange kein verlässlicher Service.

Genau hier setzt Koog an. Die Kotlin-basierte Library von JetBrains verspricht, KI-Agenten strukturiert, reproduzierbar und plattformübergreifend umsetzbar zu machen. In diesem Blog-Beitrag nehme ich euch mit in einen kleinen Proof of Concept (PoC) und zeige, wie sich mit Koog, Kotlin und Compose Multiplatform ein sinnvoller Agent bauen lässt, der Ergebnisse liefert, mit denen euer Code auch wirklich etwas anfangen kann.

Warum eigentlich Koog?

Vielleicht fragt ihr euch: Brauchen wir wirklich noch ein Framework? Die Antwort lautet: Ja, wenn wir über Stabilität sprechen. Koog verfolgt einen klaren Ansatz: Nicht jeder Prompt ist gleich ein Agent.

Ein echter Agent in einer produktiven Anwendung benötigt mehr als nur ein Textfeld und eine Antwort. Er besteht aus:

  • Einer klaren Aufgabe (Mission).
  • Einer definierten Struktur (Workflow).
  • Einem kontrollierten Output (Typsicherheit).

Statt unstrukturierter Textantworten („Hier ist dein Plan, viel Erfolg dabei!“) lassen sich mit Koog reproduzierbare Ergebnisse erzeugen, die direkt in der App weiterverarbeitet werden können. Das ist besonders hilfreich für Anwendungen, die über einfache Chat-UIs hinausgehen. Da Koog Teil des Kotlin-Ökosystems ist, fühlt sich die Definition von Agenten für uns Android- und Kotlin-Entwickler:innen sofort vertraut an – typ-sicher und idiomatisch.

Das Szenario: Ein Goal-Decomposition-Agent

Um die Theorie greifbar zu machen, schauen wir uns ein konkretes Beispiel an. Statt nur simple Aufgaben oder Termine zu erkennen, setzt unser Agent einen Schritt höher an. Er ist ein Goal-Decomposition-Agent. Seine Aufgabe: Er nimmt ein abstraktes, großes Ziel entgegen und zerlegt es in machbare, strukturierte Häppchen.

Stellt euch vor, eine Nutzerin oder ein Nutzer gibt ein:

„Ich möchte in sechs Monaten einen Halbmarathon laufen.“

Ein normaler Chatbot würde nun wahrscheinlich einen langen Fließtext mit Tipps zu Laufschuhen und Ernährung ausspucken. Unser Koog-Agent hingegen liefert ein klar definiertes Ergebnisobjekt zurück, das Folgendes beinhaltet:

  • 1. Ein validiertes Hauptziel.
  • 2. Mehrere logische Teilziele (Meilensteine).
  • 3. Konkrete Aktionen für den Start.
  • 4. Mögliche Einschränkungen oder Risiken.
  • 5. Einen empfohlenen ersten Schritt.

Der Clou dabei: Das Ergebnis ist kein Markdown-Text, den wir mühsam parsen müssen, sondern eine JSON-Struktur, die direkt in eine Kotlin-Datenklasse (Data Class) übersetzt wird.

Die Macht der Struktur: Typsicherheit trifft KI

Die gesamte App arbeitet mit einer klaren Struktur. Für unser Beispiel definieren wir eine Datenklasse, die das gewünschte Ergebnis abbildet. Diese könnte in etwa so aussehen (vereinfacht):

@Serializable
data class GoalDecomposition(
val mainGoal: String,
val milestones: List<String>,
val actions: List<Action>,
val constraints: List<String>,
val firstStep: String
)

Diese Struktur ist bewusst einfach gehalten. Sie eignet sich sowohl für die direkte Darstellung in der UI als auch für eine spätere Weiterverarbeitung – etwa um die Teilziele als echte To-Dos in eine Datenbank zu speichern oder in den Kalender einzutragen.

Der Agent selbst wird in Koog sehr schlank definiert. Er benötigt im Wesentlichen drei Zutaten:

  • Das Modell: Welches LLM soll verwendet werden? (GPT-4o, Claude 3.5 oder ein lokales Modell via Ollama).
  • Der System-Prompt: Die Anweisung, wie sich der Agent verhalten soll.
  • Das erwartete Ausgabeformat: Hier geben wir unsere GoalDecomposition-Klasse an.

internal class GoalDecompositionAgent(
apiKey: String
) {
private val service = AIAgentService(
promptExecutor = simpleOpenRouterExecutor(apiKey = apiKey),
llmModel = OpenRouterModels.GPT4oMini,
systemPrompt = """
Du bist ein minimalistischer Assistent.
Zerlege ein Ziel in Goal, Subgoals, Actions, Constraints und FirstStep.
Antworte **ausschließlich** mit validem JSON im folgenden Format:

{
"goal": "Ziel",
"subgoals": ["Teilziel1", "Teilziel2"],
"actions": ["Action1", "Action2"],
"constraints": ["Constraint1", "Constraint2"],
"firstStep": "Der erste Schritt"
}
""".trimIndent()
)

suspend fun analyze(input: String): GoalDecomposition = withContext(Dispatchers.Default) {
val response = service.createAgentAndRun(input)
val parsed = response.toGoalDecompositionSafe()
parsed ?: throw IllegalStateException("Konnte JSON nicht parsen. Response: $response")
}
}

Der wichtigste Punkt ist hierbei die Validierung. Koog sorgt (oft in Kombination mit den "Structured Output"-Features moderner Modell-Provider) dafür, dass der Agent ausschließlich valides JSON liefert, das exakt zu unserer Datenklasse passt.

Durch diese strikte Trennung bleibt die App stabil. Die UI muss nicht raten, ob die KI gerade halluziniert oder Formatierungszeichen vergessen hat. Sie erhält immer ein gültiges Objekt oder einen klaren Fehler. Das macht den Unterschied zwischen einer Spielerei und einer robusten Anwendung.

One Codebase, many Platforms: Compose Multiplatform

Das Frontend unseres PoCs ist mit Compose Multiplatform umgesetzt. Das Motto „Ein Screen, ein Code-Pfad, mehrere Plattformen“ passt perfekt zum ebenfalls plattformunabhängigen Ansatz von Koog.

Der Screen zeigt:

  • Ein Eingabefeld für das Ziel.
  • Einen Ladezustand (während Koog im Hintergrund arbeitet).
  • Strukturierte Karten für die einzelnen Ergebnisbereiche (Ziele, Aktionen, Risiken).

Hier zeigt sich der Vorteil der Typsicherheit besonders stark: Die UI arbeitet nicht mit Strings oder unsicheren Maps, sondern direkt mit der GoalDecomposition-Datenklasse. Wir können durch Listen iterieren, Typen prüfen und UI-Elemente basierend auf dem Status (actions.isEmpty()) ein- oder ausblenden.

Status Quo: Wo läuft es?

Das Projekt aus dem Beispiel ist aktuell:

  • Android-ready
  • Desktop-ready (JVM)
  • iOS (noch nicht aktiv im Setup)

Da sowohl Koog als auch Compose Multiplatform stark auf Kotlin Multiplatform (KMP) setzen, ist eine iOS-Integration absolut realistisch und einer der nächsten logischen Schritte. Sie erfordert aktuell jedoch noch zusätzliche Anpassungen, insbesondere im Bereich der Netzwerk-Konfiguration und der Einbindung der iOS-spezifischen LLM-Clients.

Das Projekt ist unter https://gitlab.com/fabian-rump/smartassistant zu finden.

Ein Blick unter die Haube: Was Koog noch kann

Unser Beispiel kratzt nur an der Oberfläche. Koog bietet für komplexe Szenarien noch deutlich mehr Werkzeuge, die für Enterprise-Anwendungen spannend sind:

  • Tools & Functions: Man kann dem Agenten echte Kotlin-Funktionen als "Werkzeuge" bereitstellen. Der Agent kann dann entscheiden, ob er eine Funktion aufruft (getWeather(city: String)), um seine Antwort anzureichern.
  • Graph-basierte Strategien: Für komplexe Abläufe lässt sich der Entscheidungsprozess des Agenten als Graph modellieren. Das verhindert, dass der Agent sich in einer Endlos-Schleife verfängt, und macht das Verhalten deterministischer.
  • Testing: Da der Agent in Kotlin-Code gekapselt ist, lässt er sich deutlich besser in Unit- und Integrationstests einbinden als ein reiner String-Prompt.

Fazit

Koog zeigt eindrucksvoll, wie sich KI-Agenten jenseits von Chat-UIs sinnvoll in den Entwicklungsalltag integrieren lassen. In Kombination mit Kotlin und Compose Multiplatform entstehen Anwendungen, die strukturiert, typsicher, plattformübergreifend und vor allem gut wartbar sind.

Der hier vorgestellte Goal-Decomposition-Agent ist nur ein einfaches Beispiel. Denkbar sind Agenten für Lernpläne, automatisierte Projektplanung, Entscheidungsfindung im Business-Kontext oder sogar Code-Reflexion.

Wer KI nicht nur konsumieren, sondern aktiv und robust gestalten möchte, sollte Koog definitiv einen Blick gönnen – es bringt die Ordnung in das Chaos der Sprachmodelle, die wir für professionelle Softwareentwicklung brauchen.

Bild Fabian Rump

Autor Fabian Rump

Fabian Rump ist als Senior Software Developer bei adesso am Standort Essen tätig. Er ist Experte für die native Android-Entwicklung unter Verwendung neuester Technologien und Best Practices. Andererseits verfügt er über fundierte Kenntnisse in der Cross-Plattform-Entwicklung mit dem Framework Flutter. Darüber hinaus interessiert sich Fabian für aktuelle Trendthemen wie Big Data, Machine Learning und IoT-Technologien.