Uruchom Stanford STORM na niestandardowym endpoincie kompatybilnym z OpenAI.

Updated 2026-09-06

STORM buduje każdy model językowy jako LitellmModel, a litellm akceptuje api_base. Umieść https://api.apisrouter.com/v1 we wspólnym openai_kwargs, poprzedź identyfikatory modeli prefiksem openai/, a wszystkie pięć slotów LM pipeline'u artykułu będzie kierowanych przez jeden endpoint i jeden klucz.

Szybka odpowiedź: api_base w openai_kwargs, prefiks openai/ na identyfikatorach.

LitellmModel w STORM przechowuje dowolne kwargs, z jakimi go konstruujesz, i scala je do każdego wywołania litellm.completion(). Parametr api_base w litellm to sposób, w jaki wskazujesz dostawcę openai na inny host, więc dodanie api_base do słownika openai_kwargs, którego już używają własne przykłady STORM, jest całym nadpisaniem. Poprzedź każdy identyfikator modelu prefiksem openai/, żeby litellm mówił protokołem chat-completions do tej bazy, a string po ukośniku jest przekazywany do bramki.

Ponieważ przykłady budują jeden słownik openai_kwargs i używają go ponownie dla każdego modelu, jeden dodany klucz przekierowuje cały pipeline. Żadnych zmian w kodzie STORM, żadnego forka; to standardowe zachowanie knowledge_storm nałożone na udokumentowany routing 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)

Jak STORM dzieli artykuł na pięć slotów LM.

STORM (stanford-oval na GitHubie, około 30 tys. gwiazdek) pisze od zera raporty w stylu Wikipedii: bada temat przez symulowane rozmowy wieloperspektywiczne, buduje konspekt z tego, czego się nauczył, generuje pełny artykuł sekcja po sekcji, a potem go poleruje. STORMWikiLMConfigs eksponuje ten pipeline jako pięć niezależnie ustawialnych modeli: conv_simulator_lm i question_asker_lm napędzają rozmowy badawcze, outline_gen_lm strukturyzuje artykuł, article_gen_lm go pisze, a article_polish_lm wykonuje ostatnie przejście.

Upstreamowy README jest wprost o ekonomice: symulator rozmów działa z najwyższym wolumenem wywołań, więc rekomenduje tam szybszy model, a mocniejszy model do generowania artykułu. Ta wskazówka zakładała wybór między modelami OpenAI; za wielodostawcowym endpointem uogólnia się w coś bardziej użytecznego. Każdy slot to własny LitellmModel z własnym stringiem modelu, więc badawcza rozmowa może działać na szybkim identyfikatorze DeepSeek, podczas gdy generowanie konspektu i artykułu działa na Claude, a polerowanie na dowolnym modelu, któremu ufasz co do tonu, wszystko uwierzytelnione tym samym kluczem względem tego samego api_base.

Strona retrievalu to osobna maszyneria: runner STORM przyjmuje moduł RM (You.com, Bing i kilka innych backendów wyszukiwania) z własnym kluczem API. Zmiana tego, gdzie wskazują modele językowe, nie dotyka tego, jak pobierane są źródła.

Diagram pokazujący pięć slotów LM STORM (symulator rozmowy, zadający pytania, konspekt, artykuł, polerowanie), każdy zbudowany jako LitellmModel z api_base wskazującym na bramkę APIsRouter pod adresem api.apisrouter.com/v1, rozdzielający ruch do DeepSeek V4 Flash, Claude Sonnet, Claude Opus, GPT-5.5 i Gemini 3.1 Pro.STORM routes through LitellmModel(model='openai/<id>', api_base=...) via shared openai_kwargs to the APIsRouter gateway (api.apisrouter.com/v1), which fans out to: DeepSeek V4 Flash, Claude Sonnet, Claude Opus, GPT-5.5, Gemini 3.1 Pro.STORMviaLitellmModel(model='openai/<id>',api_base=...) viashared openai_kwargsAPIsRouterapi.apisrouter.com/v1DeepSeek V4 FlashClaude SonnetClaude OpusGPT-5.5Gemini 3.1 Pro
Jeden api_base we wspólnych kwargs; każdy etap pipeline'u wybiera własny model z katalogu.

Pełna konfiguracja: pięć slotów, jeden słownik kwargs.

Działający wzorzec odzwierciedla własne skrypty uruchomieniowe repozytorium: zbuduj wspólne kwargs raz, skonstruuj jeden LitellmModel per rola i przypisz je przez settery STORMWikiLMConfigs. api_key może mieć dowolną nazwę, jaką lubisz, ponieważ przekazujesz go jawnie; przykład używa własnej zmiennej, żeby jasno pokazać, że to nie jest poświadczenie konta OpenAI.

litellm honoruje też zmienne środowiskowe na poziomie dostawcy, a dostawca openai czyta OPENAI_API_BASE, więc możliwe jest nadpisanie wyłącznie przez środowisko. Ścieżka jawnych kwargs wciąż jest tą, którą warto preferować: jest widoczna w kodzie, który wyprodukował dany artykuł, przetrwa uruchomienie na maszynie z innym stanem środowiska i umożliwia wyjątki per slot, jeśli kiedykolwiek zechcesz mieć jeden etap na innym endpoincie.

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")

Dobór modeli per etap pipeline'u.

Traktuj pięć setterów jako pokrętło budżetu, nie boilerplate. Wskazówka service już mówi, żeby rozdzielić szybkie i mocne modele między etapy; wielodostawcowy endpoint po prostu poszerza menu per etap. Zmieniaj jeden slot na raz między uruchomieniami na tym samym temacie i porównuj wyniki, a log użycia per klucz wycenia każdą konfigurację.

  • conv_simulator_lm i question_asker_lm to etapy wolumenowe: symulowane wielotorowe wywiady w kilku perspektywach na temat. deepseek-v4-flash albo inny szybki identyfikator powstrzymuje fazę badawczą przed zdominowaniem wydatków, a niedoskonała rozmowa jest tolerowalna, ponieważ karmi notatki, nie prozę.
  • article_gen_lm to slot flagowy. Pisze długie, ustrukturyzowane, cytowane sekcje z zebranych badań, co jest pracą podtrzymanego generowania, gdzie claude-sonnet-4-6 albo gpt-5.5 widocznie przewyższają mniejsze identyfikatory.
  • outline_gen_lm to niewiele wywołań o nieproporcjonalnie dużej dźwigni, ten sam kształt co slot planujący: słaby konspekt ogranicza artykuł, bez względu na to, jak dobry jest piszący. To naturalne miejsce, żeby przetestować claude-opus-4-7.
  • article_polish_lm przepisuje dla płynności i usuwa duplikaty w złożonym artykule, co korzysta z identyfikatora o długim kontekście; warto tu zbenchmarkować gemini-3.1-pro-preview.

Płatność za użycie · poniżej cen oficjalnych

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

ModelCena oficjalnaNasza cena
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

Tryby awarii charakterystyczne dla STORM.

Goły identyfikator modelu jest kierowany przez wnioskowanie, nie przez Twój api_base. litellm czyta prefiks, żeby wybrać dostawcę, a identyfikator Claude bez prefiksu zostaje wywnioskowany jako natywne wywołanie Anthropic, które wtedy chce ANTHROPIC_API_KEY i całkowicie ignoruje Twoją bramkę. Każdy identyfikator zmierzający do bramki musi nosić prefiks openai/; prefiks nazywa protokół, nie dostawcę.

Jeden slot pozostawiony w tyle. Każdy LitellmModel przechwytuje swoje kwargs w momencie konstrukcji. Jeśli cztery sloty dzielą openai_kwargs, a piąty został zbudowany doraźnie bez api_base, ten slot po cichu wysyła do domyślnego dostawcy i zawodzi na uwierzytelnianiu, a traceback nazywa etap pipeline'u, a nie linię konfiguracji. Zbuduj każdy slot z tego samego słownika, a ta klasa błędu znika.

Awarie retrievera obwiniane na endpoint. Faza badawcza potrzebuje działającego backendu wyszukiwania; nieważny albo wyczerpany klucz retrievera (YDC_API_KEY, BING_SEARCH_API_KEY albo dowolny wybrany RM) zawodzi uruchomienia podczas zbierania informacji. Ta faza przeplata się z wywołaniami LM, więc przeczytaj traceback, żeby zobaczyć, który klient rzucił błąd, zanim dotkniesz konfiguracji LM.

secrets.toml demo to nie konfiguracja Twojego skryptu. Demo Streamlit czyta secrets.toml; uruchomienia programistyczne czytają to, co przekazuje Twój skrypt. Edytowanie jednego podczas uruchamiania drugiego to klasyczna niezgodność.

max_tokens jest też per slot. Przykłady STORM ustawiają małe limity na szybkich slotach (500) i większe na generowaniu (3000). Wskazanie slotu na model długoformowy bez podniesienia jego max_tokens po cichu przycina sekcje, co wygląda jak problem jakości modelu, ale jest liczbą konfiguracji.

Kto kieruje STORM przez bramkę.

  • Zespoły generujące raporty wiedzy w wolumenie (briefy, wewnętrzne dokumenty w stylu wiki, wprowadzenia do tematów), gdzie podział na pięć slotów czyni dostrajanie kosztu per etap wartym prawdziwych pieniędzy.
  • Badacze studiujący kompozycję pipeline'u: który etap korzysta z mocniejszego modelu, to pytanie empiryczne, a jeden endpoint czyni siatkę kombinacji slot-model trywialną do wyliczenia.
  • Twórcy uruchamiający Claude albo Gemini w slotach piszących stosu w kształcie OpenAI, bez dodawania SDK dostawcy na każdą rodzinę modeli.
  • Każdy, kto uruchamia wsadowe listy tematów, gdzie wolumen fazy badawczej mnoży się przez tematy, a log użycia staje się księgą kosztów per temat.
  • Deweloperzy bez dostępu do rozliczeń danego dostawcy. Dostęp oparty na doładowaniu, bez wymogu karty, usuwa zależność od rejestracji u każdego dostawcy z osobna.

Zweryfikuj endpoint i debuguj pierwszy artykuł.

Wylistuj najpierw modele bramki: string po openai/ w każdym slocie musi dokładnie zgadzać się z obsługiwanym identyfikatorem.

Awarie pierwszego uruchomienia podążają za kolejnością pipeline'u. Błąd uwierzytelniania wymieniający Anthropic albo Google oznacza, że identyfikator bez prefiksu został skierowany do natywnego dostawcy; dodaj openai/. 401 z bramki oznacza, że api_key w Twoich kwargs nie jest kluczem bramki. Błąd model-not-found nazywa slot, którego identyfikator ma literówkę. Awarie podczas fazy badawczej wspominające Twój backend wyszukiwania to poświadczenia retrievera, nie routing LM. A przycięte albo dziwnie krótkie sekcje artykułu to zwykle skąpe max_tokens na slocie generowania, a nie coś w service.

Pełne uruchomienie STORM to duży wybuch: symulowane rozmowy w różnych perspektywach, potem konspekt, generowanie i polerowanie. Gdy jedno się zakończy, konsola APIsRouter pokazuje model per żądanie, liczbę tokenów i wydatki, co czysto mapuje się na pięć slotów i mówi Ci dokładnie, który etap dostroić przed następną partią tematów.

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

Częste pytania

Jak STORM wspiera niestandardowy endpoint kompatybilny z OpenAI?

Przez litellm. STORM buduje każdy LM jako LitellmModel, który scala swoje kwargs konstruktora do każdego wywołania litellm.completion(), a litellm akceptuje api_base dla dostawcy openai. Dodaj api_base do słownika openai_kwargs, a każdy zbudowany z niego slot kieruje ruch do bramki.

Dlaczego identyfikatory modeli potrzebują prefiksu openai/?

litellm wybiera dostawcę z prefiksu. openai/claude-sonnet-4-6 oznacza "mów protokołem chat-completions OpenAI do mojego api_base z modelem claude-sonnet-4-6". Bez prefiksu litellm wnioskuje dostawcę z nazwy i kieruje natywnie, omijając Twój endpoint.

Czy różne etapy STORM mogą używać modeli różnych dostawców?

Tak. Każdy z pięciu slotów to niezależny LitellmModel, więc symulator rozmowy może działać na identyfikatorze DeepSeek, podczas gdy generowanie artykułu działa na Claude, a polerowanie na GPT, wszystko przez ten sam api_base i klucz. Service już rekomenduje rozdzielanie szybkich i mocnych modeli między etapy.

Czy retriever wyszukiwania zmienia się, gdy zmieniam api_base?

Nie. Retrieval działa przez moduł RM, który przekazujesz do STORMWikiRunner (You.com, Bing i inne wspierane backendy) z własnym kluczem. Routing LM i retrieval źródeł to niezależne systemy, które zawodzą w różnych fazach uruchomienia.

Czy istnieje ścieżka zmiennej środowiskowej zamiast kwargs?

litellm honoruje zmienne na poziomie dostawcy, a dostawca openai czyta OPENAI_API_BASE. To działa, ale jawny kwarg api_base jest bardziej odtwarzalny: podróżuje ze skryptem, przetrwa maszyny z innym stanem środowiska i pozwala na wyjątki per slot.

Ile tokenów zużywa jeden artykuł STORM?

Faza badawcza dominuje: wieloperspektywiczne symulowane rozmowy mnożą wywołania, zanim powstanie choć jedno słowo artykułu, potem generowanie i polerowanie dokładają długoformowe wyjście na wierzch. Pełne uruchomienia zwykle lądują w setkach tysięcy tokenów, a widok użycia per klucz pokazuje dokładny podział per etap.