10 Punkte von GN⁺ 2025-06-11 | 2 Kommentare | Auf WhatsApp teilen
  • Mit der jüngsten Zunahme beim Einsatz von KI-Agenten steigt auch der Einsatz hybrider Stacks auf Basis der Programmiersprache Go
  • Agenten zeichnen sich durch lange Laufzeiten, hohe Kosten und häufige Ein-/Ausgabe-Wartezeiten aus
  • Go bietet mit leichtgewichtigen Goroutinen, zentralisierten Abbruchmechanismen und kanalbasierter Nachrichtenübermittlung ein hochperformantes Nebenläufigkeitsmodell
  • Die Standardbibliothek ist umfangreich, und mit Profiling-Tools wie pprof lassen sich Speicher- und Thread-Lecks leicht nachverfolgen
  • Allerdings hat Go auch Grenzen, darunter ein schwächeres Machine-Learning-Ökosystem, eine nicht überragende Spitzenperformance und weniger Third-Party-Support als andere Sprachen

Was ist ein Agent?

  • Ein Agent läuft in einer wiederholten Schleife und bezeichnet einen Prozess, der den nächsten Ausführungsschritt selbst bestimmen kann
  • Anders als bei einem Workflow gibt es keinen vorab definierten Pfad; stattdessen wird anhand von Bedingungen (z. B. „Test bestanden“) oder einer maximalen Anzahl von Iterationen entschieden, ob beendet wird
  • Im produktiven Einsatz laufen Agenten tatsächlich über mehrere Sekunden bis mehrere Stunden, und durch LLM-Aufrufe oder Browser-Steuerung entstehen hohe Kosten
  • Da Eingaben von Nutzern (oder anderen Agenten) verarbeitet werden müssen, gibt es viel Ein-/Ausgabe-(I/O)-Wartezeit

Warum Go für Agenten geeignet ist

Hochperformante Nebenläufigkeit

  • Go-Goroutinen können mit nur 2 KB Speicher tausende bis zehntausende leichtgewichtige Threads gleichzeitig ausführen
  • Jede Goroutine nutzt Multi-Core-Systeme für parallele Verarbeitung, sodass sich auch I/O-lastige oder wartende Agenten problemlos betreiben lassen
  • Über Channel-basierte Kommunikation wird Synchronisation nicht durch geteilten Speicher, sondern durch Nachrichtenübermittlung umgesetzt (mit minimalem Einsatz von Mutexen)
  • Das eignet sich gut dafür, dass Agenten asynchron Nachrichten austauschen und ihren Zustand verwalten

Zentralisierter Abbruchmechanismus

  • Bei Verwendung von Go-context.Context unterstützen die meisten Bibliotheken und APIs Abbruchsignale, wodurch sich die Ausführung sehr einfach stoppen lässt
  • Während in Node.js oder Python verschiedene Abbruchmuster nebeneinander existieren, ermöglicht Go konsistente, sichere Abbrüche und Ressourcenfreigabe

Umfangreiche Standardbibliothek

  • Go verfügt über eine umfangreiche Standardbibliothek und deckt nahezu alle Bereiche ab, darunter HTTP/Web, Dateien und Netzwerk-I/O
  • Sämtliche I/O-Vorgänge gehen von blockierendem Verhalten innerhalb von Goroutinen aus, wodurch sich Business-Logik geradlinig schreiben lässt
  • In Python ist es komplexer, weil verschiedene Nebenläufigkeitsmuster wie asyncio, Threading und Prozesse nebeneinander verwendet werden

Profiling- und Diagnosewerkzeuge

  • Mit integrierten Tools wie Go-pprof lassen sich Speicherlecks und Goroutinen-(Thread-)Lecks in Echtzeit verfolgen
  • Das ist besonders hilfreich, um Leckprobleme in lang laufenden und gleichzeitig ausgeführten Agenten zu diagnostizieren

Starke Unterstützung beim Codieren mit LLMs

  • Dank einfacher Syntax und einer umfangreichen Standardbibliothek schreiben LLMs in Go gut Code im idiomatischen Go-Stil
  • Durch die geringe Framework-Abhängigkeit müssen LLMs sich weniger um Versionen oder Muster kümmern

Grenzen von Go

  • Bei Third-Party-Bibliotheken und im Ökosystem liegt Go hinter Python und TypeScript zurück
  • Für die direkte Implementierung von Machine Learning ist Go nicht gut geeignet (Leistung und Support sind begrenzt)
  • Wenn maximale Performance erforderlich ist, sind Rust und C++ besser
  • Für Entwickler, die beim Error Handling eher großzügig sind, kann es etwas umständlich sein

2 Kommentare

 
codemasterkimc 2025-06-11

Lieber als Java, aber Rust lieber als Go :)

 
GN⁺ 2025-06-11
Hacker-News-Kommentare
  • Es wird betont, dass in den meisten Agentensystemen der größte Latenzfaktor letztlich die LLM-Aufrufe sind. Die im Artikel genannten Vorteile seien nicht speziell für eine bestimmte Sprache vorteilhaft; vielmehr gehe es meist um langes Warten, teure Ressourcennutzung, Eingaben von Nutzern oder anderen Agenten sowie hohe I/O-Wartezeiten. Daher sei nicht die Ausführungsgeschwindigkeit oder Effizienz des Servers entscheidend, sondern eher der große Vorteil von Sprachen wie Python durch ihr umfangreiches Angebot an AI-Bibliotheken und Support. Zwar werde angemerkt, dass man sich in Python mit asyncio oder Multithreading-Bibliotheken beschäftigen müsse, doch tatsächlich sei die Agentenentwicklung nicht besonders schwierig, und da bereits jemand entsprechende Workflows entwickelt habe, könne man leicht loslegen

    • Beim Aufbau von Agenten mit Go habe sich als großer Vorteil erwiesen, dass Muster für Nebenläufigkeit und Backpressure-Management gut etabliert sind. Agenten umfassen meist Transaktionen mit langsamen externen Services, und für solche Aufgaben sind Gos Nebenläufigkeitsmuster sehr nützlich. Natürlich spiele die Sprache insgesamt keine allzu große Rolle, und JavaScript scheint am häufigsten verwendet zu werden. Für Codegenerierung spüre ich allerdings eine gute Synergie zwischen Go und LLMs

    • Go unterscheidet sich von Python durch seine hervorragende Nebenläufigkeitsverarbeitung und die einfache Bereitstellung. Bei Go muss man nur ein statisches Binary ausliefern und ist damit frei von den Umgebungs- und Abhängigkeitsproblemen von Python

    • Agenten übernehmen die Rolle einer Orchestrierungsschicht, und dafür halte ich Go, Erlang und Node für besonders geeignet. Man braucht nicht zwingend eine große Menge AI-bezogener Bibliotheken, und I/O-intensive Aufgaben kann man hinter domänenspezifischen Tool-Interfaces abstrahieren und die Subsysteme in der jeweils passenden Sprache bauen

  • Go bringt für diese Art von Workload keinen großen Vorteil, da die meiste Zeit auf I/O-Wartezeiten entfällt. Gos Typsystem ist eingeschränkt, und viele Funktionen, die moderne Sprachen von Haus aus mitbringen, muss man in Go umschiffen. TypeScript ist eine hervorragende Glue Language für AI-Zwecke und bietet zusammen mit Python sehr gute Bibliotheksunterstützung. Ich bevorzuge TypeScript gegenüber Python, weil das Typsystem viel stärker und ausgereifter ist. Python verbessert sich allerdings schnell. Die Behauptung, lang laufende Aufgaben ließen sich in Node.js und Python nur sehr schwer abbrechen, halte ich nicht für belastbar. Die meisten Werkzeuge unterstützen das bereits, und die dominierenden Sprachen sind Python und JS

    • Wenn wirklich Performance nötig ist, würde ich lieber native Module in C++ oder Rust schreiben, die schneller als V8 sind. Bis zu Go muss man dafür nicht gehen
  • Ich habe mit Agenten-Frameworks auf Basis von Elixir und BEAM experimentiert und denke, dass die Kombination aus BEAM und SQLite derzeit am idealsten für Agenten ist. Man kann Agenten sicher austauschen, ohne die Anwendung neu bereitzustellen, und die Nebenläufigkeit von BEAM hat dafür mehr als genug Spielraum. Zustandsbehaftete oder temporäre Agenten lassen sich ebenfalls sehr leicht implementieren. Künftig plane ich, Basis-Agenten in Python, TypeScript und Rust zu bauen und außerdem MCP-Server zu erstellen, damit je nach Sprachpräferenz auch komplexere Agenten entwickelt werden können

    • Ich empfehle das Extism-Projekt und das Elixir SDK. Mit dieser Kombination kann man Kernservices, Routing und Message Passing in Elixir bauen, die Vorteile von BEAM/OTP nutzen und zusätzlich kleine, leichtgewichtige, in anderen Sprachen geschriebene Agenten in Form von Wasm-Modulen wie Plugins einbetten
      Extism
      Elixir SDK

    • Mich würde interessieren, warum SQLite statt mnesia, dem integrierten Datenspeicher von BEAM, gewählt wurde
      mnesia docs

  • Die meiste Zeit eines Agenten geht für das Warten auf LLM-Antworten und Aufrufe externer Services (API, DB) drauf. Der Einfluss der Performance der Sprach-Runtime ist in der Praxis fast null. Wenn es eine Sprachfunktion gibt, die für Agentenleistung und Skalierbarkeit wirklich wichtig ist, dann wohl die Performance von JSON-Serialisierung und -Deserialisierung

    • Deshalb halte ich Sprachen wie TypeScript, die nativ mit JSON umgehen, für die bessere Wahl. Das Typsystem von TypeScript ist sogar deutlich stärker als das von Go

    • Meiner Erfahrung nach ist neben LLM-Aufrufen die teuerste Operation bei Agenten die Konfliktauflösung bei asynchronen Bearbeitungen (merge, diff, patch). Auch das kann man an Low-Level-Bibliotheken delegieren, aber ähnlich wie bei der Serialisierung ist es schwer zu optimieren

  • Das erinnert mich an diesen Leitfaden zum Bau von Agenten von ampcode.com. Durch die Natur als dynamische Sprache ist es in Python naheliegend, mit Decorators Methoden direkt in Tool-Aufrufe umzuwandeln, über Tool-Funktionen Listen zu erzeugen oder schnell in JSON-Schemata zu konvertieren. Dagegen war eine Struktur, bei der mehrere externe Trigger (z. B. Nutzereingaben, Gmail-Mails, Slack-Nachrichten usw.) neue Agentenläufe auslösen, mit Gos Channels und einer switch-for-Schleife deutlich intuitiver. In Python wird es komplizierter, weil man mehrere Queues und Threads separat bauen muss

  • Folgt man der Logik des Artikels, dann ist Elixir ideal für Agenten

  • Gos eingeschränktes und schwaches Typsystem ist für fast alle Anwendungen ungeeignet. Tatsächlich ist die größte Schwäche von Go die Sprache selbst. Gerade die außersprachlichen Aspekte seien der Grund, warum man Go überhaupt akzeptiert

    • Ich habe mehrere Jahre in Go programmiert und stimme zu, dass es viele Probleme mit dem Typsystem gibt. Im LLM-Bereich scheinen fast alle nur Python oder JavaScript zu verwenden. Ich denke, alle sollten zu moderneren Sprachen wechseln, aber Go könnte im Vergleich zum chaotischen Import- und Paketmanagement von Python/JavaScript immerhin die bessere Wahl sein

    • Ich würde gern konkret hören, wie die Grenzen von Gos Typsystem beim Bau von Agenten zum Hindernis werden

    • In Wirklichkeit bekam Go ein statisches Typsystem, weil man keinen anderen Weg fand, die Performance-Anforderungen zu erfüllen. Tatsächlich sollte man es eher als dynamisch getypte Sprache betrachten, und das Missverständnis liegt in einem falschen Verständnis der Sprachdesign-Ziele. Man kann zwar behaupten, dass dynamisch typisierte Sprachen generell ungeeignet seien, aber tatsächlich werden Python, Erlang, Elixir und andere dynamisch typisierte Sprachen aktiv verwendet, was eher darauf hindeutet, dass dynamische Typisierung für diese Problemdomäne geeigneter ist

    • Mehrere Rückgabewerte lassen sich nicht gut komponieren, die Fehlerbehandlung ist zwar besser als Exceptions, aber sehr ausführlich, Channels sind fehleranfällig, und Enum-Typen sind enttäuschend. Trotzdem funktionieren Interfaces überraschend gut, und das Paketsystem ist ziemlich reibungslos. Beim Lernen von Rust habe ich festgestellt, dass die Dateistruktur deutlich komplexer ist als in Go. Gerade weil die Sprache so einfach ist, lassen sich auch diverse Linter- und Code-Generation-Tools leichter bauen. Bei langfristiger Wartung von Go-Code mache ich mir weniger Sorgen als bei Python/JS

    • Es wäre wirklich schön, wenn es einen gut gepflegten LISP-/Scheme-Dialekt gäbe, der nach Go kompiliert

  • Bei Prozessen mit langen Wartezeiten und hohen Ausführungskosten ist ein Nachteil, dass beim Absturz des Prozesses alle Arbeiten verloren gehen. Es mag sicherer sein, während des Wartens den Zustand in eine Datenbank zu serialisieren, aber es scheint keine Sprache zu geben, die das einfach macht. Zustandsmaschinen auf Checkpoint-Basis zu schreiben ist nicht leicht

    • Es wird erklärt, dass zustandsmaschinenbasierte Checkpoints genau die Kernfunktion von Plattformen wie Hatchet(hatchet.run) und Temporal(temporal.io) sind. Sie speichern die Ausführungshistorie von Funktionen innerhalb eines Workflows und spielen diese Historie bei Unterbrechungen automatisch wieder ab. Fortschrittshistorien auf Output-Ebene seien dabei viel effizienter als Memory-Snapshots. (Hatchet-Gründer)

    • Goroutines, Threads und lang laufende Chains müssen letztlich ohnehin in atomare Arbeitseinheiten zerlegt werden, und die Serialisierung des Zustands ist unverzichtbar. So lassen sich Anforderungen wie Fehlerbehebung, Fehlerverfolgung, erneute Referenzierung von Ergebnissen und verteilte Verarbeitung über mehrere Knoten erfüllen. Das Elixir-Framework Oban(github.com/oban-bg/oban) arbeitet nach diesem Prinzip, und empfohlen wird auch dieser Artikel zu Oban, der die Bedeutung persistenter asynchroner Aufgaben betont. (Oban-Autor)

    • Ich entwickle gerade eine golang-basierte Agentenbibliothek und hatte den Eindruck, dass sich der Zustand eines Agenten jederzeit wiederherstellen lässt, wenn nur genügend Logging vorhanden ist. Wenn man Zeitstempel und den übergeordneten Run kennt, kann man einen Baum aus Child-/Branch-Runs aufbauen. Sessions lassen sich mit einer Kombination aus Maps und DB verwalten und bei Bedarf rekonstruieren. Statt einzelne Objekte zu halten, werden zustandslose Objekte per ID aus Maps geholt, während frühere Aktionen, Schritte und Kontexte in einem Zustandsobjekt liegen. Die Konsistenz von Agenten/Workflows wird ebenfalls gelöst, indem Ergebnisse per Hash verwaltet werden. Bisher habe ich nur grundlegende Agenten/Tools implementiert; Logging, Wiederherstellung und Abbruchlogik fehlen noch

    • Temporal ist für das Checkpointing lang laufender Prozesse ziemlich nützlich und zudem sprachneutral

    • Ich habe auch über Task Queues nachgedacht und überlege derzeit, eine rudimentäre Queue in Postgres zu bauen. Die Vorteile wären Lastverteilung über mehrere Server, kein Verlust von Tasks nach dem Beenden eines Prozesses und bessere Sichtbarkeit. Dafür kann die Codekomplexität stark steigen, weshalb es schwierig erscheint, die Architektur einfach zu halten

  • AI-Ingenieure meiden JavaScript extrem. Das Ende von TensorFlow for Swift war das Ende der sprachlichen Vielfalt in AI

    • Ich glaube nicht, dass die Abneigung gegen JavaScript nur für AI-Ingenieure gilt. Selbst aus Sicht von jemandem, der seit über 30 Jahren JS programmiert, kann ich dem zustimmen

    • JS ist als Sprache sehr schlecht, und es war ein Fehler, sie ins Backend zu bringen. TypeScript löst die Probleme des zugrunde liegenden JS letztlich auch nicht. Ich vermeide deshalb lieber JS oder TS und bevorzuge andere Alternativen wie Go, Rust, Python, Ruby, Elixir oder F#

    • Mich würde interessieren, warum JS für Agenten besonders gut sein soll

  • Ich habe das Gefühl, dass im ML-Bereich ein besseres Nebenläufigkeitsmodell nötig ist. Ich habe versucht, ML mit Go zu machen, aber wegen der schwachen Bibliotheksunterstützung und der Abhängigkeit von externen gRPC-Aufrufen oder Wrappern ist das praktisch unmöglich. Python hat seine Grenzen, und C++ ist so ausführlich, dass die Produktivität stark leidet