Zod-Schemata in TypeScript
Auf dieser Seite
Aktualisiert am 3. November 2026 nach der Kompilierung mit dem SDK
@mistralai/mistralai3.5.0, Zod 4.6 und TypeScript 7.0.2. Das ursprüngliche Beispiel importierte standardmäßigMistral, das SDK bietet jedoch nur einen benannten Export. Zudem wurde inmain()auf die Variableresponsezugegriffen, die nur innerhalb vonextractBook()existierte. Der Zugriff aufparsedignorierte, dasschoicesundmessagein den SDK-Typen optional sind, und behandelteparsedalsBook, obwohl sein Typ eigentlichunknownist. Der Import vonZodErrorwar unnötig:chat.parse()validiert mitsafeParseund wirft nie eine Ausnahme. Der API-Schlüssel ist nicht mehr hartcodiert. Quellen: Mistral TypeScript SDK · offizielleschat.parse-Beispiel.
Zod: Das TypeScript-Äquivalent von Pydantic
Wenn Sie in TypeScript oder JavaScript arbeiten, ist Zod die Schema-Validierungsbibliothek, die vom Mistral SDK für Custom Structured Outputs verwendet wird. Sie erfüllt exakt dieselbe Rolle wie Pydantic in Python: Sie definiert ein striktes Schema, das das Modell einhalten muss. Alles, was Sie über Schemata gelernt haben, gilt hier ebenfalls, lediglich die Syntax unterscheidet sich.
Installieren Sie sowohl das SDK als auch Zod mit einem einzigen Befehl:
npm install @mistralai/mistralai zod
Zod funktioniert nativ mit TypeScript und bietet automatische Typableitung – sein Hauptvorteil gegenüber dem manuellen Schreiben von Schnittstellen.
Definition eines einfachen Schemas
Erstellen Sie ein Zod-Schema mit z.object() und weisen Sie jedem Feld einen Validierer zu. Die Zeile z.infer macht den Unterschied: Sie leitet den TypeScript-Typ aus dem Schema ab, sodass Sie keine zwei parallelen Deklarationen pflegen müssen, die auseinanderdriften könnten.
import { z } from "zod";
const ContactSchema = z.object({
name: z.string(),
email: z.string(),
phone: z.string(),
company: z.string(),
});
// TypeScript leitet den Typ automatisch ab
type Contact = z.infer<typeof ContactSchema>;
// => { name: string; email: string; phone: string; company: string }
Typen, die Sie benötigen
Zod deckt alle Anforderungen für strukturierte Ausgaben ab. Das Ticket-Schema unten fasst gängige Fälle zusammen: Basisfelder, ein nullable-Feld für ein unzuweisbares Ticket, ein Enum für die Priorität und typisierte Arrays.
import { z } from "zod";
const TicketSchema = z.object({
// Basistypen
title: z.string(),
description: z.string(),
ticketId: z.number(),
confidence: z.number(),
isUrgent: z.boolean(),
// Optional (kann null sein)
assignee: z.string().nullable(),
// Enum (eingeschränkte Werte)
priority: z.enum(["low", "medium", "high", "critical"]),
// Listen
tags: z.array(z.string()),
relatedIds: z.array(z.number()),
});
type Ticket = z.infer<typeof TicketSchema>;
Ein Nuance verdient Aufmerksamkeit: Während Python int und float unterscheidet, kennt Zod nur z.number(). Halten Sie diese Zuordnung im Kopf, wenn Sie ein Schema von einer Sprache in die andere portieren.
| Pydantic (Python) | Zod (TypeScript) |
|---|---|
str | z.string() |
int, float | z.number() |
bool | z.boolean() |
list[str] | z.array(z.string()) |
Optional[str] | z.string().nullable() |
Enum | z.enum([...]) |
Verschachtelte Schemata
Wie bei Pydantic kann ein Schema ein anderes enthalten: Verweisen Sie einfach auf die Konstante. Die Komposition wird dann von innen nach außen gelesen: Die Adresse innerhalb des Unternehmens, das Unternehmen innerhalb der Person.
import { z } from "zod";
const AddressSchema = z.object({
street: z.string(),
city: z.string(),
postalCode: z.string(),
country: z.string(),
});
const CompanySchema = z.object({
name: z.string(),
industry: z.string(),
address: AddressSchema,
});
const PersonSchema = z.object({
firstName: z.string(),
lastName: z.string(),
email: z.string(),
role: z.string(),
company: CompanySchema,
});
type Person = z.infer<typeof PersonSchema>;
Ein vollständiger Aufruf mit dem Mistral SDK
Hier ist dieselbe Buchextraktion aus der Python-Lektion, portiert nach TypeScript. Drei Punkte verdienen Aufmerksamkeit. Der Parameter heißt responseFormat in camelCase. Sie müssen parsed vor der Verwendung testen: Das SDK validiert die Antwort gegen das Schema mit safeParse und lässt parsed als undefined zurück, wenn das JSON abgetrennt wurde oder dem Schema nicht entspricht, anstatt eine Ausnahme zu werfen. Schließlich trägt das SDK den Typ Ihres Schemas nicht mit sich: Es deklariert parsed als unknown. Das Übergeben des Objekts durch BookSchema.parse() macht es als Book typisiert und validiert es erneut im Prozess.
import { Mistral } from "@mistralai/mistralai";
import { z } from "zod";
// 1. Schema definieren
const BookSchema = z.object({
title: z.string().describe("Titel des Buches"),
authors: z.array(z.string()).describe("Liste der Autoren"),
year: z.number().nullable().describe("Erscheinungsjahr"),
genre: z.string().describe("Hauptliterarische Gattung"),
summary: z.string().describe("Zusammenfassung in 2–3 Sätzen"),
});
type Book = z.infer<typeof BookSchema>;
// 2. Client erstellen
const client = new Mistral({ apiKey: process.env.MISTRAL_API_KEY });
// 3. chat.parse() aufrufen
async function extractBook(text: string): Promise<Book> {
const response = await client.chat.parse({
model: "mistral-large-latest",
messages: [
{
role: "system",
content: "Sie sind ein erfahrener Bibliothekar. Extrahieren Sie Buchinformationen.",
},
{ role: "user", content: text },
],
responseFormat: BookSchema,
temperature: 0,
});
// 4. Das geparsedte Objekt abrufen
const message = response.choices?.[0]?.message;
if (!message?.parsed) {
throw new Error("Antwort fehlt, wurde abgetrennt oder entspricht nicht dem Schema");
}
// Das rohe JSON bleibt in message.content verfügbar
console.log(`Rohes JSON: ${message.content}`);
// parsed ist als unknown typisiert: BookSchema.parse() gibt ein typisiertes Book zurück
return BookSchema.parse(message.parsed);
}
// 5. Verwendung
async function main() {
const book = await extractBook(
"Ich habe '1984' von George Orwell geliebt, veröffentlicht 1949. Ein dystopischer Klassiker."
);
console.log(`Titel: ${book.title}`);
console.log(`Autoren: ${book.authors.join(", ")}`);
console.log(`Jahr: ${book.year}`);
console.log(`Gattung: ${book.genre}`);
}
main();
Felder mit .describe() beschreiben
In Zod erfüllt .describe() dieselbe Funktion wie Field(description=...) in Pydantic: Es sagt dem Modell, was das Feld enthalten sollte, insbesondere wenn der Name allein mehrdeutig ist. Ein score ohne Beschreibung könnte auf eine Skala von 0–100 landen; mit einer expliziten Erwähnung des Bereichs folgt es Ihrer Konvention.
const SentimentSchema = z.object({
sentiment: z.enum(["positive", "negative", "neutral"]).describe(
"Gesamte Stimmung des Textes"
),
score: z.number().describe(
"Vertrauensscore zwischen 0,0 und 1,0"
),
keywords: z.array(z.string()).describe(
"Die 3–5 wichtigsten Schlüsselwörter"
),
summary: z.string().describe(
"Zusammenfassung in maximal einem Satz"
),
});
Fehlerbehandlung
Das Retry-Muster folgt derselben Logik wie in Python, mit einer generischen Signatur, die es für jedes Schema wiederverwendbar macht. Es besteht kein Bedarf, eine ZodError abzufangen: chat.parse() wirft keine; es lässt parsed leer. Dieser Fall, zusammen mit Netzwerk- oder HTTP-Fehlern, löst ein Retry aus. Die Einschränkung z.ZodType<T> verknüpft das als Argument übergebene Schema mit dem Rückgabetyp: schema.parse() gibt ein T zurück, sodass der Aufrufer ein korrekt typisiertes Objekt erhält, ohne zusätzliche Annotationen.
// Fortsetzung derselben Datei: client, z und BookSchema sind bereits definiert
async function safeExtract<T>(
text: string,
schema: z.ZodType<T>,
maxRetries = 2
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await client.chat.parse({
model: "mistral-large-latest",
messages: [
{ role: "system", content: "Extrahieren Sie die angeforderten Informationen." },
{ role: "user", content: text },
],
responseFormat: schema,
temperature: 0,
});
const parsed = response.choices?.[0]?.message?.parsed;
if (!parsed) {
throw new Error("Antwort fehlt oder entspricht nicht dem Schema");
}
return schema.parse(parsed); // stellt Typ T wieder her
} catch (error) {
console.error(`Versuch ${attempt + 1} fehlgeschlagen:`, error);
if (attempt === maxRetries) throw error;
}
}
throw new Error("Unerwarteter Fehler");
}
// Verwendung
try {
const book = await safeExtract("Mein Text hier", BookSchema);
console.log(book.title);
} catch (error) {
console.error("Extraktion fehlgeschlagen:", error);
}
Kernaussagen
- Zod ist das TypeScript-Äquivalent von Pydantic für Custom Structured Outputs
z.infer<typeof Schema>generiert den TypeScript-Typ automatisch.describe()fügt Feldanweisungen hinzu, ähnlich wieField(description=...)in Python- Das Mistral TypeScript SDK verwendet
responseFormat(camelCase) stattresponse_format - Eine nicht konformen Antwort lässt
parsedalsundefinedzurück (keine Ausnahme): Testen Sie dies und wiederholen Sie den Vorgang bei Bedarf