- סוכן AI עובד עם
ToolLoopAgent— כולל כלים, structured output, ו-multi-step reasoning - Next.js Chat App מלא עם
useChat— streaming, tool call display, ו-message management - Multi-provider setup — אותו קוד עובד עם Claude, GPT, Gemini ו-20+ מודלים
- Smart Router שבוחר את המודל הזול/מתאים ביותר לכל שאילתה
- אינטגרציית MCP עובדת — חיבור כלים חיצוניים לסוכן
- Structured output עם Zod — סוכן שמחזיר אובייקטים מובנים, לא טקסט חופשי
- Deployment checklist ל-Vercel — כולל monitoring, cost controls, ו-secrets management
- תוכלו לבנות סוכן AI מלא ב-TypeScript עם
ToolLoopAgent, כלים, ו-structured output - תוכלו ליצור Chat UI עם
useChatשמשדר תשובות בזמן אמת ומציג tool calls - תוכלו להחליף בין מודלים ופרוביידרים בשורת קוד אחת — ללא שינוי בלוגיקה
- תוכלו לחבר MCP Servers כ-tool providers לסוכן ב-AI SDK
- תוכלו להגדיר human-in-the-loop approval, telemetry, ו-cost controls
- פרקים קודמים: פרק 1 (מה זה סוכן AI, לולאת ReAct), פרק 2 (ארכיטקטורות), פרק 3 (MCP ו-Function Calling)
- מה תצטרכו: Node.js 18+, TypeScript 5+, מפתח API אחד לפחות (Anthropic / OpenAI / Google), עורך קוד (VS Code מומלץ)
- ידע נדרש: TypeScript/JavaScript בסיסי, הבנה בסיסית של React (ל-UI sections), הכרת npm/pnpm
- זמן משוער: 4-5 שעות (כולל תרגילים)
- עלות API משוערת: $5-15
אם אתם מגיעים מ-AI SDK v5, שימו לב: ה-API השתנה משמעותית. ראו חלק טעויות נפוצות בסוף הפרק לפני שמתחילים — קוד v5 לא ירוץ ב-v6 ללא שינויים.
בפרק 5 הכרת את Claude Agent SDK — הדרך של Anthropic לבנות סוכנים. בפרק הזה תעבור ל-Vercel AI SDK — הגישה של TypeScript-first עם multi-provider. הסוכן שתבנה כאן יהיה full-stack: Backend עם ToolLoopAgent ו-Frontend עם useChat. ה-MCP Server שבנית בפרק 3 יתחבר ישירות לסוכן שלך. בפרק 7 תבנה את אותו סוכן עם OpenAI Agents SDK — ותוכל להשוות ישירות בין שלוש הגישות.
| מונח (English) | עברית | הסבר |
|---|---|---|
| AI SDK | ערכת פיתוח AI | ספריית TypeScript של Vercel לבניית אפליקציות AI. חינמית, open source (Apache 2.0), framework-agnostic |
| generateText() | יצירת טקסט | פונקציה מרכזית ב-AI SDK ליצירת טקסט — תומכת בכלים, structured output, ו-multi-step. לא streaming |
| streamText() | הזרמת טקסט | כמו generateText, אבל מחזירה tokens בזמן אמת (streaming). הפונקציה שתשתמשו בה הכי הרבה |
| ToolLoopAgent | סוכן לולאת כלים | Class ב-AI SDK v6 שמגדיר סוכן עם model, instructions, tools ו-output. ניתן לשימוש חוזר ב-generate() ו-stream() |
| useChat() | הוק צ'אט | React Hook שמנהל ממשק צ'אט: messages, streaming, tool calls, form submission — הכל אוטומטי |
| tool() | הגדרת כלי | פונקציה ב-AI SDK להגדרת כלי עם description, inputSchema (Zod), ו-execute function |
| Output.object() | פלט מובנה | Helper שמגדיר structured output עם Zod schema. מחליף את generateObject הישן ב-v6 |
| stopWhen | תנאי עצירה | מגדיר מתי הסוכן מפסיק את לולאת הכלים — למשל stepCountIs(5) או hasToolResult |
| Provider Registry | רגיסטרי ספקים | מנגנון ב-AI SDK שמאפשר להחליף בין ספקי AI (Anthropic, OpenAI, Google) עם שורת קוד אחת |
| Zod | ספריית סכמה | ספריית TypeScript לוולידציה ו-type inference של סכמות. הבסיס ל-structured output וכלים ב-AI SDK |
| AI Gateway | שער AI | שירות של Vercel שמנהל API keys, rate limiting, caching, ו-cost controls. Zero markup על מחירי tokens |
| sendMessage() | שליחת הודעה | פונקציה ב-useChat שולחת הודעה מהמשתמש לסוכן. מחליפה את handleSubmit הישן ב-v6 |
| createAgentUIStreamResponse | יצירת תגובת stream | פונקציה שמחברת ToolLoopAgent ל-useChat — מייצרת Response תקינה עם streaming |
סקירה ופילוסופיה — למה AI SDK
Vercel AI SDK הוא ה-SDK הפופולרי ביותר ל-TypeScript לבניית אפליקציות AI. הוא open source (Apache 2.0), חינמי לשימוש, framework-agnostic (עובד עם Next.js, Remix, Svelte, Vue, ועוד), ומותאם ל-streaming מהיסוד.
הפילוסופיה של AI SDK שונה מכל SDK אחר שנלמד בקורס:
- Multi-provider first: אותו קוד עובד עם Claude, GPT, Gemini, Mistral, DeepSeek ו-20+ ספקים. אין lock-in
- Streaming-first: כל פונקציה מרכזית מגיעה בשתי גרסאות —
generateText(ממתין לתשובה מלאה) ו-streamText(מזרים token-by-token) - Full-stack: לא רק Backend — גם React Hooks לבניית ממשקים (
useChat,useObject) - TypeScript-native: Type safety מלא, Zod schemas, autocompletion — ה-DX הכי טוב בתחום
- גרסה: AI SDK v6 (שוחרר פברואר 2026)
- npm package:
ai(הורדות: מיליונים בחודש) - רישיון: Apache 2.0 — חינמי לשימוש מסחרי
- ספקים נתמכים: 20+ (Anthropic, OpenAI, Google, Mistral, DeepSeek, Groq, Fireworks, ועוד)
- AI Gateway: Zero markup על מחירי tokens + $5/חודש קרדיט חינם לכל צוות
- GitHub stars: 20K+
מה חדש ב-v6
AI SDK v6, ששוחרר בפברואר 2026, הוא שדרוג משמעותי מ-v5. השינויים העיקריים:
| Feature | v5 | v6 |
|---|---|---|
| Agent abstraction | לא קיים — צריך לכתוב loops ידנית | ToolLoopAgent — class מוכן עם tools, instructions, output |
| Structured output | generateObject() / streamObject() — נפרד |
Output.object() — משולב ב-generateText/streamText |
| Stop conditions | maxSteps (מספר) |
stopWhen — composable: stepCountIs(5), hasToolResult, custom |
| Human-in-the-loop | לא קיים | needsApproval על כלים — הסוכן עוצר ומבקש אישור |
| Provider format | anthropic('claude-sonnet-4-5') |
'anthropic/claude-sonnet-4.5' — מחרוזת אחידה |
| Chat hook | handleSubmit / input |
sendMessage({ text }) — API פשוט ונקי יותר |
| MCP | experimental_createMCPClient() |
MCP client stable — אינטגרציה מלאה עם tool system |
| DevTools | לא קיים | Visual debugging של agent workflows, tool calls, ו-costs |
שלוש השכבות של AI SDK
| שכבה | Package | מה עושה | מתי להשתמש |
|---|---|---|---|
| AI SDK Core | ai |
generateText, streamText, ToolLoopAgent, Output, tool() | תמיד — זו הליבה. עובד עם כל framework |
| AI SDK UI | @ai-sdk/react |
useChat, useObject — React Hooks לבניית ממשקים | כשצריך Chat UI, streaming display, tool call rendering |
| Provider packages | @ai-sdk/anthropic, @ai-sdk/openai, @ai-sdk/google |
חיבור ל-provider ספציפי עם features ייעודיים | כשרוצים provider-specific features (extended thinking, grounding) |
כלל אצבע: התחילו עם AI SDK Core בלבד. הוסיפו UI רק כשצריך ממשק. השתמשו ב-provider packages רק כשצריכים features ספציפיים (כמו extended thinking של Claude).
התקינו AI SDK ויצרו פרויקט בסיסי:
# יצירת פרויקט חדש
mkdir ai-sdk-agent && cd ai-sdk-agent
npm init -y
npm install ai @ai-sdk/anthropic @ai-sdk/openai @ai-sdk/google zod
npx tsc --init
# הוסיפו .env עם מפתחות API
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
echo "OPENAI_API_KEY=sk-..." >> .env
echo "GOOGLE_GENERATIVE_AI_API_KEY=..." >> .env
ודאו ש-tsconfig.json כולל "module": "nodenext" ו-"moduleResolution": "nodenext".
Core Primitives — generateText, streamText ו-Output
הליבה של AI SDK מורכבת משתי פונקציות ופרימיטיב אחד — וזה מספיק לבנות כמעט כל דבר:
generateText — יצירת טקסט (non-streaming)
generateText() שולח prompt ל-LLM, ממתין לתשובה מלאה, ומחזיר את התוצאה. פשוט ונקי — מתאים ל-batch processing, background jobs, ומקרים שלא צריכים streaming.
import { generateText } from 'ai';
// שימו לב: ב-v6, המודל הוא מחרוזת אחידה
// 'provider/model-name' — לא צריך לייבא provider
const { text } = await generateText({
model: 'anthropic/claude-sonnet-4.5',
prompt: 'Explain AI agents in 3 sentences.',
});
console.log(text);
// "AI agents are software systems that use LLMs
// to reason about and execute multi-step tasks..."
streamText — הזרמת טקסט בזמן אמת
streamText() מחזיר tokens בזמן אמת — חיוני ל-UX טוב. המשתמש רואה את התשובה "נכתבת" במקום לחכות 10 שניות. זו הפונקציה שתשתמשו בה ברוב המקרים.
import { streamText } from 'ai';
const result = streamText({
model: 'openai/gpt-5',
prompt: 'Write a business plan for an AI startup in Tel Aviv.',
});
// הדפסת tokens בזמן אמת
for await (const text of result.textStream) {
process.stdout.write(text);
}
// אחרי שהסתיים — מידע שימושי
const finalResult = await result;
console.log('\n--- Usage ---');
console.log('Input tokens:', finalResult.usage.promptTokens);
console.log('Output tokens:', finalResult.usage.completionTokens);
Output — structured output (חדש ב-v6)
ב-v6, generateObject() ו-streamObject() הוחלפו ב-Output — helper שמשתלב ישירות ב-generateText ו-streamText. היתרון: structured output עובד יחד עם tool calling, לא בנפרד.
import { generateText, Output } from 'ai';
import { z } from 'zod';
const { output } = await generateText({
model: 'anthropic/claude-sonnet-4.5',
output: Output.object({
schema: z.object({
company: z.string().describe('Company name'),
founded: z.number().describe('Year founded'),
industry: z.string(),
strengths: z.array(z.string()),
risk_level: z.enum(['low', 'medium', 'high']),
}),
}),
prompt: 'Analyze Wix as a company.',
});
// output הוא typed! TypeScript יודע את המבנה
console.log(output.company); // "Wix"
console.log(output.risk_level); // "low"
console.log(output.strengths); // ["Large user base", ...]
אם עובדים עם קוד ישן, generateObject() ו-streamObject() כבר לא קיימים ב-v6. צריך להחליף ל-generateText({ output: Output.object({...}) }). היתרון: עכשיו structured output עובד יחד עם tools באותה קריאה.
כתבו את שלוש הפונקציות: (1) generateText פשוט עם Claude, (2) streamText עם GPT שמדפיס לקונסול, (3) Output.object() שמחלץ structured data מטקסט חופשי. בדקו שכל השלושה עובדים לפני שממשיכים.
Tool Calling — כלים ב-AI SDK
בפרק 3 למדנו את התיאוריה של Function Calling. עכשיו נראה איך זה עובד ב-AI SDK — וזה אלגנטי. הגדרת כלי ב-AI SDK היא type-safe מהיסוד: הפרמטרים מוגדרים ב-Zod, הסוכן קורא לכלי, ותוצאת ה-execute חוזרת אוטומטית למודל.
הגדרת כלי עם tool()
import { generateText, tool, stepCountIs } from 'ai';
import { z } from 'zod';
const result = await generateText({
model: 'anthropic/claude-sonnet-4.5',
tools: {
weather: tool({
description: 'Get current weather for a city. '
+ 'Use when the user asks about weather, temperature, '
+ 'or what to wear. Supports Hebrew and English city names.',
inputSchema: z.object({
city: z.string().describe('City name, e.g. "Tel Aviv" or "ירושלים"'),
units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
}),
execute: async ({ city, units }) => {
// כאן קוראים ל-API אמיתי
const response = await fetch(
`https://api.weather.com/v1?city=${city}&units=${units}`
);
return response.json();
},
}),
calculator: tool({
description: 'Perform mathematical calculations. '
+ 'Use for any math the user asks about. '
+ 'Returns the numeric result.',
inputSchema: z.object({
expression: z.string().describe('Math expression, e.g. "15 * 23 + 7"'),
}),
execute: async ({ expression }) => {
// בפרודקשן — השתמשו ב-math library, לא eval!
return { result: Function(`return ${expression}`)() };
},
}),
},
// ב-v6: stopWhen מחליף את maxSteps
stopWhen: stepCountIs(5),
prompt: 'מה מזג האוויר בתל אביב? וכמה זה בפרנהייט?',
});
// result.text מכיל את התשובה הסופית
console.log(result.text);
// result.steps מכיל את כל הצעדים כולל tool calls
for (const step of result.steps) {
console.log(`Step: ${step.toolCalls?.length ?? 0} tool calls`);
}
המנטל מודל: streamText + tools + stopWhen = סוכן
זה הרעיון המרכזי של AI SDK: אין צורך בקוד מיוחד כדי לבנות סוכן. כל מה שצריך זה:
streamText(אוgenerateText) — הפונקציה שקוראת ל-LLMtools— רשימת כלים שהסוכן יכול להשתמשstopWhen— תנאי עצירה (כמה צעדים, אילו תוצאות, או custom logic)
שלושת אלה ביחד הם סוכן. ה-SDK מטפל בלולאת ReAct אוטומטית: המודל רואה את הכלים, מחליט מתי לקרוא לאיזה כלי, מקבל את התוצאה, ומחליט אם לקרוא כלי נוסף או לענות למשתמש.
stopWhen — תנאי עצירה מתקדמים
ב-v5 היה רק maxSteps — מספר מקסימלי של צעדים. ב-v6 יש stopWhen שהרבה יותר גמיש:
| תנאי | מתי עוצר | שימוש |
|---|---|---|
stepCountIs(5) |
אחרי 5 צעדים | ברירת מחדל טובה — מונע לולאות אינסופיות |
hasToolResult('weather') |
כשהכלי weather החזיר תוצאה | כשצריך תוצאה ספציפית לפני שממשיכים |
toolCallCount(10) |
אחרי 10 קריאות כלים (כולל כמה בצעד אחד) | הגבלת עלויות — כל tool call = tokens |
| Custom function | לוגיקה שלכם | עצירה מותנית — למשל, כשהתקציב נגמר |
ללא stopWhen, סוכן יכול להיכנס ללולאה אינסופית — קורא לכלי, מקבל תוצאה, קורא שוב, שוב ושוב. כל קריאה עולה tokens. סוכן ללא stop condition יכול לשרוף $50+ בלילה אחד. תמיד הגדירו stopWhen: stepCountIs(10) לפחות.
בנו סוכן עם 3 כלים: (1) weather — מחזיר מזג אוויר לעיר, (2) calculator — מבצע חשבון, (3) translator — מתרגם טקסט. השתמשו ב-generateText עם stopWhen: stepCountIs(5). שלחו prompt שדורש שימוש ב-2 כלים לפחות: "מה הטמפרטורה בתל אביב בפרנהייט?"
ToolLoopAgent — סוכנים כ-Class
ב-v6, AI SDK הציג ToolLoopAgent — class שמגדיר סוכן שניתן לשימוש חוזר. במקום להעביר את אותם tools, instructions ו-model לכל קריאה ל-generateText, מגדירים agent פעם אחת ומשתמשים בו בכל מקום.
import { ToolLoopAgent, tool, Output } from 'ai';
import { z } from 'zod';
// הגדרת הסוכן — פעם אחת
const researchAgent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
instructions: `You are a research assistant specializing in the
Israeli tech ecosystem. You search for information, analyze data,
and provide structured summaries. Always cite your sources.
You speak Hebrew and English fluently.`,
tools: {
webSearch: tool({
description: 'Search the web for current information. '
+ 'Returns top 5 results with title, snippet, and URL.',
inputSchema: z.object({
query: z.string().describe('Search query in English or Hebrew'),
}),
execute: async ({ query }) => {
// חיבור ל-Brave Search API או SerpAPI
return { results: [/* ... */] };
},
}),
summarize: tool({
description: 'Summarize a long text into key points. '
+ 'Use after webSearch to distill search results.',
inputSchema: z.object({
text: z.string(),
max_points: z.number().default(5),
}),
execute: async ({ text, max_points }) => {
// summarization logic
return { points: [/* ... */] };
},
}),
},
// structured output — הסוכן מחזיר אובייקט מובנה
output: Output.object({
schema: z.object({
topic: z.string(),
summary: z.string(),
key_findings: z.array(z.string()),
sources: z.array(z.object({
title: z.string(),
url: z.string(),
})),
confidence: z.enum(['high', 'medium', 'low']),
}),
}),
});
// שימוש 1: generate (ממתין לתשובה מלאה)
const { output } = await researchAgent.generate({
prompt: 'Research the current state of AI startups in Israel.',
});
console.log(output.key_findings);
console.log(output.confidence);
// שימוש 2: stream (streaming לזמן אמת)
const { partialOutputStream } = await researchAgent.stream({
prompt: 'What are the top funded AI companies in Tel Aviv?',
});
for await (const partial of partialOutputStream) {
console.log(partial); // אובייקט חלקי שגדל לאט
}
מתי ToolLoopAgent ומתי generateText גולמי?
| מאפיין | generateText + tools | ToolLoopAgent |
|---|---|---|
| Reusability | צריך להעביר tools/model בכל קריאה | מוגדר פעם אחת, קורא generate/stream |
| UI Integration | צריך לבנות ידנית | createAgentUIStreamResponse מחבר ל-useChat |
| Testing | בודקים כל קריאה בנפרד | בודקים agent אחד עם mock tools |
| Flexibility | שליטה מלאה — אפשר לשנות כל פרמטר | פחות גמישות, יותר structure |
| Use case | One-off tasks, scripts, prototyping | Production agents, Chat apps, APIs |
כלל אצבע: התחילו עם generateText + tools כדי להבין את המכניקה. עברו ל-ToolLoopAgent כשצריכים reusability, UI integration, או structured output.
המירו את סוכן 3 הכלים שבניתם ל-ToolLoopAgent. הוסיפו instructions בעברית שמסביר את התפקיד. הוסיפו Output.object() עם schema שמחזיר { answer: string, tools_used: string[], confidence: string }. הריצו עם generate() ואז עם stream().
AI SDK UI — useChat ובניית Chat
עד עכשיו עבדנו ב-Backend — Node.js scripts שמריצים סוכנים. עכשיו נוסיף ממשק משתמש. כאן AI SDK באמת זורח: useChat() הוא React Hook שמנהל Chat UI מלא — messages, streaming, tool calls, form submission — בכמה שורות קוד.
Backend: API Route עם ToolLoopAgent
import { ToolLoopAgent, tool, createAgentUIStreamResponse } from 'ai';
import { z } from 'zod';
// הגדרת הסוכן
const chatAgent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
instructions: `You are a helpful assistant for Israeli businesses.
You can search the web, calculate numbers, and analyze data.
Respond in the language the user writes in (Hebrew or English).`,
tools: {
weather: tool({
description: 'Get weather for a city',
inputSchema: z.object({
city: z.string(),
}),
execute: async ({ city }) => {
return { temp: 28, condition: 'sunny', city };
},
}),
},
});
export async function POST(req: Request) {
const { messages } = await req.json();
// createAgentUIStreamResponse — מחבר agent ל-useChat
return createAgentUIStreamResponse({
agent: chatAgent,
messages,
});
}
Frontend: Chat UI עם useChat
'use client';
import { useChat } from '@ai-sdk/react';
import { useState } from 'react';
export default function ChatPage() {
const [input, setInput] = useState('');
const { messages, sendMessage, status } = useChat();
const isActive = status === 'streaming' || status === 'submitted';
return (
<div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
<h1>AI Agent Chat</h1>
{/* הצגת הודעות */}
<div>
{messages.map((m) => (
<div key={m.id} style={{
padding: '10px',
margin: '5px 0',
background: m.role === 'user' ? '#e3f2fd' : '#f5f5f5',
borderRadius: '8px',
}}>
<strong>{m.role === 'user' ? 'You' : 'Agent'}:</strong>
{m.parts?.map((part, i) => {
switch (part.type) {
case 'text':
return <p key={i}>{part.text}</p>;
default:
// ב-v6, tool parts הם tool-<toolName>
// למשל: 'tool-weather', 'tool-calculator'
if (part.type.startsWith('tool-')) {
return (
<div key={i} style={{
background: '#fff3e0',
padding: '5px',
borderRadius: '4px',
fontSize: '0.85em',
}}>
Tool: {part.type.replace('tool-', '')}
{'result' in part && (
<span> = {JSON.stringify(part.result)}</span>
)}
</div>
);
}
return null;
}
})}
</div>
))}
{isActive && <p>...thinking</p>}
</div>
{/* טופס שליחה */}
<form onSubmit={(e) => {
e.preventDefault();
if (input.trim()) {
sendMessage({ text: input });
setInput('');
}
}}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="שאל את הסוכן..."
style={{ width: '80%', padding: '10px' }}
disabled={isActive}
/>
<button type="submit" disabled={isActive}>שלח</button>
</form>
</div>
);
}
זהו. Chat app מלא עם סוכן AI. useChat מטפל ב:
- Message management: שומר היסטוריית הודעות אוטומטית
- Streaming: מציג tokens בזמן אמת
- Tool calls: מציג tool parts (
tool-weather,tool-calculator) ותוצאות - Status tracking:
status—'streaming','submitted','ready','error' - Error handling:
errorstate לטיפול בשגיאות
מטרה: לבנות Chat app מלא עם ToolLoopAgent ו-useChat.
- צרו Next.js app:
npx create-next-app@latest ai-chat --typescript --app - התקינו:
npm install ai @ai-sdk/react @ai-sdk/anthropic zod - צרו API route ב-
app/api/chat/route.tsעם ToolLoopAgent שכולל 3 כלים: weather, calculator, joke_generator - צרו Chat UI ב-
app/page.tsxעם useChat — כולל הצגת tool calls - הריצו
npm run devובדקו: שאלו "מה מזג האוויר בתל אביב?" ו-"ספר בדיחה"
Success criteria: הסוכן עונה, tool calls מוצגים ב-UI, streaming עובד.
עלות: ~$0.50
Multi-Provider — ה-Killer Feature
הפיצ'ר שמבדיל את AI SDK מכל SDK אחר הוא multi-provider support אמיתי. אותו קוד, אותם כלים, אותם schemas — רק שורה אחת משתנה:
import { generateText, tool } from 'ai';
import { z } from 'zod';
// הכלים זהים — לא משנים כלום
const tools = {
search: tool({
description: 'Search the web',
inputSchema: z.object({ query: z.string() }),
execute: async ({ query }) => ({ results: [`Result for: ${query}`] }),
}),
};
// שורה אחת — מחליפים provider + model
// Anthropic
const r1 = await generateText({
model: 'anthropic/claude-sonnet-4.5',
tools,
stopWhen: stepCountIs(5),
prompt: 'Search for AI startups in Israel',
});
// OpenAI — אותו קוד, אותם tools
const r2 = await generateText({
model: 'openai/gpt-5',
tools,
stopWhen: stepCountIs(5),
prompt: 'Search for AI startups in Israel',
});
// Google — אותו קוד, אותם tools
const r3 = await generateText({
model: 'google/gemini-2.5-pro',
tools,
stopWhen: stepCountIs(5),
prompt: 'Search for AI startups in Israel',
});
שלושה ספקים, אותו קוד, אותם כלים. זו ההבטחה של multi-provider — ו-AI SDK מממש אותה.
Smart Router — בחירת מודל אוטומטית
אם multi-provider מאפשר להחליף מודלים, הצעד הבא הוא לעשות את זה אוטומטי. Smart Router בוחר את המודל המתאים ביותר לכל שאילתה — על פי מורכבות, עלות, ומהירות:
import { generateText } from 'ai';
type Complexity = 'simple' | 'moderate' | 'complex';
// ניתוח מורכבות בסיסי
function classifyComplexity(prompt: string): Complexity {
const wordCount = prompt.split(' ').length;
if (wordCount < 20) return 'simple';
if (wordCount < 100) return 'moderate';
return 'complex';
}
// טבלת routing — מודל לכל רמת מורכבות
const MODEL_ROUTER: Record<Complexity, string> = {
simple: 'google/gemini-2.5-flash', // $0.30/M input — זול ומהיר
moderate: 'anthropic/claude-sonnet-4.5', // $3/M input — איזון טוב
complex: 'anthropic/claude-opus-4.6', // $5/M input — מקסימום יכולת
};
async function smartGenerate(prompt: string) {
const complexity = classifyComplexity(prompt);
const model = MODEL_ROUTER[complexity];
console.log(`[Router] ${complexity} -> ${model}`);
return generateText({
model,
prompt,
});
}
// שימוש
await smartGenerate('Hi');
// [Router] simple -> google/gemini-2.5-flash
await smartGenerate('Analyze the competitive landscape...');
// [Router] complex -> anthropic/claude-opus-4.6
Fallback Chains — גיבוי אוטומטי
מה קורה כש-Anthropic API נופל? עם multi-provider, אפשר לבנות fallback chain — נפילה אוטומטית לספק חלופי:
async function generateWithFallback(prompt: string): Promise<string> {
const models = [
'anthropic/claude-sonnet-4.5', // ראשון — מועדף
'openai/gpt-5', // שני — גיבוי
'google/gemini-2.5-pro', // שלישי — גיבוי אחרון
];
for (const model of models) {
try {
const { text } = await generateText({ model, prompt });
return text;
} catch (error) {
console.warn(`[Fallback] ${model} failed, trying next...`);
continue;
}
}
throw new Error('All providers failed');
}
| משימה | מודל מומלץ | למה | עלות לשיחה |
|---|---|---|---|
| Routing / Classification | google/gemini-2.5-flash |
מהיר וזול, מצוין לסיווג | ~$0.001 |
| Chat / Q&A כללי | anthropic/claude-sonnet-4.5 |
איזון מצוין בין איכות, מהירות ועלות | ~$0.05 |
| Code generation | anthropic/claude-sonnet-4.5 |
מוביל בקוד, tool use מדויק | ~$0.10 |
| Complex reasoning | anthropic/claude-opus-4.6 |
מקסימום יכולת חשיבה, extended thinking | ~$0.50 |
| Long context (>200K) | google/gemini-2.5-pro |
1M context, free tier זמין | ~$0.05 |
| Budget / High volume | google/gemini-flash-lite |
$0.10/M input — מינימום עלות | ~$0.0005 |
כלל הזהב: 80% מהמשימות לא צריכות את המודל היקר ביותר. Sonnet 4.5 או Gemini Flash מספיקים לרוב. שמרו את Opus ל-complex reasoning בלבד.
בדקו את ה-multi-provider: שלחו את אותו prompt ("Explain AI agents in 3 sentences") לשלושה מודלים שונים: Claude Sonnet, GPT-5, ו-Gemini Pro. השוו: (1) איכות התשובה, (2) מהירות, (3) token usage. רשמו את התוצאות — תשתמשו בהן בתרגיל 2.
Structured Output עם Zod
למה structured output חשוב לסוכנים? כי סוכן שמחזיר טקסט חופשי קשה לעבד. סוכן שמחזיר אובייקט JSON מוגדר ניתן לחיבור ל-databases, APIs, ו-UIs — אוטומטית, בלי parsing.
ב-AI SDK v6, structured output מבוסס על Zod — ספריית TypeScript לוולידציה. Zod מגדיר את צורת הפלט, AI SDK מוודא שהמודל מחזיר את המבנה הנכון, ו-TypeScript יודע את הסוג (type inference).
Output strategies
| Strategy | שימוש | דוגמה |
|---|---|---|
Output.object({ schema }) |
אובייקט יחיד מובנה | ניתוח חברה, סיכום מאמר, חילוץ מידע |
Output.array({ schema }) |
מערך של אובייקטים | רשימת מוצרים, סט המלצות, תוצאות חיפוש |
Output.choice({ options }) |
בחירה מרשימה סגורה | סיווג sentiment, ניתוב שאילתה, סינון |
Output.json() |
JSON חופשי (ללא schema) | כשלא ידוע המבנה מראש |
import { generateText, Output } from 'ai';
import { z } from 'zod';
// חילוץ מידע ממייל
const { output } = await generateText({
model: 'anthropic/claude-sonnet-4.5',
output: Output.object({
schema: z.object({
sender: z.string(),
subject: z.string(),
urgency: z.enum(['low', 'medium', 'high', 'critical']),
action_required: z.boolean(),
action_items: z.array(z.string()),
due_date: z.string().nullable(),
sentiment: z.enum(['positive', 'neutral', 'negative']),
}),
}),
prompt: `Extract structured data from this email:
"Hi Team, urgent - the client demo is tomorrow at 2pm.
We need to fix the login bug and prepare the slide deck
before end of day. Thanks, Yossi"`,
});
// output typed!
console.log(output.urgency); // "high"
console.log(output.action_required); // true
console.log(output.action_items);
// ["Fix the login bug", "Prepare the slide deck"]
console.log(output.due_date); // "end of day"
Structured output + Tool calling (ביחד!)
הפיצ'ר הכי חזק ב-v6: structured output ו-tool calling עובדים ביחד. הסוכן קורא לכלים, אוסף מידע, ואז מחזיר אובייקט מובנה. לפני v6 זה לא היה אפשרי.
import { ToolLoopAgent, Output, tool } from 'ai';
import { z } from 'zod';
const weatherReporter = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
tools: {
weather: tool({
description: 'Get weather for a city',
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({
temp_c: 28,
condition: 'sunny',
humidity: 65,
city,
}),
}),
},
output: Output.object({
schema: z.object({
summary: z.string(),
temperature: z.number(),
recommendation: z.string(),
should_bring_umbrella: z.boolean(),
}),
}),
});
const { output } = await weatherReporter.generate({
prompt: 'What is the weather in Tel Aviv? Should I bring an umbrella?',
});
// הסוכן קרא ל-weather tool, קיבל תוצאה,
// ואז מילא את ה-structured output
console.log(output.temperature); // 28
console.log(output.should_bring_umbrella); // false
console.log(output.recommendation);
// "No umbrella needed - it's sunny and warm in Tel Aviv!"
בנו "Email Analyzer": סוכן שמקבל גוף מייל כ-prompt ומחזיר structured output עם: sender, urgency, action_items, sentiment. בדקו עם 3 מיילים שונים (עברית, אנגלית, ומעורב).
אינטגרציית MCP
בפרק 3 בניתם MCP Server. עכשיו נחבר אותו לסוכן ב-AI SDK. ב-v6, MCP integration יצא מ-experimental ומהווה חלק stable מה-SDK.
הרעיון: MCP Servers חושפים כלים, ו-AI SDK טוען אותם אוטומטית כ-tools. אפשר לשלב MCP tools עם native AI SDK tools באותו סוכן.
import { generateText, stepCountIs } from 'ai';
import { createMCPClient } from 'ai/mcp';
// יצירת MCP client שמתחבר לשרת
const mcpClient = await createMCPClient({
transport: {
type: 'sse',
url: 'http://localhost:3001/mcp', // MCP Server ששומע ב-HTTP
},
});
// טעינת כלים מה-MCP Server
const mcpTools = await mcpClient.tools();
// שימוש בכלים מ-MCP יחד עם כלים native
const result = await generateText({
model: 'anthropic/claude-sonnet-4.5',
tools: {
...mcpTools, // כלים מ-MCP Server
myNativeTool: tool({ // כלי native נוסף
description: 'My custom tool',
inputSchema: z.object({ input: z.string() }),
execute: async ({ input }) => ({ result: input.toUpperCase() }),
}),
},
stopWhen: stepCountIs(5),
prompt: 'Use the available tools to help me.',
});
// סגירת ה-MCP connection כשסיימנו
await mcpClient.close();
MCP עם stdio transport (תהליך מקומי)
import { createMCPClient } from 'ai/mcp';
const mcpClient = await createMCPClient({
transport: {
type: 'stdio',
command: 'node',
args: ['./my-mcp-server.js'],
},
});
const tools = await mcpClient.tools();
// עכשיו אפשר להשתמש ב-tools ב-generateText/ToolLoopAgent
כל כלי MCP שנטען מוסיף את ה-description וה-schema שלו ל-System Prompt. 10 כלים MCP עם תיאורים ארוכים = 2,000-5,000 tokens נוספים בכל קריאה. בסוכן שרץ 50 פעם ביום, זה יכול לעלות $5-10 בחודש רק על tool definitions. טענו רק כלים רלוונטיים.
MCP עם ToolLoopAgent — שילוב מלא
השילוב החזק ביותר: ToolLoopAgent שמשלב MCP tools חיצוניים עם native tools, structured output, ו-human-in-the-loop. הנה דוגמה מלאה:
import { ToolLoopAgent, tool, Output, stepCountIs } from 'ai';
import { createMCPClient } from 'ai/mcp';
import { z } from 'zod';
// חיבור ל-MCP Server (למשל: GitHub MCP Server)
const githubMCP = await createMCPClient({
transport: {
type: 'stdio',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-github'],
env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN! },
},
});
// טעינת כלים מ-MCP — סינון רק מה שצריך!
const allMCPTools = await githubMCP.tools();
const relevantTools = Object.fromEntries(
Object.entries(allMCPTools).filter(([name]) =>
['create_issue', 'list_issues', 'search_code'].includes(name)
)
);
// הגדרת סוכן משולב
const devAgent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
instructions: `You are a developer assistant.
You can search GitHub repos, create issues, and analyze code.
Always explain what you're doing before using a tool.
Respond in the user's language.`,
tools: {
// כלים מ-MCP
...relevantTools,
// כלי native — code analysis
analyzeCode: tool({
description: 'Analyze a code snippet for bugs, performance, '
+ 'and best practices. Returns structured feedback.',
inputSchema: z.object({
code: z.string().describe('Code snippet to analyze'),
language: z.enum(['python', 'typescript', 'javascript']),
}),
execute: async ({ code, language }) => {
// לוגיקת ניתוח (במציאות — LLM call נוסף או static analysis)
return {
issues_found: 2,
severity: 'medium',
suggestions: [
'Add error handling for API calls',
'Consider using async/await instead of callbacks'
],
};
},
}),
},
output: Output.object({
schema: z.object({
summary: z.string(),
actions_taken: z.array(z.string()),
github_links: z.array(z.string()).optional(),
}),
}),
});
// שימוש
const { output } = await devAgent.generate({
prompt: 'Find open issues in my repo labeled "bug" and create '
+ 'a summary issue linking them all together.',
});
console.log(output.summary);
console.log(output.actions_taken);
// ניקוי — חשוב! סוגרים MCP connections
await githubMCP.close();
שרתי MCP כמו GitHub חושפים עשרות כלים. לעולם אל תטענו את כולם. סננו רק את הכלים הרלוונטיים למשימה — כמו בדוגמה למעלה. פחות כלים = פחות tokens בכל קריאה, דיוק גבוה יותר בבחירת כלי, ועלות נמוכה יותר.
Error Handling ב-MCP Connections
חיבורי MCP יכולים להיכשל — השרת לא זמין, ה-connection נפל, או הכלי מחזיר שגיאה. טיפול נכון בשגיאות MCP הוא קריטי:
import { createMCPClient } from 'ai/mcp';
async function createRobustMCPClient(config: {
transport: any;
retries?: number;
timeoutMs?: number;
}) {
const { transport, retries = 3, timeoutMs = 10000 } = config;
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const client = await Promise.race([
createMCPClient({ transport }),
new Promise((_, reject) =>
setTimeout(
() => reject(new Error('MCP connection timeout')),
timeoutMs
)
),
]);
console.log(`MCP connected (attempt ${attempt})`);
return client;
} catch (error) {
console.warn(
`MCP connection attempt ${attempt}/${retries} failed:`,
(error as Error).message
);
if (attempt === retries) {
console.error('All MCP connection attempts failed');
return null; // Graceful degradation — agent works without MCP
}
// Wait before retry
await new Promise(r =>
setTimeout(r, 1000 * Math.pow(2, attempt - 1))
);
}
}
return null;
}
// שימוש — הסוכן עובד גם אם MCP לא זמין
const mcpClient = await createRobustMCPClient({
transport: { type: 'sse', url: 'http://localhost:3001/mcp' },
retries: 3,
timeoutMs: 5000,
});
const mcpTools = mcpClient
? await mcpClient.tools()
: {}; // Fallback — אין MCP tools
const agent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
tools: {
...mcpTools, // ריק אם MCP לא זמין
// native tools עובדים תמיד
myTool: tool({ /* ... */ }),
},
});
Human-in-the-Loop — אישור פעולות
חדש ב-v6: needsApproval. כשסוכן רוצה לבצע פעולה "מסוכנת" — שליחת מייל, מחיקת קובץ, ביצוע רכישה — אפשר לדרוש אישור מהמשתמש לפני הביצוע.
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';
const agent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
tools: {
// כלי בטוח — לא צריך אישור
search: tool({
description: 'Search the web (safe, read-only)',
inputSchema: z.object({ query: z.string() }),
execute: async ({ query }) => ({ results: [/* ... */] }),
}),
// כלי מסוכן — דורש אישור!
sendEmail: tool({
description: 'Send an email. REQUIRES USER APPROVAL.',
inputSchema: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
// needsApproval: הסוכן עוצר ומבקש אישור
needsApproval: true,
execute: async ({ to, subject, body }) => {
// נקרא רק אחרי שהמשתמש אישר
await sendEmailAPI(to, subject, body);
return { sent: true };
},
}),
// אפשר גם needsApproval דינאמי — פונקציה שמחליטה
deleteFile: tool({
description: 'Delete a file from the system',
inputSchema: z.object({ path: z.string() }),
needsApproval: ({ path }) => {
// קבצים ב-/tmp לא צריכים אישור, השאר כן
return !path.startsWith('/tmp/');
},
execute: async ({ path }) => {
// delete logic
return { deleted: true };
},
}),
},
});
ב-UI, כש-needsApproval חוסם את הביצוע, ה-useChat hook מקבל tool-<toolName> part (למשל tool-sendEmail) ללא result — כי הביצוע טרם אושר. אפשר לבנות כפתורי "Approve" / "Reject" בממשק שמחליטים אם לבצע את הכלי.
סווגו את הכלים שלכם: מתוך הכלים שבניתם עד עכשיו, סמנו אילו צריכים needsApproval: true. כלל אצבע: כל כלי שכותב, שולח, מוחק, או עולה כסף — צריך אישור.
Telemetry ו-Monitoring
סוכן AI בפרודקשן בלי monitoring הוא כמו מכונית בלי dashboard — לא יודעים מתי ייגמר הדלק (תקציב), מתי המנוע מתחמם (latency), או מתי יש תקלה (errors). AI SDK כולל תמיכה built-in ב-OpenTelemetry.
מה עוקבים אחריו
| Metric | למה חשוב | סף אזהרה |
|---|---|---|
| Token usage | שליטה בעלויות | מעל 50K tokens לשיחה = בדוק לולאות |
| Latency (TTFT) | חוויית משתמש | מעל 5 שניות ל-first token = UX problem |
| Tool call count | זיהוי לולאות | מעל 10 tool calls בשיחה = חשוד |
| Error rate | אמינות | מעל 5% = בעיה שדורשת טיפול |
| Cost per session | שליטה פיננסית | מעל $1 לשיחה = בדוק model routing |
import { generateText } from 'ai';
const result = await generateText({
model: 'anthropic/claude-sonnet-4.5',
prompt: 'Hello!',
// הפעלת telemetry
experimental_telemetry: {
isEnabled: true,
functionId: 'chat-agent',
metadata: {
userId: 'user-123',
sessionId: 'session-456',
},
},
});
// Token usage מדויק
console.log('Input tokens:', result.usage.promptTokens);
console.log('Output tokens:', result.usage.completionTokens);
// חישוב עלות
const inputCost = result.usage.promptTokens * 3 / 1_000_000; // $3/M for Sonnet
const outputCost = result.usage.completionTokens * 15 / 1_000_000;
console.log(`Cost: $${(inputCost + outputCost).toFixed(4)}`);
ל-production monitoring, חברו את ה-OpenTelemetry traces לפלטפורמות observability: Vercel Observability (built-in אם deploying על Vercel), Langfuse (open source), Datadog, או Helicone.
Cost Alerts — התראות עלות
מעבר ל-telemetry בסיסי, חשוב להגדיר התראות עלות — כדי לתפוס בעיות לפני שהן הופכות לחשבון של $500:
// Cost tracker שנשמר בזיכרון (בפרודקשן — Redis/DB)
const dailyCosts = new Map<string, number>();
function trackCost(userId: string, usage: {
promptTokens: number;
completionTokens: number;
}, model: string) {
// חישוב עלות
const prices: Record<string, [number, number]> = {
'anthropic/claude-sonnet-4.5': [3, 15],
'google/gemini-2.5-flash': [0.15, 0.60],
};
const [inPrice, outPrice] = prices[model] ?? [5, 15];
const cost = (
usage.promptTokens * inPrice +
usage.completionTokens * outPrice
) / 1_000_000;
// עדכון סכום יומי
const today = new Date().toISOString().slice(0, 10);
const key = `${today}:${userId}`;
const current = dailyCosts.get(key) ?? 0;
dailyCosts.set(key, current + cost);
// התראות
const dailyTotal = dailyCosts.get(key)!;
if (dailyTotal > 5) {
console.error(
`[COST ALERT] User ${userId} exceeded $5/day: $${dailyTotal.toFixed(2)}`
);
// בפרודקשן: שליחת Slack notification / email
}
return { cost, dailyTotal };
}
מעבר ל-tracking שלכם, הגדירו alerts ישירות אצל Anthropic, OpenAI ו-Google. כל הספקים מציעים usage alerts בחינם. הגדירו alert ב-80% מהתקציב החודשי — כדי שתדעו לפני שנגמר הכסף.
Deployment על Vercel ו-Next.js
AI SDK נבנה לעבוד מצוין על Vercel, אבל הוא לא נעול ל-Vercel. אפשר לפרוס על כל פלטפורמה שמריצה Node.js — AWS Lambda, Cloudflare Workers, Railway, Render, Docker.
Deployment על Vercel
- Push to GitHub: Vercel מתחבר ל-repo ועושה auto-deploy
- Environment Variables: ב-Vercel Dashboard, הוסיפו API keys כ-Environment Variables (לא ב-code!)
- Function type: API routes עם streaming = Serverless Functions (לא Edge — Edge לא תומך ב-streaming ארוך)
- Timeout: ב-Vercel Pro, Function timeout הוא 60 שניות. סוכנים מורכבים עם כלים רבים עלולים לחרוג — הגדירו
maxDuration: 60
// app/api/chat/route.ts
import { ToolLoopAgent, tool, createAgentUIStreamResponse } from 'ai';
import { z } from 'zod';
// maxDuration מגדיל את ה-timeout של ה-Serverless Function
export const maxDuration = 60; // שניות
const agent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
instructions: 'You are a helpful assistant.',
tools: { /* ... */ },
});
export async function POST(req: Request) {
const { messages } = await req.json();
return createAgentUIStreamResponse({ agent, messages });
}
Environment Variables — Secrets Management
AI SDK קורא API keys אוטומטית מ-environment variables: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY. ב-Vercel — הגדירו ב-Dashboard. ב-local — קובץ .env.local (לא .env!) שנמצא ב-.gitignore. טעות נפוצה: commit של .env ל-Git = המפתח חשוף לכל העולם.
Deployment על פלטפורמות אחרות
AI SDK הוא framework-agnostic. הנה דפוסי deployment לפלטפורמות פופולריות מחוץ ל-Vercel:
// Cloudflare Worker — AI SDK עם streaming
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
const { messages } = await request.json();
// AI SDK עובד ב-Cloudflare Workers!
const result = streamText({
model: 'anthropic/claude-sonnet-4.5',
messages,
// ב-Workers, API key מגיע מ-environment
// שהוגדר ב-wrangler.toml
});
// מחזירים Response עם streaming (v6: toUIMessageStreamResponse לצ'אט, toTextStreamResponse לטקסט)
return result.toUIMessageStreamResponse();
},
} satisfies ExportedHandler<Env>;
# Dockerfile לסוכן AI SDK
FROM node:20-alpine
WORKDIR /app
# Dependencies
COPY package*.json ./
RUN npm ci --production
# קוד
COPY . .
RUN npm run build
# Environment variables — מוזרקים ב-runtime, לא ב-build!
# API keys צריכים להיות ב-docker run -e או docker-compose
ENV PORT=3000
EXPOSE 3000
CMD ["node", "dist/server.js"]
# הרצה:
# docker build -t ai-agent .
# docker run -p 3000:3000 \
# -e ANTHROPIC_API_KEY=sk-ant-... \
# -e OPENAI_API_KEY=sk-... \
# ai-agent
Rate Limiting — הגנה מפני abuse
ללא rate limiting, כל אחד יכול לשלוח אלפי בקשות ולשרוף את תקציב ה-API שלכם. דפוס פשוט ויעיל:
// Simple in-memory rate limiter
const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
function checkRateLimit(
userId: string,
maxRequests = 20,
windowMs = 60_000 // 1 דקה
): { allowed: boolean; remaining: number } {
const now = Date.now();
const entry = rateLimitMap.get(userId);
if (!entry || now > entry.resetTime) {
rateLimitMap.set(userId, { count: 1, resetTime: now + windowMs });
return { allowed: true, remaining: maxRequests - 1 };
}
if (entry.count >= maxRequests) {
return { allowed: false, remaining: 0 };
}
entry.count++;
return { allowed: true, remaining: maxRequests - entry.count };
}
// שימוש ב-API Route
export async function POST(req: Request) {
const userId = req.headers.get('x-user-id') ?? 'anonymous';
const { allowed, remaining } = checkRateLimit(userId);
if (!allowed) {
return new Response(
JSON.stringify({ error: 'Rate limit exceeded. Try again in 1 minute.' }),
{
status: 429,
headers: {
'X-RateLimit-Remaining': '0',
'Retry-After': '60',
},
}
);
}
// ... המשך לסוכן
}
Deployment Checklist
| # | פריט | קריטי? |
|---|---|---|
| 1 | API keys ב-Environment Variables (לא ב-code) | כן |
| 2 | .env.local ב-.gitignore | כן |
| 3 | stopWhen מוגדר על כל סוכן | כן |
| 4 | maxDuration מוגדר ב-API routes | כן |
| 5 | Fallback chain מוגדר (provider חלופי) | מומלץ |
| 6 | Rate limiting על API routes (למנוע abuse) | כן |
| 7 | Telemetry מופעל עם cost tracking | מומלץ |
| 8 | Error handling — שגיאות ידידותיות, לא stack traces | כן |
| 9 | CORS headers מוגדרים נכון | כן |
| 10 | Billing alerts מוגדרים אצל הספקים | כן |
Error Handling Patterns ב-AI SDK
סוכנים בפרודקשן נכשלים. API נופל, מודל מחזיר שגיאה, כלי לא זמין. הנה שלושה דפוסים שחייבים להיות בכל סוכן AI SDK:
Pattern 1: Graceful Provider Fallback
import { generateText } from 'ai';
type ProviderError = {
provider: string;
error: string;
retryable: boolean;
};
async function generateWithSmartFallback(
prompt: string,
tools?: Record<string, any>
): Promise<{ text: string; provider: string; errors: ProviderError[] }> {
const providers = [
{ model: 'anthropic/claude-sonnet-4.5', name: 'Anthropic' },
{ model: 'openai/gpt-4o', name: 'OpenAI' },
{ model: 'google/gemini-2.5-pro', name: 'Google' },
];
const errors: ProviderError[] = [];
for (const { model, name } of providers) {
try {
const result = await generateText({
model,
prompt,
tools,
// Timeout per provider
abortSignal: AbortSignal.timeout(30_000),
});
return { text: result.text, provider: name, errors };
} catch (error) {
const err = error as Error;
const isRetryable =
err.message.includes('429') ||
err.message.includes('503') ||
err.message.includes('timeout');
errors.push({
provider: name,
error: err.message,
retryable: isRetryable,
});
console.warn(`[Fallback] ${name} failed: ${err.message}`);
}
}
throw new Error(
`All providers failed: ${errors.map(e => e.provider).join(', ')}`
);
}
Pattern 2: Tool Execution Safety Wrapper
כלים יכולים לזרוק exceptions, להחזיר נתונים לא תקינים, או לתקוע (hang). עטפו כל כלי ב-safety wrapper:
import { tool } from 'ai';
import { z } from 'zod';
// Helper: עוטף כל execute function בהגנה
function safeTool<T extends z.ZodTypeAny>(config: {
description: string;
inputSchema: T;
execute: (args: z.infer<T>) => Promise<any>;
timeoutMs?: number;
fallback?: any;
}) {
const {
description, inputSchema, execute,
timeoutMs = 10000, fallback = { error: 'Tool unavailable' }
} = config;
return tool({
description,
inputSchema,
execute: async (args) => {
try {
const result = await Promise.race([
execute(args),
new Promise((_, reject) =>
setTimeout(
() => reject(new Error('Tool timeout')),
timeoutMs
)
),
]);
return result;
} catch (error) {
console.error(
`Tool error: ${description.slice(0, 50)}`,
(error as Error).message
);
return fallback;
}
},
});
}
// שימוש — כל כלי מוגן אוטומטית
const weatherTool = safeTool({
description: 'Get weather for a city',
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => {
const res = await fetch(`https://api.weather.com/v1?q=${city}`);
if (!res.ok) throw new Error(`API error: ${res.status}`);
return res.json();
},
timeoutMs: 5000,
fallback: { error: 'Weather service unavailable', city },
});
Pattern 3: Cost Guard — הגנה על תקציב
// מעקב עלות per-session עם התראה
class CostGuard {
private totalCost = 0;
private readonly maxCost: number;
private readonly onBudgetAlert: (cost: number) => void;
constructor(
maxCostPerSession: number,
onAlert: (cost: number) => void
) {
this.maxCost = maxCostPerSession;
this.onBudgetAlert = onAlert;
}
trackUsage(usage: {
promptTokens: number;
completionTokens: number;
}, model: string) {
// מחירים משוערים per 1M tokens
const prices: Record<string, [number, number]> = {
'anthropic/claude-sonnet-4.5': [3, 15],
'openai/gpt-4o': [2.5, 10],
'google/gemini-2.5-pro': [1.25, 5],
'google/gemini-2.5-flash': [0.15, 0.60],
};
const [inputPrice, outputPrice] = prices[model] ?? [5, 15];
const cost =
(usage.promptTokens * inputPrice +
usage.completionTokens * outputPrice) /
1_000_000;
this.totalCost += cost;
if (this.totalCost > this.maxCost * 0.8) {
this.onBudgetAlert(this.totalCost);
}
return {
stepCost: cost,
totalCost: this.totalCost,
budgetRemaining: this.maxCost - this.totalCost,
overBudget: this.totalCost > this.maxCost,
};
}
}
// שימוש
const guard = new CostGuard(1.0, (cost) => {
console.warn(`Budget alert! $${cost.toFixed(4)} spent`);
});
אף אחד מהדפוסים האלה לא אופציונלי בסוכן production. בלי Provider Fallback — כל outage של Anthropic מפיל את הסוכן. בלי Tool Safety — exception אחד בכלי שובר את כל הסוכן. בלי Cost Guard — באג בלולאה יכול לייצר חשבון של מאות דולרים. שלבו את שלושתם מהיום הראשון.
מטרה: בניית Smart Router שבוחר מודל אוטומטית לפי מורכבות השאילתה.
- כתבו פונקציית
classifyComplexity(prompt)שמשתמשת ב-generateTextעםOutput.choice()ומודל זול (Gemini Flash) לסווג שאילתות ל-simple/moderate/complex - בנו טבלת routing: simple → Gemini Flash, moderate → Sonnet 4.5, complex → Opus 4.6
- שלחו 5 שאילתות שונות ורשמו: מודל שנבחר, latency, token usage, ועלות
- חשבו: כמה הייתם חוסכים לעומת שימוש ב-Opus לכל שאילתה?
Success criteria: Router עובד, שאילתות מנותבות נכון, חיסכון של לפחות 60% לעומת Opus בלבד.
עלות: ~$1
מטרה: בניית סוכן שמקבל טקסט חופשי (מיילים, חשבוניות, CVs) ומחזיר structured data.
- הגדירו
ToolLoopAgentעםOutput.object()לחילוץ מידע מחשבונית:vendor, amount, currency, date, items[], tax - הגדירו
ToolLoopAgentנוסף לחילוץ מידע מ-CV:name, email, experience_years, skills[], languages[] - שלחו 3 דוגמאות לכל סוכן (עברית ואנגלית) ובדקו את הדיוק
- הוסיפו כלי
validateEmailשמוודא שכתובת מייל תקינה
Success criteria: הסוכנים מחלצים 90%+ של השדות נכון מטקסטים בעברית ואנגלית.
עלות: ~$0.50
מטרה: חיבור כל מה שלמדנו — ToolLoopAgent + useChat + MCP + multi-provider + structured output.
- צרו Next.js app עם
useChatChat UI - הגדירו
ToolLoopAgentעם 5 כלים: 2 native + 3 מ-MCP Server (מפרק 3 או MCP server ציבורי) - הוסיפו Smart Router — שאילתות פשוטות → Gemini Flash, מורכבות → Sonnet
- הגדירו
needsApproval: trueעל כלים שכותבים/שולחים - הוסיפו telemetry שמדפיס cost per session
- Deploy ל-Vercel (או הריצו locally)
Success criteria: Chat app עובד עם MCP tools, Smart routing, tool approval, ו-cost tracking.
עלות: ~$3
טעויות נפוצות — ואיך להימנע מהן
מה קורה: מפתח כותב generateObject(), maxSteps, handleSubmit — שלא קיימים ב-v6.
למה זה בעיה: AI SDK v6 הוא שדרוג שובר (breaking change). רוב הטוטוריאלים באינטרנט הם עדיין ל-v5. קוד v5 לא ירוץ ב-v6.
הפתרון: קראו את Migration Guide הרשמי. השינויים העיקריים: generateObject → Output.object(), maxSteps → stopWhen, handleSubmit/input → sendMessage, provider format שונה.
מה קורה: סוכן עם tools אבל בלי stopWhen. המודל קורא לכלים בלולאה אינסופית.
למה זה בעיה: כל צעד = API call נוסף = tokens + עלות. סוכן ב-loop יכול לשרוף $50+ בשעה.
הפתרון: תמיד הגדירו stopWhen: stepCountIs(10) לפחות. לפרודקשן — הוסיפו גם token budget limit וגם time limit.
מה קורה: מפתח מחבר 5 MCP Servers עם 30 כלים לסוכן אחד.
למה זה בעיה: כל tool definition נכנס ל-System Prompt. 30 כלים = 10,000-15,000 tokens בכל קריאה. העלות מתפוצצת, וגם הדיוק יורד — המודל מתקשה לבחור מ-30 כלים.
הפתרון: 5-8 כלים לסוכן. אם צריך יותר — Router Agent שמפנה לסוכנים מתמחים, או dynamic tool loading לפי הקשר.
מה קורה: מפתח פורס את ה-API route כ-Edge Function ב-Vercel.
למה זה בעיה: Edge Functions יש להן timeout של 30 שניות ומגבלות זיכרון. סוכנים עם כלים רבים יכולים לקחת 60+ שניות.
הפתרון: השתמשו ב-Serverless Functions (לא Edge) לסוכנים. הגדירו export const maxDuration = 60; ב-API route. Edge Functions טובות לדברים מהירים כמו routing ו-authentication.
מה קורה: קובץ .env (או .env.local) מועלה ל-GitHub.
למה זה בעיה: כל מי שרואה את ה-repo רואה את ה-API keys שלכם. בוטים סורקים GitHub ומשתמשים במפתחות תוך דקות. אפשר להתעורר עם חשבון של $1,000+.
הפתרון: (1) ודאו ש-.env* ב-.gitignore לפני ה-commit הראשון, (2) ב-Vercel — הגדירו API keys ב-Dashboard (Settings → Environment Variables), (3) אם כבר עשיתם commit — שנו את כל ה-API keys מיד (revoke + generate new).
| תדירות | משימה | זמן |
|---|---|---|
| יומי | בדוק token usage ו-cost per session ב-Telemetry dashboard | 3 דק' |
| יומי | סקור error logs — שגיאות provider, timeouts, tool failures | 5 דק' |
| שבועי | בדוק אם יש AI SDK version update — npm outdated ai | 5 דק' |
| שבועי | A/B test מודל אחד — בדקו אם מודל זול יותר נותן תוצאות דומות | 15 דק' |
| חודשי | בדקו מחירי מודלים עדכניים — Anthropic, OpenAI, Google משנים מחירים לעתים | 10 דק' |
| חודשי | בדקו MCP Servers חדשים שרלוונטיים — חברו את המתאימים לסוכן | 15 דק' |
כתבו קובץ TypeScript אחד עם ToolLoopAgent שכולל כלי אחד (כמו weather) ו-Output.object() עם Zod schema. הריצו generate() ותראו איך הסוכן קורא לכלי, מקבל תוצאה, ומחזיר אובייקט typed. ברגע שתראו LLM מחזיר אובייקט TypeScript מובנה ומוגדר — ולא טקסט חופשי — תבינו למה structured output משנה את הכל.
- מה ההבדל בין
generateText()ל-streamText(), ומתי תבחרו בכל אחד? (רמז: UX vs batch processing) - מה עושה
stopWhenולמה הוא קריטי? מה קורה בלעדיו? (רמז: עלויות, לולאות) - איך
ToolLoopAgentשונה משימוש ישיר ב-generateText+ tools? מתי תבחרו בכל אחד? (רמז: reusability, UI integration) - הסבירו איך multi-provider עובד ב-AI SDK ותנו דוגמה של Smart Router פשוט. (רמז: מחרוזת model אחידה, fallback)
- מהם שלושת הדברים הראשונים שצריך לבדוק לפני deploy של סוכן AI לפרודקשן? (רמז: keys, stopWhen, monitoring)
עברתם 4 מתוך 5? מצוין — אתם מוכנים לפרק 7.
בפרק הזה בניתם סוכן AI מלא ב-TypeScript עם Vercel AI SDK v6 — מ-Backend ועד UI. התחלנו עם Core Primitives (generateText, streamText, Output), עברנו ל-Tool Calling עם tool() ו-stopWhen, הכרנו את ToolLoopAgent — class שמגדיר סוכנים לשימוש חוזר, בניתם Chat UI מלא עם useChat, למדתם את ה-Killer Feature — multi-provider שמאפשר להחליף מודלים בשורה אחת, ועבדתם עם structured output שהופך את הסוכן ממחזיר טקסט חופשי למחזיר אובייקטים typed.
הנקודה המרכזית: AI SDK הוא הגישה של "TypeScript first, multi-provider always." בניגוד ל-Claude Agent SDK (Anthropic-only) או OpenAI Agents SDK (OpenAI-only), AI SDK נותן חופש מלא לבחור ולהחליף מודלים. המחיר: פחות provider-specific features (כמו extended thinking של Claude).
בפרק הבא (פרק 7) תבנו את אותו סוכן עם OpenAI Agents SDK — ותוכלו להשוות ישירות בין שלוש הגישות: Claude Agent SDK (פרק 5), AI SDK (פרק 6), ו-OpenAI Agents SDK (פרק 7).
צ'קליסט — סיכום פרק 6
- מבין/ה את הפילוסופיה של AI SDK: multi-provider, streaming-first, TypeScript-native
- יודע/ת את ההבדל בין שלוש שכבות ה-SDK: Core, UI, Provider packages
- יודע/ת להשתמש ב-
generateText(),streamText(), ו-Output.object() - מגדיר/ה כלים עם
tool()— כולל Zod schema, description מפורט, ו-execute function - משתמש/ת ב-
stopWhenתמיד — מבין/ה את הסיכון של סוכן בלי תנאי עצירה - בנית
ToolLoopAgentעם instructions, tools, ו-output — ומבין/ה מתי להשתמש בו - בנית Chat UI עם
useChatשמציג messages, streaming, ו-tool calls - שלחת את אותו prompt ל-3 providers ומבין/ה את ההבדלים
- בנית Smart Router שבוחר מודל לפי מורכבות
- חיברת MCP Server ל-AI SDK עם
createMCPClient - מכיר/ה את
needsApprovalויודע/ת אילו כלים צריכים אישור - מכיר/ה את מנגנון ה-telemetry ויודע/ת לעקוב אחרי token usage ועלויות
- עברת על Deployment Checklist (10 פריטים) ומבין/ה כל סעיף
- עבדת על לפחות 3 מתוך 4 תרגילים (Chat App, Smart Router, Structured Output, Full-Stack)
- עניתי על 4 מתוך 5 שאלות ב"בדוק את עצמך"