4 שלב 2 — Skill-Building

Agent SDK — TypeScript — בניית סוכנים ב-TypeScript

איך לבנות סוכני AI עם ה-TypeScript SDK: התקנה, async/await, streaming, בניית API endpoints עם Express ו-Next.js, עיבוד webhooks, דפוסי TypeScript ייחודיים, ופריסה לייצור. אחרי הפרק הזה, תוכלו לבנות שרת שמונע על ידי Claude — מקבלת HTTP request ועד תשובה חכמה.

מה יהיה לך בסוף הפרק הזה
מה תוכלו לעשות אחרי הפרק הזה
דרישות קדם
הפרויקט שלך

בפרק 3 בניתם סוכנים ב-Python — סוכן Data Pipeline שמעבד קבצים אוטומטית, סוכן Monitoring שעוקב אחרי מערכות, ולמדתם לעבוד עם Tools, Results, MCP, וטיפול בשגיאות. עכשיו אנחנו לוקחים את אותם העקרונות ומיישמים אותם ב-TypeScript — אבל עם טוויסט: הסוכנים שלכם הפעם יהיו חלק מ-Web APIs שמקבלים HTTP requests ומחזירים תשובות חכמות. בפרק 5 ניקח את הסוכנים הבודדים האלה ונרכיב מהם צוותי סוכנים — Agent Teams שעובדים יחד על משימות מורכבות.

מילון מונחים — מושגים חדשים בפרק
מונח (English) תרגום הסבר
@anthropic-ai/claude-code ה-SDK של TypeScript החבילה ב-npm שמספקת את אותן יכולות של Claude Code כספרייה מתוכנתת ב-TypeScript
ESM (ES Modules) מודולים של ES תקן ה-import/export של JavaScript המודרני. ה-SDK דורש ESM — לא CommonJS
Streaming זרימה / סטרימינג קבלת תשובה חלקית בזמן אמת במקום לחכות לתשובה מלאה — חיוני ל-UX טוב
SSE (Server-Sent Events) אירועים מצד השרת פרוטוקול HTTP שמאפשר לשרת לשלוח עדכונים לצד הלקוח בזמן אמת — פשוט מ-WebSocket
AbortController בקר ביטול מנגנון סטנדרטי ב-JavaScript לביטול פעולות אסינכרוניות — כולל ריצות סוכן
Webhook וובהוק HTTP callback — שירות חיצוני (GitHub, Stripe) שולח בקשת POST לכתובת שלכם כשמשהו קורה
Zod ספריית ולידציה ספרייה פופולרית ב-TypeScript לולידציה ופרסור של נתונים עם Type Safety מלא
tsx TypeScript Execute כלי שמריץ TypeScript ישירות בלי שלב build — npx tsx my-agent.ts
Cold Start התחלה קרה הזמן שלוקח לפונקציית Serverless להתחיל מאפס — רלוונטי ל-Lambda ו-Vercel
Idempotency אידמפוטנטיות פעולה שנותנת את אותה תוצאה גם אם מריצים אותה כמה פעמים — קריטי ל-webhooks
מתחיל 10 דקות מושג חינם

ה-TypeScript SDK — אותה עוצמה, אקוסיסטם אחר

בפרק הקודם בנינו סוכנים ב-Python. למדנו את כל העקרונות: Agent Loop, Tools, Results, MCP, טיפול בשגיאות. עכשיו אנחנו עוברים ל-TypeScript — ושואלים שאלה לגיטימית: למה בכלל צריך SDK נוסף?

התשובה פשוטה: הסוכן צריך לחיות באקוסיסטם שלכם. אם הסטאק שלכם מבוסס Node.js — שרת Express, פרויקט Next.js, פונקציות Serverless, Workers — אתם לא רוצים להוסיף Python dependency רק בשביל סוכן. ה-TypeScript SDK נותן לכם את אותה עוצמה בדיוק: Tools, Agent Loop, Context Management, MCP — אבל עם async/await טבעי, Type Safety מלא, ואינטגרציה חלקה עם Node.js.

@anthropic-ai/claude-code — זו החבילה ב-npm. גרסה v0.2.71 נכון למרץ 2026. כמו ה-Python SDK, גם כאן אנחנו מתחת ל-1.0, מה שאומר שצריך לנעול גרסה ב-package.json ולצפות לשינויים שוברים בין גרסאות minor.

ההבדלים העיקריים מ-Python SDK:

היבט Python SDK (פרק 3) TypeScript SDK (פרק 4)
חבילה pip install claude-agent-sdk npm install @anthropic-ai/claude-code
Async agent.arun() (async אופציונלי) await agent.run() (async כברירת מחדל)
Streaming for chunk in agent.stream() for await (const chunk of agent.stream())
Type Safety Type Hints (אופציונליים) Full TypeScript types (אכיפה בזמן קומפילציה)
Use Case עיקרי Data pipelines, ML workflows, scripts Web APIs, webhook processors, Next.js, Express
ביטול Custom cancellation AbortController (Web API סטנדרטי)
מסגרת החלטה: Python SDK מול TypeScript SDK
אם... אז השתמשו ב... הסיבה
הסטאק שלכם Node.js/TypeScript TypeScript SDK אין dependency חיצוני, async/await טבעי
אתם בונים Web API או webhook processor TypeScript SDK אינטגרציה טבעית עם Express/Fastify/Next.js
הסטאק שלכם Python Python SDK אין dependency חיצוני, עובד עם pandas/numpy/ML
אתם בונים Data Pipeline או ML workflow Python SDK האקוסיסטם של Python לעיבוד נתונים עשיר יותר
אתם צריכים שניהם Microservices כל שירות בשפה שלו — אל תערבבו בלי סיבה
טעות נפוצה: לבחור SDK לפי העדפה אישית

מפתחים רבים בוחרים Python כי "זה מה שמכירים" — גם כשכל הפרויקט ב-TypeScript. התוצאה: dependency על Python runtime, Docker image גדול יותר, שני מנגנוני dependency management, וקונפליקטים ב-CI. הכלל: ה-SDK צריך להתאים לסטאק, לא להעדפה. אם הפרויקט ב-Node.js — השתמשו ב-TypeScript SDK.

TypeScript בישראל — נתונים

לפי סקר Stack Overflow 2025, TypeScript הוא אחת השפות הנפוצות ביותר בקרב מפתחים. בהייטק הישראלי, Node.js/TypeScript הוא הסטאק הנפוץ ביותר ל-backend אחרי Java, לפי נתוני משרות ב-LinkedIn ישראל ו-AllJobs. חברות כמו Monday.com, Wix, Fiverr, Taboola, ו-ironSource בנויות על Node.js. המשמעות: ה-TypeScript SDK רלוונטי לרוב מפתחי ה-backend בישראל (אומדן מבוסס על ניתוח שוק העבודה הטכנולוגי, 2025-2026).

הקשר ישראלי: למה TypeScript SDK רלוונטי?

חברות סטארטאפ ישראליות רבות בונות את ה-backend שלהן עם Node.js/TypeScript — Monday.com, Wix, Fiverr, ועוד רבות. אם אתם עובדים בסטארטאפ ישראלי, סיכוי טוב שהסטאק שלכם כולל Express או Next.js. ה-TypeScript SDK מאפשר לכם להוסיף יכולות AI ישירות לשרת הקיים — בלי להרים שירות Python נפרד, בלי Docker compose מסובך, בלי תקורה תפעולית. npm install אחד והסוכן חי בתוך ה-API שלכם.

✍ עשו עכשיו 2 דקות

פתחו טרמינל ובדקו: node --version — צריך להיות 18 ומעלה. אם יש לכם nvm, הריצו nvm use 20. וודאו שיש לכם ANTHROPIC_API_KEY מוגדר: echo $ANTHROPIC_API_KEY — אם ריק, הגדירו עם export ANTHROPIC_API_KEY=sk-ant-....

מתחיל 15 דקות הקמה חינם

התקנה וסוכן ראשון ב-TypeScript

בואו נקים פרויקט מאפס ונבנה סוכן ראשון. הכל ב-5 פקודות.

שלב 1: הקמת פרויקט

mkdir my-agent-ts && cd my-agent-ts
npm init -y
npm install @anthropic-ai/claude-code
npm install -D typescript tsx @types/node

שימו לב: אנחנו מתקינים tsx כ-dev dependency. זה כלי שמריץ TypeScript ישירות — בלי שלב build. מושלם לפיתוח ופרוטוטיפינג: npx tsx my-agent.ts ומקבלים תוצאה מייד.

שלב 2: הגדרת TypeScript

צרו קובץ tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

ESM-first: ה-SDK דורש ES Modules. הוסיפו ל-package.json: "type": "module". אם הפרויקט שלכם CommonJS, תצטרכו dynamic import או מעבר ל-ESM.

טעות נפוצה: לא לנעול גרסת SDK

ה-SDK נמצא בגרסה v0.2.71 (נכון למרץ 2026) — תת-1.0. זה אומר ששינויים שוברים (breaking changes) יכולים לקרות בין גרסאות minor. תמיד: נעלו גרסה מדויקת ב-package.json"@anthropic-ai/claude-code": "0.2.71" (בלי ^ או ~). לפני שדרוג, קראו את ה-changelog ובדקו בסביבת staging. שדרוג עיוור של SDK ב-production עלול לשבור API endpoints.

שלב 3: הסוכן הראשון

צרו src/first-agent.ts:

import { Agent } from '@anthropic-ai/claude-code';

const agent = new Agent({
  model: 'claude-sonnet-4-6',
  maxTurns: 10,
  maxTokens: 4096
});

const result = await agent.run(
  'List all TypeScript files in the current directory and summarize the project structure'
);

console.log(result.text);
console.log(`Cost: $${result.cost.toFixed(4)}`);
console.log(`Tools used: ${result.toolCalls.length}`);

והריצו:

npx tsx src/first-agent.ts

מה קורה פה? ה-SDK יוצר סוכן עם Sonnet 4.6, מגביל אותו ל-10 סבבי Tools, ומגביל את אורך התשובה ל-4,096 טוקנים. הסוכן מקבל את ה-prompt, מחליט לבד אילו tools להשתמש (Read, Glob, Bash), סוקר את הקבצים, ומחזיר סיכום. בדיוק כמו ב-Python — אבל עם TypeScript types שנותנים IntelliSense מלא בעורך.

IntelliSense הוא המנצח: כשתקלידו new Agent({, VS Code יציג לכם את כל האפשרויות — model, maxTurns, maxTokens, allowedTools, disallowedTools, workingDirectory — עם תיעוד על כל שדה. בלי לפתוח דוקומנטציה.

Configuration Deep Dive

בואו נסתכל על כל אפשרויות הקונפיגורציה שה-IntelliSense יציע לכם:

פרמטר סוג ברירת מחדל הסבר
model string 'claude-sonnet-4-6' איזה מודל להשתמש — sonnet, opus, haiku
maxTurns number 10 מספר מקסימלי של סבבי tool use — מונע ריצות אינסופיות
maxTokens number 4096 אורך מקסימלי של התשובה — קצר = זול ומהיר
allowedTools string[] הכל רשימה לבנה של tools — ['Read', 'Grep'] ליצירת סוכן read-only
disallowedTools string[] [] רשימה שחורה — ['Write', 'Edit', 'Bash'] לסוכן שרק קורא
workingDirectory string cwd תיקיית העבודה של הסוכן — פעולות קבצים מתבצעות ביחס אליה
systemPrompt string undefined הוראות מערכת שנשלחות לפני ה-prompt — מגדירות התנהגות

Best practice: תמיד הגדירו maxTurns. סוכן בלי מגבלת סבבים יכול לרוץ 50+ סבבים על משימה מורכבת — מה שעולה $5-10 על ריצה בודדת. ב-API endpoint, maxTurns: 3-5 הוא טווח סביר. בסקריפט batch — maxTurns: 10-20.

ESM vs CommonJS — מה שחייבים לדעת

ה-SDK הוא ESM-first. זה אומר שהוא משתמש ב-import/export ולא ב-require(). אם הפרויקט שלכם CommonJS (רוב הפרויקטים הישנים), יש שתי אפשרויות:

  1. הוספת "type": "module" ל-package.json — הדרך הנקייה. כל הקבצים עוברים ל-ESM. צריך לשנות require() ל-import.
  2. Dynamic import — אם לא רוצים לשנות את כל הפרויקט:
    // בתוך קובץ CommonJS
    const { Agent } = await import('@anthropic-ai/claude-code');

המלצה: אם מתחילים פרויקט חדש — ESM. אם משלבים בפרויקט קיים CommonJS — dynamic import. אל תלחמו עם module system — תבחרו את הדרך הפחות כואבת.

טעות נפוצה: לשכוח type: module ב-package.json

אם אתם מקבלים SyntaxError: Cannot use import statement outside a module — הוסיפו "type": "module" ל-package.json. בלי זה, Node.js מנסה לקרוא את הקובץ כ-CommonJS ומתרסק על import statements. זו הטעות הנפוצה ביותר בהתחלה.

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

הקימו פרויקט TypeScript חדש, התקינו את ה-SDK, צרו את הסוכן הראשון והריצו עם npx tsx. וודאו שאתם רואים פלט, עלות, ומספר tool calls. שמרו את תיקיית הפרויקט — נשתמש בה לכל התרגילים בפרק.

בינוני 15 דקות מושג חינם

Async/Await ודפוסי Streaming

ב-Python, agent.run() היה סינכרוני כברירת מחדל ואפשר להשתמש ב-agent.arun() לגרסה אסינכרונית. ב-TypeScript, הכל async כברירת מחדל. agent.run() מחזיר Promise<AgentResult> — חייבים await.

run() — כשצריכים את התוצאה הסופית בלבד

const result = await agent.run('Analyze the package.json and list all dependencies');
console.log(result.text);       // התשובה הסופית
console.log(result.toolCalls);  // רשימת כל ה-tools שהופעלו
console.log(result.cost);       // עלות בדולרים
console.log(result.tokensUsed); // פירוט: input, output, thinking

זה הדפוס הפשוט. שולחים prompt, מחכים עד שהסוכן מסיים, מקבלים תוצאה מלאה. מתאים לסקריפטים, batch jobs, ולכל מקרה שלא צריך עדכונים בזמן אמת.

stream() — כשצריכים עדכונים בזמן אמת

for await (const event of agent.stream('Review the code in src/')) {
  switch (event.type) {
    case 'text':
      process.stdout.write(event.text);  // טקסט חלקי — הציגו מייד
      break;
    case 'tool_use':
      console.log(`\n🔧 Using tool: ${event.toolName}`);
      break;
    case 'tool_result':
      console.log(`✅ Tool completed: ${event.toolName}`);
      break;
    case 'done':
      console.log(`\nTotal cost: $${event.result.cost.toFixed(4)}`);
      break;
  }
}

Streaming הוא הדפוס הטבעי ב-TypeScript. כל chunk מגיע מייד ברגע שהוא מוכן. המשתמש רואה את התשובה נבנית בזמן אמת במקום לחכות 10-30 שניות לתשובה מלאה. זה ההבדל בין UX סביר ל-UX מצוין.

ביטול עם AbortController

const controller = new AbortController();

// ביטול אחרי 30 שניות
setTimeout(() => controller.abort(), 30_000);

try {
  const result = await agent.run('Complex analysis...', {
    signal: controller.signal
  });
} catch (err) {
  if (err.name === 'AbortError') {
    console.log('Agent run was cancelled');
  }
}

AbortController הוא Web API סטנדרטי שנתמך ב-Node.js מגרסה 15. תמיד השתמשו בו ב-API endpoints — אם הלקוח ניתק, אין טעם להמשיך לשרוף טוקנים.

הרצת סוכנים במקביל

const [analysis, tests, security] = await Promise.all([
  agent.run('Analyze the code structure'),
  agent.run('Run the test suite'),
  agent.run('Check for security issues')
]);

console.log('Analysis:', analysis.text);
console.log('Tests:', tests.text);
console.log('Security:', security.text);
console.log(`Total cost: $${(analysis.cost + tests.cost + security.cost).toFixed(4)}`);

Promise.all מריץ שלושה סוכנים במקביל. כל אחד עובד עצמאי, ואנחנו מחכים עד שכולם מסיימים. שלוש ריצות שאורכות 20 שניות כל אחת — ביחד לוקחות 20 שניות במקום 60. העלות זהה, אבל הזמן שליש.

מסגרת החלטה: run() מול stream()
שימוש הדפוס למה
סקריפט / cron job await agent.run() אין UI, צריכים רק את התוצאה הסופית
API שמחזיר JSON await agent.run() הלקוח מחכה ל-JSON מלא
UI שמציג תשובה בזמן אמת for await (... of agent.stream()) UX טוב = תשובה שנבנית מול העיניים
SSE endpoint for await (... of agent.stream()) כל chunk הולך ישירות ללקוח
Webhook processor await agent.run() צריכים את התוצאה הסופית כדי לפעול עליה

Continuing Conversations — המשך שיחה

בדיוק כמו ב-Python SDK, אפשר להמשיך שיחה מנקודה שהסוכן הפסיק:

// שלב 1: ניתוח ראשוני
const result1 = await agent.run('Read package.json and list all dependencies');

// שלב 2: המשך מאותה שיחה
const result2 = await agent.run(
  'Now check which of those dependencies are outdated',
  { messages: result1.messages }
);

// הסוכן "זוכר" את התוצאות של שלב 1

מתי זה שימושי? כשרוצים לפרק משימה מורכבת לשלבים, עם בקרה ביניהם. במקום prompt אחד ארוך שאומר "תנתח ואז תתקן" — מפרקים: שלב 1 מנתח, אתם בודקים את התוצאה, שלב 2 מתקן.

Agent Configuration Patterns — דפוסי קונפיגורציה

כשבונים סוכנים ב-TypeScript, יש כמה דפוסי קונפיגורציה שכדאי להכיר:

// 1. Read-Only Agent — לניתוח בלבד, בלי שינויים
const analyzer = new Agent({
  model: 'claude-sonnet-4-6',
  maxTurns: 5,
  allowedTools: ['Read', 'Grep', 'Glob']  // רק קריאה
});

// 2. Restricted Agent — רק כלים ספציפיים
const codeReviewer = new Agent({
  model: 'claude-sonnet-4-6',
  maxTurns: 10,
  disallowedTools: ['Write', 'Bash']  // הכל חוץ מכתיבה והרצת פקודות
});

// 3. Focused Agent — תיקייה ספציפית בלבד
const frontendAgent = new Agent({
  model: 'claude-sonnet-4-6',
  maxTurns: 5,
  workingDirectory: '/app/src/frontend'
});

הכלל: ככל שהסוכן חשוף פחות, כך הוא בטוח יותר ומהיר יותר. סוכן שקורא בלבד לא יכול לשבור קבצים. סוכן שמוגבל לתיקייה ספציפית לא יכול להשפיע על חלקים אחרים של הפרויקט. הגדרה צרה = פחות risks.

טעות נפוצה: לתת לסוכן API הרשאות מלאות

כשבונים סוכן שרץ מאחורי API — אל תתנו לו Bash tool. סוכן עם גישה ל-shell ב-production server יכול לעשות נזק אם prompt injection עובר דרך ה-input של המשתמש. כלל אצבע: ב-API endpoints, השתמשו ב-allowedTools עם רשימה מינימלית — רק מה שהסוכן צריך.

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

צרו סוכן read-only עם allowedTools: ['Read', 'Grep', 'Glob']. נסו לתת לו הוראה שדורשת כתיבה — ראו איך הוא מתמודד עם המגבלה. הסוכן צריך להסביר שאין לו הרשאה לבצע את הפעולה.

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

שנו את הסוכן שבניתם ל-streaming: החליפו await agent.run() ב-for await (const event of agent.stream(...)). הציגו כל event type — text, tool_use, tool_result, done. שימו לב להבדל בחוויה: במקום לחכות, אתם רואים את הסוכן עובד בזמן אמת.

בינוני 20 דקות תרגול חינם

בניית Web APIs מונעי סוכן

הנה ה-use case הנפוץ ביותר של ה-TypeScript SDK: HTTP request נכנס, סוכן מעבד, תשובה חכמה יוצאת. במקום לכתוב business logic ידני, אתם נותנים לסוכן Claude להבין את הבקשה ולייצר את התשובה.

הדפוס הבסיסי עם Express

import express from 'express';
import { Agent } from '@anthropic-ai/claude-code';

const app = express();
app.use(express.json());

app.post('/api/analyze', async (req, res) => {
  const { text, format } = req.body;

  // יוצרים סוכן חדש לכל request — חשוב!
  const agent = new Agent({
    model: 'claude-sonnet-4-6',
    maxTurns: 5,
    maxTokens: 2048
  });

  try {
    const result = await agent.run(
      `Analyze the following text and return a JSON with sentiment, key topics,
       and a one-line summary. Format: ${format || 'json'}.

       Text: ${text}`
    );

    res.json({
      analysis: JSON.parse(result.text),
      tokensUsed: result.tokensUsed,
      cost: result.cost
    });
  } catch (err) {
    res.status(500).json({ error: 'Agent failed', details: err.message });
  }
});

app.listen(3000, () => console.log('Agent API running on :3000'));

כלל ברזל: סוכן חדש לכל request. למה? כי סוכנים צוברים state — הודעות, תוצאות tools, הקשר שיחה. אם תשתפו סוכן בין requests, בקשה של משתמש אחד תשפיע על בקשה של משתמש אחר. זו באג רציני של data leakage.

Streaming Endpoint עם SSE

app.get('/api/stream', async (req, res) => {
  const prompt = req.query.prompt as string;

  // הגדרת SSE headers
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  const agent = new Agent({
    model: 'claude-sonnet-4-6',
    maxTurns: 5
  });

  for await (const event of agent.stream(prompt)) {
    if (event.type === 'text') {
      res.write(`data: ${JSON.stringify({ type: 'text', content: event.text })}\n\n`);
    }
    if (event.type === 'tool_use') {
      res.write(`data: ${JSON.stringify({ type: 'tool', name: event.toolName })}\n\n`);
    }
    if (event.type === 'done') {
      res.write(`data: ${JSON.stringify({ type: 'done', cost: event.result.cost })}\n\n`);
    }
  }

  res.end();
});

SSE (Server-Sent Events) הוא הדרך הפשוטה ביותר לשלוח עדכונים בזמן אמת ללקוח. בצד הלקוח, משתמשים ב-EventSource. פשוט יותר מ-WebSocket ומתאים מצוין ל-agent streaming כי הזרימה היא חד-כיוונית: שרת → לקוח.

מגבלות Timeout

ריצת סוכן יכולה לקחת בין 5 שניות (prompt פשוט) ל-2 דקות (משימה מורכבת עם הרבה tool calls). ב-API endpoint, אתם חייבים לקבוע timeouts בשתי רמות:

רמה הגדרה ערך מומלץ
HTTP Timeout Express / Nginx / Load Balancer 60-120 שניות
Agent Timeout AbortController + setTimeout 30-60 שניות

Agent timeout תמיד קצר מ-HTTP timeout. אם הסוכן מגיע ל-timeout, אתם יכולים להחזיר תשובה חלקית או הודעת שגיאה. אם HTTP מגיע ל-timeout — הלקוח מקבל 504 בלי הסבר.

עלויות API Agent טיפוסיות

סוכן עם Sonnet 4.6, 3-5 tool calls, prompt של 200 מילים: $0.01-0.03 לבקשה. בממוצע, 1,000 בקשות ביום = $10-30 לחודש. עם Haiku לבקשות פשוטות ו-Sonnet לבקשות מורכבות, אפשר לחתוך ב-40-60% (אומדנים מבוססים על תמחור Anthropic, מרץ 2026).

טעות נפוצה: לשתף סוכן בין requests

מפתחים שמגיעים מ-pattern של database connection pool חושבים שאפשר לעשות "agent pool" ולשתף סוכנים. אי אפשר. סוכן צובר conversation state — הודעות, tool results, context. שיתוף סוכן בין requests = data leakage בין משתמשים. תמיד: new Agent() בתוך ה-request handler.

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

התקינו Express: npm install express @types/express. צרו קובץ src/server.ts עם ה-endpoint הבסיסי מלמעלה. הריצו עם npx tsx src/server.ts ובדקו עם curl -X POST http://localhost:3000/api/analyze -H "Content-Type: application/json" -d '{"text":"This product is amazing!"}'.

🛠 תרגיל: בניית Agent-Powered API

זמן: 20 דקות | תוצר: Express API עם שלושה endpoints

  1. צרו פרויקט Express TypeScript חדש עם ה-SDK
  2. בנו endpoint POST /api/analyze — מקבל טקסט, מחזיר ניתוח sentiment + נושאים + סיכום כ-JSON
  3. בנו endpoint GET /api/stream?prompt=... — SSE streaming של תשובת הסוכן בזמן אמת
  4. בנו endpoint GET /health — בודק שה-API key תקין ושהשרת עובד
  5. הוסיפו AbortController עם timeout של 30 שניות לכל ריצת סוכן
  6. בדקו את שלושת ה-endpoints עם curl

תוצאה צפויה: שרת Express עובד שמחזיר ניתוח AI, סטרימינג בזמן אמת, ו-health check.

בינוני 20 דקות כלי חינם

אינטגרציה עם Express, Fastify ו-Next.js

ה-SDK עובד עם כל Node.js framework — אבל לכל אחד יש דפוס אינטגרציה שמתאים לארכיטקטורה שלו. בואו נראה את שלושת הפופולריים.

Express: דפוס Middleware

// middleware/claude.ts
import { Agent } from '@anthropic-ai/claude-code';

export function claudeMiddleware(config = {}) {
  return (req, res, next) => {
    req.claude = () => new Agent({
      model: 'claude-sonnet-4-6',
      maxTurns: 5,
      ...config
    });
    next();
  };
}

// שימוש ב-routes
app.use(claudeMiddleware({ maxTokens: 2048 }));

app.post('/api/summarize', async (req, res) => {
  const agent = req.claude();  // סוכן חדש לכל request
  const result = await agent.run(`Summarize: ${req.body.text}`);
  res.json({ summary: result.text });
});

הדפוס הזה מזריק factory function ל-req. כל route handler קורא ל-req.claude() ומקבל סוכן חדש עם הקונפיגורציה שהוגדרה ב-middleware. נקי ואחיד.

Fastify: דפוס Plugin

// plugins/claude.ts
import fp from 'fastify-plugin';
import { Agent } from '@anthropic-ai/claude-code';

export default fp(async (fastify) => {
  fastify.decorate('createAgent', (overrides = {}) => {
    return new Agent({
      model: 'claude-sonnet-4-6',
      maxTurns: 5,
      ...overrides
    });
  });
});

// שימוש ב-routes
fastify.post('/api/analyze', async (request, reply) => {
  const agent = fastify.createAgent({ maxTurns: 3 });
  const result = await agent.run(`Analyze: ${request.body.text}`);
  return { analysis: result.text };
});

Next.js API Routes: App Router

// app/api/analyze/route.ts
import { Agent } from '@anthropic-ai/claude-code';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const { text } = await request.json();

  const agent = new Agent({
    model: 'claude-sonnet-4-6',
    maxTurns: 5
  });

  const result = await agent.run(
    `Analyze and return JSON: ${text}`
  );

  return NextResponse.json({
    analysis: result.text,
    cost: result.cost
  });
}

// חשוב: Node.js runtime, לא Edge
export const runtime = 'nodejs';

שימו לב: ה-SDK לא עובד ב-Edge Runtime. הוא צריך Node.js APIs מלאים — file system, child processes, network. ב-Next.js, הוסיפו export const runtime = 'nodejs'; לכל route שמשתמש ב-SDK. ב-Cloudflare Workers, תצטרכו את ה-nodejs_compat flag, או להריץ את הסוכן על שרת Node.js נפרד.

Error Handling ב-Web Context

app.post('/api/analyze', async (req, res) => {
  try {
    const result = await agent.run(req.body.prompt);
    res.json({ result: result.text });
  } catch (err) {
    if (err.name === 'AbortError') {
      res.status(408).json({ error: 'Request timed out' });
    } else if (err.message?.includes('rate_limit')) {
      res.status(429).json({ error: 'Rate limited, try again later' });
    } else if (err.message?.includes('authentication')) {
      res.status(500).json({ error: 'API configuration error' });
    } else {
      res.status(500).json({ error: 'Agent execution failed' });
    }
  }
});

מפו שגיאות סוכן ל-HTTP status codes מתאימים: timeout = 408, rate limit = 429, שגיאת auth = 500, שגיאה כללית = 500. הלקוח צריך לדעת מה קרה — לא רק "something went wrong".

טיפ: הגדרו maxTurns נמוך ל-API endpoints

ב-API endpoint, הגדירו maxTurns: 3-5. סוכן שרץ 20 סבבים של tools ב-API request = timeout, עלות גבוהה, וחוויית משתמש גרועה. אם המשימה דורשת יותר מ-5 סבבים, שקלו לפרק אותה למספר requests או להשתמש בזרימה אסינכרונית (queue + webhook).

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

אם אתם עובדים עם Next.js — צרו app/api/test/route.ts עם ה-SDK. אם Express — הוסיפו את ה-middleware pattern לפרויקט שלכם. הריצו ובדקו עם curl. שימו לב ל-runtime = 'nodejs' ב-Next.js.

בינוני 20 דקות הקמה בתשלום

פריסת Agent APIs לייצור

יש לכם סוכן שעובד ב-localhost. עכשיו צריך להעלות אותו לייצור. ארבע אופציות:

1. Docker — הגישה המומלצת

# Dockerfile
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx tsc

# Runtime stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./

ENV NODE_ENV=production
EXPOSE 3000

CMD ["node", "dist/server.js"]

Multi-stage build: שלב ראשון מקמפל TypeScript, שלב שני לוקח רק את ה-JavaScript והדפנדנסיות. Image קטן יותר, בלי כלי פיתוח.

חשוב: ANTHROPIC_API_KEY מועבר כ-environment variable בזמן ריצה — לעולם לא ב-Dockerfile:

docker build -t my-agent-api .
docker run -p 3000:3000 -e ANTHROPIC_API_KEY=sk-ant-... my-agent-api

2. VPS עם PM2

# build
npx tsc

# run with PM2 (process manager)
pm2 start dist/server.js --name agent-api

# auto-restart on crash, load balancer for multi-core
pm2 start dist/server.js -i max --name agent-api

PM2 מנהל את התהליך: auto-restart על קריסות, cluster mode לניצול כל ה-cores, log management. מאחורי Nginx כ-reverse proxy. הגישה הפשוטה ביותר אם יש לכם VPS.

הקשר ישראלי: איפה לארח Agent API?

ה-API של Anthropic נמצא בארה"ב. מישראל, latency טיפוסי הוא 100-200ms לבקשה. אם הסוכן מבצע 5 tool calls — זה עוד 500-1000ms. המלצות: אם הלקוחות שלכם בישראל, שרת VPS מקומי (Kamatera, AWS il-central-1) מוריד latency ב-HTTP ושומר על GDPR/Privacy Protection Act compliance. אם הלקוחות גלובליים — AWS us-east-1 קרוב ל-Anthropic API ונותן latency מינימלי. תכניסו את המיקום לחישוב ה-timeout שלכם: agent timeout = estimated turns * 3 שניות + 500ms latency.

3. Serverless (Lambda / Vercel)

פונקציות Serverless עובדות — אבל עם הסתייגויות:

אתגר פתרון
Cold start (1-2 שניות נוספות) Provisioned concurrency / pre-initialization
Function timeout (10-300 שניות) הגדירו maxTurns: 3, timeout קצר
Memory limits 1GB+ memory בהגדרות הפונקציה
// טיפ לשיפור Serverless: אתחול מחוץ ל-handler
const agentConfig = {
  model: 'claude-sonnet-4-6',
  maxTurns: 3,
  maxTokens: 2048
};

// handler — רק יוצר agent ומריץ
export async function handler(event) {
  const agent = new Agent(agentConfig);
  const result = await agent.run(event.body);
  return { statusCode: 200, body: JSON.stringify({ text: result.text }) };
}

4. Health Check

app.get('/health', async (req, res) => {
  try {
    const agent = new Agent({ model: 'claude-sonnet-4-6', maxTurns: 1 });
    const result = await agent.run('Say "ok"');
    res.json({ status: 'healthy', model: 'claude-sonnet-4-6' });
  } catch (err) {
    res.status(500).json({ status: 'unhealthy', error: err.message });
  }
});

Health check שמוודא שה-API key תקין ושהחיבור ל-Anthropic עובד. הוסיפו אותו לכל deployment — Docker health check, load balancer health check, ו-monitoring.

Scaling — סקיילינג סוכנים

סוכנים הם stateless per-request — כל request מקבל סוכן חדש, אין shared state. זה אומר שסקיילינג אופקי עובד באופן טבעי: פשוט הוסיפו instances. צוואר הבקבוק האמיתי הוא API rate limits של Anthropic, לא המשאבים שלכם.

שכבה צוואר הבקבוק פתרון
Node.js CPU bound (נדיר — רוב הזמן ב-I/O wait) PM2 cluster mode / multiple containers
Anthropic API Rate limits (requests per minute) Queue + rate limiter + retry with backoff
Budget עלויות טוקנים Model routing + maxTurns limits + caching
שימו לב: API key לעולם לא ב-Dockerfile

שמנו ANTHROPIC_API_KEY כ-environment variable בזמן docker run. לעולם אל תכניסו API key ל-Dockerfile, ל-.env שנדחף ל-Git, או לקוד עצמו. ב-production, השתמשו ב-secrets manager (AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets) או ב-environment variables של פלטפורמת הפריסה. טעות כזו עלולה לעלות אלפי דולרים אם מישהו מוצא את ה-key.

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

צרו Dockerfile לפרויקט ה-Express שבניתם. בנו עם docker build -t my-agent .. הריצו עם docker run -p 3000:3000 -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY my-agent. בדקו עם curl שהכל עובד — גם ה-health endpoint.

מתקדם 25 דקות תרגול בתשלום

דוגמה מהשטח: API Backend Agent

בואו נבנה משהו אמיתי: שרת שמקבל שאלות בשפה טבעית ומחזיר מידע מובנה. דמיינו API פנימי בחברה — עובד שואל "כמה משתמשים נרשמו החודש?" והסוכן מתרגם את זה לשאילתת SQL, מריץ, ומחזיר תוצאות מפורמטות.

הארכיטקטורה

// Full example: Natural Language Query API
import express from 'express';
import { Agent } from '@anthropic-ai/claude-code';

const app = express();
app.use(express.json());

// system prompt שמגדיר את ההתנהגות
const SYSTEM_PROMPT = `You are a data analyst assistant.
When given a natural language question:
1. Translate it to a SQL query
2. Explain what the query does
3. Return a JSON response with:
   - sql: the SQL query
   - explanation: what it does in Hebrew
   - estimatedRows: approximate result count

IMPORTANT: Only SELECT queries. Never UPDATE, DELETE, or DROP.
Database: PostgreSQL with tables: users, orders, products, sessions.`;

app.post('/api/query', async (req, res) => {
  const { question } = req.body;
  const controller = new AbortController();
  setTimeout(() => controller.abort(), 30_000);

  try {
    const agent = new Agent({
      model: 'claude-sonnet-4-6',
      maxTurns: 3,
      maxTokens: 2048,
      systemPrompt: SYSTEM_PROMPT
    });

    const result = await agent.run(question, { signal: controller.signal });

    res.json({
      answer: JSON.parse(result.text),
      meta: {
        tokensUsed: result.tokensUsed,
        cost: result.cost,
        toolCalls: result.toolCalls.length
      }
    });
  } catch (err) {
    if (err.name === 'AbortError') {
      res.status(408).json({ error: 'Query timed out after 30 seconds' });
    } else {
      res.status(500).json({ error: err.message });
    }
  }
});

// Rate limiting per user
const requestCounts = new Map<string, number>();

app.use((req, res, next) => {
  const userId = req.headers['x-user-id'] as string || 'anonymous';
  const count = requestCounts.get(userId) || 0;
  if (count >= 50) {
    return res.status(429).json({ error: 'Rate limit: 50 queries/hour' });
  }
  requestCounts.set(userId, count + 1);
  next();
});

// Reset counts every hour
setInterval(() => requestCounts.clear(), 60 * 60 * 1000);

app.listen(3000);

שימו לב לשכבות ההגנה: system prompt שמגביל ל-SELECT בלבד, AbortController עם timeout, maxTurns: 3 שמונע ריצות ארוכות, ו-rate limiting שמגביל 50 שאילתות לשעה למשתמש.

אימות ובטיחות

ב-production, תוסיפו:

✍ עשו עכשיו 2 דקות

רשמו 3 סיכוני אבטחה שרלוונטיים לסוכן שחשוף כ-API endpoint. לכל סיכון — כתבו את ההגנה שתיישמו. זו רשימה שתלווה אתכם בכל deployment. (רמז: prompt injection, API key exposure, data leakage, resource exhaustion)

Logging ומעקב — Observability

ב-production, אתם חייבים לדעת מה הסוכן עושה. כל request צריך להיות logged — לא רק התוצאה, אלא גם ה-tools שהופעלו, הזמן שלקח, העלות, ושגיאות:

app.post('/api/query', async (req, res) => {
  const startTime = Date.now();
  const requestId = crypto.randomUUID();

  console.log(JSON.stringify({
    event: 'agent_request',
    requestId,
    prompt: req.body.question.substring(0, 100),  // Don't log full prompt
    timestamp: new Date().toISOString()
  }));

  try {
    const agent = new Agent({ model: 'claude-sonnet-4-6', maxTurns: 3 });
    const result = await agent.run(req.body.question);

    const duration = Date.now() - startTime;

    console.log(JSON.stringify({
      event: 'agent_success',
      requestId,
      duration,
      cost: result.cost,
      toolCalls: result.toolCalls.length,
      tokensUsed: result.tokensUsed
    }));

    res.json({ answer: result.text });
  } catch (err) {
    console.log(JSON.stringify({
      event: 'agent_error',
      requestId,
      error: err.message,
      duration: Date.now() - startTime
    }));

    res.status(500).json({ error: 'Failed' });
  }
});

Structured logging (JSON) מאפשר לכם לחפש ולנתח ב-Grafana, DataDog, או כל מערכת observability. שאלות שתוכלו לענות עליהן: "מה זמן התגובה הממוצע?", "כמה עלות בשעה?", "איזה endpoints הכי יקרים?", "מה ה-error rate?". בלי logging — אתם עיוורים.

Budget Protection — הגנה על התקציב

ב-production, אתם חייבים מנגנון שמונע הוצאות בלתי צפויות. מפתח API חשוף או באג ב-loop יכולים לעלות מאות דולרים בתוך שעות:

// Budget tracker — per-hour spending limit
let hourlySpend = 0;
const HOURLY_BUDGET = 50; // $50/hour max

setInterval(() => { hourlySpend = 0; }, 60 * 60 * 1000); // reset every hour

app.post('/api/analyze', async (req, res) => {
  if (hourlySpend >= HOURLY_BUDGET) {
    return res.status(429).json({
      error: 'Budget limit reached',
      message: 'Agent spending paused. Resets next hour.',
      currentSpend: hourlySpend
    });
  }

  const agent = new Agent({ model: 'claude-sonnet-4-6', maxTurns: 3 });
  const result = await agent.run(req.body.text);

  hourlySpend += result.cost;

  console.log(JSON.stringify({
    event: 'budget_update',
    hourlySpend,
    budget: HOURLY_BUDGET,
    remaining: HOURLY_BUDGET - hourlySpend
  }));

  res.json({ result: result.text, cost: result.cost });
});

שילוב עם monitoring: הוסיפו alert ב-PagerDuty/OpsGenie כש-hourlySpend מגיע ל-80% מהתקציב. כך אתם מקבלים התרעה לפני שהמערכת נעצרת. ב-production אמיתי, השתמשו ב-Redis במקום variable מקומי — כדי שהתקציב יהיה משותף בין כל ה-instances.

✍ עשו עכשיו 4 דקות

קחו את ה-Express server שבניתם והוסיפו system prompt שמגביל את הסוכן לתחום ספציפי (למשל: ניתוח טקסט בלבד, או שאלות על מוצרים). שלחו request ובדקו שהסוכן עונה רק בתחום שהגדרתם. נסו לשלוח שאלה מחוץ לתחום — הסוכן צריך לסרב.

מתקדם 20 דקות תרגול בתשלום

דוגמה מהשטח: Webhook Processor

ה-use case השני הנפוץ: שירות שמקבל webhooks ולוקח פעולה חכמה. GitHub שולח webhook כש-PR נפתח — הסוכן בודק את הקוד ומגיב. Stripe שולח webhook כשתשלום נכשל — הסוכן מנתח את ההיסטוריה של הלקוח ומכין הודעת follow-up.

GitHub PR Webhook — סוכן שבודק קוד

import express from 'express';
import crypto from 'crypto';
import { Agent } from '@anthropic-ai/claude-code';

const app = express();
app.use(express.json());

// Webhook signature verification
function verifyGitHubSignature(req: express.Request): boolean {
  const signature = req.headers['x-hub-signature-256'] as string;
  const hmac = crypto.createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET);
  const digest = 'sha256=' + hmac.update(JSON.stringify(req.body)).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}

// Idempotency tracking
const processedWebhooks = new Set<string>();

app.post('/webhook/github', async (req, res) => {
  // 1. Validate signature
  if (!verifyGitHubSignature(req)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // 2. Idempotency check
  const deliveryId = req.headers['x-github-delivery'] as string;
  if (processedWebhooks.has(deliveryId)) {
    return res.status(200).json({ status: 'already processed' });
  }
  processedWebhooks.add(deliveryId);

  // 3. Acknowledge immediately
  res.status(200).json({ status: 'processing' });

  // 4. Process asynchronously
  const { action, pull_request } = req.body;
  if (action !== 'opened' && action !== 'synchronize') return;

  const agent = new Agent({
    model: 'claude-sonnet-4-6',
    maxTurns: 10,
    systemPrompt: `You are a code reviewer. Review the PR diff and provide:
      1. Summary of changes
      2. Potential issues (bugs, security, performance)
      3. Suggestions for improvement
      Be constructive and specific. Format as Markdown.`
  });

  try {
    const result = await agent.run(
      `Review this PR: "${pull_request.title}"\nDiff: ${pull_request.diff_url}`
    );
    // Post comment back to GitHub via API
    console.log('Review posted:', result.text.substring(0, 100));
  } catch (err) {
    console.error('Webhook processing failed:', err);
  }
});

הדפוס: Receive → Validate → Ack → Process

שימו לב למבנה: acknowledge מייד (שורה 28) ואז process אסינכרונית. למה? כי שירותים חיצוניים (GitHub, Stripe) מצפים לתשובה תוך 5-10 שניות. אם לא עונים — הם שולחים שוב. ושוב. ושוב. ופתאום יש לכם 5 ריצות סוכן כפולות.

מסגרת החלטה: The Webhook Agent Pattern
שלב פעולה דוגמה
1. Receive קבלו את ה-HTTP POST app.post('/webhook/github', ...)
2. Validate וודאו חתימה / signature HMAC SHA-256 verification
3. Deduplicate בדקו אם כבר עובד Check delivery ID in Set/Redis
4. Ack החזירו 200 מייד res.status(200).json({status: 'processing'})
5. Process הריצו סוכן אסינכרונית agent.run(prompt) בלי await ב-handler
6. Action פעלו על התוצאה Post GitHub comment, send email
7. Log תעדו הכל Delivery ID, result, cost, duration

Stripe Webhook — סוכן שמטפל בתשלומים כושלים

דוגמה שנייה מעולם האמיתי — חברת SaaS ישראלית שמוכרת מנויים. Stripe שולח webhook כשתשלום נכשל. הסוכן מנתח את ההיסטוריה של הלקוח ומכין הודעת follow-up מותאמת:

app.post('/webhook/stripe', async (req, res) => {
  // Verify Stripe signature
  const sig = req.headers['stripe-signature'] as string;
  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
  } catch (err) {
    return res.status(400).json({ error: 'Invalid signature' });
  }

  res.status(200).json({ received: true });

  if (event.type === 'invoice.payment_failed') {
    const invoice = event.data.object;
    const agent = new Agent({
      model: 'claude-sonnet-4-6',
      maxTurns: 5,
      systemPrompt: `You are a customer success assistant for an Israeli SaaS company.
        Given a failed payment event, draft a personalized follow-up email in Hebrew.
        Be empathetic, offer help, suggest updating payment method.
        Include the amount in ILS (use current exchange rate ~3.6).`
    });

    const result = await agent.run(
      `Payment failed for customer ${invoice.customer_email}.
       Amount: $${(invoice.amount_due / 100).toFixed(2)}.
       Attempt: ${invoice.attempt_count}. Plan: ${invoice.lines.data[0]?.description}.
       Draft a follow-up email.`
    );

    // Send email via your email service
    await sendEmail(invoice.customer_email, 'בנוגע לחשבון שלך', result.text);
  }
});

שימו לב לפרטים: הסכום מומר לש"ח (בשער ~3.6 נכון למרץ 2026), ההודעה בעברית, הטון אמפתי — לא "התשלום נכשל" אלא "נשמח לעזור לעדכן את אמצעי התשלום". הסוכן מתאים את המסר למספר הניסיון — ניסיון ראשון = הודעה עדינה, ניסיון שלישי = הודעה דחופה יותר.

הקשר ישראלי: Webhooks ב-SaaS ישראלי

חברות SaaS ישראליות רבות משתמשות ב-Stripe לתשלומים בינלאומיים, אבל גם ב-Tranzila, CardCom, או PayMe לתשלומים מקומיים בש"ח. כל אחד מהם שולח webhooks בפורמט שונה. הדפוס הזה — Receive, Validate, Ack, Process — עובד לכולם. פשוט החליפו את ה-signature verification לפי התיעוד של כל ספק. טיפ: אם אתם עובדים עם חשבונית ירוקה (Green Invoice), הם גם שולחים webhooks על מצב חשבוניות — סוכן יכול לנתח דוחות כספיים אוטומטית.

עיבוד בנפח גבוה: Queue Pattern

אם מגיעים הרבה webhooks (10+ בדקה), אל תריצו סוכנים ישירות. דחפו לתור (queue):

// במקום לעבד ישירות:
app.post('/webhook/github', async (req, res) => {
  res.status(200).json({ status: 'queued' });

  // Push to queue instead of processing immediately
  await redisClient.lPush('webhook-queue', JSON.stringify({
    deliveryId: req.headers['x-github-delivery'],
    payload: req.body,
    receivedAt: Date.now()
  }));
});

// Worker process (separate file)
while (true) {
  const item = await redisClient.brPop('webhook-queue', 0);
  const { payload } = JSON.parse(item.element);
  const agent = new Agent({ model: 'claude-sonnet-4-6', maxTurns: 10 });
  await agent.run(`Review PR: ${payload.pull_request.title}`);
}

Queue pattern מפריד בין קבלת webhook לעיבוד. היתרונות: שליטה ב-concurrency (עובד אחד בכל רגע), ניסיון חוזר על כישלון, ואין אובדן webhooks גם אם השרת נפל.

טעות נפוצה: לא לבדוק idempotency

Webhooks יכולים להגיע כמה פעמים. GitHub שולח שוב אם לא קיבל 200 תוך 10 שניות. Stripe שולח שוב אם ה-endpoint החזיר שגיאה. אם לא בודקים delivery ID — אתם מריצים סוכן כפול, מבזבזים טוקנים, ועלולים ליצור תגובות כפולות. תמיד: שמרו Set של delivery IDs שכבר עובדו.

🛠 תרגיל: בניית Webhook Processor

זמן: 25 דקות | תוצר: שרת Express שמקבל webhooks ומעבד עם סוכן

  1. צרו endpoint POST /webhook/github שמקבל JSON payload
  2. הוסיפו signature verification (אפשר לדמות עם secret קבוע לתרגול)
  3. הוסיפו idempotency check עם Set
  4. האזינו רק לאירוע pull_request.opened
  5. הריצו סוכן שמנתח את שם ה-PR והתיאור ומחזיר סיכום + risk assessment
  6. הדפיסו את התוצאה ל-console (בייצור — שלחו ל-GitHub API כ-comment)
  7. בדקו עם curl -X POST -H "Content-Type: application/json" -H "x-github-delivery: test-123" -d '{"action":"opened","pull_request":{"title":"Add user auth","body":"Implements JWT authentication"}}' http://localhost:3000/webhook/github

תוצאה צפויה: שרת שמקבל webhook, מוודא signature, בודק כפילויות, ומחזיר ניתוח AI.

מתקדם 15 דקות מושג חינם

דפוסי TypeScript ייחודיים

עד עכשיו עבדנו עם דפוסים שקיימים גם ב-Python. עכשיו בואו נראה דברים שרק TypeScript יכול לעשות — Type Safety, Zod validation, ו-compile-time checks שמונעים באגים בזמן כתיבת הקוד.

Zod — וואלידציית פלט סוכן

הבעיה: סוכן מחזיר טקסט חופשי. גם אם ביקשתם JSON — אין ערובה שהוא תקין, שהשדות קיימים, שהסוגים נכונים. Zod פותר את זה.

import { z } from 'zod';
import { Agent } from '@anthropic-ai/claude-code';

// הגדרת הסכמה הצפויה
const AnalysisSchema = z.object({
  sentiment: z.enum(['positive', 'negative', 'neutral']),
  confidence: z.number().min(0).max(1),
  topics: z.array(z.string()).min(1).max(10),
  summary: z.string().max(200)
});

type Analysis = z.infer<typeof AnalysisSchema>;

async function analyzeText(text: string): Promise<Analysis> {
  const agent = new Agent({
    model: 'claude-sonnet-4-6',
    maxTurns: 3
  });

  const result = await agent.run(
    `Analyze this text and return ONLY valid JSON matching this schema:
     { sentiment: "positive"|"negative"|"neutral", confidence: 0-1,
       topics: string[], summary: string (max 200 chars) }

     Text: ${text}`
  );

  // Zod validates AND gives TypeScript types
  const analysis = AnalysisSchema.parse(JSON.parse(result.text));
  return analysis;  // TypeScript knows this is Analysis type
}

מה Zod נותן לכם: וואלידציה בזמן ריצה (אם הסוכן החזיר JSON לא תקין — Zod זורק שגיאה ברורה) + Type Safety בזמן קומפילציה (TypeScript יודע שה-analysis הוא בדיוק Analysis). שני עולמות בפקודה אחת.

Type-Safe Prompt Templates

// Type-safe prompt builder
interface PromptConfig {
  task: 'analyze' | 'summarize' | 'translate';
  inputText: string;
  outputFormat: 'json' | 'markdown' | 'plain';
  maxLength?: number;
}

function buildPrompt(config: PromptConfig): string {
  const formatInstructions = {
    json: 'Return valid JSON only',
    markdown: 'Format as Markdown',
    plain: 'Return plain text'
  };

  return `Task: ${config.task}
Format: ${formatInstructions[config.outputFormat]}
${config.maxLength ? `Max length: ${config.maxLength} characters` : ''}

Input: ${config.inputText}`;
}

// TypeScript catches errors at compile time:
buildPrompt({
  task: 'analyze',           // OK
  inputText: 'Hello world',  // OK
  outputFormat: 'json'       // OK
});

// buildPrompt({ task: 'dance' }) // ERROR: 'dance' is not assignable

בדיקות עם Vitest

import { describe, it, expect, vi } from 'vitest';
import { Agent } from '@anthropic-ai/claude-code';

// Mock the Agent
vi.mock('@anthropic-ai/claude-code', () => ({
  Agent: vi.fn().mockImplementation(() => ({
    run: vi.fn().mockResolvedValue({
      text: '{"sentiment": "positive", "confidence": 0.95}',
      cost: 0.001,
      toolCalls: [],
      tokensUsed: { input: 100, output: 50, thinking: 200 }
    })
  }))
}));

describe('analyzeText', () => {
  it('should return valid Analysis', async () => {
    const result = await analyzeText('Great product!');
    expect(result.sentiment).toBe('positive');
    expect(result.confidence).toBeGreaterThan(0);
  });
});

הכלל: Mock tests לשכבת הלוגיקה (הרבה, מהירים, חינם). Integration tests עם API אמיתי — רק כשבאמת צריך לוודא שהסוכן מתנהג נכון. כל ריצת integration test עולה טוקנים.

Monorepo Setup

אם יש לכם monorepo (turborepo, nx, pnpm workspaces) — שתפו agent config בין packages:

// packages/agent-config/index.ts
export const defaultAgentConfig = {
  model: 'claude-sonnet-4-6' as const,
  maxTurns: 5,
  maxTokens: 2048
};

// packages/api/src/routes.ts
import { defaultAgentConfig } from '@myorg/agent-config';
const agent = new Agent({ ...defaultAgentConfig, maxTurns: 3 });

Error Handling מותאם ל-TypeScript

TypeScript מאפשר error handling מדויק יותר מ-Python, בזכות Union Types ו-type narrowing:

type AgentResult =
  | { success: true; data: Analysis; cost: number }
  | { success: false; error: 'timeout' | 'rate_limit' | 'invalid_output' | 'unknown'; message: string };

async function safeAnalyze(text: string): Promise<AgentResult> {
  try {
    const agent = new Agent({ model: 'claude-sonnet-4-6', maxTurns: 3 });
    const result = await agent.run(`Analyze: ${text}`);
    const data = AnalysisSchema.parse(JSON.parse(result.text));
    return { success: true, data, cost: result.cost };
  } catch (err) {
    if (err.name === 'AbortError') {
      return { success: false, error: 'timeout', message: 'Agent timed out' };
    }
    if (err.message?.includes('rate_limit')) {
      return { success: false, error: 'rate_limit', message: 'Please retry later' };
    }
    if (err instanceof z.ZodError) {
      return { success: false, error: 'invalid_output', message: err.message };
    }
    return { success: false, error: 'unknown', message: String(err) };
  }
}

// Usage with type narrowing:
const result = await safeAnalyze('Great product!');
if (result.success) {
  console.log(result.data.sentiment);  // TypeScript knows 'data' exists
} else {
  console.log(result.error);           // TypeScript knows 'error' exists
}

הדפוס הזה — Result Type — פופולרי ב-TypeScript כי הוא מכריח אתכם לטפל בשגיאות. TypeScript לא ייתן לכם לגשת ל-result.data בלי לבדוק קודם ש-result.success === true. שגיאות שב-Python מתגלות רק בזמן ריצה, כאן נתפסות בזמן קומפילציה.

TypeScript strict mode = פחות באגים

הפעילו "strict": true ב-tsconfig.json. ה-SDK מגדיר types מקיפים — strict mode תופס שגיאות קונפיגורציה בזמן קומפילציה. למשל: model: 'claude-sonnnet-4' (שגיאת כתיב) — TypeScript יתפוס את זה. ב-Python, תגלו רק בזמן ריצה.

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

התקינו Zod: npm install zod. צרו סכמה עם z.object() שמגדירה את המבנה שאתם מצפים מהסוכן. הריצו סוכן שמחזיר JSON ועבירו דרך Schema.parse(). ראו מה קורה כשהסוכן מחזיר JSON לא תקין — הודעת השגיאה של Zod מדויקת ומועילה.

מתקדם 15 דקות אסטרטגיה בתשלום

אופטימיזציית ביצועים

סוכן שעובד ב-development לא בהכרח מספיק טוב ל-production. ב-production אתם צריכים לאזן בין שלושה מימדים: מהירות (כמה מהר הלקוח מקבל תשובה), עלות (כמה טוקנים שורפים), ו-איכות (כמה טובה התשובה).

Model Routing — בחירת מודל דינמית

function chooseModel(request: RequestData): string {
  // Simple requests -> Haiku (cheap, fast)
  if (request.text.length < 100 && request.type === 'classify') {
    return 'claude-haiku-3';
  }
  // Medium complexity -> Sonnet (balanced)
  if (request.type === 'analyze' || request.type === 'summarize') {
    return 'claude-sonnet-4-6';
  }
  // Critical / complex -> Opus (highest quality)
  return 'claude-opus-4-6';
}

app.post('/api/process', async (req, res) => {
  const model = chooseModel(req.body);
  const agent = new Agent({ model, maxTurns: 5 });
  // ...
});

Model routing חוסך 40-60% בעלויות. 70% מהבקשות טיפוסיות הן פשוטות (סיווג, סיכום קצר, תרגום) — Haiku מספיק. 25% הן בינוניות — Sonnet. רק 5% דורשות Opus. אם אתם מריצים הכל על Sonnet, אתם משלמים פי 3 על 70% מהבקשות (אומדנים מבוססים על ניתוח traffic טיפוסי של API endpoints).

Prompt Caching

Anthropic API תומך ב-prompt caching אוטומטי. אם אותו system prompt חוזר על עצמו (וזה קורה בכל request!) — ה-SDK מטמיע caching ברמת ה-API. אתם לא צריכים לעשות כלום — זה קורה אוטומטית. החיסכון: ~90% על input tokens של system prompt חוזר.

Context Window Management

// DON'T: Unlimited turns
const agent = new Agent({ maxTurns: 50 });  // Expensive!

// DO: Limit based on use case
const agent = new Agent({
  maxTurns: 3,       // API endpoint: fast, cheap
  maxTokens: 1024    // Short responses save money
});

// ADVANCED: Dynamic limits
const maxTurns = isComplexQuery(req.body) ? 8 : 3;
const agent = new Agent({ maxTurns });

Response Caching

שאילתות נפוצות? תשמרו את התשובה ב-cache. אין טעם להריץ סוכן כל פעם שמישהו שואל "What is your return policy?" — התשובה זהה.

import { createHash } from 'crypto';

const responseCache = new Map<string, { text: string; cachedAt: number }>();
const CACHE_TTL = 60 * 60 * 1000; // 1 hour

function getCacheKey(prompt: string): string {
  return createHash('sha256').update(prompt.toLowerCase().trim()).digest('hex');
}

app.post('/api/query', async (req, res) => {
  const key = getCacheKey(req.body.prompt);
  const cached = responseCache.get(key);

  if (cached && Date.now() - cached.cachedAt < CACHE_TTL) {
    return res.json({ text: cached.text, cached: true, cost: 0 });
  }

  const agent = new Agent({ model: 'claude-sonnet-4-6', maxTurns: 3 });
  const result = await agent.run(req.body.prompt);

  responseCache.set(key, { text: result.text, cachedAt: Date.now() });
  res.json({ text: result.text, cached: false, cost: result.cost });
});

ב-production, החליפו את ה-Map ב-Redis — הוא שורד restart ומשותף בין instances. Cache hit rate של 20-30% על שאלות חוזרות = חיסכון משמעותי בעלויות ובזמן תגובה (0ms במקום 5-10 שניות).

Connection Reuse

ה-SDK מנהל HTTP connection pool פנימית. אל תיצרו Agent instances מיותרים — כל instance הוא קצת overhead. Config object אפשר לשתף; Agent instance — לא (בגלל state).

Streaming = UX טוב יותר בזמן חכיה זהה

עם run(), המשתמש מחכה 10 שניות ואז מקבל תשובה שלמה. עם stream(), המשתמש רואה את המילה הראשונה אחרי 500ms. הזמן הכולל זהה — אבל ה-UX שונה לחלוטין. Time to First Byte הוא המטריקה שמרגישה למשתמש.

משולש הביצועים
אופטימיזציה חיסכון משוער המחיר
Model routing (Haiku/Sonnet/Opus) 40-60% בעלויות מורכבות routing logic
maxTurns נמוך (3-5) 30-50% בזמן תשובות פחות עמוקות
maxTokens מוגבל 20-40% בעלויות תשובות קצרות יותר
Prompt caching (אוטומטי) ~90% על system prompt ללא מחיר — קורה אוטומטית
Streaming Time to First Byte: 500ms במקום 10s קצת יותר קוד בצד הלקוח

אומדנים מבוססים על תמחור Anthropic ודפוסי שימוש טיפוסיים, מרץ 2026.

🛠 תרגיל: Agent API עם אופטימיזציית ביצועים

זמן: 25 דקות | תוצר: API endpoint מאופטמז לייצור

  1. קחו את ה-Express server שבניתם והוסיפו chooseModel() שבוחר מודל לפי מורכבות הבקשה
  2. הגדירו maxTurns ו-maxTokens שונים לפי סוג הבקשה
  3. הוסיפו logging שמתעד עלות, זמן, ומודל לכל request
  4. הוסיפו SSE streaming endpoint עם model routing
  5. שלחו 5 בקשות שונות (קצרה, בינונית, מורכבת) ובדקו שכל אחת מקבלת את המודל המתאים
  6. סכמו את העלויות — חשבו כמה הייתם משלמים אם הכל היה רץ על Opus

תוצאה צפויה: API שמנתב בקשות למודל הנכון, עם logs שמראים חיסכון בעלויות.

🛠 תרגיל מסכם: Production-Ready TypeScript Agent

זמן: 30 דקות | תוצר: אפליקציית TypeScript מלאה מוכנה לייצור

  1. בחרו use case: API Backend Agent או Webhook Processor
  2. בנו Express/Fastify server עם לפחות 2 endpoints
  3. הוסיפו error handling מלא (timeout, rate limit, auth errors)
  4. הוסיפו Zod validation על פלט הסוכן
  5. הוסיפו health check endpoint
  6. צרו Dockerfile עם multi-stage build
  7. בנו Docker image והריצו — בדקו שהכל עובד
  8. כתבו לפחות 2 unit tests עם Vitest

תוצאה צפויה: אפליקציה שעובדת ב-Docker, עם error handling, validation, health check, ובדיקות.

סיכום מעשי: Python SDK מול TypeScript SDK

לאחר שבניתם סוכנים בשתי השפות, הנה ההשוואה המעשית שתעזור לכם לבחור בפרויקטים עתידיים:

קריטריון Python SDK (פרק 3) TypeScript SDK (פרק 4)
סוכן מינימלי 6 שורות 8 שורות (כולל types)
Web API Flask/FastAPI — אפשרי אבל לא טבעי Express/Next.js — אינטגרציה מושלמת
Data Processing pandas, numpy, ML — אקוסיסטם עשיר אפשרי אבל פחות ספריות
Type Safety Type hints אופציונליים Full types + Zod runtime validation
Streaming for chunk in agent.stream() for await (const chunk of agent.stream())
Testing pytest + mock Vitest/Jest + vi.mock
Deployment pip install, virtualenv, Gunicorn npm install, Docker, PM2
Serverless AWS Lambda (Python runtime) Lambda/Vercel/Netlify (Node.js runtime)

כלל האצבע: אם יש לכם פרויקט חדש ואתם בוחרים שפה — TypeScript לכל דבר שקשור ל-web (APIs, webhooks, real-time, frontend-backend), Python לכל דבר שקשור ל-data (pipelines, ML, ניתוח, scripts). אם הפרויקט כבר קיים — השתמשו בשפה של הפרויקט.

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

כתבו "מסמך השוואה אישי" — קובץ קצר שמסכם את ההבדלים שגיליתם בפועל בין Python SDK (פרק 3) ל-TypeScript SDK (פרק זה). מה היה קל יותר? מה היה מסובך יותר? איפה הרגשתם יותר בבית? מה תשתמשו בפרויקט הבא? שמרו את הקובץ — זה חלק מה-deliverables.

שגרת עבודה — TypeScript Agent SDK

בנוסף לשגרה מפרקים קודמים — פרק 1 (headless mode ו-CLI automation), פרק 2 (GitHub Actions CI/CD), ופרק 3 (Python SDK, testing, error handling):

תדירות משימה פרטים
יומי בדקו API logs שגיאות, timeouts, עלויות חריגות? טפלו מייד
יומי בדקו health endpoint /health מחזיר 200? API key תקין? Connection עובד?
שבועי ניתוח עלויות לפי endpoint איזה endpoint הכי יקר? אפשר להוריד maxTurns? להחליף מודל?
שבועי עדכון SDK version בדקו npm outdated @anthropic-ai/claude-code — עדכנו אחרי בדיקה
שבועי Review webhook processing webhooks שנכשלו? כפילויות? שיפור system prompts?
חודשי סקירת model routing מודלים חדשים זמינים? Haiku חדש? עדכנו את chooseModel()
חודשי Performance benchmarks זמני תגובה, throughput, עלות ממוצעת. השוו לחודש קודם
🎯 אם אתם עושים רק דבר אחד מהפרק הזה

בנו Express endpoint אחד שמקבל טקסט ומחזיר ניתוח AI כ-JSON — עם AbortController ו-timeout. 15 דקות: npm install @anthropic-ai/claude-code express, 30 שורות TypeScript, npx tsx server.ts. מהרגע הזה יש לכם API שמונע על ידי Claude. כל שיפור נוסף — streaming, webhooks, Docker, Zod — הוא בונוס שנבנה על הבסיס הזה.

✅ בדוק את עצמך — 5 שאלות

ענו על לפחות 4 מתוך 5 כדי לעבור:

  1. למה צריך ליצור סוכן חדש (new Agent()) לכל HTTP request, ולא לשתף סוכן אחד בין requests?
    (רמז: state, conversation history, data leakage בין משתמשים)
  2. מה ההבדל בין run() ל-stream(), ומתי כל אחד עדיף?
    (רמז: UX, Time to First Byte, batch processing מול ממשק אינטראקטיבי)
  3. למה webhook processor חייב להחזיר 200 מייד ולעבד אסינכרונית — ומה קורה בלי idempotency check?
    (רמז: timeout של השולח, retries, עיבוד כפול, עלויות מיותרות)
  4. איך Zod עוזר בבניית סוכנים ב-TypeScript, ומה הוא נותן שטיפוסי TypeScript רגילים לא?
    (רמז: compile-time vs runtime, פלט סוכן הוא string שצריך validation)
  5. איך model routing חוסך עלויות, ומה הסיכון בלנתב בקשה מורכבת למודל זול?
    (רמז: Haiku מול Opus, quality degradation, שגיאות בפלט)
סיכום הפרק

בפרק הזה עברנו מ-Python ל-TypeScript — לא רק שפה אחרת, אלא שימוש אחר. אם ב-Python SDK בנינו סוכנים שרצים בשרת כ-scripts ו-data pipelines, כאן בנינו סוכנים שחיים בתוך Web APIs. התובנה המרכזית: הכוח של ה-TypeScript SDK הוא האינטגרציה הטבעית עם האקוסיסטם — Express endpoint שמחזיר ניתוח AI, webhook processor שמגיב בזמן אמת, Next.js route שמשתמש ב-Claude כ-backend engine. בשילוב עם TypeScript types, Zod validation, ו-streaming — מקבלים API שהוא גם חכם וגם בטוח מבחינת types. עכשיו שיש לנו סוכנים בודדים חזקים בשתי שפות, בפרק הבא נעבור לצוותי סוכנים — Agent Teams שמשתפים פעולה על משימות שגדולות מדי לסוכן יחיד.

☑ צ'קליסט השלמת הפרק