הריצו את Stanford STORM על endpoint תואם OpenAI מותאם אישית.

Updated 2026-07-29

STORM בונה כל מודל שפה כ-LitellmModel, ו-litellm מקבל api_base. שימו את https://api.apisrouter.com/v1 ב-openai_kwargs המשותף שלכם, קדמו model ids עם openai/, וכל חמשת סלוטי ה-LM של צינור המאמר מנותבים דרך endpoint אחד ומפתח אחד.

תשובה מהירה: api_base ב-openai_kwargs, קידומת openai/ על ה-ids.

ה-LitellmModel של STORM שומר כל kwargs שאתם בונים אותו איתם ומשלב אותם בכל קריאת litellm.completion(). הפרמטר api_base של litellm הוא איך מכוונים את ספק ה-openai ל-host אחר, אז הוספת api_base ל-dict של openai_kwargs שהדוגמאות של STORM עצמן כבר משתמשות בו היא כל הדריסה. קדמו כל model id עם openai/ כך ש-litellm ידבר את פרוטוקול chat-completions לבסיס הזה, והמחרוזת אחרי הסלאש מועברת אל ה-gateway. מכיוון שהדוגמאות בונות dict אחד של openai_kwargs ומשתמשות בו מחדש לכל מודל, מפתח אחד שנוסף מנתב מחדש את כל הצינור. אין שינויי קוד ב-STORM, אין fork; זו התנהגות רגילה של knowledge_storm על גבי ניתוב מתועד של litellm.

openai_kwargs = {
    "api_key": os.getenv("APISROUTER_API_KEY"),
    "api_base": "https://api.apisrouter.com/v1",
    "temperature": 1.0,
    "top_p": 0.9,
}
fast = LitellmModel(model="openai/deepseek-v4-flash", max_tokens=500, **openai_kwargs)
strong = LitellmModel(model="openai/claude-sonnet-4-6", max_tokens=3000, **openai_kwargs)

איך STORM מפצל מאמר על פני חמישה סלוטי LM.

STORM (stanford-oval ב-GitHub, כ-30K כוכבים) כותב דוחות בסגנון ויקיפדיה מאפס: הוא חוקר נושא דרך שיחות מדומות רב-נקודתיות-מבט, בונה תוכן עניינים ממה שהוא למד, מייצר את המאמר המלא סעיף אחר סעיף, ואז מלטש. STORMWikiLMConfigs חושף את הצינור הזה כחמישה מודלים ניתנים להגדרה עצמאית: conv_simulator_lm ו-question_asker_lm מניעים את שיחות המחקר, outline_gen_lm מבנה את המאמר, article_gen_lm כותב אותו, ו-article_polish_lm עושה את המעבר הסופי. ה-README ב-upstream מפורש לגבי הכלכלה: סימולטור השיחה רץ בנפח הקריאות הגבוה ביותר, אז הוא ממליץ על מודל מהיר יותר שם ומודל חזק יותר ליצירת המאמר. ההנחיה הזו הניחה בחירה בין מודלי OpenAI; מאחורי endpoint רב-ספקים היא מוכללת למשהו שימושי יותר. כל סלוט הוא LitellmModel משלו עם מחרוזת model משלו, כך שהשיחה המחקרית יכולה לרוץ על id מהיר של DeepSeek בעוד תוכן העניינים ויצירת המאמר רצים על Claude, והליטוש על כל מודל שאתם סומכים עליו לגבי הטון, כל זאת מאומת באותו מפתח מול אותו api_base. צד האחזור הוא מכניקה נפרדת: ה-runner של STORM לוקח מודול RM (You.com, Bing, וכמה backends חיפוש אחרים) עם מפתח API משלו. שינוי לאן מודלי השפה מצביעים לא נוגע באיך מקורות נשלפים.

הגדרה מלאה: חמישה סלוטים, dict אחד של kwargs.

התבנית העובדת משקפת את סקריפטי ההרצה של המאגר עצמו: בנו את ה-kwargs המשותף פעם אחת, בנו LitellmModel אחד לכל תפקיד, והקצו אותם דרך ה-setters של STORMWikiLMConfigs. ה-api_key יכול להיות כל שם שתרצו מכיוון שאתם מעבירים אותו במפורש; הדוגמה משתמשת במשתנה משלה כדי להבהיר שזה לא אישור חשבון OpenAI. litellm גם מכבד משתני סביבה ברמת-ספק, וספק ה-openai קורא OPENAI_API_BASE, אז דריסה מבוססת-סביבה-בלבד אפשרית. נתיב ה-kwargs המפורש עדיין זה שכדאי להעדיף: הוא גלוי בקוד שיצר מאמר נתון, הוא שורד הרצה על מכונה עם מצב סביבה שונה, והוא מאפשר חריגות לכל סלוט אם אי-פעם תרצו שלב אחד על endpoint אחר.

import os
from knowledge_storm import STORMWikiRunnerArguments, STORMWikiRunner, STORMWikiLMConfigs
from knowledge_storm.lm import LitellmModel
from knowledge_storm.rm import YouRM

openai_kwargs = {
    "api_key": os.getenv("APISROUTER_API_KEY"),
    "api_base": "https://api.apisrouter.com/v1",
    "temperature": 1.0,
    "top_p": 0.9,
}
fast = LitellmModel(model="openai/deepseek-v4-flash", max_tokens=500, **openai_kwargs)
strong = LitellmModel(model="openai/claude-sonnet-4-6", max_tokens=3000, **openai_kwargs)

lm_configs = STORMWikiLMConfigs()
lm_configs.set_conv_simulator_lm(fast)
lm_configs.set_question_asker_lm(fast)
lm_configs.set_outline_gen_lm(strong)
lm_configs.set_article_gen_lm(strong)
lm_configs.set_article_polish_lm(strong)

engine_args = STORMWikiRunnerArguments(output_dir="./results")
rm = YouRM(ydc_api_key=os.getenv("YDC_API_KEY"), k=engine_args.search_top_k)
runner = STORMWikiRunner(engine_args, lm_configs, rm)
runner.run(topic="Small modular reactors")

בחירת מודלים לכל שלב בצינור.

התייחסו לחמשת ה-setters כחוגת תקציב, לא boilerplate. ההנחיה מ-upstream כבר אומרת לפצל מודלים מהירים וחזקים על פני שלבים; endpoint רב-ספקים רק מרחיב את התפריט לכל שלב. שנו סלוט אחד בכל פעם בין הרצות על אותו נושא והשוו את הפלטים, כאשר לוג השימוש לפי מפתח מתמחר כל הגדרה.

  • conv_simulator_lm ו-question_asker_lm הם שלבי הנפח: ראיונות מדומים רב-תוריים על פני כמה נקודות מבט לכל נושא. deepseek-v4-flash או id מהיר אחר שומר על שלב המחקר מלשלוט בהוצאה, ופטפוט לא מושלם נסבל מכיוון שהוא מזין הערות, לא פרוזה.
  • article_gen_lm הוא הסלוט הדגל. הוא כותב סעיפים ארוכים, מובנים, עם ציטוטים מתוך המחקר שנצבר, שהיא עבודת יצירה-מתמשכת שבה claude-sonnet-4-6 או gpt-5.5 עולים בבירור על ids קטנים יותר.
  • outline_gen_lm הוא מעט קריאות עם מינוף חריג, אותה צורה כמו סלוט תכנון: תוכן עניינים חלש מגביל את המאמר לא משנה כמה טוב הכותב. זה המקום הטבעי לבדוק את claude-opus-4-7.
  • article_polish_lm כותב מחדש לזרימה ומסיר כפילויות על פני המאמר המורכב, מה שמרוויח מ-id בהקשר-ארוך; gemini-3.1-pro-preview שווה benchmark כאן.

תשלום לפי שימוש · מתחת למחיר הרשמי

Selected models are priced below official list prices. Exact input, output, cache, and per-request prices are shown for each model.

מודלמחיר רשמיהמחיר שלנו
DeepSeek V4 Flash$0.14 / $0.28 per M$0.10 / $0.30 per M
Claude Sonnet 4.6$3.00 / $15.00 per M$2.40 / $12.00 per M
Claude Opus 4.7$5.00 / $25.00 per M$4.00 / $20.00 per M
GPT-5.5$5.00 / $30.00 per M$4.00 / $24.00 per M
Gemini 3.1 Pro Preview$2.00 / $12.00 per M$1.60 / $9.60 per M

מצבי הכשל הספציפיים ל-STORM.

model id חשוף מנותב לפי הסקה, לא לפי ה-api_base שלכם. litellm קורא את הקידומת כדי לבחור ספק, ו-id של Claude בלי קידומת מוסק כקריאה נטיבית ל-Anthropic, שאז רוצה ANTHROPIC_API_KEY ומתעלם לגמרי מה-gateway שלכם. כל id שמיועד ל-gateway חייב לשאת את הקידומת openai/; הקידומת נוקבת בשם הפרוטוקול, לא הספק. סלוט אחד שנשאר מאחור. כל LitellmModel לוכד את ה-kwargs שלו בזמן הבנייה. אם ארבעה סלוטים חולקים openai_kwargs וסלוט חמישי נבנה ad hoc בלי api_base, הסלוט הזה שולח בשקט לברירת המחדל של הספק ונכשל על אימות, וה-traceback נוקב בשם שלב בצינור ולא שורת הגדרה. בנו כל סלוט מאותו dict והבעיה הזו נעלמת. כשלי retriever שמואשמים ב-endpoint. שלב המחקר צריך backend חיפוש עובד; מפתח retriever לא תקין או מוצה (YDC_API_KEY, BING_SEARCH_API_KEY, או כל RM שבחרתם) נכשל בהרצות במהלך איסוף המידע. השלב הזה משתלב עם קריאות LM, אז קראו את ה-traceback לגבי איזה client זרק לפני שאתם נוגעים בהגדרת ה-LM. secrets.toml של ה-demo אינו ההגדרה של הסקריפט שלכם. ה-demo של Streamlit קורא secrets.toml; הרצות פרוגרמטיות קוראות מה שהסקריפט שלכם מעביר. עריכת אחד תוך הרצת השני היא חוסר-התאמה קלאסי. max_tokens הוא גם לכל-סלוט. הדוגמאות של STORM מגדירות מגבלות קטנות על הסלוטים המהירים (500) וגדולות יותר על היצירה (3000). כיוון סלוט למודל ארוך-טופס בלי להעלות את ה-max_tokens שלו גוזם בשקט סעיפים, מה שנראה כבעיית איכות מודל אבל הוא מספר הגדרה.

מי מנתב את STORM דרך gateway.

  • צוותים שמייצרים דוחות ידע בנפח (תדריכים, מסמכים פנימיים בסגנון wiki, מבואות נושא), שם הפיצול לחמישה סלוטים הופך כיוונון עלות לכל שלב לכסף אמיתי.
  • חוקרים שחוקרים הרכב צינורות: איזה שלב מרוויח ממודל חזק יותר היא שאלה אמפירית, ו-endpoint אחד הופך את הרשת של שילובי סלוט-מודל לטריוויאלית לספירה.
  • בונים שמריצים Claude או Gemini בסלוטי הכתיבה של ערימה בצורת OpenAI, בלי להוסיף SDK ספק לכל משפחת מודלים.
  • כל מי שמריץ רשימות נושאים באצווה, שם נפח שלב-המחקר מוכפל על פני נושאים ולוג השימוש הופך לספר החשבונות לכל נושא.
  • מפתחים ללא גישה לחיוב של ספק נתון. גישה מבוססת-הטענה בלי דרישת כרטיס מסירה את התלות בהרשמה לכל ספק.

אמתו את ה-endpoint ובצעו דיבוג למאמר הראשון.

רשמו את מודלי ה-gateway קודם: המחרוזת אחרי openai/ בכל סלוט חייבת להתאים בדיוק ל-id מוגש. כשלי הרצה ראשונה עוקבים אחר סדר הצינור. שגיאת אימות שנוקבת ב-Anthropic או Google אומרת ש-id חשוף נותב לספק נטיבי; הוסיפו openai/. 401 מה-gateway אומר שה-api_key ב-kwargs שלכם אינו מפתח ה-gateway. שגיאת model-not-found נוקבת בשם הסלוט שה-id שלו כולל טעות הקלדה. כשלים במהלך שלב המחקר שמזכירים את ה-backend החיפוש שלכם הם אישורי retriever, לא ניתוב LM. וסעיפי מאמר קטומים או קצרים באופן מוזר הם בדרך כלל max_tokens קמצני על סלוט היצירה ולא משהו ב-upstream. הרצת STORM מלאה היא פרץ גדול: שיחות מדומות על פני נקודות מבט, ואז תוכן עניינים, יצירה, וליטוש. ברגע שהרצה אחת מסתיימת, קונסולת APIsRouter מציגה מודל לכל בקשה, ספירות טוקן, והוצאה, מה שממופה בבירור לחמשת הסלוטים ואומר לכם בדיוק איזה שלב לכוונן מחדש לפני אצוות הנושאים הבאה.

curl -s https://api.apisrouter.com/v1/models \
  -H "Authorization: Bearer $APISROUTER_API_KEY" | head -50

שאלות נפוצות

איך STORM תומך ב-endpoint תואם OpenAI מותאם אישית?

דרך litellm. STORM בונה כל LM כ-LitellmModel, שמשלב את ה-kwargs של הבנאי שלו בכל קריאת litellm.completion(), ו-litellm מקבל api_base עבור ספק ה-openai. הוסיפו api_base ל-dict של openai_kwargs וכל סלוט שנבנה ממנו מנותב אל ה-gateway.

למה model ids צריכים את הקידומת openai/?

litellm בוחר את הספק לפי הקידומת. openai/claude-sonnet-4-6 אומר "דבר את פרוטוקול ה-chat-completions של OpenAI מול ה-api_base שלי עם model claude-sonnet-4-6". בלי הקידומת, litellm מסיק את הספק מהשם ומנתב באופן נטיבי, עוקף את ה-endpoint שלכם.

האם שלבי STORM שונים יכולים להשתמש במודלים של ספקים שונים?

כן. כל אחד מחמשת הסלוטים הוא LitellmModel עצמאי, כך שסימולטור השיחה יכול לרוץ id של DeepSeek בעוד יצירת המאמר רצה Claude והליטוש רץ GPT, כל זאת דרך אותו api_base ומפתח. upstream כבר ממליץ לפצל מודלים מהירים וחזקים על פני שלבים.

האם ה-retriever של החיפוש משתנה כשאני משנה את api_base?

לא. אחזור רץ דרך מודול ה-RM שאתם מעבירים ל-STORMWikiRunner (You.com, Bing, ו-backends נתמכים אחרים) עם מפתח משלו. ניתוב LM ואחזור מקורות הם מערכות עצמאיות שנכשלות בשלבים שונים של הרצה.

האם יש נתיב משתנה-סביבה במקום kwargs?

litellm מכבד משתנים ברמת-ספק, וספק ה-openai קורא OPENAI_API_BASE. זה עובד, אבל ה-kwarg המפורש api_base ניתן יותר לשחזור: הוא נוסע עם הסקריפט, שורד מכונות עם מצב סביבה שונה, ומתיר חריגות לכל סלוט.

כמה טוקנים מאמר STORM אחד צורך?

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