STORM دانشگاه استنفورد را روی یک endpoint سفارشی سازگار با OpenAI اجرا کنید.

Updated 2026-07-29

STORM هر مدل زبانی را به‌عنوان یک LitellmModel می‌سازد، و litellm پارامتر api_base را می‌پذیرد. https://api.apisrouter.com/v1 را در openai_kwargs مشترک خود بگذارید، id های مدل را با openai/ پیشوند بزنید، و هر پنج اسلات LM از pipeline مقاله از طریق یک endpoint و یک کلید مسیردهی می‌شوند.

پاسخ سریع: api_base در openai_kwargs، پیشوند openai/ روی id ها.

LitellmModel در STORM هر kwargs ای که با آن ساخته شود را ذخیره می‌کند و آن‌ها را در هر فراخوانی litellm.completion() ادغام می‌کند. پارامتر api_base در litellm روشی است که provider openai را به یک host متفاوت اشاره می‌دهید، پس اضافه‌کردن api_base به dict openai_kwargs ای که مثال‌های خود STORM از قبل استفاده می‌کنند کل override است. هر id مدل را با openai/ پیشوند بزنید تا litellm پروتکل chat-completions را با آن base صحبت کند، و رشته بعد از اسلش بدون تغییر به gateway منتقل می‌شود. چون مثال‌ها یک dict openai_kwargs می‌سازند و آن را برای هر مدل استفاده مجدد می‌کنند، یک کلید اضافه‌شده کل pipeline را هدایت مجدد می‌کند. هیچ تغییر کد 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، حدود ۳۰ هزار ستاره) گزارش‌هایی به‌سبک ویکی‌پدیا را از صفر می‌نویسد: یک موضوع را از طریق مکالمات شبیه‌سازی‌شده چندمنظری پژوهش می‌کند، از آنچه یاد گرفته یک outline می‌سازد، مقاله کامل را بخش‌به‌بخش تولید می‌کند، و سپس یک پاس صیقل انجام می‌دهد. STORMWikiLMConfigs آن pipeline را به‌عنوان پنج مدل مستقلاً قابل‌تنظیم افشا می‌کند: conv_simulator_lm و question_asker_lm مکالمات پژوهش را هدایت می‌کنند، outline_gen_lm مقاله را ساختار می‌دهد، article_gen_lm آن را می‌نویسد، و article_polish_lm پاس نهایی را انجام می‌دهد. README upstream درباره اقتصاد صریح است: شبیه‌ساز مکالمه بالاترین حجم فراخوانی را اجرا می‌کند، پس یک مدل سریع‌تر آنجا و یک مدل قوی‌تر برای تولید مقاله را توصیه می‌کند. آن راهنمایی انتخاب بین مدل‌های OpenAI را فرض می‌کرد؛ پشت یک endpoint چند-vendor به چیز مفیدتری تعمیم می‌یابد. هر اسلات LitellmModel خودش را با رشته model خودش دارد، پس چت پژوهش می‌تواند روی یک id سریع DeepSeek اجرا شود در حالی که تولید outline و مقاله روی Claude اجرا می‌شوند، و صیقل روی هر مدلی که برای لحن اعتماد دارید، همه با همان کلید در برابر همان api_base احراز هویت می‌شوند. سمت retrieval ماشین‌آلات جدایی است: runner استورم یک ماژول RM (You.com، Bing، و چند backend جستجوی دیگر) با کلید API خودش می‌گیرد. تغییر جایی که مدل‌های زبانی اشاره می‌کنند نحوه fetch شدن منابع را لمس نمی‌کند.

راه‌اندازی کامل: پنج اسلات، یک dict kwargs.

الگوی کارآمد از اسکریپت‌های اجرای خود repo پیروی می‌کند: kwargs مشترک را یک‌بار بسازید، یک LitellmModel به ازای هر نقش بسازید، و آن‌ها را از طریق setter های STORMWikiLMConfigs اختصاص دهید. api_key می‌تواند هر نامی داشته باشد که دوست دارید چون آن را صریح پاس می‌دهید؛ مثال از متغیر خودش استفاده می‌کند تا واضح کند این یک credential حساب OpenAI نیست. litellm همچنین متغیرهای محیطی سطح-provider را رعایت می‌کند، و provider openai آن OPENAI_API_BASE را می‌خواند، پس یک override فقط-محیطی هم ممکن است. مسیر 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")

انتخاب مدل به ازای هر مرحله pipeline.

پنج setter را یک قرص بودجه در نظر بگیرید، نه boilerplate. راهنمایی upstream از قبل می‌گوید مدل‌های سریع و قوی را در مراحل تقسیم کنید؛ یک endpoint چند-vendor فقط منو را به ازای هر مرحله گسترش می‌دهد. یک اسلات را در هر بار بین اجراهای روی همان موضوع تغییر دهید و خروجی‌ها را diff کنید، با usage log هر-کلید که هر پیکربندی را قیمت‌گذاری می‌کند.

  • conv_simulator_lm و question_asker_lm مراحل حجمی‌اند: مصاحبه‌های شبیه‌سازی‌شده چندنوبتی در چند منظر به ازای هر موضوع. deepseek-v4-flash یا یک id سریع دیگر مرحله پژوهش را از سلطه بر صورت‌حساب باز می‌دارد، و گفتگوی ناقص قابل‌تحمل است چون یادداشت تغذیه می‌کند، نه نثر.
  • article_gen_lm اسلات flagship است. بخش‌های بلند، ساختاریافته، citation-دار را از پژوهش انباشته می‌نویسد، که کار تولید-مداوم است جایی که claude-sonnet-4-6 یا gpt-5.5 به‌وضوح از id های کوچک‌تر برتری دارند.
  • outline_gen_lm فراخوانی‌های کمی با اهرم فوق‌العاده است، همان شکل یک اسلات برنامه‌ریزی: یک outline ضعیف مقاله را سقف می‌زند مهم نیست نویسنده چقدر خوب باشد. جای طبیعی برای تست claude-opus-4-7 است.
  • article_polish_lm برای جریان بازنویسی می‌کند و تکرار در سرتاسر مقاله مونتاژشده را حذف می‌کند، که از یک id long-context سود می‌برد؛ 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.

یک id مدل بدون‌پیشوند با استنتاج مسیردهی می‌شود، نه با api_base شما. litellm پیشوند را می‌خواند تا یک provider انتخاب کند، و یک id بی‌پیشوند Claude به‌عنوان یک فراخوانی native Anthropic استنتاج می‌شود، که سپس ANTHROPIC_API_KEY می‌خواهد و gateway شما را کاملاً نادیده می‌گیرد. هر id ای که به‌سمت gateway می‌رود باید پیشوند openai/ حمل کند؛ پیشوند پروتکل را نام می‌برد، نه vendor را. یک اسلات جاافتاده. هر LitellmModel kwargs خود را در زمان ساخت می‌گیرد. اگر چهار اسلات openai_kwargs را به اشتراک بگذارند و پنجمی بدون api_base دستی ساخته شده باشد، آن اسلات بی‌صدا به پیش‌فرض vendor پست می‌کند و روی auth شکست می‌خورد، و traceback یک مرحله pipeline را نام می‌برد نه یک خط config. همه اسلات‌ها را از همان dict بسازید و این کلاس باگ ناپدید می‌شود. شکست‌های retriever که به endpoint نسبت داده می‌شوند. مرحله پژوهش به یک backend جستجوی کارآمد نیاز دارد؛ یک کلید retriever نامعتبر یا تمام‌شده (YDC_API_KEY، BING_SEARCH_API_KEY، یا هر RM ای که انتخاب کرده‌اید) اجراها را در طول جمع‌آوری اطلاعات شکست می‌دهد. آن مرحله با فراخوانی‌های LM درهم می‌آمیزد، پس قبل از لمس config LM بخوانید traceback کدام client را raise کرده. secrets.toml دمو config اسکریپت شما نیست. دموی Streamlit secrets.toml را می‌خواند؛ اجراهای برنامه‌نویسی‌شده هرچه اسکریپت شما پاس می‌دهد را می‌خوانند. ویرایش یکی در حالی که دیگری اجرا می‌شود یک عدم‌تطابق کلاسیک است. max_tokens هم به‌ازای-هر-اسلات است. مثال‌های STORM محدودیت‌های کوچکی روی اسلات‌های سریع (۵۰۰) و بزرگ‌تر روی تولید (۳۰۰۰) تنظیم می‌کنند. اشاره‌دادن یک اسلات به یک مدل long-form بدون بالابردن max_tokens آن بی‌صدا بخش‌ها را می‌تراشد، که مثل یک مشکل کیفیت مدل به‌نظر می‌رسد اما یک عدد config است.

چه کسانی STORM را از طریق یک gateway مسیردهی می‌کنند.

  • تیم‌هایی که گزارش‌های دانش را با حجم بالا تولید می‌کنند (خلاصه، اسناد داخلی به‌سبک ویکی، مقدمه موضوع)، جایی که تقسیم پنج-اسلات تنظیم هزینه به‌ازای-هر-مرحله را ارزش پول واقعی می‌کند.
  • پژوهشگرانی که ترکیب pipeline را مطالعه می‌کنند: اینکه کدام مرحله از یک مدل قوی‌تر سود می‌برد یک سؤال تجربی است، و یک endpoint شبکه ترکیبات اسلات-مدل را بی‌اهمیت برای شمارش می‌کند.
  • سازندگانی که Claude یا Gemini را در اسلات‌های نوشتاری یک stack به‌شکل OpenAI اجرا می‌کنند، بدون اضافه‌کردن یک SDK vendor به ازای هر خانواده مدل.
  • هر کسی که فهرست‌های موضوع دسته‌ای اجرا می‌کند، جایی که حجم مرحله-پژوهش در سرتاسر موضوعات ضرب می‌شود و usage log به دفتر هزینه به‌ازای-هر-موضوع تبدیل می‌شود.
  • توسعه‌دهندگان بدون دسترسی به صورت‌حساب یک vendor خاص. دسترسی مبتنی بر شارژ بدون الزام کارت وابستگی ثبت‌نام هر-provider را حذف می‌کند.

endpoint را تأیید کنید و اولین مقاله را عیب‌یابی کنید.

ابتدا مدل‌های gateway را فهرست کنید: رشته بعد از openai/ در هر اسلات باید دقیقاً با یک id سرویس‌داده‌شده مطابقت داشته باشد. شکست‌های اجرای اول از ترتیب pipeline پیروی می‌کنند. یک خطای auth که Anthropic یا Google را نام می‌برد یعنی یک id بی‌پیشوند به یک provider بومی مسیردهی شده؛ openai/ اضافه کنید. یک 401 از gateway یعنی api_key در kwargs شما کلید gateway نیست. یک خطای model-not-found اسلاتی که id آن غلط‌تایپی دارد را نام می‌برد. شکست‌های در طول مرحله پژوهش که backend جستجوی شما را ذکر می‌کنند credential های retriever هستند، نه مسیردهی LM. و بخش‌های مقاله کوتاه‌شده یا عجیب معمولاً یک max_tokens خسیس روی اسلات تولید است نه هیچ‌چیز بالادست. یک اجرای کامل STORM یک انفجار بزرگ است: مکالمات شبیه‌سازی‌شده در سرتاسر منظرها، سپس outline، تولید، و صیقل. وقتی یکی تکمیل شود، کنسول APIsRouter مدل، شمارش token، و هزینه هر-درخواست را نشان می‌دهد، که تمیز روی پنج اسلات نگاشت می‌شود و دقیقاً می‌گوید کدام مرحله را قبل از دسته بعدی موضوعات باید دوباره تنظیم کنید.

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 برای provider openai از api_base می‌پذیرد. api_base را به dict openai_kwargs اضافه کنید و هر اسلات ساخته‌شده از آن به gateway مسیردهی می‌شود.

چرا id های مدل به پیشوند openai/ نیاز دارند؟

litellm provider را از پیشوند انتخاب می‌کند. openai/claude-sonnet-4-6 یعنی «پروتکل chat-completions OpenAI را با api_base من با مدل claude-sonnet-4-6 صحبت کن». بدون پیشوند، litellm vendor را از نام استنتاج می‌کند و به‌طور بومی مسیردهی می‌کند، دورزدن endpoint شما.

آیا مراحل مختلف STORM می‌توانند از مدل‌های vendor های مختلف استفاده کنند؟

بله. هر یک از پنج اسلات یک LitellmModel مستقل است، پس شبیه‌ساز مکالمه می‌تواند یک id DeepSeek اجرا کند در حالی که تولید مقاله Claude اجرا می‌کند و صیقل GPT اجرا می‌کند، همه از طریق همان api_base و کلید. upstream از قبل توصیه می‌کند مدل‌های سریع و قوی را در مراحل تقسیم کنید.

آیا retriever جستجو وقتی api_base را تغییر می‌دهم عوض می‌شود؟

خیر. retrieval از طریق ماژول RM ای که به STORMWikiRunner پاس می‌دهید اجرا می‌شود (You.com، Bing، و backend های پشتیبانی‌شده دیگر) با کلید خودش. مسیردهی LM و بازیابی منبع سیستم‌های مستقلی هستند که در مراحل مختلف یک اجرا شکست می‌خورند.

آیا مسیر متغیر-محیطی به‌جای kwargs وجود دارد؟

litellm متغیرهای سطح-provider را رعایت می‌کند، و provider openai آن OPENAI_API_BASE را می‌خواند. کار می‌کند، اما kwarg صریح api_base قابل‌بازتولیدتر است: با اسکریپت سفر می‌کند، روی ماشین‌هایی با وضعیت محیط متفاوت زنده می‌ماند، و استثنائات به‌ازای-هر-اسلات را ممکن می‌کند.

یک مقاله STORM چقدر token مصرف می‌کند؟

مرحله پژوهش غالب است: مکالمات شبیه‌سازی‌شده چندمنظری فراخوانی‌ها را قبل از اینکه یک کلمه از مقاله وجود داشته باشد ضرب می‌کنند، سپس تولید و صیقل خروجی long-form اضافه می‌کنند. اجراهای کامل معمولاً به صدها هزار token می‌رسند، و نمای usage هر-کلید تقسیم دقیق هر-مرحله را نشان می‌دهد.