6 בניית מיומנויות

Vercel AI SDK — Full-Stack AI עם TypeScript

ה-SDK הפופולרי ביותר ל-TypeScript לבניית אפליקציות AI. בפרק הזה תלמדו לבנות סוכנים עם generateText, streamText, ToolLoopAgent ו-useChat — מ-Backend ועד UI. תחוו את הכוח של multi-provider (אותו קוד, כל מודל), structured output עם Zod, אינטגרציית MCP, ו-human-in-the-loop. בסוף הפרק — יהיה לכם Next.js app עובד עם סוכן AI שמשתמש בכלים, משדר תשובות בזמן אמת, ומוכן לפרודקשן.

מה יהיה לך בסוף הפרק הזה
מה תוכלו לעשות אחרי הפרק הזה
לפני שמתחילים
מגיעים מ-AI SDK v5?

אם אתם מגיעים מ-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 — ותוכל להשוות ישירות בין שלוש הגישות.

מילון מונחים — פרק 6
מונח (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
מתחיל 10 דקות חינם

סקירה ופילוסופיה — למה AI SDK

Vercel AI SDK הוא ה-SDK הפופולרי ביותר ל-TypeScript לבניית אפליקציות AI. הוא open source (Apache 2.0), חינמי לשימוש, framework-agnostic (עובד עם Next.js, Remix, Svelte, Vue, ועוד), ומותאם ל-streaming מהיסוד.

הפילוסופיה של AI SDK שונה מכל SDK אחר שנלמד בקורס:

AI SDK במספרים (מרץ 2026)

מה חדש ב-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

Framework: "AI SDK Layer Cake" — שלוש שכבות לשלוש מטרות
שכבה 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).

עשה עכשיו 5 דקות

התקינו 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".

בינוני 15 דקות פרקטי

Core Primitives — generateText, streamText ו-Output

הליבה של AI SDK מורכבת משתי פונקציות ופרימיטיב אחד — וזה מספיק לבנות כמעט כל דבר:

generateText — יצירת טקסט (non-streaming)

generateText() שולח prompt ל-LLM, ממתין לתשובה מלאה, ומחזיר את התוצאה. פשוט ונקי — מתאים ל-batch processing, background jobs, ומקרים שלא צריכים streaming.

TypeScript — generateText בסיסי
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 שניות. זו הפונקציה שתשתמשו בה ברוב המקרים.

TypeScript — streamText עם streaming לקונסול
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, לא בנפרד.

TypeScript — Output.object() עם Zod 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({
      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", ...]
שינוי שבירה ב-v6: generateObject הוסר

אם עובדים עם קוד ישן, generateObject() ו-streamObject() כבר לא קיימים ב-v6. צריך להחליף ל-generateText({ output: Output.object({...}) }). היתרון: עכשיו structured output עובד יחד עם tools באותה קריאה.

עשה עכשיו 5 דקות

כתבו את שלוש הפונקציות: (1) generateText פשוט עם Claude, (2) streamText עם GPT שמדפיס לקונסול, (3) Output.object() שמחלץ structured data מטקסט חופשי. בדקו שכל השלושה עובדים לפני שממשיכים.

בינוני 20 דקות פרקטי

Tool Calling — כלים ב-AI SDK

בפרק 3 למדנו את התיאוריה של Function Calling. עכשיו נראה איך זה עובד ב-AI SDK — וזה אלגנטי. הגדרת כלי ב-AI SDK היא type-safe מהיסוד: הפרמטרים מוגדרים ב-Zod, הסוכן קורא לכלי, ותוצאת ה-execute חוזרת אוטומטית למודל.

הגדרת כלי עם tool()

TypeScript — הגדרת כלים עם 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: אין צורך בקוד מיוחד כדי לבנות סוכן. כל מה שצריך זה:

  1. streamText (או generateText) — הפונקציה שקוראת ל-LLM
  2. tools — רשימת כלים שהסוכן יכול להשתמש
  3. 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!

ללא stopWhen, סוכן יכול להיכנס ללולאה אינסופית — קורא לכלי, מקבל תוצאה, קורא שוב, שוב ושוב. כל קריאה עולה tokens. סוכן ללא stop condition יכול לשרוף $50+ בלילה אחד. תמיד הגדירו stopWhen: stepCountIs(10) לפחות.

עשה עכשיו 10 דקות

בנו סוכן עם 3 כלים: (1) weather — מחזיר מזג אוויר לעיר, (2) calculator — מבצע חשבון, (3) translator — מתרגם טקסט. השתמשו ב-generateText עם stopWhen: stepCountIs(5). שלחו prompt שדורש שימוש ב-2 כלים לפחות: "מה הטמפרטורה בתל אביב בפרנהייט?"

בינוני 15 דקות פרקטי

ToolLoopAgent — סוכנים כ-Class

ב-v6, AI SDK הציג ToolLoopAgent — class שמגדיר סוכן שניתן לשימוש חוזר. במקום להעביר את אותם tools, instructions ו-model לכל קריאה ל-generateText, מגדירים agent פעם אחת ומשתמשים בו בכל מקום.

TypeScript — ToolLoopAgent בסיסי
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.

עשה עכשיו 10 דקות

המירו את סוכן 3 הכלים שבניתם ל-ToolLoopAgent. הוסיפו instructions בעברית שמסביר את התפקיד. הוסיפו Output.object() עם schema שמחזיר { answer: string, tools_used: string[], confidence: string }. הריצו עם generate() ואז עם stream().

בינוני 20 דקות פרקטי

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

TypeScript — Next.js API Route (app/api/chat/route.ts)
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

TypeScript — React Component (app/page.tsx)
'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 מטפל ב:

תרגיל 1: בניית Next.js Chat App עם סוכן 30 דקות

מטרה: לבנות Chat app מלא עם ToolLoopAgent ו-useChat.

  1. צרו Next.js app: npx create-next-app@latest ai-chat --typescript --app
  2. התקינו: npm install ai @ai-sdk/react @ai-sdk/anthropic zod
  3. צרו API route ב-app/api/chat/route.ts עם ToolLoopAgent שכולל 3 כלים: weather, calculator, joke_generator
  4. צרו Chat UI ב-app/page.tsx עם useChat — כולל הצגת tool calls
  5. הריצו npm run dev ובדקו: שאלו "מה מזג האוויר בתל אביב?" ו-"ספר בדיחה"

Success criteria: הסוכן עונה, tool calls מוצגים ב-UI, streaming עובד.

עלות: ~$0.50

בינוני 15 דקות פרקטי

Multi-Provider — ה-Killer Feature

הפיצ'ר שמבדיל את AI SDK מכל SDK אחר הוא multi-provider support אמיתי. אותו קוד, אותם כלים, אותם schemas — רק שורה אחת משתנה:

TypeScript — החלפת מודל בשורה אחת
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 בוחר את המודל המתאים ביותר לכל שאילתה — על פי מורכבות, עלות, ומהירות:

TypeScript — 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 — נפילה אוטומטית לספק חלופי:

TypeScript — Fallback chain עם retry
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');
}
Framework: "Provider Selection Matrix" — איזה מודל לאיזה משימה
משימה מודל מומלץ למה עלות לשיחה
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 בלבד.

עשה עכשיו 5 דקות

בדקו את ה-multi-provider: שלחו את אותו prompt ("Explain AI agents in 3 sentences") לשלושה מודלים שונים: Claude Sonnet, GPT-5, ו-Gemini Pro. השוו: (1) איכות התשובה, (2) מהירות, (3) token usage. רשמו את התוצאות — תשתמשו בהן בתרגיל 2.

בינוני 15 דקות פרקטי

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) כשלא ידוע המבנה מראש
TypeScript — חילוץ structured data מטקסט חופשי
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 זה לא היה אפשרי.

TypeScript — ToolLoopAgent עם structured output + tools
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!"
עשה עכשיו 5 דקות

בנו "Email Analyzer": סוכן שמקבל גוף מייל כ-prompt ומחזיר structured output עם: sender, urgency, action_items, sentiment. בדקו עם 3 מיילים שונים (עברית, אנגלית, ומעורב).

בינוני 15 דקות פרקטי

אינטגרציית MCP

בפרק 3 בניתם MCP Server. עכשיו נחבר אותו לסוכן ב-AI SDK. ב-v6, MCP integration יצא מ-experimental ומהווה חלק stable מה-SDK.

הרעיון: MCP Servers חושפים כלים, ו-AI SDK טוען אותם אוטומטית כ-tools. אפשר לשלב MCP tools עם native AI SDK tools באותו סוכן.

TypeScript — חיבור MCP Server ל-AI SDK
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 (תהליך מקומי)

TypeScript — MCP עם stdio (תהליך מקומי)
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 tools = עלות tokens נוספת

כל כלי 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. הנה דוגמה מלאה:

TypeScript — ToolLoopAgent עם MCP + native tools
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

שרתי MCP כמו GitHub חושפים עשרות כלים. לעולם אל תטענו את כולם. סננו רק את הכלים הרלוונטיים למשימה — כמו בדוגמה למעלה. פחות כלים = פחות tokens בכל קריאה, דיוק גבוה יותר בבחירת כלי, ועלות נמוכה יותר.

Error Handling ב-MCP Connections

חיבורי MCP יכולים להיכשל — השרת לא זמין, ה-connection נפל, או הכלי מחזיר שגיאה. טיפול נכון בשגיאות MCP הוא קריטי:

TypeScript — Robust MCP Connection Pattern
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({ /* ... */ }),
  },
});
בינוני 10 דקות פרקטי

Human-in-the-Loop — אישור פעולות

חדש ב-v6: needsApproval. כשסוכן רוצה לבצע פעולה "מסוכנת" — שליחת מייל, מחיקת קובץ, ביצוע רכישה — אפשר לדרוש אישור מהמשתמש לפני הביצוע.

TypeScript — Human-in-the-loop tool approval
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" בממשק שמחליטים אם לבצע את הכלי.

עשה עכשיו 3 דקות

סווגו את הכלים שלכם: מתוך הכלים שבניתם עד עכשיו, סמנו אילו צריכים needsApproval: true. כלל אצבע: כל כלי שכותב, שולח, מוחק, או עולה כסף — צריך אישור.

בינוני 10 דקות חינם

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
TypeScript — הפעלת telemetry
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:

TypeScript — Cost alerting pattern
// 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 };
}
הגדירו billing alerts אצל הספקים!

מעבר ל-tracking שלכם, הגדירו alerts ישירות אצל Anthropic, OpenAI ו-Google. כל הספקים מציעים usage alerts בחינם. הגדירו alert ב-80% מהתקציב החודשי — כדי שתדעו לפני שנגמר הכסף.

בינוני 15 דקות פרקטי

Deployment על Vercel ו-Next.js

AI SDK נבנה לעבוד מצוין על Vercel, אבל הוא לא נעול ל-Vercel. אפשר לפרוס על כל פלטפורמה שמריצה Node.js — AWS Lambda, Cloudflare Workers, Railway, Render, Docker.

Deployment על Vercel

  1. Push to GitHub: Vercel מתחבר ל-repo ועושה auto-deploy
  2. Environment Variables: ב-Vercel Dashboard, הוסיפו API keys כ-Environment Variables (לא ב-code!)
  3. Function type: API routes עם streaming = Serverless Functions (לא Edge — Edge לא תומך ב-streaming ארוך)
  4. Timeout: ב-Vercel Pro, Function timeout הוא 60 שניות. סוכנים מורכבים עם כלים רבים עלולים לחרוג — הגדירו maxDuration: 60
TypeScript — API Route עם maxDuration
// 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

לעולם אל תשימו API keys ב-code!

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:

TypeScript — Deployment על Cloudflare Workers
// 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>;
TypeScript — Deployment עם Docker
# 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 שלכם. דפוס פשוט ויעיל:

TypeScript — Rate limiting middleware
// 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

# פריט קריטי?
1API keys ב-Environment Variables (לא ב-code)כן
2.env.local ב-.gitignoreכן
3stopWhen מוגדר על כל סוכןכן
4maxDuration מוגדר ב-API routesכן
5Fallback chain מוגדר (provider חלופי)מומלץ
6Rate limiting על API routes (למנוע abuse)כן
7Telemetry מופעל עם cost trackingמומלץ
8Error handling — שגיאות ידידותיות, לא stack tracesכן
9CORS headers מוגדרים נכוןכן
10Billing alerts מוגדרים אצל הספקיםכן

Error Handling Patterns ב-AI SDK

סוכנים בפרודקשן נכשלים. API נופל, מודל מחזיר שגיאה, כלי לא זמין. הנה שלושה דפוסים שחייבים להיות בכל סוכן AI SDK:

Pattern 1: Graceful Provider Fallback

TypeScript — Provider fallback עם error classification
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:

TypeScript — Safe tool 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 — הגנה על תקציב

TypeScript — Cost tracking middleware
// מעקב עלות 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`);
});
שלושת ה-Patterns הם חובה בפרודקשן

אף אחד מהדפוסים האלה לא אופציונלי בסוכן production. בלי Provider Fallback — כל outage של Anthropic מפיל את הסוכן. בלי Tool Safety — exception אחד בכלי שובר את כל הסוכן. בלי Cost Guard — באג בלולאה יכול לייצר חשבון של מאות דולרים. שלבו את שלושתם מהיום הראשון.

תרגיל 2: Smart Router + Multi-Provider 25 דקות

מטרה: בניית Smart Router שבוחר מודל אוטומטית לפי מורכבות השאילתה.

  1. כתבו פונקציית classifyComplexity(prompt) שמשתמשת ב-generateText עם Output.choice() ומודל זול (Gemini Flash) לסווג שאילתות ל-simple/moderate/complex
  2. בנו טבלת routing: simple → Gemini Flash, moderate → Sonnet 4.5, complex → Opus 4.6
  3. שלחו 5 שאילתות שונות ורשמו: מודל שנבחר, latency, token usage, ועלות
  4. חשבו: כמה הייתם חוסכים לעומת שימוש ב-Opus לכל שאילתה?

Success criteria: Router עובד, שאילתות מנותבות נכון, חיסכון של לפחות 60% לעומת Opus בלבד.

עלות: ~$1

תרגיל 3: סוכן חילוץ מידע מובנה 20 דקות

מטרה: בניית סוכן שמקבל טקסט חופשי (מיילים, חשבוניות, CVs) ומחזיר structured data.

  1. הגדירו ToolLoopAgent עם Output.object() לחילוץ מידע מחשבונית: vendor, amount, currency, date, items[], tax
  2. הגדירו ToolLoopAgent נוסף לחילוץ מידע מ-CV: name, email, experience_years, skills[], languages[]
  3. שלחו 3 דוגמאות לכל סוכן (עברית ואנגלית) ובדקו את הדיוק
  4. הוסיפו כלי validateEmail שמוודא שכתובת מייל תקינה

Success criteria: הסוכנים מחלצים 90%+ של השדות נכון מטקסטים בעברית ואנגלית.

עלות: ~$0.50

תרגיל 4 (מתקדם): Full-Stack Agent עם MCP 45 דקות

מטרה: חיבור כל מה שלמדנו — ToolLoopAgent + useChat + MCP + multi-provider + structured output.

  1. צרו Next.js app עם useChat Chat UI
  2. הגדירו ToolLoopAgent עם 5 כלים: 2 native + 3 מ-MCP Server (מפרק 3 או MCP server ציבורי)
  3. הוסיפו Smart Router — שאילתות פשוטות → Gemini Flash, מורכבות → Sonnet
  4. הגדירו needsApproval: true על כלים שכותבים/שולחים
  5. הוסיפו telemetry שמדפיס cost per session
  6. Deploy ל-Vercel (או הריצו locally)

Success criteria: Chat app עובד עם MCP tools, Smart routing, tool approval, ו-cost tracking.

עלות: ~$3

מתחיל 8 דקות חינם

טעויות נפוצות — ואיך להימנע מהן

טעות 1: שימוש ב-API ישן של v5 ב-v6

מה קורה: מפתח כותב generateObject(), maxSteps, handleSubmit — שלא קיימים ב-v6.

למה זה בעיה: AI SDK v6 הוא שדרוג שובר (breaking change). רוב הטוטוריאלים באינטרנט הם עדיין ל-v5. קוד v5 לא ירוץ ב-v6.

הפתרון: קראו את Migration Guide הרשמי. השינויים העיקריים: generateObjectOutput.object(), maxStepsstopWhen, handleSubmit/inputsendMessage, provider format שונה.

טעות 2: שכחת stopWhen

מה קורה: סוכן עם tools אבל בלי stopWhen. המודל קורא לכלים בלולאה אינסופית.

למה זה בעיה: כל צעד = API call נוסף = tokens + עלות. סוכן ב-loop יכול לשרוף $50+ בשעה.

הפתרון: תמיד הגדירו stopWhen: stepCountIs(10) לפחות. לפרודקשן — הוסיפו גם token budget limit וגם time limit.

טעות 3: העמסת כלים על סוכן אחד

מה קורה: מפתח מחבר 5 MCP Servers עם 30 כלים לסוכן אחד.

למה זה בעיה: כל tool definition נכנס ל-System Prompt. 30 כלים = 10,000-15,000 tokens בכל קריאה. העלות מתפוצצת, וגם הדיוק יורד — המודל מתקשה לבחור מ-30 כלים.

הפתרון: 5-8 כלים לסוכן. אם צריך יותר — Router Agent שמפנה לסוכנים מתמחים, או dynamic tool loading לפי הקשר.

טעות 4: שימוש ב-Edge Functions לסוכנים

מה קורה: מפתח פורס את ה-API route כ-Edge Function ב-Vercel.

למה זה בעיה: Edge Functions יש להן timeout של 30 שניות ומגבלות זיכרון. סוכנים עם כלים רבים יכולים לקחת 60+ שניות.

הפתרון: השתמשו ב-Serverless Functions (לא Edge) לסוכנים. הגדירו export const maxDuration = 60; ב-API route. Edge Functions טובות לדברים מהירים כמו routing ו-authentication.

טעות 5: Commit של .env ל-Git

מה קורה: קובץ .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).

שגרת עבודה — פרק 6
תדירותמשימהזמן
יומיבדוק token usage ו-cost per session ב-Telemetry dashboard3 דק'
יומיסקור error logs — שגיאות provider, timeouts, tool failures5 דק'
שבועיבדוק אם יש AI SDK version update — npm outdated ai5 דק'
שבועיA/B test מודל אחד — בדקו אם מודל זול יותר נותן תוצאות דומות15 דק'
חודשיבדקו מחירי מודלים עדכניים — Anthropic, OpenAI, Google משנים מחירים לעתים10 דק'
חודשיבדקו MCP Servers חדשים שרלוונטיים — חברו את המתאימים לסוכן15 דק'
אם אתם עושים רק דבר אחד מהפרק הזה 15 דקות

כתבו קובץ TypeScript אחד עם ToolLoopAgent שכולל כלי אחד (כמו weather) ו-Output.object() עם Zod schema. הריצו generate() ותראו איך הסוכן קורא לכלי, מקבל תוצאה, ומחזיר אובייקט typed. ברגע שתראו LLM מחזיר אובייקט TypeScript מובנה ומוגדר — ולא טקסט חופשי — תבינו למה structured output משנה את הכל.

בדוק את עצמך — 5 שאלות
  1. מה ההבדל בין generateText() ל-streamText(), ומתי תבחרו בכל אחד? (רמז: UX vs batch processing)
  2. מה עושה stopWhen ולמה הוא קריטי? מה קורה בלעדיו? (רמז: עלויות, לולאות)
  3. איך ToolLoopAgent שונה משימוש ישיר ב-generateText + tools? מתי תבחרו בכל אחד? (רמז: reusability, UI integration)
  4. הסבירו איך multi-provider עובד ב-AI SDK ותנו דוגמה של Smart Router פשוט. (רמז: מחרוזת model אחידה, fallback)
  5. מהם שלושת הדברים הראשונים שצריך לבדוק לפני 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