Zum Inhalt springen
EN DE

Challenge 5.1: The Template

Wenn Du einen System Prompt schreibst — wie stellst Du sicher, dass er bei 20 verschiedenen API-Calls konsistent bleibt?

Variables (role, audience, tone) fliessen in ein Prompt Template mit XML Tags (task-context, background-data, examples, rules, conversation-history, the-ask, output-format), das dann generateText und LLM zu konsistentem Output fuehrt

Das Diagramm zeigt die Kernidee: Ein Prompt Template definiert eine feste Struktur mit XML Tags. Variablen werden dynamisch injiziert. Das Ergebnis ist ein konsistenter, wiederverwendbarer Prompt für beliebig viele API-Calls.

Ohne Template: Du kopierst Prompts per Copy-Paste in jede Datei. Jeder Prompt sieht leicht anders aus. Aenderungen am Ton oder Format müssen an 20 Stellen gleichzeitig gemacht werden. Das Ergebnis ist inkonsistent und nicht testbar.

Mit Template: Ein Prompt, eine Stelle, viele Aufgaben. Du aenderst die Variablen — der Rest bleibt gleich. DRY (Don’t Repeat Yourself), testbar, wartbar.

Schicht 1: Die Grundstruktur (XML Tags für Sections)

Abschnitt betitelt „Schicht 1: Die Grundstruktur (XML Tags für Sections)“

Anthropic empfiehlt, Prompts mit XML Tags zu strukturieren. Jeder Tag hat eine bestimmte Funktion und Position — die Reihenfolge folgt der Aufmerksamkeitsverteilung von LLMs:

<task-context>
Rolle und Aufgabe — steht am ANFANG (hoher Einfluss)
</task-context>
<background-data>
Dokumente, Kontext — steht in der MITTE
</background-data>
<examples>
Few-Shot Beispiele — steht in der MITTE
</examples>
<rules>
Detaillierte Anweisungen — steht in der MITTE
</rules>
<conversation-history>
Chat-Verlauf — steht in der MITTE
</conversation-history>
<the-ask>
Die eigentliche Frage — steht am ENDE (hoher Einfluss)
</the-ask>
<output-format>
Ausgabeformat — steht am ENDE (hoher Einfluss)
</output-format>

LLMs gewichten Anfang und Ende staerker als die Mitte. Deshalb stehen <task-context> und <output-format> an den Raendern — dort, wo sie den groessten Einfluss haben.

Die XML-Struktur allein ist statisch. Um sie wiederverwendbar zu machen, nutzt Du TypeScript Template Literals. Variablen werden an den richtigen Stellen injiziert:

// Template Literal: Variablen werden zur Laufzeit eingesetzt
// Die ${...}-Ausdruecke werden durch die config-Werte ersetzt:
// config.role → Rolle dynamisch
// config.audience → Zielgruppe dynamisch
// config.tone → Ton dynamisch
// config.outputFormat → Format dynamisch
const systemPrompt = `
<task-context>
Du bist ein ${config.role}.
Deine Zielgruppe: ${config.audience}.
</task-context>
<rules>
- Schreibe ${config.tone}
- Fachbegriffe beim ersten Auftreten erklären
- Keine Annahmen ohne Kennzeichnung
</rules>
<output-format>
Formatiere Deine Antwort als: ${config.outputFormat}
</output-format>
`;

Jede Variable macht den Prompt flexibel, ohne die Struktur zu veraendern. Derselbe Prompt funktioniert für einen “technischen Redakteur” genauso wie für einen “Marketing-Texter” — nur die Variablen ändern sich.

Die letzte Schicht kapselt alles in einer wiederverwendbaren Funktion:

import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
interface PromptConfig {
role: string;
audience: string;
tone: string;
outputFormat: string;
}
function buildSystemPrompt(config: PromptConfig): string { // ← Reusable Funktion
return `
<task-context>
Du bist ein ${config.role}.
Deine Zielgruppe: ${config.audience}.
</task-context>
<rules>
- Schreibe ${config.tone}
- Fachbegriffe beim ersten Auftreten erklären
- Keine Annahmen ohne Kennzeichnung
- Codebeispiele müssen lauffaehig sein
</rules>
<output-format>
Formatiere Deine Antwort als: ${config.outputFormat}
</output-format>
`.trim();
}
// Derselbe Prompt, verschiedene Konfigurationen:
const result = await generateText({
model: anthropic('claude-sonnet-4-5-20250514'),
system: buildSystemPrompt({ // ← Template wird hier genutzt
role: 'technischer Redakteur',
audience: 'Entwickler mit 2 Jahren Erfahrung',
tone: 'professionell aber zugaenglich',
outputFormat: 'Markdown mit Codebeispielen',
}),
prompt: 'Erklaere, was Context Engineering ist.',
});
console.log(result.text);

Die Funktion nimmt ein PromptConfig-Objekt und gibt einen vollstaendigen System Prompt als String zurück. Du kannst sie in Tests mocken, in verschiedenen Kontexten wiederverwenden und an einer Stelle ändern.

Aufgabe: Baue Deine eigene buildSystemPrompt-Funktion. Sie soll ein Template mit XML Tags zurueckgeben, das mindestens vier Sektionen hat.

Erstelle die Datei challenge-5-1.ts und fuehre sie aus mit: npx tsx challenge-5-1.ts

import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
interface PromptConfig {
role: string;
audience: string;
tone: string;
outputFormat: string;
}
// TODO: Implementiere buildSystemPrompt
// 1. Nutze Template Literals (Backticks)
// 2. Setze XML Tags für die Struktur ein:
// - <task-context> für Rolle und Zielgruppe
// - <rules> für Tonalitaet und Einschraenkungen
// - <output-format> für das gewuenschte Format
// 3. Injiziere die config-Variablen an den richtigen Stellen
function buildSystemPrompt(config: PromptConfig): string {
// Dein Code hier
return '';
}
const result = await generateText({
model: anthropic('claude-sonnet-4-5-20250514'),
system: buildSystemPrompt({
role: 'technischer Redakteur',
audience: 'Entwickler mit 2 Jahren Erfahrung',
tone: 'professionell aber zugaenglich',
outputFormat: 'Markdown mit Codebeispielen',
}),
prompt: 'Erklaere, was Context Engineering ist.',
});
console.log(result.text);

Checkliste:

  • Template nutzt XML Tags für Struktur
  • Mindestens 4 Sektionen (role, audience, tone, format)
  • Funktion gibt einen String zurück
  • Code laeuft mit generateText
Lösung anzeigen
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
interface PromptConfig {
role: string;
audience: string;
tone: string;
outputFormat: string;
}
function buildSystemPrompt(config: PromptConfig): string {
return `
<task-context>
Du bist ein ${config.role}.
Deine Zielgruppe: ${config.audience}.
</task-context>
<rules>
- Schreibe ${config.tone}
- Fachbegriffe beim ersten Auftreten erklären
- Keine Annahmen ohne Kennzeichnung
- Codebeispiele müssen lauffaehig sein
</rules>
<output-format>
Formatiere Deine Antwort als: ${config.outputFormat}
</output-format>
`.trim();
}
const result = await generateText({
model: anthropic('claude-sonnet-4-5-20250514'),
system: buildSystemPrompt({
role: 'technischer Redakteur',
audience: 'Entwickler mit 2 Jahren Erfahrung',
tone: 'professionell aber zugaenglich',
outputFormat: 'Markdown mit Codebeispielen',
}),
prompt: 'Erklaere, was Context Engineering ist.',
});
console.log(result.text);

Erklärung: Die Funktion nutzt <task-context> am Anfang für maximalen Einfluss auf die Rolle und <output-format> am Ende für maximale Kontrolle über das Format. Die <rules> stehen in der Mitte. Alle vier config-Variablen werden dynamisch injiziert.

Erwarteter Output (ungefaehr — LLM-Ausgaben variieren):

Context Engineering ist die systematische Gestaltung des Inputs...
Variables fliessen in buildSystemPrompt, dann in streamText, das einen Text Stream erzeugt der als Terminal Output ausgegeben wird

Uebung: Nutze Dein Template jetzt mit streamText statt generateText. Streame die Antwort ins Terminal.

Du brauchst:

  1. Ersetze generateText durch streamText
  2. Iteriere über result.textStream mit for await...of
  3. Gib jeden Chunk mit process.stdout.write(chunk) aus

Optional Stretch Goal: Fuege einen zweiten Aufruf hinzu, der dasselbe Template mit einer anderen Konfiguration nutzt (z.B. role: 'Marketing-Texter'). Vergleiche die Outputs.

Part of AI Learning — free courses from prompt to production. Jan on LinkedIn