Capability Cards: warum bei mir jedes Modul eine bekommt
Ein Agent hat mir ein Flag vorgeschlagen, das es nicht gibt. Seitdem bekommt jedes Modul eine maschinenlesbare Karte statt Prosa.
Ein Agent hat mir letzte Woche ein Flag vorgeschlagen, das es nicht gibt. Plausibel benannt, an der richtigen Stelle im Kommando, sauber begründet. Nur existiert es nirgends im Code. Er hatte die README gelesen und den Rest interpoliert.
Interpolation sieht bei einem Sprachmodell exakt so aus wie Wissen. Das ist kein Modellfehler, das ist ein Materialfehler.
Seitdem bekommt bei mir jedes Modul und jedes Werkzeug eine Capability Card: eine einzelne Markdown-Datei, die einer KI sagt, was ein Werkzeug kann, wie man es aufruft und wo es gefährlich wird. Aktuell liegen 98 davon im Repository, 88 für Odoo-Module und 10 für Kommandozeilen-Werkzeuge.
Was auf einer Capability Card steht
Bei einem CLI-Werkzeug liegt die Karte unter usage/AGENT.md, bei einem Odoo-Modul
unter doc/capability_card.md. Der Aufbau ist immer derselbe.
Zuerst, was das Ding kann, formuliert in Aufgaben statt in Modellnamen. Dann die vollständige Oberfläche als Tabelle: jedes Kommando, jedes Feld, jede Route. Danach drei bis acht Rezepte für Aufgaben, die wirklich anfallen. Zum Schluss die Guardrails, also alles, was Daten zerstört oder vorher existieren muss.
Englisch, immer. Auch wenn die Hilfeseiten daneben zweisprachig sind.
Und mit Budget. Rund 300 Zeilen sind das Maximum, meine Karten liegen im Median bei 7,9 KB.
Das Skript besitzt die Fakten, ich besitze die Bedeutung
Der wichtigste Teil ist eine Arbeitsteilung, die ich am Anfang unterschätzt habe. Namen, Flags, Felder und Methoden schreibe ich nicht. Die holt ein Introspektions-Skript deterministisch aus dem Code, bei Odoo statisch und ganz ohne laufenden Server.
Dass das ohne Server funktioniert, liegt an Odoo selbst. Ein Modul deklariert seine Oberfläche in Dateien statt zur Laufzeit. Modelle und Felder stehen als Klassenattribute im Python-Code. Ansichten und Menüs liegen im XML, die Zugriffsrechte in einer CSV daneben.
Das Skript liest den Python-Teil als abstrakten Syntaxbaum, ohne eine Zeile davon auszuführen, und parst den Rest direkt aus den Dateien. Heraus fallen zwei Ergebnisse. Eine strukturierte JSON, gegen die der Prüfschritt später abgleicht, und ein Markdown mit fertigen Tabellen, die ich unverändert in die Karte übernehme. Nötig ist dafür nur ein Python ab Version 3.10, keine Datenbank und kein Odoo-Import.
Bei Kommandozeilen-Werkzeugen läuft dasselbe über den Befehlsbaum: Das Skript geht die
Click-Struktur durch und sammelt jedes Unterkommando mit seinen Optionen ein. Ist das
Werkzeug nicht mit Click gebaut, bleibt der Weg über --help, dann allerdings mit dem
Vermerk in der Karte, dass die Flag-Abdeckung nur so gut ist wie der Hilfetext.
Was ich beisteuere, ist alles, was im Code nicht steht: wofür man das Ding benutzt, in welcher Reihenfolge, und wo es wehtut.
Wenn dir das nach Bürokratie klingt, ging es mir genauso. In Wahrheit ist es die ganze Absicherung. Eine handgetippte Flag-Tabelle driftet ab dem Tag ihrer Entstehung. Eine generierte kann nicht driften, weil sie beim nächsten Lauf neu entsteht. Genau diese Drift war es, die meinem Agenten das erfundene Flag eingeflüstert hat.
Warum nicht einfach den Quellcode lesen lassen?
Weil die Rechnung nicht aufgeht. Ein Agent kann Dateien lesen, aber er bezahlt jede gelesene Zeile mit Kontext, der ihm danach für die eigentliche Arbeit fehlt.
Ein Beispiel aus meinem Bestand: eq_helper hat 98 KB Quellcode in 24 Dateien, die
Karte dazu ist 12,9 KB groß. Wer die Frage „was kann dieses Modul“ über den Quellcode
beantwortet, verbrennt das Achtfache für eine Antwort, die schlechter ausfällt, weil
sie aus Implementierungsdetails zusammengeraten wurde.
Der zweite Grund wiegt schwerer. Quellcode sagt, was passiert. Er sagt nicht, was man tun sollte.
Warum Karte und Hilfeseite nicht zusammengehören
Bei Odoo-Modulen liegt die Karte direkt neben den Hilfeseiten für Menschen, und das führt regelmäßig zu der Frage, warum man das nicht zusammenlegt. Die Antwort ist, dass die beiden Dateien entgegengesetzte Leser haben.
Die Hilfeseite erklärt einer Sachbearbeiterin, wo sie klicken muss. Sie ist
zweisprachig, sie darf ausschweifen, sie zeigt Screenshots. Die Karte erklärt einem
Modell, welche Methode es über call_kw aufrufen darf und welche Zugriffsregel dabei
greift.
Deshalb wird die Karte bewusst nicht in der Hilfe-Navigation registriert. Der Viewer zeigt sie nie an, der Chatbot holt sie sich über eine eigene Methode. Dasselbe Verzeichnis, entgegengesetztes Publikum. Beide Dateien werden schlechter, sobald man sie verschmilzt.
Die Karte ist ein Build-Artefakt, keine Doku
Der Punkt, an dem das Verfahren erst trägt, ist ein Prüfschritt. Bevor eine Karte geschrieben wird, vergleicht ein Skript die Introspektions-Daten gegen den Karteninhalt. Fehlt ein Kommando, ein Feld oder eine Route, bricht es ab.
Bei mir geht das noch weiter. Mein afterwork-Ablauf wertet ein Flag, das im Code
existiert, aber nicht auf der Karte steht, als Workflow-Verstoß. Die Karte hat damit
denselben Status wie ein fehlgeschlagener Test.
Und weil niemand 88 Module von Hand durchsieht, übernimmt ein vorgelagerter Schritt das Auffinden: Er prüft pro Modul, ob Karte, Hilfeseite, README und Icon vorhanden sind, und rüstet das Fehlende in einem gebündelten Versionssprung nach. Erst dadurch wird aus einer guten Idee ein Bestand.
Das Werkzeug trägt seine Karte bei sich
Für eigene Python-Werkzeuge gibt es noch einen Schritt. Sie bekommen ein Kommando
namens capability-card, das die eigene Karte auf stdout druckt. odoodev macht das
seit Version 0.52.0.
Damit braucht kein Agent mehr Zugriff auf mein Repository oder meine Website. Er installiert das Werkzeug und fragt es, was es kann. Die Versionsnummer wird beim Drucken live aus dem Paket injiziert, damit der Kopf der Karte nicht altern kann.
Eine veraltete Karte ist schlimmer als keine
Ehrlich bleiben gehört dazu. Eine Karte kostet Aufwand, und eine veraltete Karte ist schädlicher als gar keine, weil sie Autorität ausstrahlt, die sie nicht mehr verdient.
Deshalb gilt bei mir eine Regel, mit der ich anfangs gefremdelt habe: Widersprechen sich Introspektion und handgeschriebene Doku, gewinnt die Introspektion. Nicht weil das Skript klüger wäre, sondern weil es nicht vergisst.
Wenn du selbst anfangen willst, fang bei dem Werkzeug an, das deine KI am häufigsten falsch bedient. Dort zahlt sich die erste Karte innerhalb eines Nachmittags aus.
Der eigentliche Denkfehler steckt nämlich woanders. Man hält die Karte für Dokumentation, weil sie aussieht wie Dokumentation. Sie ist aber eine Schnittstelle. Und Schnittstellen schreibt man nicht in Prosa.