Faites tourner Stanford STORM sur un endpoint compatible OpenAI personnalisé.

Updated 2026-07-29

STORM construit chaque modèle de langage comme un LitellmModel, et litellm accepte api_base. Mettez https://api.apisrouter.com/v1 dans votre openai_kwargs partagé, préfixez les ids de modèles avec openai/, et les cinq emplacements LM du pipeline d'article acheminent via un seul endpoint et une seule clé.

Réponse rapide : api_base dans openai_kwargs, préfixe openai/ sur les ids.

Le LitellmModel de STORM stocke quels que soient les kwargs avec lesquels vous le construisez et les fusionne dans chaque appel litellm.completion(). Le paramètre api_base de litellm est la façon de pointer le fournisseur openai vers un hôte différent, donc ajouter api_base au dict openai_kwargs que les propres exemples de STORM utilisent déjà est toute la surcharge. Préfixez chaque id de modèle avec openai/ pour que litellm parle le protocole chat-completions à cette base, et la chaîne après la barre oblique est transmise telle quelle à la passerelle. Comme les exemples construisent un seul dict openai_kwargs et le réutilisent pour chaque modèle, une seule clé ajoutée reroute tout le pipeline. Aucun changement de code STORM, aucun fork ; c'est un comportement standard de knowledge_storm superposé au routage documenté de 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)

Comment STORM répartit un article sur cinq emplacements LM.

STORM (stanford-oval sur GitHub, environ 30 000 étoiles) écrit des rapports de style Wikipédia depuis zéro : il fait des recherches sur un sujet via des conversations simulées multi-perspectives, construit un plan à partir de ce qu'il a appris, génère l'article complet section par section, puis le peaufine. STORMWikiLMConfigs expose ce pipeline comme cinq modèles réglables indépendamment : conv_simulator_lm et question_asker_lm pilotent les conversations de recherche, outline_gen_lm structure l'article, article_gen_lm l'écrit, et article_polish_lm fait la passe finale. Le README officiel est explicite sur l'économie : le simulateur de conversation a le plus haut volume d'appels, donc il recommande un modèle plus rapide là et un modèle plus puissant pour la génération d'article. Cette recommandation supposait de choisir entre modèles OpenAI ; derrière un endpoint multi-fournisseurs, elle se généralise en quelque chose de plus utile. Chaque emplacement est son propre LitellmModel avec sa propre chaîne de modèle, donc le bavardage de recherche peut tourner sur un id DeepSeek rapide tandis que la génération de plan et d'article tourne sur Claude, et le peaufinage sur quel que soit le modèle en qui vous avez confiance pour le ton, tous authentifiés par la même clé contre le même api_base. Le côté récupération est une machinerie séparée : le runner de STORM prend un module RM (You.com, Bing, et plusieurs autres backends de recherche) avec sa propre clé API. Changer où pointent les modèles de langage ne touche pas la façon dont les sources sont récupérées.

Configuration complète : cinq emplacements, un seul dict de kwargs.

Le motif qui fonctionne reflète les propres scripts d'exécution du dépôt : construisez les kwargs partagés une fois, construisez un LitellmModel par rôle, et assignez-les via les setters de STORMWikiLMConfigs. L'api_key peut porter n'importe quel nom puisque vous la passez explicitement ; l'exemple utilise sa propre variable pour bien montrer que ce n'est pas un identifiant de compte OpenAI. litellm honore aussi des variables d'environnement au niveau du fournisseur, et le fournisseur openai lit OPENAI_API_BASE, donc une surcharge uniquement par environnement est possible. Le chemin explicite par kwargs reste celui à préférer : il est visible dans le code qui a produit un article donné, il survit à une exécution sur une machine avec un état d'environnement différent, et il rend possibles des exceptions par emplacement si jamais vous voulez qu'une étape utilise un endpoint différent.

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

Choisir des modèles par étape du pipeline.

Traitez les cinq setters comme un cadran de budget, pas du boilerplate. Les recommandations officielles disent déjà de répartir des modèles rapides et puissants entre les étapes ; un endpoint multi-fournisseurs élargit simplement le menu par étape. Changez un emplacement à la fois entre des exécutions sur le même sujet et comparez les sorties, avec le journal d'usage par clé chiffrant chaque configuration.

  • conv_simulator_lm et question_asker_lm sont les étapes de volume : des interviews simulées multi-tours à travers plusieurs perspectives par sujet. deepseek-v4-flash ou un autre id rapide empêche la phase de recherche de dominer la dépense, et un bavardage imparfait est tolérable parce qu'il alimente des notes, pas de la prose.
  • article_gen_lm est l'emplacement phare. Il écrit de longues sections structurées et citées à partir de la recherche accumulée, un travail de génération soutenue où claude-sonnet-4-6 ou gpt-5.5 surpasse visiblement les ids plus petits.
  • outline_gen_lm est peu d'appels avec un effet de levier démesuré, la même forme qu'un emplacement de planification : un plan faible plafonne l'article peu importe la qualité du rédacteur. C'est l'endroit naturel pour tester claude-opus-4-7.
  • article_polish_lm réécrit pour la fluidité et retire les doublons à travers l'article assemblé, ce qui bénéficie d'un id longue contexte ; gemini-3.1-pro-preview vaut la peine d'être benchmarké ici.

Paiement à l'usage · en dessous du tarif officiel

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

ModèlePrix officielNotre prix
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

Les modes d'échec spécifiques à STORM.

Un id de modèle nu route par inférence, pas par votre api_base. litellm lit le préfixe pour choisir un fournisseur, et un id Claude sans préfixe est déduit comme un appel natif Anthropic, qui veut alors ANTHROPIC_API_KEY et ignore entièrement votre passerelle. Chaque id destiné à la passerelle doit porter le préfixe openai/ ; le préfixe nomme le protocole, pas le fournisseur. Un emplacement laissé de côté. Chaque LitellmModel capture ses kwargs à la construction. Si quatre emplacements partagent openai_kwargs et qu'un cinquième a été construit ad hoc sans api_base, cet emplacement poste silencieusement vers le défaut du fournisseur et échoue sur l'authentification, et la trace nomme une étape du pipeline plutôt qu'une ligne de config. Construisez chaque emplacement à partir du même dict et cette classe de bug disparaît. Des échecs de retriever attribués à tort à l'endpoint. La phase de recherche a besoin d'un backend de recherche fonctionnel ; une clé de retriever invalide ou épuisée (YDC_API_KEY, BING_SEARCH_API_KEY, ou quel que soit le RM que vous avez choisi) fait échouer les exécutions pendant la collecte d'informations. Cette phase s'entrelace avec les appels LM, donc lisez la trace pour savoir quel client a levé une erreur avant de toucher à la config LM. Le secrets.toml de la démo n'est pas la config de votre script. La démo Streamlit lit secrets.toml ; les exécutions programmatiques lisent ce que passe votre script. Éditer l'un en exécutant l'autre est un décalage classique. max_tokens est aussi par emplacement. Les exemples de STORM règlent de petites limites sur les emplacements rapides (500) et plus grandes sur la génération (3000). Pointer un emplacement vers un modèle de longue forme sans relever son max_tokens tronque silencieusement des sections, ce qui ressemble à un problème de qualité de modèle mais est un chiffre de config.

Qui route STORM via une passerelle.

  • Les équipes qui génèrent des rapports de connaissance en volume (briefs, docs internes de style wiki, introductions à des sujets), où la répartition en cinq emplacements rend l'ajustement de coût par étape rentable en vrai argent.
  • Les chercheurs qui étudient la composition du pipeline : quelle étape bénéficie d'un modèle plus puissant est une question empirique, et un seul endpoint rend triviale l'énumération de la grille de combinaisons emplacement-modèle.
  • Les créateurs qui font tourner Claude ou Gemini dans les emplacements de rédaction d'une pile à la forme OpenAI, sans ajouter un SDK fournisseur par famille de modèles.
  • Quiconque fait tourner des listes de sujets en lot, où le volume de la phase de recherche se multiplie à travers les sujets et le journal d'usage devient le grand livre de coûts par sujet.
  • Les développeurs sans accès à la facturation d'un fournisseur donné. Un accès basé sur la recharge, sans exigence de carte, supprime la dépendance à une inscription par fournisseur.

Vérifiez l'endpoint et déboguez le premier article.

Listez d'abord les modèles de la passerelle : la chaîne après openai/ dans chaque emplacement doit correspondre exactement à un id servi. Les échecs au premier lancement suivent l'ordre du pipeline. Une erreur d'authentification nommant Anthropic ou Google signifie qu'un id sans préfixe a été routé vers un fournisseur natif ; ajoutez openai/. Un 401 de la passerelle signifie que l'api_key dans vos kwargs n'est pas la clé de la passerelle. Une erreur model-not-found nomme l'emplacement dont l'id a une faute de frappe. Les échecs pendant la phase de recherche qui mentionnent votre backend de recherche sont des identifiants de retriever, pas du routage LM. Et des sections d'article tronquées ou étrangement courtes tiennent généralement à un max_tokens chiche sur l'emplacement de génération plutôt qu'à quoi que ce soit en amont. Une exécution complète de STORM est une grande salve : des conversations simulées à travers des perspectives, puis plan, génération et peaufinage. Une fois qu'une exécution se termine, la console APIsRouter affiche le modèle par requête, le nombre de tokens et la dépense, ce qui se cartographie proprement sur les cinq emplacements et vous dit exactement quelle étape réajuster avant le prochain lot de sujets.

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

Questions fréquentes

Comment STORM prend-il en charge un endpoint compatible OpenAI personnalisé ?

Via litellm. STORM construit chaque LM comme un LitellmModel, qui fusionne ses kwargs de constructeur dans chaque appel litellm.completion(), et litellm accepte api_base pour le fournisseur openai. Ajoutez api_base au dict openai_kwargs et chaque emplacement construit à partir de lui route vers la passerelle.

Pourquoi les ids de modèles ont-ils besoin du préfixe openai/ ?

litellm choisit le fournisseur d'après le préfixe. openai/claude-sonnet-4-6 signifie « parle le protocole chat-completions OpenAI à mon api_base avec le modèle claude-sonnet-4-6 ». Sans le préfixe, litellm déduit le fournisseur du nom et route nativement, en contournant votre endpoint.

Différentes étapes de STORM peuvent-elles utiliser des modèles de différents fournisseurs ?

Oui. Chacun des cinq emplacements est un LitellmModel indépendant, donc le simulateur de conversation peut faire tourner un id DeepSeek tandis que la génération d'article tourne sur Claude et le peaufinage sur GPT, tous via le même api_base et la même clé. Le projet officiel recommande déjà de répartir des modèles rapides et puissants entre les étapes.

Le retriever de recherche change-t-il quand je change api_base ?

Non. La récupération tourne via le module RM que vous passez à STORMWikiRunner (You.com, Bing, et d'autres backends supportés) avec sa propre clé. Le routage LM et la récupération de sources sont des systèmes indépendants qui échouent dans différentes phases d'une exécution.

Existe-t-il un chemin par variable d'environnement plutôt que par kwargs ?

litellm honore des variables au niveau du fournisseur, et le fournisseur openai lit OPENAI_API_BASE. Ça fonctionne, mais le kwarg explicite api_base est plus reproductible : il voyage avec le script, survit à des machines avec un état d'environnement différent, et permet des exceptions par emplacement.

Combien de tokens consomme un article STORM ?

La phase de recherche domine : des conversations simulées multi-perspectives multiplient les appels avant qu'un seul mot de l'article n'existe, puis la génération et le peaufinage ajoutent de la sortie longue forme par-dessus. Les exécutions complètes atterrissent couramment dans les centaines de milliers de tokens, et la vue d'usage par clé montre la répartition exacte par étape.