Who picks the tool
-->
Direkt zum Hauptbereich
Mein Webserver läuft auf einer günstigen Linux-Box ohne GPU, das Embedding-Modell aber will Rechenpower. Die Lösung: Das Modell läuft auf meinem MacBook, die App auf dem Server. Und der Code, der die Inferenz aufruft, sieht exakt gleich aus, als liefe alles auf einer Maschine.
Das ist der Teil des Projekts, bei dem die meisten Leute, denen ich es zeige, kurz innehalten. Nicht weil es kompliziert wäre, sondern weil es so wenig Code ist für etwas, das in anderen Stacks ein eigenes Infrastruktur-Projekt wäre.
Das Problem: Die App läuft produktiv auf einer kleinen Linux-Maschine, die für Phoenix und Postgres reicht. Ein 278-MB-Transformer-Modell mit EXLA-Inferenz wird darauf aber zäh. Die nötige Rechenleistung sitzt woanders, nämlich in meinem MacBook Pro mit ordentlich CPU und GPU.
In der typischen Microservice-Welt heißt das jetzt: das Modell in einen eigenen Service packen, eine API drumherum bauen (gRPC oder REST), Serialisierung definieren, dazu Service-Discovery, Timeouts, Retries, ein zweites Deployment-Artefakt und Monitoring für die Verbindung dazwischen. Das ist tagelange Arbeit, und am Ende hast du einen neuen verteilten Fehlerfall geschaffen.
Auf der BEAM ist „eine andere Maschine" dagegen eingebaut. Die Erlang-VM ist seit den 80ern für verteilte Systeme gemacht. Mehrere Knoten verbinden sich zu einem Cluster und rufen sich gegenseitig auf, als wären sie ein einziger Prozess. Das Werkzeug für ML-Inferenz, Nx.Serving, ist von Grund auf verteilungs-transparent: Es kann lokal laufen oder den Aufruf an einen anderen Knoten weiterreichen. Der aufrufende Code ist in beiden Fällen identisch:
Die ganze Architektur fällt auf eine einzige Rollen-Unterscheidung beim Start zusammen. Es ist dieselbe Codebasis und dieselbe Binary-Logik. Eine Umgebungsvariable entscheidet, was ein Knoten hochfährt:
Verbunden werden sie über libcluster, und die schwere Nx.Serving.batched_run-Berechnung wird automatisch zum ML-Knoten geroutet. Mein Klassifikations-Code weiß nicht und muss auch nicht wissen, auf welcher Maschine die Matrix-Multiplikationen passieren.
Der wichtigste Teil ist die Frage: Was, wenn das MacBook aus ist? Eine naive verteilte App würde abstürzen oder hängen bleiben. Hier fange ich den Fall, dass der ML-Knoten nicht erreichbar ist, ab und behandle ihn als eigenständigen Zustand statt als Fehler:
Die Folge: Ist das Modell offline, bleibt die Transaktion einfach unklassifiziert liegen, das UI zeigt einen „später erneut versuchen"-Hinweis, und nichts wird falsch einsortiert. „Ehrlich unsicher" statt „selbstbewusst falsch", dasselbe Prinzip wie beim Schwellwert aus Artikel 2, nur jetzt auf der Infrastruktur-Ebene.
Die Lektion daraus: Verteilte ML-Inferenz gilt oft als schweres Infrastruktur-Thema, und in vielen Stacks ist sie das auch. Auf der BEAM ist „lass das woanders rechnen" kein Architektur-Projekt, sondern ein Baustein der Plattform. Du bezahlst nicht mit einem Service-Mesh, sondern mit ein paar Zeilen Rollen-Logik und einem catch.
Das ist es, was ich mit „guter ML-Unterstützung" bei Elixir meine. Es geht nicht nur darum, dass man Modelle laden kann, sondern darum, dass die Architektur drumherum schon da ist, bevor du anfängst: Fehlertoleranz, Verteilung, Nebenläufigkeit.
Im nächsten Teil, zum Abschluss: der Teil des ML-Features, der bewusst kein ML ist.
→ Weiter zu Teil 5: Wann du KEIN ML nehmen solltest
→ Ein ML-Modell und eine Web-App auf zwei BEAM-Knoten trennen — der technische Bauplan
Kommentare