Faites tourner paper-qa contre un endpoint compatible OpenAI personnalisé.

Updated 2026-07-30

paper-qa configure ses modèles via des dicts de routeur LiteLLM, et litellm_params accepte api_base. Pointez-la vers https://api.apisrouter.com/v1, passez une seule clé, et les emplacements answer, summary et agent peuvent chacun faire tourner n'importe quel modèle du catalogue sur votre propre bibliothèque d'articles.

Réponse rapide : un dict de routeur avec api_base, réutilisé par emplacement.

L'objet Settings de paper-qa prend un nom de modèle plus une config de routeur LiteLLM optionnelle par emplacement. La config de routeur est un model_list dont les litellm_params portent api_base et api_key, ce qui est le même motif documenté que le README utilise pour les serveurs compatibles OpenAI hébergés localement ; une passerelle n'est simplement que ce motif avec une URL publique et une vraie clé. Réglez llm et summary_llm sur le model_name que vous avez déclaré, attachez la config aux deux emplacements, et paper-qa route via la passerelle. La chaîne de modèle à l'intérieur de litellm_params garde la convention de fournisseur de litellm : openai/<id> indique à litellm de parler chat-completions à votre api_base, et l'id après la barre oblique est transmis à l'endpoint, donc les ids Claude, GPT, Gemini et GLM sont tous adressables avec le même dict.

gateway_config = dict(
    model_list=[
        dict(
            model_name="claude-sonnet-4-6",
            litellm_params=dict(
                model="openai/claude-sonnet-4-6",
                api_base="https://api.apisrouter.com/v1",
                api_key=os.getenv("APISROUTER_API_KEY"),
                temperature=0.1,
            ),
        )
    ]
)

Où paper-qa dépense des tokens : trois emplacements plus les embeddings.

paper-qa (Future-House sur GitHub, environ 9 000 étoiles) fait de la réponse à des questions à récupération augmentée sur des PDF scientifiques avec une boucle agentique par-dessus : un agent décide quand chercher dans votre bibliothèque, rassemble des extraits de preuve, résume leur pertinence, et compose une réponse citée. Cela se répartit sur trois emplacements LLM configurables séparément. summary_llm évalue et condense la preuve par extrait récupéré, ce qui en fait l'emplacement de volume. llm rédige la réponse finale à partir de la preuve assemblée, l'étape critique pour la qualité. Et agent_llm (dans les réglages de l'agent) prend les décisions de sélection d'outil qui pilotent la boucle. Les trois pointent par défaut vers un modèle OpenAI, et chacun a un champ _config correspondant (llm_config, summary_llm_config, agent_llm_config) qui accepte le même dict de routeur, donc un seul objet de config de passerelle peut être attaché à chaque emplacement tandis que le nom de modèle par emplacement reste indépendant. Une répartition courante est un id rapide qui résume la preuve et un id de pointe qui rédige les réponses, tous deux via un seul endpoint et une seule clé. Les embeddings sont la quatrième charge de travail et délibérément séparés : le réglage embedding (défaut text-embedding-3-small) construit l'index vectoriel de vos articles. Déplacer les emplacements de chat vers une passerelle ne déplace pas les embeddings, et paper-qa supporte les sentence-transformers locaux (le préfixe st-, via les extras locaux) si vous voulez que l'index soit totalement indépendant de tout endpoint distant.

Configuration complète : Settings avec des configs par emplacement.

Le motif complet déclare une entrée de routeur par modèle que vous voulez adressable et attache les configs emplacement par emplacement. Déclarer deux entrées, une rapide pour les résumés et une puissante pour les réponses, garde toute la configuration dans un seul dict. Le même routage fonctionne depuis la CLI, puisque pqa expose la surface de réglages, mais le chemin Python est le reproductible pour un usage de recherche : l'objet Settings qui a produit une réponse peut être journalisé à côté de la réponse elle-même.

import os
from paperqa import Settings, ask
from paperqa.settings import AgentSettings

def entry(model_id, **params):
    return dict(
        model_name=model_id,
        litellm_params=dict(
            model=f"openai/{model_id}",
            api_base="https://api.apisrouter.com/v1",
            api_key=os.getenv("APISROUTER_API_KEY"),
            **params,
        ),
    )

gateway = dict(model_list=[
    entry("claude-sonnet-4-6", temperature=0.1),
    entry("claude-haiku-4-5-20251001", temperature=0.1),
])

answer = ask(
    "What is the evidence for LK-99 room-temperature superconductivity?",
    settings=Settings(
        llm="claude-sonnet-4-6",
        llm_config=gateway,
        summary_llm="claude-haiku-4-5-20251001",
        summary_llm_config=gateway,
        agent=AgentSettings(
            agent_llm="claude-sonnet-4-6",
            agent_llm_config=gateway,
        ),
        paper_directory="./papers",
    ),
)

Choisir des modèles par emplacement.

Ajustez avec le pipeline de preuve fixe : même bibliothèque, mêmes questions, changez un emplacement à la fois. Derrière un seul endpoint, chaque candidat n'est qu'une chaîne model_name, et le journal d'usage par clé chiffre chaque configuration par question, le chiffre sur lequel un labo budgète réellement.

  • summary_llm tourne une fois par extrait de preuve, à chaque question. Sur une bibliothèque sérieuse, c'est l'écrasante majorité des appels, donc un id rapide (claude-haiku-4-5-20251001) fixe le plancher de coût pour tout le système tout en n'ayant qu'à juger la pertinence, pas à rédiger de la prose.
  • llm compose la réponse citée à partir de la preuve assemblée. C'est là qu'une écriture scientifique nuancée et précise se produit ou non ; claude-sonnet-4-6 et gpt-5.5 sont les choix fiables, et l'emplacement représente peu d'appels par question donc le surcoût est borné.
  • agent_llm pilote la boucle : chercher encore, rassembler plus de preuve, ou répondre. Des décisions faibles ici gaspillent des tokens partout ailleurs, ce qui fait d'un id de niveau intermédiaire ou meilleur le choix économique malgré le faible volume de l'emplacement.
  • Les ids à long contexte comme gemini-3.1-pro-preview valent la peine d'être testés comme emplacement answer quand les questions tirent de la preuve depuis de nombreux articles à la fois.

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
Claude Sonnet 4.6$3.00 / $15.00 per M$2.40 / $12.00 per M
Claude Haiku 4.5 20251001$1.00 / $5.00 per M$0.80 / $4.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
GLM-5.2$1.14 / $4.00 per M$1.10 / $4.00 per M

Les modes d'échec spécifiques à paper-qa.

Un emplacement laissé sur son défaut. Régler llm et llm_config mais pas summary_llm_config laisse la summarisation sur le modèle OpenAI par défaut, qui demande alors OPENAI_API_KEY et échoue (ou répartit silencieusement votre routage sur deux endpoints si cette clé existe). Chaque emplacement a son propre champ _config ; attachez le dict de passerelle à chaque emplacement que vous comptez déplacer, agent_llm_config inclus. Des noms qui ne s'alignent pas. Settings.llm doit être égal à un model_name dans model_list ; litellm_params.model est ce qui part réellement sur le fil. Faites un mauvais nom externe et le routeur n'a pas de route ; faites une faute de frappe dans l'id interne et la passerelle renvoie model-not-found. En débogant, vérifiez les deux chaînes séparément parce qu'elles échouent différemment. Supposer que les embeddings suivent. L'emplacement embedding construit et interroge l'index vectoriel et a son propre défaut et sa propre config. Si vous n'avez pas de clé OpenAI pour l'embedding par défaut, configurez embedding explicitement, ou utilisez des sentence-transformers locaux via le préfixe st-. Repointer les embeddings plus tard signifie aussi réindexer : les vecteurs de différents modèles d'embedding ne se mélangent pas. Des limites de génération manquantes pour les longues réponses. litellm_params accepte max_tokens par entrée, et les exemples d'endpoint local en amont le règlent délibérément. Un emplacement answer sans limite raisonnable peut tronquer de longues réponses citées, ce qui se présente comme une faiblesse du modèle mais est un paramètre. Blâmer le routage pour des problèmes d'analyse. La qualité de paper-qa dépend de l'analyse et du découpage des PDF avant qu'aucun modèle ne voie du texte. Si les réponses ne citent rien sur une bibliothèque que vous savez pertinente, inspectez l'étape d'indexation ; la passerelle ne voit que ce que lui envoie la récupération.

Qui route paper-qa via une passerelle.

  • Les groupes de recherche qui font tourner du QA de littérature sur des bibliothèques partagées, où l'usage par clé transforme "combien dépense le labo par question" d'une supposition en un rapport.
  • Les équipes qui veulent une écriture scientifique de qualité Claude dans l'emplacement answer tout en gardant le volume de summarisation sur un id rapide, une seule clé pour les deux.
  • Les développeurs qui intègrent paper-qa dans des outils internes, remplaçant un lot de secrets fournisseur par un seul identifiant de passerelle par environnement.
  • Ceux qui font des benchmarks comparant des modèles de réponse sur des pipelines de preuve fixes, où chaque candidat n'est qu'une chaîne de config plutôt qu'une intégration fournisseur.
  • 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 la première question.

Confirmez que la passerelle sert les ids que vous avez déclarés ; la chaîne litellm_params.model après openai/ doit correspondre exactement à un id servi. L'échelle des échecs sur un premier ask() : une erreur demandant OPENAI_API_KEY signifie qu'un emplacement est encore sur son modèle par défaut sans config attachée ; trouvez lequel de llm, summary_llm et agent_llm vous n'avez pas déplacé. Un 401 de la passerelle vient de l'api_key à l'intérieur de litellm_params. Une erreur de routeur à propos d'un modèle inconnu signifie que Settings.llm ne correspond à aucun model_name dans la liste. Des échecs pendant l'indexation plutôt que la réponse pointent vers le réglage embedding ou l'analyse PDF, pas le routage du chat. Une question se répartit en de nombreux appels de résumé plus des étapes d'agent plus la réponse finale, donc après la première exécution réussie, la vue par requête de la console APIsRouter montre la répartition par emplacement en tokens réels. C'est le chiffre à surveiller à mesure que la bibliothèque grandit, parce que le volume de résumé évolue avec la preuve récupérée, pas seulement avec le nombre de questions.

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

Questions fréquentes

Comment paper-qa prend-il en charge une URL de base compatible OpenAI personnalisée ?

Via ses configs de routeur LiteLLM : chacun de llm_config, summary_llm_config et agent_llm_config accepte un model_list dont les litellm_params incluent api_base et api_key. C'est le même motif documenté que paper-qa utilise pour les serveurs compatibles OpenAI hébergés localement, pointé à la place vers une URL de passerelle.

Les modèles answer et summary peuvent-ils venir de fournisseurs différents ?

Oui. Chaque emplacement associe un nom de modèle à sa propre config, donc un id Claude rapide peut résumer la preuve tandis que GPT-5.5 ou Gemini rédige la réponse finale, tous via un seul api_base et une seule clé. Déclarez une entrée model_list par id et référencez-les par emplacement.

Dois-je aussi changer le modèle d'embedding ?

Non, et généralement vous ne devriez pas le faire dans la même étape. Le réglage embedding est indépendant des emplacements de chat, et changer de modèle d'embedding invalide votre index vectoriel existant. Si vous manquez de clé pour l'embedding par défaut, réglez embedding explicitement ou utilisez des sentence-transformers locaux avec le préfixe st-.

Qu'est-ce que l'emplacement agent_llm et a-t-il aussi besoin de la config ?

agent_llm, dans AgentSettings, pilote la sélection d'outils : quand chercher, rassembler de la preuve, ou répondre. Il pointe par défaut vers un modèle OpenAI comme les autres emplacements, donc attachez agent_llm_config avec le même dict de passerelle sinon il essaiera toujours de router vers le fournisseur par défaut.

Pourquoi paper-qa demande-t-il encore OPENAI_API_KEY après ma surcharge ?

Au moins un emplacement est encore sur son modèle par défaut sans config de routeur attachée. Vérifiez llm, summary_llm et agent_llm plus leurs champs _config ; l'erreur nomme le modèle qu'il a essayé d'appeler, ce qui identifie l'emplacement que vous avez manqué.

Cela fonctionne-t-il depuis la CLI pqa aussi bien qu'en Python ?

La CLI expose la même surface de réglages, mais pour le routage via passerelle, le chemin Python est le pratique : les dicts de routeur sont maladroits comme flags de ligne de commande, et un objet Settings journalisé à côté des résultats rend les exécutions de recherche reproductibles.