Rode o Stanford STORM em um endpoint personalizado compatível com OpenAI.

Updated 2026-07-29

O STORM constrói todo modelo de linguagem como um LitellmModel, e o litellm aceita api_base. Coloque https://api.apisrouter.com/v1 no seu openai_kwargs compartilhado, prefixe ids de modelo com openai/, e todos os cinco slots de LM do pipeline de artigo roteiam por um endpoint e uma chave.

Resposta rápida: api_base em openai_kwargs, prefixo openai/ nos ids.

O LitellmModel do STORM armazena quaisquer kwargs com os quais você o constrói e os mescla em toda chamada litellm.completion(). O parâmetro api_base do litellm é como você aponta o provedor openai para um host diferente, então adicionar api_base ao dict openai_kwargs que os próprios exemplos do STORM já usam é a sobrescrita inteira. Prefixe cada id de modelo com openai/ para que o litellm fale o protocolo chat-completions com essa base, e a string depois da barra é passada direto para o gateway. Como os exemplos montam um dict openai_kwargs e o reutilizam para todo modelo, uma chave adicionada reroteia o pipeline inteiro. Nenhuma mudança de código do STORM, nenhum fork; isso é comportamento padrão do knowledge_storm em camadas sobre o roteamento documentado do 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)

Como o STORM divide um artigo em cinco slots de LM.

O STORM (stanford-oval no GitHub, cerca de 30 mil estrelas) escreve relatórios no estilo Wikipedia do zero: pesquisa um tópico através de conversas simuladas multi-perspectiva, constrói um esboço a partir do que aprendeu, gera o artigo completo seção por seção, e depois polimenta. STORMWikiLMConfigs expõe esse pipeline como cinco modelos configuráveis independentemente: conv_simulator_lm e question_asker_lm comandam as conversas de pesquisa, outline_gen_lm estrutura o artigo, article_gen_lm o escreve, e article_polish_lm faz a passada final. O README upstream é explícito sobre a economia: o simulador de conversa roda o maior volume de chamadas, então recomenda um modelo mais rápido ali e um modelo mais poderoso para geração de artigo. Essa orientação assumia escolher entre modelos da OpenAI; atrás de um endpoint multi-fornecedor ela generaliza para algo mais útil. Cada slot é seu próprio LitellmModel com sua própria string de modelo, então a conversa de pesquisa pode rodar em um id DeepSeek rápido enquanto geração de esboço e artigo rodam em Claude, e o polimento em qualquer modelo que você confie para o tom, tudo autenticado pela mesma chave contra o mesmo api_base. O lado de recuperação é maquinaria separada: o runner do STORM recebe um módulo RM (You.com, Bing, e vários outros backends de busca) com sua própria chave de API. Mudar para onde os modelos de linguagem apontam não toca em como as fontes são buscadas.

Configuração completa: cinco slots, um dict de kwargs.

O padrão que funciona espelha os próprios scripts de execução do repositório: monte os kwargs compartilhados uma vez, construa um LitellmModel por papel, e atribua-os pelos setters de STORMWikiLMConfigs. O api_key pode ter qualquer nome que você quiser já que você o passa explicitamente; o exemplo usa sua própria variável para deixar claro que essa não é uma credencial de conta OpenAI. O litellm também honra variáveis de ambiente no nível de provedor, e o provedor openai lê OPENAI_API_BASE, então uma sobrescrita só por ambiente é possível. O caminho explícito de kwargs ainda é o preferível: ele fica visível no código que produziu um artigo específico, sobrevive a ser rodado em uma máquina com estado de ambiente diferente, e torna possíveis exceções por slot se você algum dia quiser uma etapa em um endpoint diferente.

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

Escolhendo modelos por etapa do pipeline.

Trate os cinco setters como um dial de orçamento, não boilerplate. A orientação upstream já diz para dividir modelos rápidos e fortes entre etapas; um endpoint multi-fornecedor só amplia o cardápio por etapa. Mude um slot de cada vez entre execuções sobre o mesmo tópico e compare as saídas, com o log de uso por chave precificando cada configuração.

  • conv_simulator_lm e question_asker_lm são as etapas de volume: entrevistas simuladas multi-turno através de várias perspectivas por tópico. deepseek-v4-flash ou outro id rápido evita que a fase de pesquisa domine o gasto, e conversa imperfeita é tolerável porque alimenta anotações, não prosa.
  • article_gen_lm é o slot carro-chefe. Ele escreve seções longas, estruturadas, e citadas a partir da pesquisa acumulada, o que é trabalho de geração sustentada onde claude-sonnet-4-6 ou gpt-5.5 supera visivelmente ids menores.
  • outline_gen_lm são poucas chamadas com influência desproporcional, o mesmo formato de um slot de planejamento: um esboço fraco limita o artigo não importa quão bom seja o escritor. É o lugar natural para testar claude-opus-4-7.
  • article_polish_lm reescreve para fluidez e remove duplicação através do artigo montado, o que se beneficia de um id de contexto longo; gemini-3.1-pro-preview vale a pena testar aqui.

Pague pelo uso · abaixo do preço oficial

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

ModeloPreço oficialNosso preço
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

Os modos de falha específicos do STORM.

Um id de modelo nu roteia por inferência, não pelo seu api_base. O litellm lê o prefixo para escolher um provedor, e um id Claude sem prefixo é inferido como uma chamada nativa Anthropic, que então quer ANTHROPIC_API_KEY e ignora seu gateway inteiramente. Todo id destinado ao gateway precisa carregar o prefixo openai/; o prefixo nomeia o protocolo, não o fornecedor. Um slot deixado para trás. Cada LitellmModel captura seus kwargs na construção. Se quatro slots compartilham openai_kwargs e um quinto foi construído ad hoc sem api_base, esse slot silenciosamente publica para o padrão do fornecedor e falha na autenticação, e o traceback nomeia uma etapa do pipeline em vez de uma linha de configuração. Construa todo slot a partir do mesmo dict e essa classe de bug desaparece. Falhas de retriever culpadas no endpoint. A fase de pesquisa precisa de um backend de busca funcionando; uma chave de retriever inválida ou esgotada (YDC_API_KEY, BING_SEARCH_API_KEY, ou qualquer RM que você escolheu) falha execuções durante a coleta de informação. Essa fase se intercala com chamadas de LM, então leia o traceback para ver qual cliente disparou o erro antes de mexer na configuração de LM. O secrets.toml da demo não é a configuração do seu script. A demo Streamlit lê secrets.toml; execuções programáticas leem o que quer que seu script passe. Editar um enquanto roda o outro é um descompasso clássico. max_tokens também é por slot. Os exemplos do STORM definem limites pequenos nos slots rápidos (500) e maiores na geração (3000). Apontar um slot para um modelo de forma longa sem elevar seu max_tokens trunca seções silenciosamente, o que parece um problema de qualidade de modelo mas é um número de configuração.

Quem roteia o STORM por um gateway.

  • Times gerando relatórios de conhecimento em volume (briefings, documentos internos no estilo wiki, primers de tópico), onde a divisão de cinco slots torna o ajuste de custo por etapa dinheiro de verdade.
  • Pesquisadores estudando composição de pipeline: qual etapa se beneficia de um modelo mais forte é uma pergunta empírica, e um endpoint torna trivial enumerar a grade de combinações slot-modelo.
  • Criadores rodando Claude ou Gemini nos slots de escrita de uma stack no formato OpenAI, sem adicionar um SDK de fornecedor por família de modelo.
  • Qualquer um rodando listas de tópicos em lote, onde o volume da fase de pesquisa se multiplica pelos tópicos e o log de uso vira o livro-razão de custo por tópico.
  • Desenvolvedores sem acesso ao faturamento de um determinado fornecedor. O acesso baseado em recarga sem exigência de cartão remove a dependência de cadastro por provedor.

Verifique o endpoint e depure o primeiro artigo.

Liste os modelos do gateway primeiro: a string depois de openai/ em cada slot precisa corresponder exatamente a um id servido. Falhas na primeira execução seguem a ordem do pipeline. Um erro de autenticação nomeando Anthropic ou Google significa que um id sem prefixo roteou para um provedor nativo; adicione openai/. Um 401 do gateway significa que a api_key nos seus kwargs não é a chave do gateway. Um erro model-not-found nomeia o slot cujo id tem um erro de digitação. Falhas durante a fase de pesquisa que mencionam seu backend de busca são credenciais de retriever, não roteamento de LM. E seções de artigo truncadas ou estranhamente curtas geralmente são um max_tokens apertado demais no slot de geração em vez de qualquer coisa upstream. Uma execução completa do STORM é uma grande explosão: conversas simuladas através de perspectivas, depois esboço, geração, e polimento. Assim que uma completa, o console da APIsRouter mostra modelo, contagens de token e gasto por requisição, que mapeia de forma limpa nos cinco slots e te diz exatamente qual etapa reajustar antes do próximo lote de tópicos.

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

Perguntas frequentes

Como o STORM suporta um endpoint personalizado compatível com OpenAI?

Através do litellm. O STORM constrói todo LM como um LitellmModel, que mescla seus kwargs de construtor em toda chamada litellm.completion(), e o litellm aceita api_base para o provedor openai. Adicione api_base ao dict openai_kwargs e todo slot construído a partir dele roteia para o gateway.

Por que ids de modelo precisam do prefixo openai/?

O litellm escolhe o provedor pelo prefixo. openai/claude-sonnet-4-6 significa "fale o protocolo chat-completions da OpenAI com meu api_base usando o modelo claude-sonnet-4-6". Sem o prefixo, o litellm infere o fornecedor pelo nome e roteia nativamente, ignorando seu endpoint.

Etapas diferentes do STORM podem usar modelos de fornecedores diferentes?

Sim. Cada um dos cinco slots é um LitellmModel independente, então o simulador de conversa pode rodar um id DeepSeek enquanto a geração de artigo roda Claude e o polimento roda GPT, tudo através do mesmo api_base e chave. O upstream já recomenda dividir modelos rápidos e fortes entre etapas.

O retriever de busca muda quando eu mudo api_base?

Não. A recuperação roda através do módulo RM que você passa para STORMWikiRunner (You.com, Bing, e outros backends suportados) com sua própria chave. Roteamento de LM e recuperação de fonte são sistemas independentes que falham em fases diferentes de uma execução.

Existe um caminho de variável de ambiente em vez de kwargs?

O litellm honra variáveis no nível de provedor, e o provedor openai lê OPENAI_API_BASE. Funciona, mas o kwarg explícito api_base é mais reproduzível: ele viaja com o script, sobrevive a máquinas com estado de ambiente diferente, e permite exceções por slot.

Quantos tokens um artigo do STORM consome?

A fase de pesquisa domina: conversas simuladas multi-perspectiva multiplicam chamadas antes de uma palavra do artigo existir, depois geração e polimento adicionam saída de forma longa em cima. Execuções completas comumente chegam a centenas de milhares de tokens, e a visão de uso por chave mostra a divisão exata por etapa.