Fai girare Stanford STORM su un endpoint compatibile OpenAI personalizzato.

Updated 2026-07-29

STORM costruisce ogni modello linguistico come un LitellmModel, e litellm accetta api_base. Metti https://api.apisrouter.com/v1 nel tuo openai_kwargs condiviso, prefissa gli id di modello con openai/, e tutti e cinque gli slot LM della pipeline di articoli si instradano attraverso un endpoint e una chiave.

Risposta rapida: api_base in openai_kwargs, prefisso openai/ sugli id.

Il LitellmModel di STORM memorizza qualsiasi kwargs con cui lo costruisci e li unisce a ogni chiamata litellm.completion(). Il parametro api_base di litellm è come punti il provider openai verso un host diverso, quindi aggiungere api_base al dict openai_kwargs che gli esempi stessi di STORM già usano è l'intero override. Prefissa ogni id di modello con openai/ così che litellm parli il protocollo chat-completions verso quella base, e la stringa dopo la barra viene passata al gateway. Poiché gli esempi costruiscono un unico dict openai_kwargs e lo riusano per ogni modello, una chiave aggiunta reindirizza l'intera pipeline. Nessuna modifica al codice di STORM, nessun fork; questo è comportamento standard di knowledge_storm stratificato sopra il routing documentato di 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)

Come STORM divide un articolo tra cinque slot LM.

STORM (stanford-oval su GitHub, circa 30K stelle) scrive report in stile Wikipedia da zero: ricerca un argomento attraverso conversazioni simulate multi-prospettiva, costruisce una scaletta da ciò che ha imparato, genera l'articolo completo sezione per sezione, e poi lo rifinisce. STORMWikiLMConfigs espone quella pipeline come cinque modelli impostabili in modo indipendente: conv_simulator_lm e question_asker_lm guidano le conversazioni di ricerca, outline_gen_lm struttura l'articolo, article_gen_lm lo scrive, e article_polish_lm fa il passaggio finale. Il README upstream è esplicito sull'economia: il simulatore di conversazione ha il volume di chiamate più alto, quindi raccomanda un modello più veloce lì e un modello più potente per la generazione dell'articolo. Quella indicazione presupponeva di scegliere tra modelli OpenAI; dietro un endpoint multi-fornitore si generalizza in qualcosa di più utile. Ogni slot è il proprio LitellmModel con la propria stringa di modello, quindi la chiacchiera di ricerca può girare su un id DeepSeek veloce mentre la generazione di scaletta e articolo gira su Claude, e la rifinitura su qualsiasi modello di cui ti fidi per il tono, tutti autenticati dalla stessa chiave contro lo stesso api_base. Il lato del recupero è un meccanismo separato: il runner di STORM prende un modulo RM (You.com, Bing, e diversi altri backend di ricerca) con la propria chiave API. Cambiare dove puntano i modelli linguistici non tocca il modo in cui le fonti vengono recuperate.

Configurazione completa: cinque slot, un dict di kwargs.

Il pattern funzionante rispecchia gli script di esecuzione dello stesso repository: costruisci i kwargs condivisi una volta, costruisci un LitellmModel per ruolo, e assegnali tramite i setter di STORMWikiLMConfigs. La api_key può avere qualsiasi nome tu preferisca dato che la passi esplicitamente; l'esempio usa la sua variabile per chiarire che questa non è una credenziale di un account OpenAI. litellm rispetta anche le variabili d'ambiente a livello di provider, e il provider openai legge OPENAI_API_BASE, quindi è possibile un override solo tramite ambiente. Il percorso esplicito con i kwargs resta comunque quello da preferire: è visibile nel codice che ha prodotto un dato articolo, sopravvive all'esecuzione su una macchina con uno stato d'ambiente diverso, e rende possibili eccezioni per singolo slot se mai volessi che una fase usasse un endpoint diverso.

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

Scegliere i modelli per fase della pipeline.

Tratta i cinque setter come una manopola di budget, non come boilerplate. L'indicazione upstream dice già di dividere modelli veloci e potenti tra le fasi; un endpoint multi-fornitore allarga solo il menu per fase. Cambia uno slot alla volta tra un'esecuzione e l'altra sullo stesso argomento e confronta gli output, con il log di utilizzo per chiave che mette un prezzo a ogni configurazione.

  • conv_simulator_lm e question_asker_lm sono le fasi ad alto volume: interviste simulate multi-turno attraverso diverse prospettive per argomento. deepseek-v4-flash o un altro id veloce evita che la fase di ricerca domini la spesa, e una chiacchiera imperfetta è tollerabile perché alimenta appunti, non prosa.
  • article_gen_lm è lo slot di punta. Scrive sezioni lunghe, strutturate e con citazioni a partire dalla ricerca accumulata, il che è un lavoro di generazione sostenuta dove claude-sonnet-4-6 o gpt-5.5 superano visibilmente gli id più piccoli.
  • outline_gen_lm è poche chiamate con una leva sproporzionata, la stessa forma di uno slot di pianificazione: una scaletta debole limita l'articolo per quanto bravo sia lo scrittore. È il posto naturale per testare claude-opus-4-7.
  • article_polish_lm riscrive per il flusso e rimuove le duplicazioni nell'articolo assemblato, il che beneficia di un id a contesto lungo; gemini-3.1-pro-preview merita un benchmark qui.

Pagamento a consumo · sotto i prezzi ufficiali

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

ModelloPrezzo ufficialeIl nostro prezzo
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

Le modalità di errore specifiche di STORM.

Un id di modello nudo si instrada per inferenza, non tramite il tuo api_base. litellm legge il prefisso per scegliere un provider, e un id Claude senza prefisso viene dedotto come chiamata nativa Anthropic, che poi vuole ANTHROPIC_API_KEY e ignora del tutto il tuo gateway. Ogni id diretto al gateway deve portare il prefisso openai/; il prefisso indica il protocollo, non il fornitore. Uno slot lasciato indietro. Ogni LitellmModel cattura i suoi kwargs al momento della costruzione. Se quattro slot condividono openai_kwargs e un quinto è stato costruito ad hoc senza api_base, quello slot posta silenziosamente verso il default del fornitore e fallisce sull'autenticazione, e il traceback nomina una fase della pipeline invece di una riga di configurazione. Costruisci ogni slot dallo stesso dict e questa classe di bug scompare. Fallimenti del retriever attribuiti all'endpoint. La fase di ricerca ha bisogno di un backend di ricerca funzionante; una chiave del retriever non valida o esaurita (YDC_API_KEY, BING_SEARCH_API_KEY, o qualunque RM tu abbia scelto) fa fallire le esecuzioni durante la raccolta di informazioni. Quella fase si intreccia con le chiamate LM, quindi leggi il traceback per capire quale client ha sollevato l'errore prima di toccare la configurazione LM. Il secrets.toml della demo non è la configurazione del tuo script. La demo Streamlit legge secrets.toml; le esecuzioni programmatiche leggono qualsiasi cosa passi il tuo script. Modificare uno mentre esegui l'altro è un classico disallineamento. max_tokens è anch'esso per singolo slot. Gli esempi di STORM impostano limiti piccoli sugli slot veloci (500) e più grandi sulla generazione (3000). Puntare uno slot verso un modello per contenuti lunghi senza alzare il suo max_tokens tronca silenziosamente le sezioni, il che sembra un problema di qualità del modello ma è un numero di configurazione.

Chi instrada STORM tramite un gateway.

  • Team che generano report di conoscenza in volume (brief, documenti interni in stile wiki, primer sugli argomenti), dove la suddivisione in cinque slot rende la regolazione del costo per fase un risparmio reale.
  • Ricercatori che studiano la composizione della pipeline: quale fase beneficia di un modello più potente è una domanda empirica, e un unico endpoint rende banale enumerare la griglia di combinazioni slot-modello.
  • Builder che fanno girare Claude o Gemini negli slot di scrittura di uno stack a forma OpenAI, senza aggiungere un SDK per fornitore per famiglia di modelli.
  • Chiunque esegua liste di argomenti in batch, dove il volume della fase di ricerca si moltiplica tra gli argomenti e il log di utilizzo diventa il libro mastro dei costi per argomento.
  • Sviluppatori senza accesso alla fatturazione di un dato fornitore. L'accesso basato su ricarica senza obbligo di carta rimuove la dipendenza dalla registrazione per provider.

Verifica l'endpoint e fai debug del primo articolo.

Elenca prima i modelli del gateway: la stringa dopo openai/ in ogni slot deve corrispondere esattamente a un id servito. I fallimenti al primo avvio seguono l'ordine della pipeline. Un errore di autenticazione che nomina Anthropic o Google significa che un id senza prefisso si è instradato verso un provider nativo; aggiungi openai/. Un 401 dal gateway significa che la api_key nei tuoi kwargs non è la chiave del gateway. Un errore modello-non-trovato nomina lo slot il cui id ha un errore di battitura. Fallimenti durante la fase di ricerca che menzionano il tuo backend di ricerca sono credenziali del retriever, non routing LM. E sezioni di articolo troncate o stranamente corte di solito sono un max_tokens avaro sullo slot di generazione piuttosto che qualcosa a monte. Un'esecuzione completa di STORM è un grande picco: conversazioni simulate attraverso più prospettive, poi scaletta, generazione e rifinitura. Una volta che una si completa, la console di APIsRouter mostra il modello per richiesta, i conteggi dei token e la spesa, che si mappano in modo pulito sui cinque slot e ti dicono esattamente quale fase regolare prima del prossimo batch di argomenti.

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

Domande frequenti

Come supporta STORM un endpoint compatibile OpenAI personalizzato?

Tramite litellm. STORM costruisce ogni LM come un LitellmModel, che unisce i suoi kwargs del costruttore a ogni chiamata litellm.completion(), e litellm accetta api_base per il provider openai. Aggiungi api_base al dict openai_kwargs e ogni slot costruito da esso si instrada verso il gateway.

Perché gli id di modello hanno bisogno del prefisso openai/?

litellm sceglie il provider dal prefisso. openai/claude-sonnet-4-6 significa "parla il protocollo chat-completions di OpenAI verso il mio api_base con il modello claude-sonnet-4-6". Senza il prefisso, litellm deduce il fornitore dal nome e si instrada in modo nativo, bypassando il tuo endpoint.

Fasi diverse di STORM possono usare modelli di fornitori diversi?

Sì. Ognuno dei cinque slot è un LitellmModel indipendente, quindi il simulatore di conversazione può girare su un id DeepSeek mentre la generazione dell'articolo gira su Claude e la rifinitura su GPT, tutti tramite lo stesso api_base e la stessa chiave. L'indicazione upstream raccomanda già di dividere modelli veloci e potenti tra le fasi.

Il retriever di ricerca cambia quando cambio api_base?

No. Il recupero passa attraverso il modulo RM che passi a STORMWikiRunner (You.com, Bing e altri backend supportati) con la propria chiave. Il routing LM e il recupero delle fonti sono sistemi indipendenti che falliscono in fasi diverse di un'esecuzione.

Esiste un percorso tramite variabili d'ambiente invece dei kwargs?

litellm rispetta le variabili a livello di provider, e il provider openai legge OPENAI_API_BASE. Funziona, ma il kwarg esplicito api_base è più riproducibile: viaggia con lo script, sopravvive a macchine con stati d'ambiente diversi, e permette eccezioni per singolo slot.

Quanti token consuma un articolo di STORM?

La fase di ricerca domina: conversazioni simulate multi-prospettiva moltiplicano le chiamate prima ancora che esista una parola dell'articolo, poi generazione e rifinitura aggiungono sopra output lungo. Le esecuzioni complete arrivano comunemente a centinaia di migliaia di token, e la vista di utilizzo per chiave mostra la suddivisione esatta per fase.