- פרויקט TypeScript מוגדר ומותקן עם
@anthropic-ai/claude-code— מוכן לפיתוח סוכנים - סוכן TypeScript בסיסי שרץ עם
npx tsx— ללא שלב build - Express API endpoint שמונע על ידי Claude — מקבל בקשה ומחזיר תשובה חכמה
- Streaming endpoint עם SSE שמציג תשובות בזמן אמת בצד הלקוח
- Webhook processor שמקבל webhook מ-GitHub/Stripe ומפעיל סוכן
- Dockerfile מוכן לפריסה — multi-stage build עם TypeScript
- סכמת Zod לוואלידציית פלט סוכן — Type-Safe Agent Responses
- מסמך השוואה אישי: Python SDK מול TypeScript SDK — מתי להשתמש במה
- תוכלו להתקין את ה-TypeScript SDK ולבנות סוכן פונקציונלי מאפס ב-TypeScript
- תוכלו לבנות API endpoint שמשתמש ב-Claude כ-backend engine עם Express או Next.js
- תוכלו להחליט מתי להשתמש ב-
run()(תשובה מלאה) מולstream()(תשובה בזמן אמת) - תוכלו לעצב webhook processor שמקבל אירועים ומפעיל סוכנים חכמים
- תוכלו לפרוס סוכן TypeScript לייצור — Docker, VPS, או Serverless
- פרקים קודמים: פרק 3 (Agent SDK — Python) — הבנת Agent Loop, Tools, Results, MCP, ו-Error Handling
- כלים נדרשים: Node.js 18+ מותקן, מפתח ANTHROPIC_API_KEY, עורך קוד (VS Code מומלץ), Claude Code v2.1.81+
- ידע נדרש: בסיס ב-TypeScript או JavaScript, היכרות עם async/await ו-Promises, הבנת HTTP ו-REST APIs
- זמן משוער: 4-5 שעות | עלות משוערת: $5-15 (טוקנים לתרגילים)
בפרק 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 |
ה-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 סטנדרטי) |
| אם... | אז השתמשו ב... | הסיבה |
|---|---|---|
| הסטאק שלכם 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 | כל שירות בשפה שלו — אל תערבבו בלי סיבה |
מפתחים רבים בוחרים Python כי "זה מה שמכירים" — גם כשכל הפרויקט ב-TypeScript. התוצאה: dependency על Python runtime, Docker image גדול יותר, שני מנגנוני dependency management, וקונפליקטים ב-CI. הכלל: ה-SDK צריך להתאים לסטאק, לא להעדפה. אם הפרויקט ב-Node.js — השתמשו ב-TypeScript SDK.
לפי סקר Stack Overflow 2025, TypeScript הוא אחת השפות הנפוצות ביותר בקרב מפתחים. בהייטק הישראלי, Node.js/TypeScript הוא הסטאק הנפוץ ביותר ל-backend אחרי Java, לפי נתוני משרות ב-LinkedIn ישראל ו-AllJobs. חברות כמו Monday.com, Wix, Fiverr, Taboola, ו-ironSource בנויות על Node.js. המשמעות: ה-TypeScript SDK רלוונטי לרוב מפתחי ה-backend בישראל (אומדן מבוסס על ניתוח שוק העבודה הטכנולוגי, 2025-2026).
חברות סטארטאפ ישראליות רבות בונות את ה-backend שלהן עם Node.js/TypeScript — Monday.com, Wix, Fiverr, ועוד רבות. אם אתם עובדים בסטארטאפ ישראלי, סיכוי טוב שהסטאק שלכם כולל Express או Next.js. ה-TypeScript SDK מאפשר לכם להוסיף יכולות AI ישירות לשרת הקיים — בלי להרים שירות Python נפרד, בלי Docker compose מסובך, בלי תקורה תפעולית. npm install אחד והסוכן חי בתוך ה-API שלכם.
פתחו טרמינל ובדקו: node --version — צריך להיות 18 ומעלה. אם יש לכם nvm, הריצו nvm use 20. וודאו שיש לכם ANTHROPIC_API_KEY מוגדר: echo $ANTHROPIC_API_KEY — אם ריק, הגדירו עם export ANTHROPIC_API_KEY=sk-ant-....
התקנה וסוכן ראשון ב-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 נמצא בגרסה 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 (רוב הפרויקטים הישנים), יש שתי אפשרויות:
- הוספת
"type": "module"ל-package.json — הדרך הנקייה. כל הקבצים עוברים ל-ESM. צריך לשנותrequire()ל-import. - Dynamic import — אם לא רוצים לשנות את כל הפרויקט:
// בתוך קובץ CommonJS const { Agent } = await import('@anthropic-ai/claude-code');
המלצה: אם מתחילים פרויקט חדש — ESM. אם משלבים בפרויקט קיים CommonJS — dynamic import. אל תלחמו עם module system — תבחרו את הדרך הפחות כואבת.
אם אתם מקבלים SyntaxError: Cannot use import statement outside a module — הוסיפו "type": "module" ל-package.json. בלי זה, Node.js מנסה לקרוא את הקובץ כ-CommonJS ומתרסק על import statements. זו הטעות הנפוצה ביותר בהתחלה.
הקימו פרויקט TypeScript חדש, התקינו את ה-SDK, צרו את הסוכן הראשון והריצו עם npx tsx. וודאו שאתם רואים פלט, עלות, ומספר tool calls. שמרו את תיקיית הפרויקט — נשתמש בה לכל התרגילים בפרק.
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. העלות זהה, אבל הזמן שליש.
| שימוש | הדפוס | למה |
|---|---|---|
| סקריפט / 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 — אל תתנו לו Bash tool. סוכן עם גישה ל-shell ב-production server יכול לעשות נזק אם prompt injection עובר דרך ה-input של המשתמש. כלל אצבע: ב-API endpoints, השתמשו ב-allowedTools עם רשימה מינימלית — רק מה שהסוכן צריך.
צרו סוכן read-only עם allowedTools: ['Read', 'Grep', 'Glob']. נסו לתת לו הוראה שדורשת כתיבה — ראו איך הוא מתמודד עם המגבלה. הסוכן צריך להסביר שאין לו הרשאה לבצע את הפעולה.
שנו את הסוכן שבניתם ל-streaming: החליפו await agent.run() ב-for await (const event of agent.stream(...)). הציגו כל event type — text, tool_use, tool_result, done. שימו לב להבדל בחוויה: במקום לחכות, אתם רואים את הסוכן עובד בזמן אמת.
בניית 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 בלי הסבר.
סוכן עם Sonnet 4.6, 3-5 tool calls, prompt של 200 מילים: $0.01-0.03 לבקשה. בממוצע, 1,000 בקשות ביום = $10-30 לחודש. עם Haiku לבקשות פשוטות ו-Sonnet לבקשות מורכבות, אפשר לחתוך ב-40-60% (אומדנים מבוססים על תמחור Anthropic, מרץ 2026).
מפתחים שמגיעים מ-pattern של database connection pool חושבים שאפשר לעשות "agent pool" ולשתף סוכנים. אי אפשר. סוכן צובר conversation state — הודעות, tool results, context. שיתוף סוכן בין requests = data leakage בין משתמשים. תמיד: new Agent() בתוך ה-request handler.
התקינו 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!"}'.
זמן: 20 דקות | תוצר: Express API עם שלושה endpoints
- צרו פרויקט Express TypeScript חדש עם ה-SDK
- בנו endpoint
POST /api/analyze— מקבל טקסט, מחזיר ניתוח sentiment + נושאים + סיכום כ-JSON - בנו endpoint
GET /api/stream?prompt=...— SSE streaming של תשובת הסוכן בזמן אמת - בנו endpoint
GET /health— בודק שה-API key תקין ושהשרת עובד - הוסיפו
AbortControllerעם timeout של 30 שניות לכל ריצת סוכן - בדקו את שלושת ה-endpoints עם
curl
תוצאה צפויה: שרת Express עובד שמחזיר ניתוח AI, סטרימינג בזמן אמת, ו-health check.
אינטגרציה עם 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".
ב-API endpoint, הגדירו maxTurns: 3-5. סוכן שרץ 20 סבבים של tools ב-API request = timeout, עלות גבוהה, וחוויית משתמש גרועה. אם המשימה דורשת יותר מ-5 סבבים, שקלו לפרק אותה למספר requests או להשתמש בזרימה אסינכרונית (queue + webhook).
אם אתם עובדים עם Next.js — צרו app/api/test/route.ts עם ה-SDK. אם Express — הוסיפו את ה-middleware pattern לפרויקט שלכם. הריצו ובדקו עם curl. שימו לב ל-runtime = 'nodejs' ב-Next.js.
פריסת 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.
ה-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 |
שמנו 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.
צרו Dockerfile לפרויקט ה-Express שבניתם. בנו עם docker build -t my-agent .. הריצו עם docker run -p 3000:3000 -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY my-agent. בדקו עם curl שהכל עובד — גם ה-health endpoint.
דוגמה מהשטח: 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, תוסיפו:
- JWT Authentication: וודאו שרק משתמשים מורשים שולחים queries
- Input validation: בדקו שה-question לא ארוך מדי (500 תווים) ולא מכיל prompt injection
- Output validation: וודאו שה-JSON שחוזר תקין לפני שליחה ללקוח
- Logging: תעדו כל query, תוצאה, עלות, וזמן ריצה — הביקורת שלכם
- Caching: שאילתות נפוצות? Cache ב-Redis. חוסך טוקנים ומהיר יותר
- Rate limiting per-user: הגבילו כל משתמש ל-50-100 queries בשעה. משתמש שמנסה prompt injection ייחסם מהר
רשמו 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.
קחו את ה-Express server שבניתם והוסיפו system prompt שמגביל את הסוכן לתחום ספציפי (למשל: ניתוח טקסט בלבד, או שאלות על מוצרים). שלחו request ובדקו שהסוכן עונה רק בתחום שהגדרתם. נסו לשלוח שאלה מחוץ לתחום — הסוכן צריך לסרב.
דוגמה מהשטח: 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 ריצות סוכן כפולות.
| שלב | פעולה | דוגמה |
|---|---|---|
| 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), ההודעה בעברית, הטון אמפתי — לא "התשלום נכשל" אלא "נשמח לעזור לעדכן את אמצעי התשלום". הסוכן מתאים את המסר למספר הניסיון — ניסיון ראשון = הודעה עדינה, ניסיון שלישי = הודעה דחופה יותר.
חברות 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 גם אם השרת נפל.
Webhooks יכולים להגיע כמה פעמים. GitHub שולח שוב אם לא קיבל 200 תוך 10 שניות. Stripe שולח שוב אם ה-endpoint החזיר שגיאה. אם לא בודקים delivery ID — אתם מריצים סוכן כפול, מבזבזים טוקנים, ועלולים ליצור תגובות כפולות. תמיד: שמרו Set של delivery IDs שכבר עובדו.
זמן: 25 דקות | תוצר: שרת Express שמקבל webhooks ומעבד עם סוכן
- צרו endpoint
POST /webhook/githubשמקבל JSON payload - הוסיפו signature verification (אפשר לדמות עם secret קבוע לתרגול)
- הוסיפו idempotency check עם
Set - האזינו רק לאירוע
pull_request.opened - הריצו סוכן שמנתח את שם ה-PR והתיאור ומחזיר סיכום + risk assessment
- הדפיסו את התוצאה ל-console (בייצור — שלחו ל-GitHub API כ-comment)
- בדקו עם
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.
דפוסי 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 מתגלות רק בזמן ריצה, כאן נתפסות בזמן קומפילציה.
הפעילו "strict": true ב-tsconfig.json. ה-SDK מגדיר types מקיפים — strict mode תופס שגיאות קונפיגורציה בזמן קומפילציה. למשל: model: 'claude-sonnnet-4' (שגיאת כתיב) — TypeScript יתפוס את זה. ב-Python, תגלו רק בזמן ריצה.
התקינו Zod: npm install zod. צרו סכמה עם z.object() שמגדירה את המבנה שאתם מצפים מהסוכן. הריצו סוכן שמחזיר JSON ועבירו דרך Schema.parse(). ראו מה קורה כשהסוכן מחזיר JSON לא תקין — הודעת השגיאה של Zod מדויקת ומועילה.
אופטימיזציית ביצועים
סוכן שעובד ב-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.
זמן: 25 דקות | תוצר: API endpoint מאופטמז לייצור
- קחו את ה-Express server שבניתם והוסיפו
chooseModel()שבוחר מודל לפי מורכבות הבקשה - הגדירו
maxTurnsו-maxTokensשונים לפי סוג הבקשה - הוסיפו logging שמתעד עלות, זמן, ומודל לכל request
- הוסיפו SSE streaming endpoint עם model routing
- שלחו 5 בקשות שונות (קצרה, בינונית, מורכבת) ובדקו שכל אחת מקבלת את המודל המתאים
- סכמו את העלויות — חשבו כמה הייתם משלמים אם הכל היה רץ על Opus
תוצאה צפויה: API שמנתב בקשות למודל הנכון, עם logs שמראים חיסכון בעלויות.
זמן: 30 דקות | תוצר: אפליקציית TypeScript מלאה מוכנה לייצור
- בחרו use case: API Backend Agent או Webhook Processor
- בנו Express/Fastify server עם לפחות 2 endpoints
- הוסיפו error handling מלא (timeout, rate limit, auth errors)
- הוסיפו Zod validation על פלט הסוכן
- הוסיפו health check endpoint
- צרו Dockerfile עם multi-stage build
- בנו Docker image והריצו — בדקו שהכל עובד
- כתבו לפחות 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). אם הפרויקט כבר קיים — השתמשו בשפה של הפרויקט.
כתבו "מסמך השוואה אישי" — קובץ קצר שמסכם את ההבדלים שגיליתם בפועל בין Python SDK (פרק 3) ל-TypeScript SDK (פרק זה). מה היה קל יותר? מה היה מסובך יותר? איפה הרגשתם יותר בבית? מה תשתמשו בפרויקט הבא? שמרו את הקובץ — זה חלק מה-deliverables.
בנוסף לשגרה מפרקים קודמים — פרק 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 — הוא בונוס שנבנה על הבסיס הזה.
ענו על לפחות 4 מתוך 5 כדי לעבור:
- למה צריך ליצור סוכן חדש (
new Agent()) לכל HTTP request, ולא לשתף סוכן אחד בין requests?
(רמז: state, conversation history, data leakage בין משתמשים) - מה ההבדל בין
run()ל-stream(), ומתי כל אחד עדיף?
(רמז: UX, Time to First Byte, batch processing מול ממשק אינטראקטיבי) - למה webhook processor חייב להחזיר 200 מייד ולעבד אסינכרונית — ומה קורה בלי idempotency check?
(רמז: timeout של השולח, retries, עיבוד כפול, עלויות מיותרות) - איך Zod עוזר בבניית סוכנים ב-TypeScript, ומה הוא נותן שטיפוסי TypeScript רגילים לא?
(רמז: compile-time vs runtime, פלט סוכן הוא string שצריך validation) - איך 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 שמשתפים פעולה על משימות שגדולות מדי לסוכן יחיד.
- ☐ התקנתי
@anthropic-ai/claude-codeבפרויקט TypeScript - ☐ הגדרתי
tsconfig.jsonעם ESM ו-strict mode - ☐ בניתי סוכן ראשון שרץ עם
npx tsx - ☐ ניסיתי streaming עם
for await (... of agent.stream()) - ☐ בניתי Express API endpoint מונע סוכן
- ☐ הוספתי SSE streaming endpoint
- ☐ הוספתי
AbortControllerעם timeout לכל ריצת סוכן - ☐ בניתי health check endpoint שמוודא API connectivity
- ☐ ניסיתי אינטגרציה עם לפחות framework אחד (Express/Fastify/Next.js)
- ☐ בניתי webhook processor עם signature verification ו-idempotency
- ☐ התקנתי Zod והגדרתי סכמה לוואלידציית פלט סוכן
- ☐ יצרתי Dockerfile עם multi-stage build
- ☐ הרצתי את ה-Docker image ובדקתי שעובד
- ☐ כתבתי מסמך השוואה אישי: Python SDK מול TypeScript SDK