דילוג לתוכן
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after a top-up of $10 or more. Until Nov 30.

How it works

מה זה API של LLM? בקשות, הודעות, טוקנים ושגיאות

איך בנויה בקשה ל-API של צ׳אט: מודל, הודעות, טוקנים, סטרימינג, מפתחות API, מגבלות קצב ושגיאות HTTP, עם דוגמאות מוערות.

מתחילים10 דקות קריאהמאת NotHoly

API של LLM הוא endpoint של web שמקבל טקסט ומחזיר את התשובה של מודל. התוכנית שלך שולחת בקשת HTTP עם שם מודל, רשימת הודעות ומגבלת טוקנים. השירות מחזיר את הטקסט שנוצר, סיבה לעצירה וספירה של הטוקנים שהוא קרא וכתב. אפליקציית צ׳אט עושה את אותה עבודה מאחורי תיבת טקסט. ה-API נותן לקוד שלך את החלקים הגולמיים, כך שהקוד מחליט מה נכנס ומה קורה לתשובה.

מה זה API של LLM

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

API מדלג על הממשק. הקוד שלך בונה את רשימת ההודעות, קובע את מגבלת הטוקנים ומטפל בתשובה. לשירות לא משנה אם הקורא הוא דפדפן, סקריפט או משימה מתוזמנת. הוא בודק את פורמט הבקשה ואת המפתח.

שלושה מונחים חשובים כבר מההתחלה:

  • Model: השם של המודל שמייצר את התשובה. כל שירות מפרסם מזהי מודל משלו, והרשימה משתנה עם הזמן.
  • Messages: רשימה מסודרת של תורות, כל אחד עם תפקיד כמו user או assistant.
  • Tokens: יחידות הטקסט שהמודל קורא וכותב. טוקן קצר לעיתים קרובות ממילה. מגבלות, ספירות שימוש ומחירים נמדדים כולם בטוקנים.

למה זה חשוב למפתחים

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

איך בקשה עובדת, שלב אחר שלב

1. הקוד שלך שולח בקשת HTTP

כמעט כל API של LLM הוא web API מעל HTTP. לבקשה יש מתודה, URL, כותרות ובדרך כלל גוף JSON. הקריאה למודל היא כמעט תמיד POST. RFC 9110, תקן הסמנטיקה הנוכחי של HTTP, מגדיר מה החלקים האלה אומרים ואיך שרת חייב לענות. קודי הסטטוס והכותרות שמופיעים בהמשך מגיעים ממנו.

2. הגוף מציין את המודל ואת ההודעות

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

json
{
  "model": "<MODEL_ID_FROM_CURRENT_CATALOG>",
  "max_tokens": 300,
  "system": "You explain security concepts in plain English. Keep answers under 120 words.",
  "messages": [
    {
      "role": "user",
      "content": "What is the difference between hashing and encryption?"
    }
  ],
  "stream": false
}

הערות:

  • model הוא מזהה המודל שמייצר את התשובה. מעתיקים אותו מהרשימה הנוכחית של הספק, ולא ממדריך ישן.
  • max_tokens מגביל כמה טוקנים המודל רשאי לכתוב. הוא לא מגביל את הפרומפט שלך.
  • system מחזיק הנחיות קבועות. בפורמט הזה הוא נמצא מחוץ לרשימת ההודעות.
  • messages מחזיק את השיחה. כאן יש בו תור user אחד.
  • stream הוא false, כך שהשרת מחכה לתשובה המלאה לפני שהוא עונה.

3. להודעות יש תפקידים

לכל הודעה יש תפקיד שאומר מי מדבר:

  • user: טקסט ממשתמש הקצה או מהתוכנית שלך.
  • assistant: תשובה שהמודל כתב קודם. שולחים אותה בחזרה כדי להמשיך את השיחה.
  • system או developer: הנחיות קבועות. השם תלוי בפורמט.
  • tool: התוצאה של קריאה לכלי, שמכוסה במאמר מאוחר יותר.

כדי להמשיך שיחה, מוסיפים את התשובה האחרונה של המודל כהודעת assistant, ואז את הודעת user החדשה. אם תורות קודמים נשכחים, המודל עונה על השאלה החדשה בלי הקשר, ושום דבר לא מדווח על שגיאה. זה אחד הבאגים הנפוצים ביותר.

4. מגבלת הטוקנים תוחמת את התשובה

max_tokens (או המקביל שלו) מגביל כמה המודל כותב. אם התשובה מגיעה למגבלה, הטקסט נעצר באמצע משפט והתגובה מציינת את זה. בודקים את סיבת העצירה, שמכוסה בשלב 6, ולא מניחים שכל תשובה שלמה. המגבלה לא מקטינה את מה שמשלמים על הפרומפט, כי טוקני קלט עדיין נספרים בשימוש.

5. סטרימינג משנה את המסירה, לא את המשמעות

עם "stream": true, השרת שולח את התשובה בחלקים בזמן שהוא מייצר אותם, באמצעות server-sent events. משתמשים רואים מילים מופיעות בזו אחר זו. הקוד שלך קורא את האירועים ומחבר את החלקים, ונתוני השימוש וסיבת העצירה מגיעים באירועים המסיימים. סטרימינג מתאים כשאדם צופה. במשימת batch ששומרת את התשובה, קל יותר לכתוב ולבדוק בקשה רגילה.

6. התגובה נושאת את הטקסט, הסיבה והספירות

תגובה אופיינית מכילה:

  • מזהה של הבקשה או ההודעה, שימושי כשמדווחים על בעיה.
  • התוכן שנוצר. בפורמט Messages הוא רשימה של content blocks. בפורמט Chat Completions הוא נמצא במערך choices.
  • סיבת עצירה שמסבירה למה היצירה הסתיימה, כמו סוף טבעי של תור, מגבלת הטוקנים או קריאה לכלי.
  • ספירות שימוש של טוקני קלט ופלט.

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

7. לוג של שימוש, לא של תוכן

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

שני פורמטי בקשה זה לצד זה

שני סגנונות בקשה נפוצים בתיעוד ציבורי, ושירותים רבים מעתיקים אחד מהם כדי שקוד לקוח קיים ימשיך לעבוד עם base URL אחר. ההיכרות עם שניהם עוזרת לקרוא דוגמאות מכל מקור.

הראשון הוא פורמט Messages, שמתועד בהפניה הראשונה תחת מקורות. הוא מקבל system כשדה ברמה העליונה, דורש max_tokens, מחזיר content blocks עם stop_reason, ומדווח על input_tokens וגם output_tokens.

השני הוא פורמט Chat Completions, שמתועד בהפניה השנייה תחת מקורות. הוא שם הנחיות system או developer בתוך messages, מקבל max_completion_tokens, ומחזיר מערך choices עם finish_reason וגם message בכל פריט. אובייקט השימוש שלו מדווח על prompt_tokens, ‏completion_tokens וגם total_tokens.

נושא פורמט Messages פורמט Chat Completions
הנחיות קבועות שדה system ברמה העליונה הודעת system או developer בתוך messages
שדות חובה model, messages, max_tokens model, messages
מגבלת פלט max_tokens (חובה) max_completion_tokens (לא חובה)
מיקום התשובה רשימת blocks בשדה content choices[0].message
למה נעצר stop_reason choices[0].finish_reason
ספירות טוקנים usage.input_tokens, usage.output_tokens usage.prompt_tokens, usage.completion_tokens, usage.total_tokens
סטרימינג stream: true, ‏server-sent events stream: true, נתחי server-sent event

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

אותה שאלה בפורמט Chat Completions:

json
{
  "model": "<MODEL_ID_FROM_CURRENT_CATALOG>",
  "max_completion_tokens": 300,
  "messages": [
    {
      "role": "developer",
      "content": "You explain security concepts in plain English. Keep answers under 120 words."
    },
    {
      "role": "user",
      "content": "What is the difference between hashing and encryption?"
    }
  ],
  "stream": false
}

תשובה בפורמט הזה נראית כך. הערכים הם לדוגמה ולא תגובה שנצפתה:

json
{
  "id": "<RESPONSE_ID>",
  "object": "chat.completion",
  "model": "<MODEL_ID_FROM_CURRENT_CATALOG>",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "<GENERATED_TEXT>" },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 0,
    "completion_tokens": 0,
    "total_tokens": 0
  }
}

הקוד שלך קורא את choices[0].message.content, את choices[0].finish_reason ואז את אובייקט השימוש. פורמט Messages שומר את אותו מידע בשדות content, stop_reason וגם usage, בשמות אחרים.

אימות עם מפתחות API

מפתח API מזהה את החשבון שמשלם על קריאה וקובע אילו מודלים ומגבלות חלים. שני הפורמטים שולחים אותו אחרת. פורמט Chat Completions משתמש בכותרת Authorization עם הסכמה Bearer. פורמט Messages משתמש בכותרת מפתח ייעודית ודורש גם כותרת גרסת API, כדי שהספק יוכל לשמור על התנהגות ישנה יציבה.

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

bash
# Placeholder shape only. Do not run this with a real key in a shared shell history.
curl https://<API_HOST>/<MESSAGES_PATH> \
  -H "x-api-key: <YOUR_API_KEY>" \
  -H "anthropic-version: <VERSION_FROM_CURRENT_DOCS>" \
  -H "content-type: application/json" \
  -d '{"model":"<MODEL_ID>","max_tokens":300,"messages":[{"role":"user","content":"Hello"}]}'
bash
# Placeholder shape only. Same caution as above.
curl https://<API_HOST>/<CHAT_COMPLETIONS_PATH> \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"model":"<MODEL_ID>","messages":[{"role":"user","content":"Hello"}]}'

הערות:

  • המפתח הוא אישור גישה. כל מי שמחזיק בו יכול להוציא את היתרה שלך ולקרוא את מה שהחשבון יכול לקרוא.
  • Authorization: Bearer היא סכמת ה-HTTP הסטנדרטית לטוקנים. RFC 9110 מגדיר את הכותרת Authorization בסעיף האימות שלו.
  • פקודות שמוקלדות יכולות להגיע להיסטוריית ה-shell. משתמשים במשתנה סביבה, כפי שמוצג בהמשך, ולא במפתח מילולי.

מפתחות נשארים מחוץ לקוד ול-Git

כאן מתחילות רוב העלויות והדליפות שאפשר להימנע מהן. הכללים הבאים הולכים בעקבות OWASP Secrets Management Cheat Sheet.

מפתחות נשארים מחוץ לקוד המקור. מפתח שנכתב בקובץ Python קיים בכל עותק של הקובץ: המחשב הנייד שלך, המחשבים של חברי הצוות, מערכת ה-CI וכל commit שנגע בו. OWASP מציין סודות בקידוד קשיח בקוד מקור ובקובצי קונפיגורציה כפרקטיקה מסוכנת.

לעולם לא עושים commit למפתח. Git שומר היסטוריה. מחיקת המפתח ב-commit מאוחר יותר לא מסירה אותו מהישן. OWASP מציין שהסרת סוד מההיסטוריה עשויה לדרוש שכתוב, שיש לו תופעות לוואי. מפתח שהגיע ל-repository משותף הוא מפתח חשוף. מבטלים אותו, יוצרים חדש ובודקים בחשבון אם יש שימוש שלא הגיע ממך.

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

לא אופים מפתחות לתוך תמונות קונטיינר. OWASP מזהיר מפני סודות בהוראות ENV או ARG של Docker, כי הערך מגיע למטא־דאטה של התמונה ולהיסטוריית ה-build.

עדיף מאגר סודות. OWASP אומר שמשתני סביבה נגישים בדרך כלל לכל התהליכים ועשויים להופיע בלוגים או ב-dumps, ולכן הוא ממליץ עליהם רק כשאין אפשרויות טובות יותר. במחשב נייד, מנהל סיסמאות או מאגר האישורים של מערכת ההפעלה מתאימים. בשרתים משתמשים בשירות סודות מנוהל או בקבצים שמחוברים ב-mount עם הרשאות מוגבלות.

להגביל ולהחליף. נותנים לכל מפתח רק את הגישה שהוא צריך, ומחליפים מפתחות לפי לוח זמנים. OWASP אומר שלסודות חייבות להיות ההרשאות המינימליות וממליץ על החלפה אוטומטית איפה שאפשר.

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

python
# Read the key from the environment. Fail early if it is missing.
import os

api_key = os.environ.get("LLM_API_KEY")
if not api_key:
    raise SystemExit("LLM_API_KEY is not set. Set it in your shell or secret store.")

מוסיפים קובצי הגדרות מקומיים לקובץ .gitignore לפני שיוצרים אותם. עושים commit לתבנית כמו .env.example שמפרטת את שמות המשתנים עם ערכים ריקים, ולעולם לא עושים commit לקובץ האמיתי.

מגבלות קצב ומכסות

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

כשמגיעים למגבלה, השרת מחזיר בדרך כלל 429 Too Many Requests. RFC 6585 מגדיר את הקוד הזה, שמרחיב את הקבוצה הבסיסית של HTTP. התגובה עשויה לכלול Retry-After, שאומר כמה זמן לחכות. RFC 9110 מגדיר אותו בסעיף שדות התגובה שלו.

הקוד שלך צריך לקרוא את הסטטוס לפני הגוף, לחכות לפני ניסיון חוזר (עם Retry-After כשהוא קיים, אחרת השהיה עולה עם מעט jitter אקראי), ולעצור אחרי כמה ניסיונות במקום לרוץ בלולאה אינסופית. סופרים טוקנים לפני שליחת פרומפטים גדולים מאוד, כי בקשה גדולה מדי נכשלת ללא קשר ליתרה.

שגיאות HTTP נפוצות

הספרה הראשונה של קוד סטטוס אומרת את הסוג: בעיה בצד הלקוח, כשל בשרת או משהו אחר. RFC 9110 מגדיר את הסוגים ואת הקודים עצמם. אלה השגיאות שאינטגרציות חדשות פוגשות בתדירות הגבוהה ביותר.

סטטוס שם מה זה בדרך כלל אומר מה לבדוק
400 Bad Request הגוף פגום או ששדה לא תקין תחביר JSON, שמות שדות, שדות חובה, טווחי ערכים
401 Unauthorized המפתח חסר, פגום או בוטל שם הכותרת, הקידומת Bearer, ערך המפתח בסביבה שלך
403 Forbidden המפתח תקף, אבל החשבון הזה לא רשאי להשתמש במשאב גישה למודל, הרשאות החשבון, סטטוס חיוב
404 Not Found הנתיב שגוי, או שמזהה המודל לא קיים בחשבון שלך Base URL, נתיב, מזהה מודל מדויק מהקטלוג הנוכחי
429 Too Many Requests הגעה למגבלת קצב או מכסה Retry-After, דף השימוש, נפח הבקשות
500 Internal Server Error השרת נכשל בצד שלו ניסיון חוזר עם backoff, ואז בדיקה בדף הסטטוס של הספק
503 Service Unavailable השירות עמוס או בתחזוקה ניסיון חוזר עם backoff ומספר ניסיונות מרבי

שגיאת 4xx אומרת שהבקשה צריכה שינוי, ולכן שליחה חוזרת של אותה בקשה לא תעזור. שגיאת 5xx או 429 עשויה להצליח מאוחר יותר, ולכן backoff הגיוני שם. רושמים בלוג את קוד הסטטוס, מזהה המודל ומזהה הבקשה. לעולם לא רושמים את המפתח או את הפרומפט.

טעויות נפוצות

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

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

קידוד קשיח של מזהה המודל. מזהי מודל יוצאים משימוש ומקבלים שמות חדשים. שומרים את המזהה בקונפיגורציה, כך ששינוי הוא עריכה של שורה אחת.

ערבוב שני הפורמטים. שליחת גוף Chat Completions ל-endpoint של Messages, או להפך, מייצרת שגיאות 400. מוודאים איזה פורמט ה-endpoint מתעד לפני שמעתיקים דוגמה.

איך ממשיכים

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

ה-gateway של NotHoly מתעד את השדות וקודי השגיאה שלו. כדאי להשוות אותם לשתי הצורות שלמעלה, ולראות בתיעוד שלו את הסמכות להתנהגות שלו.

כדי שהמודל יקרא לקוד שלך, השלב הבא הוא שימוש בכלים: תור של assistant שסיבת העצירה שלו מבקשת מהתוכנית שלך להריץ משהו ולשלוח בחזרה את התוצאה. המאמר הבא עוסק בלולאה הזאת.

מקורות

מקורות

  1. Messages API reference
  2. Chat Completions API reference
  3. RFC 9110: HTTP Semantics
  4. RFC 6585: Additional HTTP Status Codes
  5. OWASP Secrets Management Cheat Sheet

עוד בנושא מודלי LLM ו-API

מודלי LLM ו-API

מעבר מ-Verboo ל-NotHoly: מה משתנה ומה נשאר

איך מעבירים זרימת עבודה תואמת OpenAI מ-Verboo, מ-Venice AI או מ-Drael ל-NotHoly: base URL, מפתח, מזהי מודל, חיוב, רישיון וכלים.

ביניים5 דקות קריאה