Darkwood Blog Blog
  • Artikel
  • Beobachtung
  • Releases
  • Creators
de
  • en
  • fr
Anmeldung
  • Blog
  • Artikel
  • Beobachtung
  • Releases
  • Creators

📚 Wenn KI zur Bibliothek wird: Das Nolife-Lokalexperiment

vom 23. August 2026

Anmelden um auf diesen Beitrag zu reagieren

🚀 1

Der Großteil der KI-Entwicklung, die ich beobachte, kreist immer noch um denselben Schwerpunkt: immer grĂ¶ĂŸere, universelle Sprachmodelle. Diese Reaktion ist verstĂ€ndlich. Ein Modell, das Gedichte schreiben, VertrĂ€ge analysieren, PHP-Code generieren, Dokumente zusammenfassen und Fragen beantworten kann, erscheint wie ein universelles Werkzeug. Wenn jedes Problem auch nur entfernt „KI-Ă€hnlich“ aussieht, ist die Versuchung groß, es an denselben Endpunkt zu senden.

Viele Anwendungsprobleme benötigen jedoch kein Modell, das all das kann. Sie benötigen ein einziges Modell, das eine bestimmte Rechenaufgabe hervorragend erfĂŒllt.

Nolife Local ist ein Experiment, das diese Alternative untersucht. Anstatt ein Bild an ein universelles multimodales LLM zu senden, integrierte ich ein spezialisiertes Bildverarbeitungsmodell direkt in eine Symfony-Anwendung und machte es zu einem Bestandteil der normalen AusfĂŒhrungspipeline. Das konkrete Fallbeispiel ist Depth Anything 3: monokulare TiefenschĂ€tzung, lokal auf Apple Silicon ĂŒber PHP FFI ausgefĂŒhrt, mit Darkwood Flow orchestriert und mit einem Codierungsagenten in einer Schleife erstellt, der sich mit dem Wachstum des Projekts schrittweise verĂ€nderte.

Anfangs wirkte diese Schleife vertraut:

idea → ask agent to implement → inspect result

Am Ende sah es eher so aus:

observe → collect evidence → formulate problem → define constraints
→ let agent investigate → implement → measure → validate

Die zweite Schleife startet langsamer, ist aber deutlich zuverlÀssiger. Der Artikel beschreibt sowohl die Laufzeitarchitektur als auch die verÀnderte Definition der Arbeit selbst.

Dies ist kein Argument dafĂŒr, dass LLMs schlecht sind, dass lokale KI immer besser ist oder dass kleine Modelle allgemeine ersetzen. Die stĂ€rkere Schlussfolgerung ist einfacher:

Die Aufgabe sollte das Modell und die Architektur bestimmen.

Allgemeine Modelle sind nach wie vor hervorragend geeignet, wenn das Problem ein umfassendes semantisches Schließen erfordert. Spezialisierte Modelle werden interessant, wenn das gewĂŒnschte Ergebnis selbst eine spezialisierte Berechnung darstellt.

Nicht jedes KI-Problem ist ein LLM-Problem

Der Standardreflex von Entwicklern im Jahr 2026 ist bekannt:

problem → large model → prompt → parse text

Es funktioniert oft genug, dass wir nicht mehr hinterfragen, ob es die richtige Abstraktion ist. FĂŒr viele Aufgaben ist es das. FĂŒr andere ist es, als wĂŒrde man Pixel in einer Tabellenkalkulation bearbeiten: theoretisch möglich, praktisch aufwendig, und die resultierende Form ist falsch.

Ich wollte die gegenteilige Form sehen:

problem → specialized model → dense numerical result → application code

TiefenschĂ€tzung eignet sich gut als Stresstest fĂŒr dieses Konzept. Das gewĂŒnschte Ergebnis ist kein Satz ĂŒber den Raum, sondern ein Zahlenfeld – ein Wert pro Pixel –, das zu einer Farbskala, einer KameraschĂ€tzung oder einer 3D-Rekonstruktion werden kann. Sprache ist dafĂŒr ungeeignet. Ein Modell, das darauf trainiert ist, Tiefeninformationen zu liefern, ist deutlich besser.

Diese Unterscheidung ist entscheidend, bevor man ĂŒberhaupt ĂŒber PHP, FFI oder Symfony spricht. Wenn die Berechnung selbst spezialisiert ist, sollte auch das Modell spezialisiert sein. Derselbe Gedanke sollte spĂ€ter meine Verwendung des Codierungsagenten beeinflussen: Allgemeine Aufgabenstellungen fĂŒhren zu allgemeinen (und oft fehlerhaften) Ergebnissen. Spezielle Problemstellungen fĂŒhren zu prĂ€ziseren, nachvollziehbaren Änderungen.

Tiefeninformationen aus einem Bild

Die monokulare TiefenschĂ€tzung beantwortet eine trĂŒgerisch einfache Frage: Können wir anhand eines einzelnen RGB-Fotos abschĂ€tzen, wie weit Objekte von der Kamera entfernt sind?

single RGB image
        ↓
Depth Anything 3
        ↓
dense geometric estimation

Menschen tun dies stĂ€ndig. Ein Foto von Bergen wirkt bereits dreidimensional. Software muss diese Struktur ohne Stereobilder, LiDAR oder einen zweiten Blickwinkel wiederherstellen. Das Modell muss Verdeckung, TexturverlĂ€ufe, Perspektive und Objektgrenzen berĂŒcksichtigen – das Ergebnis ist jedoch numerisch, nicht erzĂ€hlerisch.

Depth Anything 3 (ByteDance) ist eine Familie von ViT-basierten Modellen mit DualDPT-Architektur. Die Portierung depth-anything.cpp des LocalAI-Teams ermöglicht die lokale AusfĂŒhrung dieser Modelle ĂŒber ggml. Dabei werden weder Python zur Inferenzzeit noch PyTorch oder eine Cloud-Vision-API verwendet.

In Nolife Local verwenden wir DA3-BASE. Über die native C-API liefert eine dichte Inferenz folgendes Ergebnis:

  • eine Tiefenkarte (width × height Gleitkommazahlen)
  • eine Konfidenzkarte (gleiche Auflösung, falls vorhanden)
  • Kameraextrinsik (3×4)
  • Kamera-Intrinsik (3×3)
  • ein Flag, das angibt, ob die Tiefe metrisch ist.

FĂŒr DA3-BASE ist diese Option deaktiviert. Die Tiefenwerte sind relativ, nicht in Metern angegeben. Auf unserem Referenzfoto betrug die beobachtete Entfernung in relativen Einheiten etwa 0,43–3,47. Diese Werte als Entfernungen in Metern zu interpretieren, wĂ€re ein sachlicher Fehler. Metrische Tiefenangaben sind in anderen DA3-Varianten (verschachtelte/monoplastische Pfade) verfĂŒgbar; diese Demo verwendet sie jedoch nicht.

Aus diesen Ausgaben kann die native Bibliothek auch eine glTF/GLB-Punktwolke erstellen, indem sie gĂŒltige Pixel in Weltkoordinaten zurĂŒckprojiziert. So entsteht in der Symfony-Demo ein 3D-Viewer – nicht etwa durch die Erfindung von Geometrie in PHP.

Der Weg von der Forschung zur Laufzeit sieht folgendermaßen aus:

ByteDance PyTorch checkpoint
  ↓  convert once to GGUF (Python, offline)
GGUF weights on disk
  ↓  ModelLoader (C++)
ggml compute graph
  ↓  Metal / CPU / other backends
depth + confidence + camera
  ↓  optional GLB export
application artifacts

Wir haben Depth Anything 3 nicht trainiert. Wir haben es weder feinabgestimmt noch LoRA angewendet oder ein Studentennetzwerk destilliert. Spezialisierung bedeutet hier die Auswahl eines bestehenden, speziell trainierten Modells und dessen Integration in eine spezialisierte lokale AusfĂŒhrungspipeline. Die Quantisierung (q4_k versus f32) ist eine separate Dimension: eine Entscheidung zur Laufzeit bzw. zur Modelldarstellung, nicht zum Training.

Vor der PHP-Entwicklung bestand die erste Aufgabe darin, die Funktionsweise des nativen Stacks zu verstehen – welche C-API-Funktionen existierten, welche SpeichervertrĂ€ge sie implizierten und welche Ausgaben tatsĂ€chlich vorhanden waren und nicht nur anhand von Modellnamen erschlossen wurden. Diese Untersuchung wurde zum ersten „Problem“ des Projekts: Inferenz verstehen, bevor sie gebunden wird. Diesen Schritt zu ĂŒberspringen, wĂ€re fĂŒr den Agenten gleichbedeutend damit gewesen, ein allgemeines Modell zu wĂ€hlen, nur weil es leistungsfĂ€hig erscheint.

Spezialisiertes Modell versus allgemeines multimodales Modell

Der Kontrast besteht nicht nur in der GrĂ¶ĂŸe.

Allgemeiner multimodaler Pfad:

image
  ↓
large general model
  ↓
semantic interpretation
  ↓
text / structured answer

Spezieller Tiefenpfad:

image
  ↓
DA3
  ↓
dense numerical representation
  ↓
depth / confidence / camera / geometry

Ein multimodales LLM kann rĂ€umliche Beziehungen sprachlich beschreiben. Das ist eine echte FĂ€higkeit, und fĂŒr manche Produkte genau das, was man braucht. Es ist nicht dasselbe wie die Offline-Ausgabe eines deterministischen 504×280-Gleitkommafelds aus einer ca. 99 MB großen GGUF-Datei mit strukturierten Kameraparametern und einem GLB-Exportpfad.

Der Unterschied liegt in der Art der Berechnung. Sobald das Ergebnis eine dichte geometrische SchÀtzung ist, sieht das Modell nicht mehr wie ein Chatbot aus, sondern wie eine Anwendungsprimitive: etwas, das man aufruft, misst, zwischenspeichert und zusammensetzt.

Das ist der architektonische Wandel, den Nolife Local zur Laufzeit untersucht. Ein paralleler Wandel wĂŒrde sich in der Entwicklung vollziehen: Man wĂŒrde nicht mehr nach allgemeinen Verbesserungen fragen, sondern nach den spezifischen Berechnungs- oder Integrationsschritten, die die Anwendung tatsĂ€chlich benötigt.

Das Ungewöhnliche daran: PHP

Dann folgt die bewusst ungeschickte EinschrÀnkung.

Das Ziel war nicht:

Aufruf eines Python-Mikrodienstes aus Symfony.

Noch:

Sende das Bild an eine KI-API.

Das Experiment fragte:

Wie weit lÀsst sich das native Modell direkt in eine Symfony-Anwendung integrieren?

Das fĂŒhrt zu PHP FFI.

PHP fĂŒhrt die neuronalen Netzwerkkerne nicht selbst aus. Das Gegenteil zu behaupten, wĂ€re Marketing, nicht technisches Know-how. Die ZustĂ€ndigkeitsgrenzen in der finalen Anwendung sind ungefĂ€hr wie folgt:

Browser
   ↓
Symfony
   ↓
Darkwood Flow
   ↓
NativeDepthBridge
   ↓
PHP FFI
   ↓
libdepthanything
   ↓
ggml / Metal
   ↓
Depth Anything 3

Die nativen Ergebnisse werden dann zur Weiterverarbeitung auf Anwendungsebene an PHP zurĂŒckgegeben.

PHP besitzt HTTP- und Upload-Verarbeitung, Validierung, Orchestrierung, Flow-Schritt-Timing, native Lebenszykluskoordination, Ergebnisdarstellung, GD-Farbkartenrendering, Twig / Symfony UX, CLI-Tools, Observability-OberflÀchen und Artefaktverwaltung unter var/inference/.

Der native Code umfasst die Vorverarbeitung (GrĂ¶ĂŸenĂ€nderung, Normalisierung, Patch-Ausrichtung), Tensoroperationen, das ViT-Backbone, DualDPT-Tiefen-/Konfidenzköpfe, KameraschĂ€tzung, Metal-AusfĂŒhrung und native GLB-Rekonstruktion.

Symfony bleibt ein Webframework. Depth Anything bleibt eine native Bildverarbeitungs-Engine. FFI bildet die Schnittstelle zwischen beiden.

Mehrere verlockende AnsĂ€tze wurden bewusst verworfen: reine PHP-Tensorinferenz, eine Messenger-Worker-Farm, DDD-Layering, CQRS oder – nebenbei – eine Linux-Portierung. Keiner dieser AnsĂ€tze löste das aktuelle Problem. Sie hĂ€tten den Anwendungsbereich erweitert, weil ein Agent sie generieren könnte, nicht weil das Experiment sie erforderte. Der Anwendungsbereich, so lernte ich, muss vom Menschen festgelegt und dokumentiert werden, bevor der Agent mit dem Schreiben beginnt.

PHP FFI als Grenze

FFI ist deshalb wichtig, weil es PHP ermöglicht, den Lebenszyklus eines nativen Kontexts selbst zu steuern, anstatt sich an eine CLI zu wenden und auf das Beste zu hoffen.

Die erste sinnvolle Frage war nicht, wie viel C++-Code ich in PHP umschreiben könnte. Vielmehr ging es darum, wo die sinnvolle Grenze tatsĂ€chlich verlief. Die Aufgabenstellung fĂŒr die FFI-BrĂŒcke war bewusst eng gefasst:

Use da_capi_depth_dense, not a CLI wrapper.
Honor da_capi_free_floats for native buffers.
Respect that da_ctx is not thread-safe.
Produce an observable CLI or HTTP result.

Das ist bereits ein Problem im Kleinen: Problem, EinschrÀnkungen, erwartetes Ergebnis.

NativeDepthBridge lĂ€dt libdepthanything.dylib, bindet die C-API aus config/da_capi.h und stellt dem Rest der Anwendung eine schmale Schnittstelle zur VerfĂŒgung: Kontext laden, dichte Inferenz ausfĂŒhren, GLB aus dem Cache exportieren, Puffer freigeben.

Ein fokussierter Ausschnitt der Abgrenzung sieht folgendermaßen aus:

$rc = $ffi->da_capi_depth_dense(
    $this->ctx,
    $imagePath,
    FFI::addr($outH),
    FFI::addr($outW),
    FFI::addr($outDepthPtr),
    FFI::addr($outConfPtr),
    FFI::addr($outSkyPtr),
    $ext,
    $intr,
    FFI::addr($isMetric),
);

$depthPtr = $this->ffi->cast('float*', $outDepthPtr);
$depth = $this->copyFloatsFromPtr($depthPtr, $count);

Das Muster ist absichtlich langweilig:

PHP
→ native function
→ native result pointer
→ copy into PHP arrays
→ da_capi_free_floats

Die Lebensdauer von Ressourcen ist ein wichtiger Aspekt. Die Bridge verwaltet einen da_ctx, solange der Modellpfad unverĂ€ndert bleibt, gibt ihn bei Zerstörung oder Modelltausch frei und serialisiert den Zugriff mit einer Dateisperre, da der native Kontext nicht threadsicher ist. Von der C-API zurĂŒckgegebene Float-Puffer werden in PHP-Arrays kopiert und anschließend sofort freigegeben.

Die Aufgabenstellung war unvollstĂ€ndig. Bei der Implementierung fĂŒhrte das Umwandeln eines großen float*-Werts in float[141120] unter PHP 8.5 zu einem Segmentierungsfehler. Indizierte Lesezugriffe ($ptr[$i]) funktionierten. Diese Beobachtung musste in den Arbeitskontext aufgenommen werden – beobachtetes Verhalten vor einer plausiblen ErklĂ€rung. Agenten geben sich oft sehr sicher bezĂŒglich des Speicherlayouts. Die Reproduktion war erfolgreich.

SpÀter haben wir diese Lebensdauer innerhalb eines PHP-Prozesses einem Stresstest unterzogen:

php -d ffi.enable=1 bin/console app:depth-stress \
    -i public/samples/mountains.jpg \
    -r 10

Zehn vollstĂ€ndige Pipeline-DurchlĂ€ufe im laufenden Prozess hielten den GLB-Export auf dem zwischengespeicherten Pfad (jeweils ca. 20 ms) und erforderten kein Neuladen des Modells zwischen den Iterationen. Genau diese Art der Validierung macht aus „FFI funktioniert einmal“ ein „FFI funktioniert als Anwendungsinfrastruktur“.

Es zu einem Symfony-Feature machen

Sobald die BrĂŒcke existiert, wird das native Modell zu einer gewöhnlichen Symfony-Funktion und nicht mehr zu einem mysteriösen externen Dienst.

Der Benutzerablauf ist wie folgt:

upload image
↓
validate
↓
run local inference
↓
extract depth / confidence / camera
↓
generate visualization (PHP GD turbo colormap)
↓
reuse native result for GLB
↓
build result
↓
render Symfony UX

Die Upload-Seite ist bewusst klein gehalten: Bild hochladen, q4_k oder f32 auswĂ€hlen, absenden. WĂ€hrend die Anfrage lĂ€uft, zeigt die BenutzeroberflĂ€che den Verarbeitungsstatus an. Wenige Sekunden spĂ€ter zeigt die Ergebnisseite das Originalfoto neben der Tiefenfarbkarte, ein Konfidenzintervall, Kamerazusammenfassungen, Pipeline-Zeiten und ein „ ` fĂŒr die GLB.

Keine benutzerdefinierte WebGL-Engine. Keine Messenger-Worker. Kein Python-Sidecar. Die Symfony-Anwendung hostet die Interaktion; die native Bibliothek fĂŒhrt die rechenintensiven Operationen durch; der Browser visualisiert lediglich.

Die CLI-Tools folgen demselben Prinzip. Die Diagnosetools behandeln das Modell wie jede andere AbhÀngigkeit:

FFI                OK
GD                 OK
Native library     OK
q4_k model         OK
f32 model          OK
Inference dir      OK

READY

Diese Ausgabe stammt von php bin/console app:depth-check. Das Modell ist kein undurchsichtiger KI-Endpunkt mehr, sondern eine Anwendungsinfrastruktur mit einer BereitschaftsprĂŒfung.

Die CLI-Inferenz gibt dieselbe Pipeline-Historie aus, die auch die Web-UI anzeigt:

OK id=
 504x336 infer=3179.4ms 

  validate: 3.49ms
  infer: 3180.28ms
  colormap: 75.52ms
  glb: 19.65ms
  result: 0.00ms

FĂŒr Leser von Benchmarks ist es wichtig zu verstehen, dass infer= der Flow-Inferenzschritt ist und kein verstecktes Synonym fĂŒr „gesamte Anfrage“. Sobald diese Terminologie in CLI-, Web- und Agenten-Briefings einheitlich verwendet wird, werden Diskussionen ĂŒber Latenzzeiten deutlich weniger mysteriös.

Inferenz orchestrieren mit Darkwood Flow

Nach der ersten funktionierenden Version fand die Orchestrierung innerhalb einer einzigen Servicemethode statt: Validierung, Ableitung, Farbzuordnung, Export, Zusammenstellung. Die Pipeline war zwar vorhanden, aber implizit. Zeitangaben fehlten entweder oder wurden zu einer einzigen Zahl zusammengefasst, die sehr unterschiedliche Arbeitsschritte vermischte.

Ich habe Darkwood Flow nicht als Produktplatzierung vorgestellt, sondern weil ein aufwÀndiges lokales Modell die gleichen technischen Eigenschaften benötigt wie jede andere aufwÀndige Berechnung: explizite Schritte, Zeitangaben, Fehlerbehandlung und einen Anhaltspunkt, wenn etwas nicht stimmt.

Die endgĂŒltige Pipeline ist:

Validate
   ↓
Infer
   ↓
Colormap
   ↓
Export scene
   ↓
Build result

Flow ist ĂŒber der FFI-BrĂŒcke angesiedelt. NativeDepthBridge bleibt ein einfaches Grundelement. Flow ist fĂŒr die Sequenzierung und die schrittweisen Wanduhrzeitmessungen zustĂ€ndig.

Die explizite Darstellung der Pipeline erwies sich als wichtiger als deren Raffinesse. Die Integrationsiteration begann mit einer kleinen, klar definierten Beobachtung – die Orchestrierung war monolithisch – und einem konkreten Ziel: Zeitangaben fĂŒr jeden einzelnen Schritt in der Befehlszeile und spĂ€ter in der Web-OberflĂ€che. Das ist problemorientiertes Arbeiten, noch bevor das Muster benannt wird.

Der Ablauf beschleunigte die Schlussfolgerung nicht. Er machte lediglich die aufwĂ€ndigen Schritte sichtbar. Transparenz ist eine Voraussetzung fĂŒr ehrliche Problemstellungen.

Der Benchmark, der keinen Sinn ergab

Anfangs sah die Symfony-Inferenz ungefĂ€hr so ​​aus:

~2.6 seconds

Das entsprach den warmen nativen Zahlen so genau, dass ich ihr vertraute. Die Demo fĂŒhlte sich an wie „etwa drei Sekunden“. Dann machte Flow jeden Schritt sichtbar, und die Berechnung ergab keinen Sinn mehr.

Ein Warmlauf nach der Flow-Integration sah eher so aus:

Schritt ms
Validieren ~3
InferDepth ~2639
Farbskala ~86
ExportScene ~2535
Gesamt ~5260

Der InferDepth-Schritt allein Ă€hnelte noch dem vorherigen Verlauf von ca. 2,6 Sekunden. Die gesamte Anfrage, auf die der Benutzer wartete, dauerte eher fĂŒnf Sekunden.

ExportScene „schrieb keine GLB-Datei“. Es bezahlte fĂŒr eine andere native Inferenz.

In diesem Moment bestand der wertvolle Beitrag nicht mehr darin, „bitte optimieren Sie den PHP-Code“. Es handelte sich um ein prĂ€zise formuliertes technisches Problem:

Observation:
The complete pipeline takes much longer than infer-only.

Evidence:
Flow timings show another expensive native operation during GLB export.
InferDepth ≈ 2.6 s. ExportScene ≈ 2.5 s.

Constraint:
Preserve depth, confidence, camera, and GLB output.

Desired result:
One native inference per request.

Non-goal:
Do not solve this by adding concurrency or extra architecture layers.

Vergleichen Sie das mit einer frĂŒhen Aufforderung wie „Leistung verbessern“. Die zweite Aufforderung ist prĂ€ziser und deutlich wirkungsvoller. Der Mitarbeiter braucht nicht mehr Handlungsfreiheit, sondern einen besseren Kontext.

Die zweite Schlussfolgerung finden

Der Aufrufpfad vor der Korrektur war:

InferDepth
  → da_capi_depth_dense()
  → full ViT + DPT inference

ExportScene
  → da_capi_export_glb()
  → full ViT + DPT inference again
  → write GLB

da_capi_depth_dense() und da_capi_export_glb() waren unabhĂ€ngige Einstiegspunkte. Der Exportpfad bereitete Tiefe, Konfidenzintervall, Pose und RGB-Werte durch erneutes AusfĂŒhren von depth_pose_native() auf. Es gab keinen Cache dazwischen. Aus Sicht der Anwendung war das Tiefenergebnis bereits vorhanden; aus Sicht der nativen API wusste der GLB-Export dies jedoch nicht.

Dies ist ein Fehler, der wie ein Performance-Problem aussieht, aber tatsĂ€chlich ein Architekturproblem ist. Ohne Schrittzeiten ist der zweite Durchlauf in einer einzigen, fĂŒr den Benutzer sichtbaren Wartezeit verborgen. Mit Schrittzeiten wird er deutlich:

InferDepth ≈ 2.6 s
ExportScene ≈ 2.5 s

Zwei nahezu gleich teure Schritte sind selten ein Zufall.

Die Untersuchung basierte auf Beweisen, nicht auf Spekulationen. Ich habe nicht von vornherein Metal, die Quantisierung, den PHP-Overhead oder die Speicherbandbreite verantwortlich gemacht. Ich habe die Laufzeiten bis zum Aufrufpfad verfolgt und anschließend den Code zur Vorbereitung des nativen Exports analysiert. Diese Vorgehensweise war wichtig, da ein Codierungsagent in kĂŒrzester Zeit ebenso plausible, aber falsche ErklĂ€rungen generieren kann. Beobachtetes Verhalten, Protokolle, Laufzeiten, Codepfade und Reproduktion sollten Vorrang vor bloßen ErzĂ€hlungen haben.

Eine Schlussfolgerung, mehrere Ergebnisse

Die Lösung bestand nicht in asynchronen Workern, Messenger oder einer umfassenderen Neuprogrammierung. Sie bestand in der Wiederverwendung.

Konzeptionell:

BEFORE

image
 ├── infer → depth
 └── infer again → GLB
AFTER

image
   ↓
one inference
   ↓
native result
   ├── depth
   ├── confidence
   ├── camera
   └── GLB

Auf der nativen Seite wurde dies zu ABI v11: da_capi_export_glb_cached(). Dichte Inferenz fĂŒllt einen Export-Cache im Kontext; GLB-SchreibvorgĂ€nge verbrauchen diesen Cache ohne ein zweites depth_pose_native(). Auf der PHP-Seite ruft ExportScene exportGlbCached() auf.

Das entscheidende Signal war der GLB-Schritt, nicht eine umstrittene Millisekunde bei InferDepth.

Vorher (Warm-Flow-Lauf, der den Fehler aufdeckte):

Schritt ms
Validieren ~3
InferDepth ~2639
Farbskala ~86
ExportScene ~2535
Gesamt ~5260

Nach (experiments/004-pipeline-single-pass, mountains.jpg → 504×336, ​​q4_k, warm, Apple M4 / Metal):

Schritt Median (ms)
Validieren ~3
InferDepth ~3165
Farbskala ~88
ExportScene (zwischengespeichert) ~19
Gesamt ~3274

Native Inferenz pro Anfrage: 2 → 1. ExportScene wurde von einer weiteren vollstĂ€ndigen Inferenz auf einen Schreibvorgang aus dem Cache von ca. 20 ms reduziert. InferDepth liegt in der „Nachher“-Tabelle höher, da diese Messung den grĂ¶ĂŸeren Demo-Sample-Tensor (504×336) verwendet; die Optimierung besteht im Wegfall des zweiten Durchlaufs und nicht in einer Beschleunigung von InferDepth selbst.

Die resultierende CodeÀnderung war relativ gering. Der wertvolle Aspekt bestand darin, genau zu identifizieren, welche Arbeitsschritte dupliziert waren. Mithilfe von Observability konnte der duplizierte Code gefunden werden. Die Leistungsverbesserung ergab sich durch dessen Löschung.

Diese Erkenntnis ist gewichtiger als die bloße Aufforderung, irgendwo Caching hinzuzufĂŒgen. Die aufwendige Berechnung hatte bereits alle Tensoren erzeugt, die der GLB-Exporter benötigte. Die Architektur hat dies lediglich nicht erkannt.

Was die Messwerte tatsÀchlich aussagen

Alle unten aufgefĂŒhrten Werte wurden auf Apple M4, macOS arm64, Metal gemessen. Es handelt sich nicht um universelle Benchmarks fĂŒr Depth Anything 3. Unterschiedliche TensorgrĂ¶ĂŸen bedeuten unterschiedliche Arbeitslasten.

Messung vor Optimierung wurde zur wiederkehrenden Regel. Nicht „PHP ist langsam“, sondern: native Inferenz, Flow/PHP-Schritte, GLB-Export, vollstĂ€ndige Pipeline – jede mit benannten Grenzen.

Die Auflösung ist Teil des Benchmarks

DA3 wird so skaliert, dass die lĂ€ngste Seite etwa 504 Pixel betrĂ€gt, wobei die Abmessungen an die PatchgrĂ¶ĂŸe 14 angepasst sind. Das SeitenverhĂ€ltnis der Quelle Ă€ndert daher den verarbeiteten Tensor:

Quelle Verarbeitet Warmer Inferenz-Median (q4_k)
Referenzfoto 1280×720 504×280 ~2495 ms
1024×680 Demo-Beispiele 504×336 ~3,0–3,2 s

Diese Zahlen widersprechen sich nicht. 504×336 hat etwa 20 % mehr Pixel als 504×280. Sie zu vergleichen, ohne die verarbeitete Auflösung anzugeben, ist eine Methode, um Scheinregressionen zu erzeugen.

Als ein Durchlauf etwa 2495 ms und ein anderer etwa 3175 ms dauerte, war der nÀchste Schritt nicht eine weitere Optimierungsanfrage. Vielmehr war eine prÀzisere Problembeschreibung angebracht: gleiche Hardware, gleiche Variante, unterschiedliche verarbeitete Dimensionen. Sobald die Zahlen voneinander abwichen, war die Untersuchung wichtiger als Spekulation.

Warm Infer-only bei 504×280

Aus experiments/003-warm-q4k-f32 (app:depth-bench, AufwÀrmen 1, Wiederholung 10, gleicher PHP-Prozess):

Variante Median (ms) ModellgrĂ¶ĂŸe
q4_k 2494,9 ~99 MB
f32 2495.1 ~393 MB

Kaltstart ist ein anderes PhÀnomen

Der erste q4_k-Ladevorgang in einem Prozess dauerte im Referenz-CLI-Experiment etwa 6,9 Sekunden, einschließlich der Metal-Shader-Kompilierung. Anschließende Warmstarts dauerten nur noch wenige zehn bis wenige hundert Millisekunden. Das HinzufĂŒgen von Kaltstartwerten zu einer „Latenz“-Tabelle ohne entsprechende Kennzeichnung fĂŒhrt zu unsinnigen Ergebnissen.

Flow / PHP-Overhead

Verwendung von Schrittzeiten im gleichen Durchlauf auf einer warmen, vollen Pipeline (experiments/005-flow-overhead, mountains.jpg):

validate   ~3.5 ms
colormap   ~90 ms
glb        ~20 ms   (cached)
result     ~0 ms

Die nicht-nativen Flow/PHP-Schritte summierten sich auf etwa 114 ms. In diesem Experiment beanspruchte die Orchestrierungsseite etwa 0,1 Sekunden der Anfrage. Neuronale Inferenz war dominant.

Flow hat Depth Anything nicht beschleunigt. Es hat den Doppelpass-Fehler aufdeckbar gemacht und anschließend den verbleibenden Overhead transparent gemacht.

CLI-Referenz (native da3-cli, 504×280)

Aus experiments/001-reference-cpp:

Variante ModellgrĂ¶ĂŸe Last Medianwert ableiten Maximaler RSS-Wert
q4_k ~99 MB ~6869 ms kalt* 2589 ms ~457 MB
f32 ~393 MB ~194 ms Warmlaufzeit 2580 ms ~1028 MB
  • Der Kaltstart von q4_k beinhaltet die Initialisierung der Metal-Bibliothek beim ersten Start.

q4_k versus f32

Quantisierung wird oft als kostenlose Geschwindigkeitssteigerung beworben. Dieses Experiment konnte diese Behauptung auf dieser Hardware nicht bestÀtigen.

Die Frage lautete nicht: „Was sollte sich schneller anfĂŒhlen?“ Es handelte sich um einen kontrollierten Vergleich: gleicher Prozess, gleiche Eingabe, AufwĂ€rmphase, dann zehn Wiederholungen, die verarbeitete Auflösung wurde aufgezeichnet. Ausgabe 009 hielt diese Details vor der Implementierung fest.

Bei einer warmen Inferenz von 504×280 waren die Medianwerte von q4_k und f32 praktisch identisch (~2495 ms). Deutlich verĂ€ndert war jedoch der Speicherbedarf: ca. 99 MB gegenĂŒber 393 MB auf der Festplatte und ein deutlich höherer maximaler RSS-Wert fĂŒr f32 im CLI-Referenzlauf.

Die begrĂŒndete Schlussfolgerung lautet daher nicht „q4_k ist viermal schneller“. Das war es nicht.

Die begrĂŒndete Schlussfolgerung lautet eher:

Die Quantisierung reduzierte in diesem Experiment den Speicherbedarf des Modells drastisch, ohne dass sich die Latenz bei dieser speziellen Apple M4 / Metal-Arbeitslast entsprechend verbesserte.

Ich habe keinen Genauigkeitssieger ermittelt. Ohne die tatsĂ€chlichen Tiefendaten der Demofotos wĂ€re ein Vergleich der beiden Messmethoden reiner Show. Die praktische Wahl fĂŒr die Demo-Standardeinstellung ist q4_k, da die Datei kleiner ist und die Latenzzeit hier zu f/32 passt. Ein Tiefen-Delta-Befehl ohne tatsĂ€chliche Tiefendaten wurde aus demselben Grund explizit ausgeschlossen.

Was „lokal“ bedeutet

Lokale Inferenz und eine vollstÀndig offlinefÀhige Anwendung sind verwandte, aber nicht identische Behauptungen.

Nach der Installation fĂŒhrt Nolife Local keinen Remote-KI-API-Aufruf durch. Die Inferenz erfolgt ĂŒber FFI → libdepthanything → GGUF auf der Festplatte → Metal. Das traf anfangs zu.

Die strengere Demo-Laufzeit-Behauptung erforderte mehr Arbeit. Die Ergebnisseite lud ursprĂŒnglich Googles „ Die Verwendung eines CDN zur Verarbeitung der Daten fĂŒhrte zu einem Trugschluss: Die KI befindet sich lokal, die Seite jedoch nicht. Wir haben public/vendor/model-viewer.min.js eingebunden, den CDN-Verweis aus dem Demopfad entfernt und dies mit HTTP-PrĂŒfungen und HTML-Inspektion verifiziert – das Verhalten wurde erneut beobachtet, nicht angenommen.

Nach dieser Änderung bestĂ€tigt:

Anspruch Ergebnis
Netzwerk fĂŒr Inferenz erforderlich Nein
Netzwerkverbindung fĂŒr Demo-Laufzeit erforderlich (Ergebnisseite) Nein

FĂŒr die Einrichtung wird weiterhin einmalig eine Netzwerkverbindung benötigt: Composer-Pakete, Modell-Download, nativer Build. Die Hot-Reload-CDN-Skripte von FrankenPHP werden nur angezeigt, wenn die optionale Umgebungsvariable gesetzt ist; die Veröffentlichungsdemo verwendet den integrierten PHP-Server ohne diese Variable.

Die korrekte Formulierung lautet also nicht „KI in der Cloud“. Sie lautet auch nicht „Das gesamte Universum ist fĂŒr immer offline“. Sie lautet vielmehr:

Nach der Installation kann diese Demo auch ohne Netzwerkverbindung ausgefĂŒhrt werden. Das Modell ist eine lokale Bibliothek.

KI-Modelle als Anwendungsinfrastruktur

Sobald sich ein spezialisiertes Modell wie eine Bibliothek verhĂ€lt, wirken die dazugehörigen Werkzeuge fĂŒr jeden Symfony-Entwickler vertraut.

php bin/console app:depth-check
php -d ffi.enable=1 bin/console app:depth-infer -i public/samples/mountains.jpg -m q4_k
php -d ffi.enable=1 bin/console app:depth-bench -i public/samples/mountains.jpg -w 1 -r 5 --json
php -d ffi.enable=1 bin/console app:depth-stress -i public/samples/mountains.jpg -r 10
php bin/console app:depth-cleanup --keep=20

Das Warmup dient Demozwecken. Das Cleanup ist notwendig, da jeder Durchlauf Artefakte erzeugt. Der Stresstest ist erforderlich, da die Lebensdauer des nativen Caches bei wiederholten Aufrufen innerhalb des Prozesses Teil der Vereinbarung ist. Metadaten werden neben den Bildern gespeichert. Die Pipeline-Gesamtwerte werden einmalig in BuildResult berechnet und nicht in Twig und CLI unterschiedlich summiert.

Das ĂŒberraschende GefĂŒhl, das sich nach einer Weile einstellt, ist, dass Depth Anything aufhört, sich wie eine „KI-Funktion“ zu lesen, und sich stattdessen wie eine „native AbhĂ€ngigkeit mit Diagnosefunktionen“ anfĂŒhlt.

Genau darum geht es.

Kontext als technisches Artefakt

Zu diesem Zeitpunkt betrachtete ich den Codierungsagenten nicht mehr als etwas, das ich zur „Projektverbesserung“ beauftragte. Jede Iteration begann mit einem kleineren, beobachteten Problem, entsprechenden Belegen, EinschrĂ€nkungen und einer Erfolgsbedingung. Die Implementierung wurde zum letzten Schritt, nicht mehr zum ersten.

Genau das meine ich mit problemorientierter Entwicklung – nicht Projektmanagement-Theater, sondern ein komprimiertes Briefing, das der Agent tatsĂ€chlich nutzen kann.

Ein Problem kann zu einer komprimierten Darstellung all dessen werden, was der Agent ĂŒber ein Problem wissen muss. Nicht nur „Etwas ist kaputt“, sondern:

what happened
where it happened
environment
evidence
why it matters
constraints
expected behavior
what not to change

FĂŒr Nolife Local umfasste dieser Kontext schließlich Quellcode, die native API-OberflĂ€che, Modellvariante, verarbeitete Auflösung, Flow-Timings, Benchmark-Methodik, Fehlerausgabe, Git-Verlauf, bekannte EinschrĂ€nkungen, erwartete Ergebnisse und explizite Nicht-Ziele. Diese Kombination funktionierte deutlich besser als eine vage Implementierungsanfrage.

Die Problemdateien des Projekts spiegeln diese Struktur wider. Problem 007 betrifft die doppelte Inferenz. Problem 009 betrifft q4_k im Vergleich zu f32. Problem 003 betrifft die FFI-BrĂŒcke, die aktualisiert wurde, als PHP 8.5 die erste Speicherannahme nicht mehr zuließ. Die Datei ITERATIONS.md dokumentiert denselben Ablauf chronologisch: Beobachtung, Problemauswahl, Änderung, Validierung, Messung, nĂ€chster Kandidat.

observe → issue → agent → implementation → measure → observe

„Ausstellung zuerst“ bedeutet nicht, dass das Eigentum an den Agenten ĂŒbertragen wird.

Mensch: wÀhlt das Problem aus, definiert die Absicht, legt EinschrÀnkungen fest, bestimmt den Umfang, wÀgt Kompromisse ab und validiert die Ergebnisse.

Ausbilder: prĂŒft, implementiert, misst, dokumentiert und wiederholt die im Auftrag vorgegebenen Maßnahmen.

Der Agent kann große Mengen an Code generieren. Das bedeutet aber nicht, dass er fĂŒr die zukĂŒnftige Ausrichtung des Projekts verantwortlich ist. Ein eingereichter Patch kann weiterhin Erkenntnisse, ArchitekturverstĂ€ndnis und Erfahrung im API-Design beinhalten. Problemorientiertes Arbeiten macht Code nicht wertlos. Es steigert vielmehr das ProblemverstĂ€ndnis, wenn die Implementierung kostengĂŒnstig ist.

Spezialisiere das Modell, spezialisiere den Kontext

Hier besteht eine Parallele, und ich betrachte sie als eine Interpretation aus dem Projekt und nicht als ein universelles Gesetz.

FĂŒr die Berechnung:

general model
        ↓
specialized task model
        ↓
more constrained computation

FĂŒr die Entwicklung:

general prompt
        ↓
well-defined issue
        ↓
more constrained implementation

Die Modellseite besagt: Verwenden Sie nicht das allgemeinste Modell, wenn die Aufgabe bereits klar definiert ist. Die Agentenseite besagt: Geben Sie nicht die allgemeinste Aufforderung, wenn das technische Problem prÀzise definiert werden kann. In beiden FÀllen kann eine Verkleinerung des Suchraums das Ergebnis verbessern.

Das Modell sollte spezialisiert werden, wenn die Berechnung spezialisiert ist. Der Kontext sollte spezialisiert werden, wenn ein Agent die nĂ€chste Änderung implementieren will.

Was mir das Experiment beigebracht hat

Ein paar Überraschungen, synthetisiert statt neu aufgelegt:

PHP FFI reichte fĂŒr die Integrationsschnittstelle aus. Der kostenintensive Teil war nicht die PHP-Orchestrierung. Die Beobachtbarkeit deckte doppelte Inferenz effektiver auf als die Intuition. Die verarbeitete Auflösung erklĂ€rte scheinbar inkonsistente Benchmarks. q4_k sparte erheblich Speicherplatz, ohne dass sich die Warmlatenz bei dieser Metal-Workload deutlich verbesserte. Und mit ausreichendem Werkzeugaufwand kann sich ein Bildverarbeitungsmodell eher wie eine native Bibliothek als wie ein KI-Dienst anfĂŒhlen.

Auf der Prozessseite wurde die Implementierung gĂŒnstiger, das VerstĂ€ndnis des eigentlichen Problems jedoch nicht. Konfiguration, Umgebung, Protokolle, Messungen und Reproduktion waren genauso wichtig wie der Code. Spekulationen, die vom Agenten generiert wurden, waren niemals gleichwertig mit beobachteten Beweisen.

Darkwood Flows wichtigste Aussage in diesem Projekt ist nicht, dass jede Symfony-Anwendung Flow benötigt. Vielmehr geht es darum, dass KI-Modelle, sobald sie zu normalen Anwendungskomponenten werden, dieselben Eigenschaften wie jede rechenintensive Anwendung benötigen: Orchestrierung, Timing, Fehlerbehandlung, Lebenszyklusmanagement und Beobachtbarkeit. In Nolife Local deckte diese Transparenz doppelt implementierte native Funktionen auf. Das sind konkrete Beweise, keine leere Behauptung.

EinschrÀnkungen

Dieses Experiment hat einen definierten Umfang.

Die Validierung erfolgte ausschließlich unter macOS arm64 / Metal. Es liegen keine Linux- oder Windows-Verifizierungen sowie kein Benchmark fĂŒr den produktiven Einsatz von PHP-FPM vor. Depth Anything 3 wurde weder trainiert noch feinabgestimmt. Die Leistungswerte sind hardwarespezifisch. Die Demo implementiert ein spezialisiertes Modell, kein Produktionsnetz mit mehreren Modellen. Die GLB-QualitĂ€t wurde anhand der Veröffentlichungsbeispiele validiert; absolute geometrische Genauigkeit im Vergleich zu den tatsĂ€chlichen Tiefendaten war nicht das Ziel.

Das sind keine Entschuldigungen. Es sind die Grenzen der Behauptung – bewusst gewĂ€hlte EinschrĂ€nkungen, kein Mangel an Ehrgeiz.

Abschluss

Ich wollte eine praktische Frage stellen: Was passiert, wenn anstatt ein Bild an ein universelles multimodales LLM zu senden, ein spezialisiertes Bildverarbeitungsmodell Teil der normalen Pipeline einer Symfony-Anwendung wird?

Depth Anything 3 verhielt sich weniger wie ein „KI-Produkt“, sondern eher wie eine Bibliothek: laden, aufrufen, freigeben, messen, diagnostizieren, Ergebnisse wiederverwenden. PHP wurde nicht zu einem ML-Framework. Es orchestrierte eine native, spezialisierte Komponente mittels FFI, machte die Pipeline mit Flow beobachtbar und wandelte eine versteckte Doppelinferenz in eine Single-Pass-Architektur um.

Der ĂŒbergeordnete Wandel könnte folgender sein: Mit zunehmender Implementierung von KI rĂŒckt die Ingenieurwissenschaft stĂ€rker in den Fokus, die richtige Berechnung auszuwĂ€hlen, das richtige Problem zu definieren und das Ergebnis zu validieren.

In diesem Experiment werden drei Schichten ĂŒbereinander gestapelt:

Depth Anything 3     → specializes computation
Darkwood Flow        → makes computation observable and composable
Issue-first briefs   → specialize the context given to the coding agent

Die Schlussfolgerung lautet nicht: „Hört auf, LLMs zu verwenden.“

Es ist nÀher an:

Hören Sie auf anzunehmen, dass jedes KI-Problem mit dem gleichen Modell oder der gleichen Art von Aufforderung gelöst werden sollte.

Eine moderne KI-Anwendung kann ein LLM, ein Bildverarbeitungsmodell, ein Einbettungsmodell, ein Sprachmodell, ein Tiefenmodell, normalen Anwendungscode und native Bibliotheken kombinieren. Jede Komponente sollte ihre jeweilige StÀrke optimal ausspielen. Symfony muss ggml nicht neu implementieren. Es muss lediglich die Schnittstelle zwischen diesen Komponenten bereitstellen können.

Historisch gesehen bedeutete ein Beitrag zu Open-Source-Projekten oft, ein Problem zu finden, einen Patch zu schreiben und einen Pull Request einzureichen. Coding Agents schwĂ€chen die Annahme, dass das Schreiben des Patches zwangslĂ€ufig der knappste Teil ist. Ein zukĂŒnftiger Beitrag kann auch dann Ă€ußerst wertvoll sein, wenn jemand eine prĂ€zise Reproduktion, Umgebungsdetails, Protokolle, Konfiguration, Anwendungsfall, erwartetes Verhalten und EinschrĂ€nkungen bereitstellt – denn diese Informationen ermöglichen es einem Maintainer und einem Agent, die korrekte Korrektur zu implementieren. Pull Requests sind nicht ĂŒberflĂŒssig. Auch gut formulierte Issues sind weiterhin relevant. Wenn die Implementierung schnell erfolgen kann, verschiebt sich die interessante Knappheit hin zu Absicht und Beweisen.

Nolife Local ist eine Variante dieser Architektur:

Symfony + PHP + FFI + Darkwood Flow + specialized native model

Nach der Installation ist keine Remote-Inferenz-API erforderlich.

Ressourcen

Upstream- und verwandte Projekte:

  • depth-anything.cpp

  • Quellcode: https://github.com/matyo91/nolife-local

  • Folien: https://github.com/matyo91/slidewire

Anmelden um auf diesen Beitrag zu reagieren

🚀 1

Site

  • Sitemap
  • Kontakt
  • Impressum

Network

  • Hello
  • Blog
  • Apps
  • Photos

Social

Darkwood 2026, alle Rechte vorbehalten