SSD Nodes Learn
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-07-24

איך לשלוט בעלויות AI agent ב-VPS

סוכן unattended עלול לצרוך tokens רבים בלולאה אינסופית. למד כיצד להגדיר hard caps, task budgets ו-prompt caching למניעת הוצאות לא צפויות ב-API.

כיצד למנוע מסוכן AI הפועל תמיד להגדיל את החשבון

בקרת עלויות של סוכן AI ב-VPS (virtual private server) מתבצעת באמצעות הגדרת תקרות לפני הפעלת הסוכן, מכיוון שאין מי שינטר את הצריכה בזמן פעולתו. הגבל כל תגובה ל-max_tokens, הגבל את מספר חזרות הלולאה בקוד שלך, שמור ב-cache את חלק ה-prompt שאינו משתנה, ותעד את נתוני השימוש של כל תגובה כדי לזהות משימות יקרות. עלות השכירות של השרת היא מחיר חודשי קבוע. ה-model API נמדד לפי token, ולולאה הפועלת ללא השגחה עלולה לצרוך tokens רבות בשקט.

המדריך מניח שקיים סוכן שכבר פועל וקורא ל-Messages API משרת בבעלותך. בניית סוכן AI עם Claude ב-VPS מסביר את המנגנון עצמו.

מדוע עלות סוכן unattended שונה

בסשן אינטראקטיבי קיים אדם. כאשר המודל פונה למסלול שגוי או קורא לוג באורך 40,000 שורות, האדם המקשיב עוצר אותו. לסוכן unattended אין בלם כזה: הוא רץ עד סיום הלולאה, ולאחר מכן טיימר מפעיל אותו מחדש.

תדירות היא המכפלה שמשתמשים נוטים לה overlook. משימה המוגדרת לכל 5 דקות רצה 288 פעמים ביום וכ-8,640 פעמים בחודש. עלות כל הרצה בודדת היא המספר שעליו יש להכפיל. סוכנים רבים מסוג "always-on" אינם חייבים להיות פעילים כל הזמן. הם צריכים להשיב תוך מספר דקות מסוים, וזהו למעשה לוח זמנים.

סוכן משלם גם על דברים שחלון צ'אט אינו משלם עליהם.

  • הגדרות כלים (Tool definitions) נשלחות בכל בקשה. ה-system prompt של שימוש בכלים עולה 290 tokens ב-Claude Opus 4.8 עם tool_choice של auto או none, ו-410 עם any או tool. כלי ה-bash מוסיף 325 tokens נוספים. כל MCP server שאתה מחבר מוסיף את ה-schemas שלו למשקל זה; MCP הוא ה-model context protocol.
  • תוצאות כלים הן input tokens. פקודה שמדפיסה 8,000 שורות מכניסה 8,000 שורות לבקשה הבאה, ולכל בקשה נוספת באותו turn.
  • דפים שנמשכו הם input tokens. דף אינטרנט ממוצע בגודל 10 kB הוא בערך 2,500 tokens, וקובץ PDF למחקר בגודל 500 kB הוא בערך 125,000 tokens. max_content_tokens מקצץ רק קבצי טקסט, כיוון שזה "חל על תוכן טקסטואלי, לא על תוכן בינארי כמו PDFs". השתמש ב-max_uses ו-allowed_domains עבור קבצי PDF.
  • חיפוש באינטרנט מתומחר לפי חיפוש, בעלות של $10 לכל 1,000 חיפושים, ללא קשר למספר התוצאות שחוזרות. חיפוש שנכשל אינו מחויב.

אף אחד מהדברים הללו אינו יקר בהרצה בודדת. כולם יקרים כשהם מתבצעים 8,640 פעמים.

Hard ceilings ו-soft ceilings פותרים בעיות שונות

max_tokens הוא מגבלה מחייבת. זהו תקרה קשיחה על סך הפלט של בקשה אחת, הכוללת טקסט חשיבה וטקסט תשובה. Claude לעולם לא מייצר מעבר לכך, והמודל אינו יכול לראות את המספר. הגעה לתקרה זו תגרום לstop_reason: "max_tokens" ולתשובה קטועה. המגבלה עבור agents: כל בקשה בלולאת שימוש בכלים (tool-use loop) נושאת max_tokens משלה, ולכן היא מגבילה תשובה בודדת ולא את המשימה כולה. עשר קריאות לכלים של 4,000 טוקנים יוצרות תקרה של 40,000 טוקנים עבור התור.

תקציב משימה (Task budget) הוא ייעוץ בלבד. task_budget נמצא בתוך output_config ומציין למודל כמה טוקנים עומדים לרשותו עבור כל לולאת ה-agent, כולל חשיבה, קריאות לכלים, תוצאות כלים ופלט.

resp = client.beta.messages.create(
    model="claude-opus-4-8",
    max_tokens=4096,
    betas=["task-budgets-2026-03-13"],
    output_config={"task_budget": {"type": "tokens", "total": 64000}},
    messages=messages,
)

"Task budgets הם רמז רך, לא תקרה קשיחה." Claude עשוי לחרוג מאחד באמצע פעולה, והמגבלה המחייבת על הפלט נשארת max_tokens. "הספירה לאחור גלויה רק למודל", ותשובות אינן כוללות שדה של תקציב נותר. ה-task_budget.total המינימלי המקובל הוא 20,000 טוקנים; ערך נמוך יותר יחזיר שגיאת 400. תקציב קטן מדי עבור העבודה גורם להתנהגות דמויית סירוב, ולכן המודל מצמצם את היקף המשימה או מפסיק מוקדם.

פרט אחד גורם להוצאה כספית במקום לחסכון. אם ה-client שלך מפחית את task_budget.remaining בכל בקשת המשך, הערך השתנה יבטל כל cached prefix המכיל אותו. הגדר זאת פעם אחת, בבקשה הראשונה.

Task budgets נמצאים בגרסת beta ב-Claude Fable 5, Claude Opus 4.8 ו-Claude Opus 4.7. Claude Sonnet 5 ו-Claude Haiku 4.5 רשומים כ-Not supported, ותקציבי משימה אינם חלים על Claude Code, ולכן Claude Code session detached in tmux תלוי בניהול תקין של ה-session.

התקרה השלישית נמצאת ב-Claude Console: הקצה ל-agent סביבת עבודה (workspace) משלו, ולאחר מכן הגדר לו מגבלת הוצאה חודשית ומגבלות קצב (rate limits) לדקה. "לא ניתן להגדיר מגבלות ב-Default Workspace", ו-"מגבלות ברמת הארגון (Organization-wide) תמיד חלות, גם אם מגבלות ה-workspace יחד עולות על כך". הוסיפו התראות הוצאה כדי שסף מסוים יתריע לכם לפני שהתקרה תושג.

בחירת מודל לכל משימה, ומה משפיע בפועל על המאמץ (effort)

בחירת המודל היא החלטה לכל משימה בנפרד. נכון ליולי 2026, המחיר למיליון טוקנים (input ואז output) הוא: Claude Fable 5 בעלות של $10 ו-$50, Claude Opus 4.8 ו-Opus 4.7 בעלות של $5 ו-$25, Claude Sonnet 5 בעלות של $3 ו-$15, ו-Claude Haiku 4.5 בעלות של $1 ו-$5. כרגע המחיר של Sonnet 5 נמוך מהמחיר הרגיל, מכיוון ש"מחיר היכרות של $2/$10 למיליון טוקנים של input/output תקף עד ה-31 באוגוסט 2026". משימה שמטרתה רק סיווג שורות לוג אינה זקוקה ל-Opus.

מאמץ (effort) הוא המשתנה השני. output_config.effort מקבל את low, medium, high, xhigh ו-max, והברירת מחדל היא high, לכן הגדרה מפורשת של high זהה להשמטתו. הפחתת המאמץ משפיעה על יותר מאורך תהליך ההסקנה (reasoning length): התיעוד מציין שהדבר גורם ל-Claude לבצע פחות קריאות לכלי (tool calls) ולאחד פעולות לפעולה אחת. עבור סוכן (agent), זהו החיסכון המשמעותי יותר, מכיוון שביטול קריאת כלי מפחית בקשה שלמה שלא תתבצע.

המלכוד הוא שהמאמץ מתנגש עם הזיכרון המטמון (cache). שינוי הערך בין בקשות מבטל את ה-prompt caching. בדוגמה המתועדת, בקשה 2 דיווחה על cache_read_input_tokens: 3546; בקשה 3, כאשר המאמץ שונה מ-high ל-medium, דיווחה על cache_creation_input_tokens של 3546 ועל cache_read_input_tokens של 0. לכן, יש לשנות את המאמץ בין עומסי עבודה שונים, אך לעולם לא בתוך שיחה אחת שמורה בזיכרון המטמון. כדי לשלוט בעומק התשובה מבלי לשבור את ה-cache, יש לעשות זאת בתוך ה-prompt: שורה כמו "Answer directly without deliberating" בהודעת המשתמש האחרונה תשמור על נקודות ההפסקה (breakpoints) הקודמות ללא שינוי.

טוקנים של חשיבה (thinking tokens) מחוייבים לפי תעריפי output ונחשבים כחלק מ-max_tokens, וזו הסיבה שתשובה קצרה מדי נובעת לעיתים קרובות מכך שתהליך החשיבה צרך את כל התקציב. קראו את usage.output_tokens_details.thinking_tokens כדי לדעת את המספר המדויק. מה באמת ממלא את החיוב של Claude מפרט את המנגנון.

שמרו ב-cache את ה-prefix היציב, והימנעו משבירת המשתנה בטעות

כתיבה ל-cache עולה 1.25 מהמחיר הבסיסי של הקלט ב-cache של חמש דקות, ו-2 מהמחיר ב-cache של שעה אחת. קריאה מה-cache עולה 0.1 מהמחיר, לכן "השימוש ב-cache משתלם כבר לאחר קריאה אחת ב-cache עבור משך של 5 דקות (1.25x כתיבה), או לאחר שתי קריאות ב-cache עבור משך של שעה אחת (2x כתיבה)".

משפט אחד מסביר מדוע זה מתאים לסוכן (agent) הפועל תמיד: "ה-cache מתעדכן ללא עלות נוספת בכל פעם שהתוכן המאוחסן משמש". משימה (job) הפועלת כל שתי דקות מול ה-cache של חמש דקות שומרת על ה-prefix שלה "חם" לאורך כל היום תמורת כתיבה אחת בלבד.

שלוש דרכים לאבד את ה-cache מבלי להבחין בכך.

Prefix שמשתנה. "Cache prefixes נוצרים בסדר הבא: tools, system, ולאחר מכן messages." כל שינוי בבית (byte) מוקדם יותר בסדר זה מבטל את כל מה שבא אחריו, ועריכה של הגדרות כלים (tool definitions) מבטלת את כל ה-cache. הטעות הנפוצה ביותר היא הוספת timestamp או run id בתוך ה-system prompt: כל בקשה אז נושאת prefix שונה, כותבת רשומה חדשה בעלות 1.25x, ולא מקבלת שום קריאה מה-cache. הסימן לכך הוא ערך usage.cache_read_input_tokens של 0 בקריאות שנראות זהות. העבירו את הטקסט המשתנה להודעת המשתמש האחרונה.

Prefix קצר מדי. לכל מודל יש אורך מינימלי הניתן לאחסון ב-cache, ומתחת לאורך זה הבקשה תעובד ללא caching ו-"no error is returned". הנתונים כוללים 1,024 tokens ב-Claude Opus 4.8 וב-Claude Sonnet 5, ו-4,096 ב-Claude Haiku 4.5, לכן מעבר של משימה מ-Sonnet ל-Haiku עלול לכבות את ה-caching בשקט.

שיחה שחורגת מחלון ההסתכלות לאחור (lookback). "חלון ה-lookback הוא 20 blocks." המערכת בודקת לכל היותר 20 מיקומים בכל breakpoint, ואז מפסיקה. בדוגמה המתוארת, תור (turn) המכיל 35 blocks עם breakpoint ב-block 35 בודק את ה-blocks מ-35 ועד 16; הרשומה של התור הקודם ב-block 15 נמצאת מחוץ לחלון, ולכן אין hit. סוכן (agent) שמצמיד מספר blocks של שימוש בכלים (tool-use) ותוצאות כלים (tool-result) בכל תור, יחצה את ה-20 תוך שניים או שלושה תורים. יש לכם ארבעה breakpoints לכל בקשה, לכן הקדישו אחד מהם להודעות האחרונות.

שלחו כל דבר שיכול לחכות ל-Batches API

"כל השימוש נגבה ב-50% ממחירי ה-API הסטנדרטיים", הן עבור ה-input והן עבור ה-output. עיבוד ב-Batch הוא אסינכרוני, "רוב ה-batches מסתיימים תוך פחות משעה אחת", והתוצאות מתקבלות כאשר כל הבקשות מסתיימות או לאחר 24 שעות, המוקדם מביניהם. זהו מצב טיפוסי, אך לא מובטח.

בצעו Poll ל-processing_status עד שערכו יהיה ended. בקשות המחזירות errored, canceled או expired אינן מחויבות בתשלום. הערה אחת עבור מי שמשתמש ב-spend cap: "batches עשויים לעלות מעט מעל מגבלת ההוצאה (spend limit) המוגדרת ב-Workspace שלכם."

ההנחות מצטברות, ומכיוון ש-batch יכול להימשך יותר מחמש דקות, התיעוד ממליץ על שימוש ב-one-hour cache עבור batches החולקים context. לכן, חלקו את העבודה: כל דבר שאדם או webhook ממתינים לו צריך להישאר ב-live path, וסיכום יומי (nightly digest) או סיווג לוגים של אתמול יישלחו ל-batch בחצי מחיר.

Log every response's usage fields to your own store

לא ניתן לייחס הוצאה שלא תועדה. כל תגובה מציינת את העלות שלה.

u = resp.usage
row = {
    "job": job_name,
    "model": resp.model,
    "uncached_input": u.input_tokens,
    "cache_write": u.cache_creation_input_tokens,
    "cache_read": u.cache_read_input_tokens,
    "output": u.output_tokens,
    "stop_reason": resp.stop_reason,
}

הוסף שורה אחת לכל קריאת API לקובץ JSON-lines, עם תגית של שם ה-job שלך. שבוע לאחר מכן תוכל לדעת אילו jobs מייצרים הוצאה ואילו רק נראו עמוסים. שים לב ל-cache_read: עמודה של אפסים היא הבאג הנפוץ ביותר בעלויות עבור agent המאוחסן באופן עצמאי (self-hosted).

שדה אחד קל לטעות בו. input_tokens סופר רק את ה-tokens שאחרי נקודת ה-cache breakpoint האחרונה, לכן גודל ה-prompt האמיתי הוא total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens. agent שמדווח על input_tokens: 400 ב-prompt גדול אינו זול: שאר העלות הגיעה מה-cache.

ספור לפני השליחה. ספירת tokens היא בחינם וה-rate limits שלה נפרדים מיצירת הודעה, לכן השתמש ב-count_tokens כדי לסרב לקובץ מצורף גדול מדי במקום לשלם כדי לגלות זאת. התוצאה היא הערכה, לכן מדוד מחדש עבור כל model ואל תשתמש שוב בספירה מ-tokenizer של ספק אחר. Claude Opus 4.7 ומודלים נוספים של Opus, Claude Fable 5 ו-Claude Sonnet 5 משתמשים ב-tokenizer חדש יותר ש"מייצר בערך 30% יותר tokens עבור אותו טקסט". Claude Sonnet 4.6 ומוקדמים יותר, כולל Claude Haiku 4.5, משתמשים ב-tokenizer הקודם.

למראה המוסמך, ה-Admin API מדווח על שימוש ב-https://api.anthropic.com/v1/organizations/usage_report/messages ועל עלות ב-https://api.anthropic.com/v1/organizations/cost_report. שניהם דורשים admin key (sk-ant-admin01-...) כ-x-api-key: $ANTHROPIC_ADMIN_KEY עם anthropic-version: 2023-06-01, ומקבלים את bucket_width=1d, group_by[]=model ו-api_key_ids[]=. מגבלה אחת: "The Admin API is unavailable for individual accounts."

הפרמטר האחרון הוא טריק זול לייחוס הוצאות: תן לכל job את ה-API key الخاص שלו, סנן באמצעות api_key_ids[], ופצל את הדו"ח לפי key באמצעות group_by[]=api_key_id. המסנן הוא ברבים, ממד הקיבוץ (grouping dimension) הוא ביחיד. שמור את ה-keys בסביבה (environment) ולא בקוד, כפי ש-a first Claude API app on a VPS מטפל בהם.

הגבל את הלולאה, כי שום דבר אחר לא יעשה זאת

מספר איטרציות מוגדר אינו אופציונלי במקרה זה. הלולאה היא באחריותך, ולכן גם המונה הוא באחריותך:

for step in range(MAX_STEPS):          # MAX_STEPS = 12, never "while True"
    resp = client.messages.create(...)
    if resp.stop_reason != "tool_use":
        break
else:
    log.warning("job %s hit MAX_STEPS=%d, giving up", job_name, MAX_STEPS)

אף אחת מהתקרה המצוית לעיל לא תבצע זאת עבורך: max_tokens מגביל תגובה אחת בלבד, והמודל מקבל רק תקציב משימה.

הוסף "בלם" שני מחוץ לתהליך. הרץ את המשימה באמצעות systemd timer במקום תהליך קבוע, והגדר את RuntimeMaxSec= ביחידת השירות (service unit) שלו. באמצעות RuntimeMaxSec=600, תהליך תקוע ייקטע לאחר עשר דקות במקום להמשיך לרוץ עד שתבחין בכך. Running a program as a systemd service and timer מסביר את קבצי היחידה (unit files) עצמם. ניתן לקרוא על מה שבוצע באמצעות journalctl -u triage-agent.service --since "1 hour ago".

הגבל גם את מספר ניסיונות החזרה (retries), מכיוון שמטפל (handler) שמנסה שוב ושוב לנצח יחייב על כל ניסיון. שגיאות 429 או 500 ראויות לכמה ניסיונות עם backoff. שגיאה 400 אינה ראויה לשום ניסיון נוסף, מכיוון שהבקשה שוב תיכשל באותו אופן.

בקרה על עלויות סוכני AI מתחילה בקריאת הנתונים שלכם

איש אינו יכול לומר לכם מהי העלות של סוכן הפועל תמיד, כיוון שהעלות היא מספר ה-tokens בכל הרצה כפול מספר ההרצות היומיות, ושני המשתנים הללו תלויים בכם. הריצו את הסוכן פעם אחת, קראו את שורת השימוש שנרשמה בלוג, והכפילו בלוח הזמנים שלכם. לאחר יומיים, בדקו את דו"ח העלויות אל מול החישוב הזה. כאשר יש חוסר התאמה, הפער נובע כמעט תמיד מ-cache תקול או מלולאה שרצה זמן רב יותר מהצפוי.

החישוב מניח שימוש ב-API key, כיוון שהסוכן הוא תוכנה עצמאית הקוראת ל-Messages API. עבור עבודה אינטראקטיבית, איזה תוכנית Claude מתאימה לסגנון העבודה שלכם מסבירה את נושא המנויים. כל מחיר ומגבלה כאן נבדקו מול התיעוד של Anthropic ביולי 2026, לכן יש לקרוא מחדש את דף המחירים לפני בניית תקציב.

FAQ

כמה עולה להריץ AI agent הפועל תמיד (always-on) על VPS?

ישנם שני חשבונות, ורק אחד מהם צפוי. עלות השרת היא מחיר חודשי קבוע. עלות ה-model API היא לפי שימוש (metered) לכל token, לכן העלות היא מכפלת כמות ה-tokens בצריכה של ריצה אחת בתדירות הריצות. Anthropic לא מפרסמת נתון עבור agent הפועל תמיד ב-self-hosted, לכן יש להתייחס לכל מספר מוצע כהערכה בלבד. קחו את ה-log usage מריצה אמיתית אחת וכפול אותו בתדירות הריצות המתוכננת שלכם.

מה ההבדל בין max_tokens לבין task budget?

max_tokens הוא הגבלה כפויה שאינה נראית למודל. הוא מגביל את הפלט של בקשה אחת, כולל ה-thinking, וחריגה ממנו תגרום ל-stop_reason: "max_tokens". task budget הוא ההפך: המודל מקבל את המספר ומנהל את ה-agentic loop בהתאם אליו, אך "Task budgets are a soft hint, not a hard cap" והמגבלה הכפויה נשארת max_tokens.

מדוע cache_read_input_tokens הוא תמיד zero עבור ה-agent שלי?

מכיוון שה-prefix משתנה בין קריאות, או שהוא קצר מדי עבור cache. הסיבה הנפוצה היא timestamp או run id שמוטמעים בתוך ה-system prompt: ה-cache מבוסס על ה-prefix, לכן כל שינוי ב-byte אחד מבטל את כל מה שבא אחריו. שינוי של tool definitions או ערך ה-effort יגרום לאותה תוצאה. אפשרות אחרת היא גודל הפרומפט, מכיוון שפרומפטים קצרים אינם נשמרים ב-cache ולא מוחזרת שגיאה.

איך עוצרים AI agent מלכת בסבב אינסופי (looping)?

ספרו איטרציות בקוד ה-loop ועצרו בערך מקסימלי קבוע, מכיוון ש-max_tokens מגביל תגובה אחת ו-agent מבצע רבות. הוסיפו הגבלת זמן (wall-clock limit) מחוץ לתהליך: הפעילו את המשימה באמצעות systemd timer עם הגדרת RuntimeMaxSec=, כך שריצה תקועה תופסק לפי לוח הזמנים. הגבילו גם את מספר ה-retries, מכיוון שכל ניסיון חוזר (retry loop) מייצר חיוב.

האם ניתן להגדיר spending limit על Claude API key בודד?

הגבלת ההוצאה המתועדת היא לפי workspace ולא לפי key, לכן הקצו ל-agent workspace משלו והגבילו את ההוצאה החודשית שלו שם. "You cannot set limits on the Default Workspace". הוסיפו התראות הוצאה (spend notifications) כדי שתקבלו התראה ברגע שמתקרבים לסף מסוים. לצורך מעקב (attribution), הקצו לכל משימה key משלה, ולאחר מכן קבצו את דו"ח השימוש באמצעות group_by[]=api_key_id.

#claude#ai#agents#api#cost