paper-qa API 配置

最后更新: 2026-07-16

paper-qa 的 Settings 对象给每个模型槽位配一份 LiteLLM 路由字典,litellm_params 里的 api_base 就是网关地址填的地方。填 https://api.kkaiapi.com/v1,传一个 Key,回答、摘要、Agent 三个槽位就都能各自挑目录里的模型,在自己的论文库上做检索增强问答。

先说结论:一份路由字典,三个槽位复用

paper-qa 的 Settings 对象接受一个模型名加一份可选的 LiteLLM 路由配置,按槽位分别指定。路由配置是一个 model_list,里面每一项的 litellm_params 都带 api_base 和 api_key,这和 paper-qa 官方 README 里给本地部署的 OpenAI 兼容服务器用的配置写法完全一样,网关不过是把这套写法换成一个公网地址和一把真实的 Key。把 llm 和 summary_llm 都设成你在 model_list 里声明的那个 model_name,两个槽位都挂上同一份配置,请求就会经网关路由。 litellm_params 里的模型字符串保留 litellm 的 provider 前缀写法:openai/<id> 告诉 litellm 用 chat-completions 协议跟你的 api_base 对话,斜杠后面的 id 原样转发给端点,所以 Claude、GPT、Gemini、GLM 的 ID 都能用同一份字典寻址。

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

paper-qa 的 token 花在哪:三个槽位加 Embedding

paper-qa(GitHub 上的 Future-House,约 9K 星标)对科研 PDF 做检索增强问答,上面再叠一层带 Agent 逻辑的循环:一个 Agent 决定什么时候检索论文库,收集证据片段,给相关性打摘要,再组装出一份带引用的答案。这对应三个可以分别配置的 LLM 槽位。summary_llm 对每一个被检索到的证据片段做评估和压缩,是走量的槽位。llm 从组装好的证据里写出最终答案,是质量决定成败的一步。agent_llm(在 agent 设置里)做工具选择的决策,决定循环怎么走。 三个槽位默认都是 OpenAI 模型,各自有一个对应的 _config 字段(llm_config、summary_llm_config、agent_llm_config)接受同样的路由字典,所以一个网关配置对象可以分别挂到每个槽位上,每个槽位的模型名彼此独立。常见的搭配是快速 ID 做摘要、旗舰 ID 写答案,两者都经同一个端点和 Key。 Embedding 是第四类工作负载,而且是刻意独立出来的:embedding 设置(默认 text-embedding-3-small)负责给论文库建向量索引。把对话槽位挪到网关上不会连带挪动 Embedding,kkaiapi 目前不提供 Embedding 模型,如果想让向量索引完全独立于任何远程端点,paper-qa 也支持本地 sentence-transformers(st- 前缀,通过本地扩展包)。

完整配置:带三槽独立设置的 Settings

完整做法是给每个想要能寻址的模型声明一条路由条目,再按槽位挂对应的配置。声明两条——一条快速的做摘要,一条强的写答案——就能把整套设置收在一个字典里。 同样的路由在 CLI 里也能用,因为 pqa 暴露的是同一套 settings 接口,但对科研场景来说 Python 路径更方便复现:产出某个答案的 Settings 对象可以和答案本身一起记录下来。

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.kkaiapi.com/v1",
            api_key=os.getenv("KKAIAPI_KEY"),
            **params,
        ),
    )

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

answer = ask(
    "室温超导 LK-99 的证据是什么?",
    settings=Settings(
        llm="claude-sonnet-5",
        llm_config=gateway,
        summary_llm="claude-haiku-4-5-20251001",
        summary_llm_config=gateway,
        agent=AgentSettings(
            agent_llm="claude-sonnet-5",
            agent_llm_config=gateway,
        ),
        paper_directory="./papers",
    ),
)

按槽位选模型

调优时固定住证据检索这一步:同一个论文库、同一批问题,每次只换一个槽位。挂在同一个端点下,每个候选就是一个 model_name 字符串,按 Key 的用量日志会把每种配置每个问题的花费算出来,这正是团队真正会拿去做预算依据的数字。

  • summary_llm 每个证据片段都会跑一次,一个问题下来往往是调用量最大的槽位。在正经规模的论文库上,这是决定整套系统成本下限的地方,一个快速 ID(claude-haiku-4-5-20251001)就够用,因为它只需要判断相关性,不需要写文笔
  • llm 从组装好的证据里写出带引用的最终答案。有把握、精确的科学写作能不能实现就看这一步,claude-sonnet-5 和 gpt-5.5 是靠谱的选择,而且这个槽位每个问题只调用几次,溢价的部分是有限的
  • agent_llm 掌控整个循环:要不要再搜一次、要不要多收集证据、还是直接回答。这里的判断差,别处的 token 就会被浪费,所以即便这个槽位调用量不大,选一个中端以上的 ID 也是划算的
  • 当一个问题需要同时从很多篇论文里抽取证据时,gemini-3.1-pro-preview 这类长上下文 ID 值得在回答槽位上试一下

主力模型价格

价格按 kkaiapi 中文区实时价格计算,单位 ¥/1M tokens(输入/输出)。

实时价格以 /pricing 为准
模型模型 IDkkaiapi 价格(输入/输出)适合槽位
Claude Haiku 4.5claude-haiku-4-5-20251001¥1 / ¥5summary_llm(走量)
Claude Sonnet 5claude-sonnet-5¥3 / ¥15llm / agent_llm
GPT-5.5gpt-5.5¥4 / ¥25llm(答案质量)
Gemini 3.1 Pro Previewgemini-3.1-pro-preview¥7 / ¥42长上下文多论文综合
GLM-5.2glm-5.2¥7.2 / ¥25.2中文文献场景

paper-qa 专属的翻车点

某个槽位留在默认值上。只设置了 llm 和 llm_config,却没设 summary_llm_config,摘要环节就会留在默认的 OpenAI 模型上,进而要求 OPENAI_API_KEY 并报错(或者悄悄把路由拆成两个不同的端点,如果这个 Key 恰好存在的话)。每个槽位都有自己的 _config 字段,打算挪动哪个槽位就把网关字典挂到哪个,agent_llm_config 也不例外。 名字对不上。Settings.llm 必须等于 model_list 里的某个 model_name,litellm_params.model 才是真正发到线上的那个值。外层名字对不上,路由就找不到路径;内层 ID 打错字,网关会返回模型不存在。排查时把这两个字符串分开检查,因为它们失败的方式不一样。 默认 Embedding 会被想当然地沿用。Embedding 槽位负责建立和查询向量索引,有自己独立的默认值和配置。如果没有默认 Embedding 模型对应的 Key,要么显式配置 embedding,要么通过 st- 前缀使用本地 sentence-transformers;kkaiapi 目前不提供 Embedding 模型,这一步不能假设网关会顺带接住。重新指定 Embedding 之后还意味着要重新建索引:不同 Embedding 模型产出的向量互不兼容。 长答案缺少生成长度限制。litellm_params 接受每条目独立的 max_tokens,上游给本地端点的示例都会刻意设置这个值。答案槽位没有合理的长度上限,长的带引用答案可能被截断,表现得像模型能力不够,其实是一个参数问题。 把解析问题怪到路由头上。paper-qa 的问答质量首先取决于模型看到文本之前的 PDF 解析和切块环节。如果在一个你确定相关的论文库上,答案完全没有引用,先检查索引这一步,网关只能看到检索环节实际发给它的内容。

谁会经网关接入 paper-qa

  • 在共享论文库上跑文献问答的研究团队,按 Key 的用量能把"这个课题组每个问题花多少钱"从猜测变成一份报告
  • 想在回答槽位用上 Claude 级别的科学写作质量,同时把摘要走量放在快速 ID 上的团队,两者共用一个 Key
  • 把 paper-qa 嵌进内部工具的开发者,用一个网关凭证替换掉一堆厂商各自的密钥
  • 在固定证据管线上对比不同回答模型的评测者,每个候选是一个配置字符串,不是一次供应商集成
  • 拿不到某家厂商计费权限的开发者。充值即用、不需要银行卡,省掉逐个厂商单独开户这层依赖

验证接入与排查首次提问

确认网关提供了你声明的模型;litellm_params.model 里 openai/ 后面的字符串必须和某个已提供的 ID 精确匹配。 首次 ask() 的失败按这个顺序排查:报错要求 OPENAI_API_KEY,说明有个槽位还留在默认模型上、没挂配置,找出 llm、summary_llm、agent_llm 里哪个没挪。网关返回 401,是 litellm_params 里的 api_key 有问题。路由报未知模型的错误,说明 Settings.llm 和 model_list 里的任何 model_name 都对不上。跑在建索引阶段而不是回答阶段失败,指向 embedding 设置或 PDF 解析,不是对话路由的问题。 一次提问会展开成很多次摘要调用加 Agent 步骤加最终答案,所以第一次成功跑通之后,kkaiapi 控制台的按请求视图会显示各槽位的真实 token 拆分。随着论文库变大,这是该盯着看的数字,因为摘要调用量是随检索到的证据量而不是问题数量在增长。

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

常见问题

paper-qa 怎么支持自定义 OpenAI 兼容 base URL?

通过它的 LiteLLM 路由配置:llm_config、summary_llm_config、agent_llm_config 各自接受一个 model_list,里面的 litellm_params 包含 api_base 和 api_key。这和 paper-qa 给本地部署的 OpenAI 兼容服务器用的写法完全一样,换成网关地址即可。

回答模型和摘要模型能来自不同厂商吗?

能。每个槽位配一个模型名加自己的配置,一个快速的 Claude ID 可以负责摘要,GPT-5.5 或 Gemini 负责写最终答案,都经同一个 api_base 和一个 Key。model_list 里每个 ID 声明一条,按槽位分别引用即可。

需要跟着换 Embedding 模型吗?

一般不建议在同一步这么做。Embedding 设置独立于对话槽位,换 Embedding 模型会让已有的向量索引失效。kkaiapi 目前不提供 Embedding 模型,如果缺少默认 Embedding 的 Key,显式配置 embedding 或者用 st- 前缀的本地 sentence-transformers。

agent_llm 槽位是做什么的,也需要配置吗?

agent_llm 在 AgentSettings 里,负责工具选择的决策:什么时候检索、收集证据还是直接回答。它和其他槽位一样默认是 OpenAI 模型,所以要单独挂上 agent_llm_config,否则它仍然会尝试路由到默认厂商。

为什么覆盖之后 paper-qa 还在要求 OPENAI_API_KEY?

至少有一个槽位还留在默认模型上、没挂路由配置。检查 llm、summary_llm、agent_llm 以及它们各自的 _config 字段;报错里点名的模型能帮你定位漏掉的是哪个槽位。

pqa 命令行也支持这套配置吗?

CLI 暴露的是同一套 settings 接口,但网关路由更适合走 Python 路径:路由字典写成命令行参数并不方便,而且把 Settings 对象和结果一起记录下来,能让科研跑批可复现。