Zero ausprobiert: eine agent-first Programmiersprache von Vercel Labs

20.05.2026 Stefan Welsch
Tech Artificial Intelligence CLI Developer Experience Hands-on Open source DevOps Testing

Banner

TLDR

  • Zero ist ein experimentelles Open-Source-Projekt von Vercel Labs: eine Programmiersprache, die von Anfang an für AI Agents gedacht ist.
  • Der spannende Teil ist nicht nur die CLI, sondern die Sprache selbst: kleine Oberfläche, explizite Typen, reguläre Syntax und Tooling, das strukturierte Fakten liefern soll.
  • Zero ist pre-1, instabil und ausdrücklich nicht produktionsreif. Du solltest es nur in isolierten, wegwerfbaren Umgebungen ausprobieren.
  • Relevant ist Zero für Entwickler:innen, DevOps-Teams und AI Engineers, die verstehen wollen, wie Programmiersprachen aussehen könnten, wenn Agents primäre Nutzer sind.
  • Kernfeatures: zero check, zero run, strukturierte Diagnostics, Graph- und Size-Informationen, Standard-Library-Fokus und agent-freundliche Reparatur-Workflows.

Einleitung

Heute schauen wir uns Zero an, ein experimentelles Projekt von Vercel Labs. Die Kurzbeschreibung im Repository ist ziemlich direkt: „The programming language for agents“. Das klingt erstmal nach AI-Hype, ist aber technisch eine interessante Frage: Was passiert eigentlich, wenn eine Programmiersprache nicht primär für Menschen entworfen wird, sondern für Agents, die Code lesen, ändern, prüfen und reparieren sollen?

Wichtig nehme ich direkt vorweg: Zero ist pre-1, absichtlich instabil und nicht für Production gedacht. Die Projektbeschreibung sagt auch klar, dass Security-Vulnerabilities zu erwarten sind und dass man Zero nicht mit sensiblen Daten oder vertrauenswürdiger Infrastruktur einsetzen sollte. Also bitte nicht auf die Idee kommen, damit morgen euren Payment-Service neu zu schreiben.

Trotzdem ist Zero spannend, weil es ein paar Designentscheidungen sichtbar macht, die auch für heutige Toolchains relevant sind: explizite Sprachelemente statt syntaktischer Magie, strukturierte Ausgaben für Diagnostics und Graphs, eine breite Standard Library und Workflows, die von Anfang an scriptable sind. In diesem Artikel geht es deshalb nicht nur um zero check oder zero run, sondern vor allem darum, wie sich die Sprache anfühlt und warum diese Regularität für Agents hilfreich sein kann.

Das Projekt positioniert sich sehr klar als Experiment in Richtung agent-first Programming Language. Genau diese Einordnung ist wichtig: Es geht nicht darum, eine weitere Allzwecksprache mit schöner Syntax zu bauen, sondern um die Frage, welche Eigenschaften Code haben sollte, wenn AI-Systeme ihn zuverlässig verstehen und verändern sollen.

Hintergrund

Warum eine Sprache für Agents?

Wenn wir heute mit Coding Agents arbeiten, landen wir schnell bei bekannten Problemen. Ein Agent kann zwar Code generieren, aber er muss eine Menge implizites Wissen rekonstruieren: Projektstruktur, Build-System, Dependency-Management, Framework-Konventionen, Test-Commands, Linting-Regeln und manchmal noch Team-spezifische Patterns.

Zero geht an dieser Stelle eine Ebene tiefer. Statt nur bessere Prompts für existierende Sprachen zu bauen, fragt das Projekt: Wie müsste eine Sprache aussehen, wenn Agents primäre Nutzer sind?

Aus der README lassen sich ein paar Ziele ableiten:

  • Agent-first learnability: Die Sprache soll klein und regelmäßig genug sein, damit Agents sie aus Beispielen, Docs und Compiler-Feedback schnell lernen können.
  • Standard-library depth: Häufige Fähigkeiten sollen in dokumentierten, kohärenten APIs liegen, statt sofort eine Dependency-Suche auszulösen.
  • Deterministic tooling: Diagnostics, Graph Facts, Size Reports und Fix Plans sollen strukturiert genug sein, damit Agents sie maschinell auswerten können.
  • Direct developer experience: Checken, Ausführen, Formatieren, Inspizieren und Reparieren soll schnell und scriptable sein.
  • Regularity over syntax: Lieber eine offensichtliche, explizite Schreibweise als mehrere clevere Kurzformen.

Gerade der letzte Punkt ist wichtig. Viele Sprachen optimieren stark auf menschliche Ergonomie: weniger Zeichen, syntaktischer Zucker, Framework-Magie. Für Agents ist das nicht immer optimal. Ein Agent profitiert oft mehr von Regelmäßigkeit, expliziten Typen und maschinenlesbarem Feedback als von besonders eleganter Syntax.

Pre-1 heisst: anschauen, nicht einplanen

Zero ist ausdrücklich ein Experiment. Die Syntax und APIs können sich ändern, und genau das ist bei so einem Projekt auch sinnvoll. Wenn eine Sprache noch herausfinden will, welche Patterns für Agents wirklich funktionieren, dann sollte sie nicht zu früh stabilisiert werden.

Für uns als Entwickler:innen heisst das: Wir können Zero sehr gut als Lernobjekt nutzen. Wir können uns anschauen, welche Tooling-Ideen dort ausprobiert werden, wie strukturierte Ausgabe aussehen kann und wie eine bewusst kleine Sprachoberfläche wirkt. Für produktive Workloads, sensible Daten oder trusted Infrastructure ist Zero aktuell aber nicht geeignet.

Hauptmerkmale

Die Sprache: explizit, klein und regelmäßig

Der besondere Punkt an Zero ist nicht, dass es wieder eine neue CLI mitbringt. Davon haben wir genug. Interessanter ist die Idee, dass die Sprache selbst möglichst leicht maschinell zu lernen und zu reparieren sein soll.

In der Praxis bedeutet das: Programme bestehen aus gut erkennbaren Bausteinen wie Packages, Structs, Funktionen, Kontrollfluss und expliziten Rückgabetypen. Statt stark impliziter Framework-Konventionen soll Code so geschrieben sein, dass ein Agent ihn direkt analysieren kann.

Ein kleines Beispiel für den Stil:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package shop.orders

struct OrderLine {
  sku: String
  quantity: Int
  unit_price_cents: Int
}

fn line_total_cents(line: OrderLine) -> Int {
  return line.quantity * line.unit_price_cents
}

Das ist nicht spektakulär, aber genau das ist der Punkt. Ein Agent muss nicht raten, was line_total_cents zurückgibt. Er sieht die Eingabe, den Rückgabetyp und die Feldzugriffe. Je weniger implizit ist, desto besser lassen sich Diagnostics, Refactorings und Fix Plans ableiten.

Natürlich: Weil Zero pre-1 ist, solltest du die konkrete Syntax nicht als endgültig betrachten. Mir geht es hier vor allem um das Sprachgefühl und um die Designrichtung.

Tooling als Teil des Sprachdesigns

Zero bringt Commands mit, die man bei modernen Sprachen erwartet: checken, ausführen, bauen. Spannend sind aber die strukturierten Analyse-Kommandos wie graph --json, size --json oder doctor --json.

Die Idee dahinter ist agent-freundlich: Ein Agent soll nicht erst Terminal-Text parsen müssen, der für Menschen formatiert wurde. Er soll strukturierte Fakten bekommen, daraus eine Entscheidung ableiten und dann gezielt Code ändern können.

Der Quick Start im Repository zeigt die ersten Schritte mit Installation, zero --version, zero check und zero run. Diese Commands wirken auf den ersten Blick normal, aber in Kombination mit den JSON-Ausgaben entsteht ein Tooling-Modell, das sich gut in automatisierte Repair- und Analyse-Loops integrieren lässt.

Hands-On

Ok, schauen wir uns Zero praktisch an. Der Fokus liegt hier bewusst auf einem kleinen Programm in der Sprache selbst. Die CLI nutzen wir nur als Werkzeug drumherum.

Unser Szenario: ein sehr kleines E-Commerce Order-Service-Modul. Es berechnet den Gesamtpreis einer Bestellung, inklusive Versandlogik. Das Beispiel ist absichtlich überschaubar, aber realistisch genug, um Structs, Funktionen, Kontrollfluss, Listen und ein main-Programm zu zeigen.

Setup in einer isolierten Umgebung

Da Zero nicht produktionsreif ist, würde ich es lokal in einem separaten Dev-Container, einer VM oder zumindest in einem Wegwerf-Verzeichnis ausprobieren.

Die Installation laut Projekt-README:

1
2
3
curl -fsSL https://zerolang.ai/install.sh | bash
export PATH="$HOME/.zero/bin:$PATH"
zero --version

Danach kannst du ein Programm prüfen und ausführen:

1
2
zero check examples/hello.0
zero run examples/add.0

Das erwartete Beispiel aus der README gibt aus:

1
math works

Für unser eigenes Beispiel legen wir diese Struktur an:

1
2
mkdir -p zero-order-demo/examples zero-order-demo/tests zero-order-demo/tools
cd zero-order-demo

Das Order-Service-Programm in Zero

Jetzt kommt der wichtigste Teil: der Code in Zero selbst. Lege die Datei examples/order_service.0 an.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package shop.orders

struct OrderLine {
  sku: String
  quantity: Int
  unit_price_cents: Int
}

struct Order {
  id: String
  country: String
  lines: List<OrderLine>
}

fn line_total_cents(line: OrderLine) -> Int {
  return line.quantity * line.unit_price_cents
}

fn shipping_cents(country: String, subtotal_cents: Int) -> Int {
  if subtotal_cents >= 10000 {
    return 0
  }

  if country == "CH" {
    return 900
  }

  return 1500
}

fn order_total_cents(order: Order) -> Int {
  let subtotal_cents = 0

  for line in order.lines {
    subtotal_cents = subtotal_cents + line_total_cents(line)
  }

  return subtotal_cents + shipping_cents(order.country, subtotal_cents)
}

fn main() {
  let order = Order {
    id: "ord-1001",
    country: "CH",
    lines: [
      OrderLine { sku: "keyboard", quantity: 1, unit_price_cents: 12900 },
      OrderLine { sku: "usb-cable", quantity: 2, unit_price_cents: 1900 }
    ]
  }

  let total_cents = order_total_cents(order)
  print("order_id=" + order.id)
  print("total_cents=" + to_string(total_cents))
}

Schauen wir uns die Sprachelemente kurz durch.

Das package am Anfang gibt dem Code einen Namespace:

1
package shop.orders

Das ist für Agents hilfreich, weil ein Modul nicht nur ein Dateiname ist, sondern eine explizite Zugehörigkeit hat. In größeren Codebases kann ein Tool daraus Graph Facts erzeugen.

Danach definieren wir Datenstrukturen:

1
2
3
4
5
struct OrderLine {
  sku: String
  quantity: Int
  unit_price_cents: Int
}

Die Felder sind explizit typisiert. Keine Reflection-Magie, kein verstecktes Schema, kein implizites JSON-Mapping. Ein Agent kann sofort erkennen: Eine OrderLine hat eine SKU, eine Menge und einen Preis in Cents.

Auch die Funktionssignatur ist sehr direkt:

1
2
3
fn line_total_cents(line: OrderLine) -> Int {
  return line.quantity * line.unit_price_cents
}

Relevant ist hier der Rückgabetyp Int. Für Menschen wirkt das banal. Für Agents ist es aber wertvoll, weil Reparaturen besser eingrenzbar sind. Wenn ein Agent später line_total_cents ändert und plötzlich einen String zurückgibt, kann ein Checker sehr konkret widersprechen.

Die Versandlogik zeigt normalen Kontrollfluss:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn shipping_cents(country: String, subtotal_cents: Int) -> Int {
  if subtotal_cents >= 10000 {
    return 0
  }

  if country == "CH" {
    return 900
  }

  return 1500
}

Hier sieht man die Regularität: Bedingung, Block, Return. Keine implizite Fallthrough-Magie. Keine versteckte DSL. Genau diese Einfachheit ist bei agent-first Design gewollt.

Programm prüfen

Mit installierter Zero CLI prüfst du das Programm so:

1
zero check examples/order_service.0

Wenn die Syntax zur installierten Zero-Version passt, sollte der Checker ohne Fehler zurückkommen. Falls nicht, ist das bei pre-1 nicht komplett überraschend. Dann ist genau der strukturierte Diagnose-Output interessant: Welche Stelle wird bemängelt? Ist der Fehler maschinenlesbar genug, damit ein Agent ihn reparieren könnte?

Typische Commands aus dem Repository sind:

1
2
3
4
5
6
7
zero check examples/hello.0
zero run examples/add.0
zero build --emit exe --target linux-musl-x64 examples/add.0 --out .zero/out/add
zero graph --json examples/systems-package
zero size --json examples/point.0
zero skills get zero --full
zero doctor --json

Besonders spannend sind hier graph --json, size --json, skills get und doctor --json, weil sie über reines Kompilieren hinausgehen. Sie zeigen, dass Zero Tooling nicht nur als Entwicklerkomfort versteht, sondern als Teil des Sprachdesigns für Agents.

Programm ausführen

Wenn zero check erfolgreich ist, kannst du das Beispiel ausführen:

1
zero run examples/order_service.0

Erwartete Ausgabe für unser Beispiel:

1
2
order_id=ord-1001
total_cents=16700

Warum 16700? Die Bestellung enthält:

  • 1 × Keyboard zu 12900 Cents
  • 2 × USB-Cable zu je 1900 Cents

Das ergibt:

1
12900 + 2 * 1900 = 16700

Weil der Subtotal über 10000 Cents liegt, ist der Versand kostenlos. Der Gesamtpreis bleibt also 16700.

Aus der Sprachstruktur Fakten ableiten

Ein Kernpunkt von Zero ist, dass Tooling strukturierte Fakten liefern soll. Das offizielle Tooling bietet dafür unter anderem zero graph --json.

Für unser Beispiel können wir uns das Prinzip mit einem kleinen Python-Skript nachbauen. Das ist nicht als Ersatz für Zero gedacht, sondern als Illustration: Je regelmäßiger die Sprache ist, desto einfacher lassen sich Fakten extrahieren.

Lege tools/zero_facts.py an:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from pathlib import Path
import json
import re

source = Path("examples/order_service.0").read_text()

facts = {
    "package": re.search(r"package\s+([a-zA-Z0-9_.]+)", source).group(1),
    "structs": re.findall(r"struct\s+([A-Za-z0-9_]+)", source),
    "functions": re.findall(r"fn\s+([a-z_]+)\(", source),
    "prints": re.findall(r'print\("([^"]+)"', source),
}

print(json.dumps(facts, indent=2, sort_keys=True))

Ausführen:

1
python tools/zero_facts.py

Terminal 2

Die Ausgabe zeigt eine kleine strukturierte Sicht auf unser Programm: Package, Structs, Funktionen und Print-Ausgaben. Genau in diese Richtung zielt Zero mit den offiziellen JSON-Kommandos, nur natürlich deutlich sauberer und semantischer als unser Regex-Spielzeug.

Contract-Tests für das Beispiel

Da Zero pre-1 ist, zeige ich zusätzlich ein kleines ausführbares Test-Harness, das unsere Beispiel-Datei verifiziert. Das ist nicht der eigentliche Zero-Compiler. Es hilft aber, den Blog-Code reproduzierbar zu prüfen und macht sichtbar, welche Eigenschaften wir am Sprachbeispiel erwarten.

Lege tests/test_order_service_contract.py an:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from pathlib import Path
import re

SOURCE = Path("examples/order_service.0").read_text()


def normalized_signatures():
    signatures = re.findall(r"fn\s+([a-z_]+)\((.*?)\)\s*->\s*([A-Za-z0-9_<>, ]+)", SOURCE, re.S)
    return [(name, " ".join(args.split()), ret.strip()) for name, args, ret in signatures]


def test_public_functions_are_explicitly_typed():
    signatures = normalized_signatures()
    assert ("line_total_cents", "line: OrderLine", "Int") in signatures
    assert ("shipping_cents", "country: String, subtotal_cents: Int", "Int") in signatures
    assert ("order_total_cents", "order: Order", "Int") in signatures


def test_order_example_calculates_expected_total():
    prices = [int(value) for value in re.findall(r"unit_price_cents:\s*(\d+)", SOURCE)]
    quantities = [int(value) for value in re.findall(r"quantity:\s*(\d+)", SOURCE)]
    subtotal = sum(quantity * price for quantity, price in zip(quantities, prices))
    shipping = 0 if subtotal >= 10000 else 900
    assert subtotal == 16700
    assert subtotal + shipping == 16700


def test_program_uses_regular_agent_friendly_constructs():
    assert "struct OrderLine" in SOURCE
    assert "struct Order" in SOURCE
    assert "for line in order.lines" in SOURCE
    assert 'print("total_cents="' in SOURCE

Dann installierst du pytest und führst die Tests aus:

1
2
pip install -q pytest
python -m pytest -q

Terminal 1

Wie die Terminal-Ausgabe zeigt, laufen alle Contract-Tests erfolgreich durch. Sie prüfen drei Dinge: Die Funktionen sind explizit typisiert, die Beispielrechnung ergibt den erwarteten Gesamtpreis und der Code verwendet die zentralen Sprachkonstrukte, die wir im Artikel besprochen haben.

Was würde ein Agent damit machen?

Der interessante Gedanke ist jetzt nicht, dass ein Python-Regex-Test besonders toll ist. Interessant ist der Workflow, den Zero systematisch unterstützen will.

Ein Agent könnte zum Beispiel:

  1. zero check examples/order_service.0 ausführen.
  2. Bei Fehlern strukturierte Diagnostics lesen.
  3. Mit zero graph --json Abhängigkeiten und betroffene Funktionen ermitteln.
  4. Nur die relevante Funktion ändern, zum Beispiel shipping_cents.
  5. Erneut zero check und Tests ausführen.
  6. Einen Fix Plan oder eine Erklärung generieren.

Das klingt erstmal ähnlich wie heutige Agent-Workflows mit TypeScript, Go oder Rust. Der Unterschied liegt im Designziel: Zero versucht, Sprache und Tooling von Anfang an so zu formen, dass dieser Loop möglichst wenig Raten erfordert.

Beispieländerung: Versandregel anpassen

Nehmen wir an, Product Management möchte kostenlosen Versand erst ab 20000 Cents. In unserem Zero-Code ändert sich nur diese Stelle:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn shipping_cents(country: String, subtotal_cents: Int) -> Int {
  if subtotal_cents >= 20000 {
    return 0
  }

  if country == "CH" {
    return 900
  }

  return 1500
}

Für unsere Beispielbestellung wäre der Gesamtpreis dann:

1
16700 + 900 = 17600

Ein Agent müsste also nicht das ganze Programm verstehen, sondern nur:

  • Welche Funktion kapselt Versandlogik?
  • Welche Inputs und Outputs hat sie?
  • Welche Tests oder Fakten hängen daran?
  • Welche erwarteten Werte ändern sich?

Genau diese Art von lokaler, strukturierter Änderung ist ein guter Fit für explizite Sprachelemente und deterministisches Tooling.

Build und native Targets

Zero bringt laut README auch einen Build-Command mit:

1
zero build --emit exe --target linux-musl-x64 examples/order_service.0 --out .zero/out/order-service

Das ist für DevOps-Teams interessant, weil native Artefakte einfacher in Container, CI/CD-Pipelines oder isolierte Testumgebungen passen können. Bei Zero würde ich das aktuell aber noch als Experiment betrachten. Pre-1 bedeutet: Toolchain, Targets und Runtime-Verhalten können sich ändern.

Für CI könntest du, sobald Zero stabiler ist, grob so denken:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
name: zero-order-service

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Zero
        run: |
          curl -fsSL https://zerolang.ai/install.sh | bash
          echo "$HOME/.zero/bin" >> "$GITHUB_PATH"
      - name: Check Zero program
        run: zero check examples/order_service.0
      - name: Inspect graph
        run: zero graph --json examples/order_service.0

Das ist noch kein Production-Rezept, sondern eher ein Ausblick. Gerade wegen der Security-Warnung im Projekt solltest du solche Install-Skripte und experimentellen Toolchains in CI nur sehr kontrolliert einsetzen.

Fazit

Zero ist aktuell weniger eine Sprache, die du sofort produktiv einsetzen solltest, und mehr ein spannender Blick darauf, wie Programming Languages für AI Agents aussehen könnten. Der Fokus auf Regularität, explizite Typen, strukturierte Diagnostics und scriptable Tooling ist sinnvoll. Gerade wenn Agents nicht nur Code schreiben, sondern auch debuggen, erklären und reparieren sollen, wird diese Maschinenlesbarkeit wichtig.

Meine persönliche Einschätzung: Zero ist im Moment ein gutes Tool zum Beobachten und Experimentieren, nicht zum Einplanen. Wenn du dich mit Developer Experience, AI-assisted Development oder Tooling beschäftigst, lohnt sich ein Blick ins Repository. Aber nutze es isoliert, erwarte Breaking Changes und betrachte die Syntax eher als laufende Forschung als als etwas, das du heute schon auswendig lernen musst.

Stefan Welsch

Stefan Welsch – Manitu, Pionier, Stuntman, Mentor. Als Gründer von b-nova ist Stefan immer auf der Suche nach neuen und vielversprechenden Entwicklungsfeldern. Er ist durch und durch Pragmatiker und schreibt daher auch am liebsten Beiträge die sich möglichst nahe an 'real-world' Szenarien anlehnen.