1. AI SDK Grundlagen — streamText, generateText, generateObject, Provider Setup
Das Vercel AI SDK (Version 4.x) vereinheitlicht alle großen KI-Provider hinter einer konsistenten API. Claude Code kennt das SDK in- und auswendig: Es generiert sofort produktionsreife Integrationen, wählt die richtigen Funktionen für den Use-Case und konfiguriert Provider korrekt — inklusive Error Handling und TypeScript-Typen.
generateText()Einmalige Textantwort, kein Streaming
streamText()Token-für-Token Streaming Response
generateObject()Typsicheres JSON via Zod-Schema
streamObject()Partial JSON streaming in Echtzeit
SETUP Installation & Provider-Konfiguration
Claude Code beginnt jede AI-SDK-Integration mit dem korrekten Package-Setup und der Provider-Konfiguration:
# Installation: Core SDK + Provider-Packages
npm install ai @ai-sdk/openai @ai-sdk/anthropic @ai-sdk/google @ai-sdk/groq zod
# TypeScript-Typen sind automatisch enthalten
# Node.js 18+ oder Edge Runtime erforderlich
CORE generateText() — Einfache Textgenerierung
Die grundlegendste Funktion: Eine Anfrage, eine vollständige Antwort. Claude Code nutzt generateText() für Batch-Verarbeitung, Background-Jobs und alle Use-Cases ohne UI-Streaming.
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
// Einfachste Form: Text generieren mit Claude
const { text, usage, finishReason } = await generateText({
model: anthropic('claude-3-5-sonnet-20241022'),
prompt: 'Erkläre den Unterschied zwischen useEffect und useLayoutEffect in React.',
});
console.log(text);
// usage.promptTokens, usage.completionTokens, usage.totalTokens
console.log(`Tokens: ${usage.totalTokens}`);
// Mit System-Prompt und strukturierter Nachricht
const { text: codeReview } = await generateText({
model: anthropic('claude-3-5-sonnet-20241022'),
system: 'Du bist ein erfahrener TypeScript-Entwickler. Gib präzises, konstruktives Feedback.',
messages: [
{ role: 'user', content: 'Review diesen Code: const x = data?.items.map(i => i.id)' }
],
maxTokens: 512,
temperature: 0.3,
});
STREAM streamText() — Streaming-Grundlagen
streamText() gibt einen ReadableStream zurück. Claude Code verwendet es direkt in API-Routes und kann verschiedene Ausgabeformate aus demselben Stream ableiten:
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
const result = await streamText({
model: openai('gpt-4o'),
prompt: 'Schreibe eine kurze Geschichte über einen KI-Entwickler.',
onChunk({ chunk }) {
// Jedes Token einzeln verarbeiten
if (chunk.type === 'text-delta') {
process.stdout.write(chunk.textDelta);
}
},
onFinish({ text, usage, finishReason }) {
console.log(`\nFertig. Tokens: ${usage.totalTokens}`);
},
});
// Stream in verschiedene Formate konvertieren
const textStream = result.textStream; // AsyncIterable<string>
const fullText = await result.text; // Promise<string> — wartet auf Ende
const dataStream = result.toDataStream(); // Für useChat Hook
const textResponse = result.toTextStreamResponse(); // Native Response
Claude Code Workflow-Tipp: Beschreibe deinen Use-Case ("Chat-UI mit Streaming" vs. "Batch-Verarbeitung von 100 Dokumenten") — Claude Code wählt automatisch zwischen streamText() und generateText() und fügt das korrekte Error Handling hinzu.
| Funktion | Rückgabe | Best für |
generateText() | { text, usage, finishReason } | Batch, Background, Scripts |
streamText() | StreamTextResult mit textStream | Chat-UIs, Echtzeit-Output |
generateObject() | { object: T } | Strukturierte Extraktion |
streamObject() | partialObjectStream | Live-Form-Befüllung |
2. Streaming in Next.js — useChat Hook, StreamingTextResponse, RSC mit AI SDK
Das AI SDK ist für Next.js App Router optimiert. Der useChat-Hook übernimmt State-Management, Message-History und Streaming komplett — Claude Code generiert vollständige Chat-Komponenten, die sofort funktionieren.
NEXT.JS API Route mit streamText()
Die Server-Seite: Eine Next.js API Route streamt Tokens direkt an den Client. Claude Code erstellt diese Route mit korrektem Edge-Runtime-Flag und CORS-Handling:
// app/api/chat/route.ts
import { streamText, convertToCoreMessages } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
export const runtime = 'edge'; // Optional: Edge Runtime für niedrigere Latenz
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: anthropic('claude-3-5-sonnet-20241022'),
system: 'Du bist ein hilfreicher KI-Assistent für Entwickler. Antworte auf Deutsch.',
messages: convertToCoreMessages(messages), // UI Messages → Core Messages
maxTokens: 2048,
temperature: 0.7,
});
// toDataStreamResponse() sendet im AI SDK Data Stream Protocol
// Kompatibel mit useChat Hook out-of-the-box
return result.toDataStreamResponse();
}
HOOK useChat — Frontend Chat-Komponente
Auf der Client-Seite übernimmt useChat alles: HTTP-Requests, Streaming, Message-State, Loading-States und Error Handling. Claude Code generiert vollständige, produktionsreife Chat-UIs:
// app/chat/page.tsx — Client Component
'use client';
import { useChat } from 'ai/react';
import { useRef, useEffect } from 'react';
export default function ChatPage() {
const {
messages, // Message[] — vollständige Chat-History
input, // string — aktueller Input-Wert
handleInputChange, // onChange Handler
handleSubmit, // onSubmit Handler
isLoading, // boolean — wartet auf Response
error, // Error | undefined
stop, // Stream abbrechen
reload, // Letzte Nachricht neu generieren
} = useChat({
api: '/api/chat',
initialMessages: [
{ id: '1', role: 'assistant', content: 'Hallo! Wie kann ich dir helfen?' }
],
onError: (error) => console.error('Chat Error:', error),
onFinish: (message) => console.log('Fertig:', message.content.length, 'Zeichen'),
});
const messagesEndRef = useRef<HTMLDivElement>(null);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto">
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((message) => (
<div key={message.id} className={message.role === 'user' ? 'text-right' : 'text-left'}>
<div className={`inline-block p-3 rounded-lg max-w-[80%] ${
message.role === 'user'
? 'bg-indigo-600 text-white'
: 'bg-gray-100 text-gray-900'
}`}>
{message.content}
</div>
</div>
))}
{isLoading && (
<div className="text-gray-400 animate-pulse">KI denkt nach...</div>
)}
<div ref={messagesEndRef} />
</div>
<form onSubmit={handleSubmit} className="p-4 border-t flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Nachricht eingeben..."
className="flex-1 border rounded-lg px-4 py-2 focus:outline-none"
disabled={isLoading}
/>
{isLoading
? <button type="button" onClick={stop}>Stop</button>
: <button type="submit" disabled={!input.trim()}>Senden</button>
}
</form>
</div>
);
}
RSC React Server Components mit AI SDK
Für Server Components ohne Client-State: Das AI SDK unterstützt direktes Streaming aus RSCs über createStreamableUI() und createAI():
// app/report/page.tsx — Server Component (RSC)
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
// Direkt in Server Component — kein useChat nötig
export default async function ReportPage({
searchParams
}: {
searchParams: { topic?: string }
}) {
const topic = searchParams.topic || 'KI-Trends 2026';
const { text } = await generateText({
model: anthropic('claude-3-5-haiku-20241022'),
prompt: `Erstelle einen kompakten Report über: ${topic}`,
maxTokens: 800,
});
return (
<article className="prose max-w-2xl mx-auto py-8">
<h1>{topic}</h1>
<div dangerouslySetInnerHTML={{ __html: text }} />
</article>
);
}
Tool Calling ist der Kern moderner KI-Agenten: Das Modell entscheidet selbstständig, welche Tools es wann aufruft. Das Vercel AI SDK macht dies typesafe mit Zod-Schemas. Claude Code generiert vollständige Tool-Definitionen inklusive Input-Validierung, Error Handling und multi-step Agenten-Loops.
TOOL Tool-Definition mit tool() und Zod
Jedes Tool besteht aus Beschreibung, Input-Schema (Zod) und Execute-Funktion. Claude Code erstellt typesafe Tools mit vollständigen JSDoc-Kommentaren:
import { generateText, tool } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
// Tool-Definitionen — typesafe mit Zod
const tools = {
getWeather: tool({
description: 'Aktuelles Wetter für eine Stadt abrufen',
parameters: z.object({
city: z.string().describe('Stadtname auf Deutsch'),
country: z.string().optional().describe('ISO-Ländercode, z.B. DE'),
}),
execute: async ({ city, country }) => {
// Echter API-Call zu einem Wetter-Service
const location = country ? `${city},${country}` : city;
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t=temperature_2m,weathercode`
);
const data = await response.json();
return {
city: location,
temperature: data.current.temperature_2m,
unit: '°C',
condition: data.current.weathercode < 3 ? 'Sonnig' : 'Bewölkt',
};
},
}),
searchDatabase: tool({
description: 'Produkte in der Datenbank suchen',
parameters: z.object({
query: z.string().describe('Suchbegriff'),
limit: z.number().min(1).max(20).default(5),
category: z.enum(['electronics', 'clothing', 'food']).optional(),
}),
execute: async ({ query, limit, category }) => {
// Datenbankabfrage (Prisma, Supabase, etc.)
const results = await db.product.findMany({
where: {
name: { contains: query, mode: 'insensitive' },
...(category && { category }),
},
take: limit,
select: { id: true, name: true, price: true, stock: true },
});
return { results, total: results.length };
},
}),
};
MULTI-STEP maxSteps — Agenten-Loop mit mehreren Tool-Calls
Mit maxSteps läuft das Modell automatisch mehrere Runden: Tool aufrufen → Ergebnis verarbeiten → nächstes Tool → finale Antwort. Claude Code baut vollständige Agenten-Loops:
import { generateText, tool } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
const { text, steps, toolCalls, toolResults } = await generateText({
model: anthropic('claude-3-5-sonnet-20241022'),
tools,
maxSteps: 5, // Max 5 Tool-Call-Runden
system: 'Du bist ein hilfreicher Assistent mit Zugriff auf Wetter und Datenbank.',
prompt: 'Wie ist das Wetter in Berlin heute und zeige mir 3 Winterjacken aus dem Shop?',
onStepFinish({ text, toolCalls, toolResults, finishReason, usage }) {
// Jeden Schritt beobachten
console.log(`Step: ${finishReason}, Tools: ${toolCalls.length}`);
toolCalls.forEach(tc => {
console.log(` → ${tc.toolName}(${JSON.stringify(tc.args)})`);
});
},
});
// steps[] enthält jeden einzelnen Schritt mit allen Details
console.log(`Gesamtschritte: ${steps.length}`);
console.log(`Alle Tool-Calls: ${toolCalls.length}`);
console.log(`Finale Antwort: ${text}`);
// Tool-Calls inspizieren
for (const step of steps) {
if (step.stepType === 'tool-result') {
console.log(`Tool: ${step.toolName} → ${JSON.stringify(step.result)}`);
}
}
STREAMING streamText mit Tools — Live Tool-Call Feedback
Tool Calling funktioniert auch mit streamText(). Der Stream enthält Tool-Call-Events neben Text-Chunks — perfekt für Agenten-UIs mit Live-Feedback:
// API Route mit streamText + Tools
import { streamText, tool, convertToCoreMessages } from 'ai';
import { z } from 'zod';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: anthropic('claude-3-5-sonnet-20241022'),
messages: convertToCoreMessages(messages),
maxSteps: 3,
tools: {
calculator: tool({
description: 'Mathematische Berechnungen durchführen',
parameters: z.object({
expression: z.string().describe('Mathematischer Ausdruck, z.B. "2 * (3 + 4)"'),
}),
execute: async ({ expression }) => {
// Sicheres Evaluieren (in Produktion: math.js oder ähnliches nutzen)
try {
const result = eval(expression.replace(/[^0-9+\-*/.() ]/g, ''));
return { result, expression };
} catch {
return { error: 'Ungültiger Ausdruck', expression };
}
},
}),
},
});
return result.toDataStreamResponse({
// Tool-Calls im Data Stream inkludieren
sendUsage: true,
});
}
Wichtig bei Tool Calling: Setze immer ein vernünftiges maxSteps-Limit (5-10). Ohne Limit kann ein Modell theoretisch endlos Tool-Calls machen. Claude Code fügt diesen Guard automatisch ein und warnt, wenn er fehlt.
4. Structured Outputs — generateObject() mit Zod-Schema, partialObjectStream
generateObject() ist der typsichere Weg, strukturierte Daten aus KI-Modellen zu extrahieren. Das Modell liefert garantiert valides JSON, das dem Zod-Schema entspricht — kein manuelles Parsing, keine Fehler durch Halluzinationen außerhalb der Struktur. Claude Code nutzt dies für Daten-Extraktion, Content-Generierung und API-Backends.
SCHEMA generateObject() — Typsicheres JSON aus dem Modell
Das Zod-Schema definiert exakt, was das Modell zurückgeben soll. Claude Code erstellt komplexe verschachtelte Schemas und wählt den richtigen Output-Mode:
import { generateObject } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
// Schema für Produktbeschreibungs-Extraktion
const ProductSchema = z.object({
name: z.string().describe('Produktname'),
category: z.enum(['electronics', 'software', 'service']),
targetAudience: z.array(z.string()).describe('Zielgruppen als Liste'),
features: z.array(z.object({
title: z.string(),
description: z.string(),
importance: z.enum(['high', 'medium', 'low']),
})).min(3).max(8),
pricing: z.object({
model: z.enum(['one-time', 'subscription', 'usage-based']),
startingPrice: z.number().optional(),
currency: z.string().default('EUR'),
}),
seoKeywords: z.array(z.string()).max(10),
});
type Product = z.infer<typeof ProductSchema>;
const { object: product } = await generateObject({
model: anthropic('claude-3-5-sonnet-20241022'),
schema: ProductSchema,
prompt: `Analysiere dieses Produkt und extrahiere alle Informationen:
"Claude Code ist Anthropics offizielles CLI für Claude, das KI-Entwicklern
hilft, Code zu schreiben, zu debuggen und zu deployen. Es integriert sich
direkt in das Terminal und unterstützt alle gängigen Editoren."`,
});
// TypeScript kennt den exakten Typ dank Zod-Inferenz
console.log(product.name); // string
console.log(product.features[0]); // { title: string, description: string, importance: ... }
console.log(product.pricing.model); // 'one-time' | 'subscription' | 'usage-based'
ARRAY generateObject mit Array-Output
Für Listen strukturierter Objekte: output: 'array' generiert ein Array mit Items, die alle dem Schema entsprechen — ideal für Batch-Generierung:
import { generateObject } from 'ai';
import { z } from 'zod';
const { object: blogIdeas } = await generateObject({
model: anthropic('claude-3-5-haiku-20241022'),
output: 'array', // Array statt einzelnes Objekt
schema: z.object({
title: z.string().describe('SEO-optimierter Blog-Titel'),
slug: z.string().describe('URL-freundlicher Slug ohne Sonderzeichen'),
keywords: z.array(z.string()).max(5),
estimatedReadTime: z.number().describe('Geschätzte Lesezeit in Minuten'),
outline: z.array(z.string()).describe('H2-Abschnitte als Liste'),
}),
prompt: 'Generiere 5 Blog-Post-Ideen über Vercel AI SDK und Next.js 15 für deutschsprachige Entwickler.',
});
// blogIdeas ist ein typisiertes Array
blogIdeas.forEach((idea, i) => {
console.log(`${i + 1}. ${idea.title} (${idea.estimatedReadTime} min)`);
console.log(` URL: /blog/${idea.slug}`);
});
PARTIAL streamObject() — partialObjectStream für Live-Updates
streamObject() gibt ein partialObjectStream zurück: Jeder Chunk enthält den bisherigen validen Teilzustand des Objekts — perfekt für Live-Form-Befüllung und Progressive Disclosure in UIs:
import { streamObject } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
const ResumeSchema = z.object({
name: z.string(),
summary: z.string(),
skills: z.array(z.string()),
experience: z.array(z.object({
company: z.string(),
role: z.string(),
duration: z.string(),
achievements: z.array(z.string()),
})),
});
const { partialObjectStream, object: finalResume } = await streamObject({
model: anthropic('claude-3-5-sonnet-20241022'),
schema: ResumeSchema,
prompt: 'Erstelle einen Lebenslauf für eine Senior TypeScript-Entwicklerin mit 8 Jahren Erfahrung.',
});
// Jeder Partial-Update enthält den aktuellen Zustand
for await (const partial of partialObjectStream) {
// partial.name kann undefined sein bevor es generiert wird
if (partial.name) console.log(`Name: ${partial.name}`);
if (partial.skills?.length) {
console.log(`Skills bisher: ${partial.skills.join(', ')}`);
}
// In einer UI: React State aktualisieren → sofortiges Feedback
updateUIState(partial);
}
// Am Ende: vollständiges, validiertes Objekt
const resume = await finalResume;
console.log(`Komplett: ${resume.name}, ${resume.experience.length} Jobs`);
5. Multi-Provider — OpenAI, Anthropic (Claude), Google Gemini, Groq wechseln
Der größte Vorteil des Vercel AI SDK: Provider-Agnostizität. Jeder Provider wird hinter derselben API abstrahiert. Claude Code kann Anwendungen schreiben, die Provider dynamisch wechseln — nach Kosten, Geschwindigkeit, Verfügbarkeit oder Fähigkeiten.
SETUP Alle Provider konfigurieren
Jeder Provider braucht nur ein Package und einen API-Key in der Umgebungsvariable. Claude Code konfiguriert alle Provider korrekt und fügt TypeScript-Typen hinzu:
// lib/ai-providers.ts — Zentrale Provider-Konfiguration
import { openai, createOpenAI } from '@ai-sdk/openai';
import { anthropic, createAnthropic } from '@ai-sdk/anthropic';
import { google, createGoogleGenerativeAI } from '@ai-sdk/google';
import { createGroq } from '@ai-sdk/groq';
import { createMistral } from '@ai-sdk/mistral';
// Standard-Instanzen (lesen API-Keys aus Env-Variablen)
// OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, GROQ_API_KEY
// Custom Instanzen mit Optionen
const customOpenAI = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
organization: process.env.OPENAI_ORG_ID,
baseURL: 'https://api.openai.com/v1', // Oder Azure OpenAI Endpoint
});
const customAnthropic = createAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
// headers: { 'anthropic-beta': 'prompt-caching-2024-07-31' }, // Beta Features
});
const groq = createGroq({
apiKey: process.env.GROQ_API_KEY,
// Groq ist extrem schnell — ideal für Low-Latency Use-Cases
});
// Provider-Modell-Map für einfaches Wechseln
export const models = {
// Anthropic Claude — beste Reasoning + Code-Qualität
claudeSonnet: anthropic('claude-3-5-sonnet-20241022'),
claudeHaiku: anthropic('claude-3-5-haiku-20241022'),
claudeOpus: anthropic('claude-opus-4-5'),
// OpenAI — breite Tool-Unterstützung, Vision
gpt4o: openai('gpt-4o'),
gpt4oMini: openai('gpt-4o-mini'),
o3: openai('o3'),
// Google Gemini — großes Context Window, Multimodal
gemini25Pro: google('gemini-2.5-pro-preview-05-06'),
gemini20Flash: google('gemini-2.0-flash'),
// Groq — Ultra-low Latency (Llama, Mixtral)
llama3: groq('llama3-70b-8192'),
mixtral: groq('mixtral-8x7b-32768'),
} as const;
ROUTING Dynamisches Provider-Routing nach Use-Case
Claude Code baut intelligente Router-Funktionen, die den optimalen Provider für jeden Use-Case wählen — nach Kosten, Latenz und Fähigkeiten:
// lib/model-router.ts — Intelligentes Provider-Routing
import { models } from './ai-providers';
import { LanguageModelV1 } from 'ai';
type UseCase =
| 'chat' // Allgemeiner Chat
| 'code' // Code-Generierung und Review
| 'extraction' // Daten-Extraktion (schnell + günstig)
| 'reasoning' // Komplexe Reasoning-Aufgaben
| 'realtime' // Ultra-low Latency
| 'longContext'; // Sehr lange Dokumente
export function selectModel(useCase: UseCase): LanguageModelV1 {
const modelMap: Record<UseCase, LanguageModelV1> = {
chat: models.claudeHaiku, // Schnell + günstig für Standard-Chat
code: models.claudeSonnet, // Claude = beste Code-Qualität
extraction: models.gpt4oMini, // Günstig für strukturierte Extraktion
reasoning: models.claudeOpus, // Beste Reasoning-Fähigkeiten
realtime: models.llama3, // Groq = schnellste Inferenz
longContext: models.gemini25Pro, // Gemini = 1M Token Context Window
};
return modelMap[useCase];
}
// Fallback-Kette bei Provider-Ausfall
export async function generateWithFallback(
prompt: string,
primaryUseCase: UseCase,
) {
const fallbackChain: LanguageModelV1[] = [
selectModel(primaryUseCase),
models.gpt4o, // Erster Fallback
models.gemini20Flash, // Zweiter Fallback
];
for (const model of fallbackChain) {
try {
const { text } = await generateText({ model, prompt, maxRetries: 1 });
return text;
} catch (error) {
console.warn(`Provider fehlgeschlagen, nächster Fallback...`, error);
}
}
throw new Error('Alle Provider fehlgeschlagen');
}
| Provider | Stärken | Package | Env-Key |
| Anthropic Claude | Code, Reasoning, Instruktionen | @ai-sdk/anthropic | ANTHROPIC_API_KEY |
| OpenAI | Tool Calling, Vision, GPT-4o | @ai-sdk/openai | OPENAI_API_KEY |
| Google Gemini | 1M Context, Multimodal | @ai-sdk/google | GOOGLE_GENERATIVE_AI_API_KEY |
| Groq | Ultra-fast Inferenz, Llama | @ai-sdk/groq | GROQ_API_KEY |
| Mistral | Europäisch, DSGVO-konform | @ai-sdk/mistral | MISTRAL_API_KEY |
6. AI SDK + RAG — embedMany(), cosineSimilarity(), Vektor-Store-Integration
Retrieval-Augmented Generation (RAG) verbindet KI-Modelle mit eigenen Wissensdatenbanken. Das Vercel AI SDK bietet native Embedding-Funktionen — embed() und embedMany() — die sich nahtlos in Vektor-Stores wie Pinecone, Supabase pgvector oder Qdrant integrieren. Claude Code baut vollständige RAG-Pipelines in einem Schritt.
EMBED embedMany() — Dokumente vektorisieren
Der erste Schritt jeder RAG-Pipeline: Dokumente in Vektoren umwandeln. Claude Code generiert Chunking-Strategien und Embedding-Pipelines mit optimalem Batch-Processing:
import { embedMany, embed } from 'ai';
import { openai } from '@ai-sdk/openai';
// Embedding-Modell (1536 Dimensionen für text-embedding-3-small)
const embeddingModel = openai.embedding('text-embedding-3-small');
// Alternativ: anthropic.textEmbeddingModel('voyage-3') für Claude-Ökosystem
// Dokument-Chunking-Strategie
function chunkDocument(text: string, chunkSize = 512, overlap = 50): string[] {
const words = text.split(' ');
const chunks: string[] = [];
for (let i = 0; i < words.length; i += chunkSize - overlap) {
const chunk = words.slice(i, i + chunkSize).join(' ');
if (chunk.trim()) chunks.push(chunk);
}
return chunks;
}
// Batch-Embedding für ein gesamtes Dokument
async function indexDocument(documentText: string, documentId: string) {
const chunks = chunkDocument(documentText);
// embedMany() vektorisiert alle Chunks in einem API-Call
const { embeddings, usage } = await embedMany({
model: embeddingModel,
values: chunks,
});
console.log(`${chunks.length} Chunks, ${usage.tokens} Tokens verbraucht`);
// In Vektor-Store speichern (Supabase pgvector Beispiel)
const records = chunks.map((chunk, i) => ({
document_id: documentId,
chunk_index: i,
content: chunk,
embedding: embeddings[i], // number[] — Vektor
metadata: {
charCount: chunk.length,
wordCount: chunk.split(' ').length,
},
}));
await supabase
.from('document_chunks')
.insert(records);
return { chunks: chunks.length, tokens: usage.tokens };
}
SEARCH cosineSimilarity() — Semantische Suche
Das AI SDK exportiert cosineSimilarity() für die Ähnlichkeitsberechnung. Claude Code implementiert die vollständige Suche inklusive Re-Ranking und Score-Threshold:
import { embed, cosineSimilarity } from 'ai';
import { openai } from '@ai-sdk/openai';
const embeddingModel = openai.embedding('text-embedding-3-small');
// Semantische Suche ohne Datenbank — direkt mit cosineSimilarity()
async function semanticSearch(
query: string,
documents: Array<{ id: string; text: string }>,
topK = 5,
threshold = 0.7,
) {
// Query vektorisieren
const { embedding: queryEmbedding } = await embed({
model: embeddingModel,
value: query,
});
// Alle Dokumente vektorisieren und Ähnlichkeit berechnen
const { embeddings } = await embedMany({
model: embeddingModel,
values: documents.map(d => d.text),
});
// cosineSimilarity() aus dem AI SDK nutzen
const scored = documents.map((doc, i) => ({
...doc,
score: cosineSimilarity(queryEmbedding, embeddings[i]),
}));
// Nach Score sortieren, Threshold anwenden, Top-K nehmen
return scored
.filter(d => d.score >= threshold)
.sort((a, b) => b.score - a.score)
.slice(0, topK);
}
// Mit pgvector (Supabase) — effizient für große Datensätze
async function searchWithPgVector(query: string, topK = 5) {
const { embedding } = await embed({
model: embeddingModel,
value: query,
});
// Supabase RPC für Vektor-Suche (cosine distance)
const { data } = await supabase.rpc('match_documents', {
query_embedding: embedding, // number[]
match_threshold: 0.7,
match_count: topK,
});
return data; // Array von Chunks mit similarity score
}
RAG Vollständige RAG-Pipeline mit generateText()
Die komplette RAG-Pipeline: Suche → Context zusammenstellen → Antwort generieren. Claude Code erstellt dies mit Prompt-Engineering für optimale Antwortqualität:
import { embed, generateText, streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { openai } from '@ai-sdk/openai';
interface RAGOptions {
query: string;
topK?: number;
includeScores?: boolean;
}
async function ragQuery({ query, topK = 5, includeScores = false }: RAGOptions) {
// Step 1: Query Embedding
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: query,
});
// Step 2: Semantische Suche im Vektor-Store
const relevantChunks = await searchWithPgVector(embedding, topK);
// Step 3: Context aus gefundenen Chunks zusammenstellen
const contextText = relevantChunks
.map((chunk, i) => `[Quelle ${i + 1}] ${chunk.content}`)
.join('\n\n');
// Step 4: RAG-Prompt erstellen
const ragPrompt = `Du bist ein hilfreicher Assistent. Beantworte die Frage ausschließlich
basierend auf den folgenden Quellen. Wenn die Antwort nicht in den Quellen enthalten ist,
sage das ehrlich.
QUELLEN:
${contextText}
FRAGE: ${query}
ANTWORT:`;
// Step 5: Antwort generieren mit Claude
const result = await streamText({
model: anthropic('claude-3-5-sonnet-20241022'),
prompt: ragPrompt,
maxTokens: 1024,
});
return {
stream: result.textStream,
sources: includeScores ? relevantChunks : relevantChunks.map(c => c.id),
chunksUsed: relevantChunks.length,
};
}
// API Route für RAG Chat
export async function POST(req: Request) {
const { query } = await req.json();
const { stream } = await ragQuery({ query, topK: 5 });
return new Response(stream);
}
RAG-Performance-Tipp: Claude Code empfiehlt Hybrid-Search (Vektor + Keyword) für beste Ergebnisse. embedMany() ist batched und kosteneffizient — nie einzeln pro Chunk embedden. Für Produktions-RAG: Pinecone oder Supabase pgvector mit HNSW-Index für O(log n) Suche.
ADVANCED RAG mit Tool Calling — Agentisches RAG
Die nächste Stufe: Das Modell entscheidet selbst, wann es suchen muss und formuliert optimierte Such-Queries. Claude Code baut Self-Querying RAG-Agenten:
import { generateText, tool } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
// Agentisches RAG: Modell wählt selbst Such-Strategie
const { text, steps } = await generateText({
model: anthropic('claude-3-5-sonnet-20241022'),
system: `Du bist ein Research-Assistent mit Zugriff auf eine Wissensdatenbank.
Nutze das search_knowledge_base Tool um relevante Informationen zu finden.
Suche mehrmals mit verschiedenen Queries wenn nötig. Synthetisiere dann eine umfassende Antwort.`,
prompt: 'Was sind die Vor- und Nachteile von Vercel AI SDK im Vergleich zu LangChain.js?',
maxSteps: 4,
tools: {
searchKnowledgeBase: tool({
description: 'Semantische Suche in der Wissensdatenbank',
parameters: z.object({
query: z.string().describe('Optimierter Such-Query für semantische Suche'),
filter: z.object({
category: z.enum(['docs', 'blog', 'tutorials']).optional(),
dateAfter: z.string().optional(),
}).optional(),
}),
execute: async ({ query, filter }) => {
// Embedding + Vektor-Suche
const results = await searchKnowledgeBase(query, filter);
return {
results: results.map(r => ({
content: r.content,
source: r.url,
score: r.similarity,
})),
query,
};
},
}),
},
});
AI-Integration-Modul im Kurs
Im Claude Code Mastery Kurs: vollständiges AI-Integration-Modul — Vercel AI SDK, LangChain.js, OpenAI SDK und Anthropic SDK für moderne KI-Anwendungen. Hands-on-Projekte: Chat-App, RAG-System, Tool-Calling-Agent, Multi-Provider-Routing.
14 Tage kostenlos testen →