跑过 document.ai 这类本地知识库脚本的开发者,几乎都遇到过同一个瞬间:.env里的OPENAI_API_KEY换成了在 TaoToken 控制台创建的新 Key,to_embeddings()却开始抛 401。问题通常不在 Key 本身,而在于openai.api_base仍然指向官方地址。要做的不是改业务逻辑,而是把openai.api_base指到 TaoToken 的兼容通道https://taotoken.net/api。在去拿 Key 之前,先打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content= 注册账号,然后回到脚本里改一行配置,向量化流程就能跑通,Qdrant 里的upsert代码完全可以保持原样。
1. 401 的现场:Key 换了,api_base 没换
1.1 原始脚本里 Key 是怎么被读取的
document.ai 这类项目的标准做法,是把密钥放在项目根目录的.env文件里:
OPENAI_API_KEY=sk-你的密钥然后在导入脚本中这样加载:
from dotenv import load_dotenv import openai import os load_dotenv() openai.api_key = os.getenv("OPENAI_API_KEY")从表面看,openai.api_key确实拿到了非空的字符串。很多人以为这样就算“配置好了”。但这里还缺了一个关键信息:OpenAI Python SDK 默认会把请求发往https://api.openai.com/v1。当你使用 TaoToken 生成的 Key 时,官方服务器不认识这把 Key,于是直接在认证环节返回401 Unauthorized。
1.2 为什么向量接口先炸
在本地知识库导入流程里,第一个调用 OpenAI 接口的地方通常是to_embeddings():
sentence_embeddings = openai.Embedding.create( model="text-embedding-ada-002", input=items[1] )这一步要发起真正的 HTTPS 请求,也是第一个暴露401 401的位置。更糟的是,后续查询知识库时还会调用openai.ChatCompletion.create,那个地方同样会 401。所以你会发现:不是“某个接口有问题”,而是“只要 Key 是 TaoToken 的,而 Base URL 没改,所有 OpenAI 调用都会挂”。
要解决这个问题,核心动作是告诉 SDK:请把请求发到 TaoToken 的通道,而不是官方地址。这就是openai.api_base的作用。
2. 去 TaoToken 拿一把新 Key,顺便确认模型 ID
2.1 注册并创建 API Key
打开 TaoToken,注册登录后进入控制台,在 API Keys 页面点「创建 Key」。把生成的密钥复制下来,先放到.env文件里,后面会用到。注意:本文所有示例都用YOUR_API_KEY占位,实际替换为你自己创建的那串字符即可。不要把它写进任何会提交到 Git 的文件里。
2.2 text-embedding-ada-002 的 ID 以模型广场为准
虽然text-embedding-ada-002是 OpenAI 的标准模型名,但为了确认 TaoToken 通道上该模型的可用 ID,建议看一眼模型广场。打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content= ,在模型列表里搜索text-embedding-ada-002以及后续要用的gpt-3.5-turbo,记下它们显示出来的准确名称。绝大多数情况下 ID 和官方一致,但确认一下不会浪费多少时间,能避免后面出现404 Model Not Found。
3. 在导入脚本里把 api_base 指到 TaoToken 通道
3.1 老版 openai SDK 的一行改动
如果你用的是openai0.x 版本(也就是openai.Embedding.create这种写法),只需要在设置api_key之后,加一行:
import openai from dotenv import load_dotenv import os load_dotenv() openai.api_key = os.getenv("OPENAI_API_KEY") openai.api_base = "https://taotoken.net/api" # 注意末尾不要加 /v1然后to_embeddings()函数保持原样。这里的关键点是:https://taotoken.net/api不能写成https://taotoken.net/api/v1,否则多了一层路径,请求会拼成https://taotoken.net/api/v1/embeddings,与通道实际路由不匹配。TaoToken 的 Base URL 就是https://taotoken.net/api,填进 SDK 时 SDK 会自动追加embeddings、chat/completions这些子路径。
3.2 新版 openai SDK 的 base_url 写法
如果你已经升级到openai1.x 版本,原来的openai.api_base不再生效,需要改用OpenAI客户端对象的base_url参数:
from openai import OpenAI client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url="https://taotoken.net/api" ) sentence_embeddings = client.embeddings.create( model="text-embedding-ada-002", input=items[1] )新版 SDK 里client.embeddings.create返回的对象结构略有不同,向量在sentence_embeddings.data[0].embedding。如果你不想改动向量取值逻辑,也可以继续用老版openai库。文章后面为了和原脚本保持一致,统一用老版写法。
3.3 导入 Qdrant 的 upsert 逻辑不用动
修改完openai.api_base后,原来的分割文本、调用to_embeddings()、构造PointStruct、执行client.upsert()这些代码都不需要改。向量接口走通后,返回的 embedding 维度仍然是 1536,Qdrant 的 collection 配置VectorParams(size=1536, distance=Distance.COSINE)也不会变。也就是说,你只需要让请求发到正确的地方,业务逻辑完全不动。这是兼容通道最大的价值:省去改代码的功夫。
4. 查询服务里的 embedding 和 chat 也要走同一通道
4.1 Flask 服务里的 openai.api_base 常常被漏掉
很多人在导入脚本里加了一行openai.api_base,跑通了数据导入,以为万事大吉。结果启动 Flask 查询服务后,搜索接口仍然报 401。原因很简单:查询服务是另一个进程,它自己也执行了一遍load_dotenv()和openai.api_key = os.getenv("OPENAI_API_KEY"),但没有设置openai.api_base。
在查询服务中,query()函数里既有:
sentence_embeddings = openai.Embedding.create( model="text-embedding-ada-002", input=text )又有:
completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=prompt(text, answers) )这两个调用都依赖openai.api_base。只要它没有指向https://taotoken.net/api,后续每个请求都会带着“TaoToken 的 Key 去敲官方的大门”,结果必然是 401。
4.2 用一个公共配置模块统一设置
为了避免导入脚本和查询服务各改一遍,之后还可能出现遗漏,建议单独建一个openai_config.py:
import openai import os from dotenv import load_dotenv load_dotenv() openai.api_key = os.getenv("OPENAI_API_KEY") openai.api_base = "https://taotoken.net/api"然后在导入脚本和 Flask 服务中都引入这个模块:
import openai_config # 保证在调用 openai 接口之前,api_base 已设置这样无论多少个脚本共用同一套 Key 和 Base URL,都只需维护一处配置。
5. 验证和排障:让向量顺利进 Qdrant
5.1 先跑导入脚本,确认没有 401
改完配置后,重新运行导入脚本。如果之前的报错是openai.error.AuthenticationError: 401,那么正常情况下应该能顺利进入 tqdm 进度条。看到每个文件都被处理、count在增长,说明text-embedding-ada-002向量接口已经通了。
如果仍然看到401,优先检查三点:
openai.api_base是否真的被赋值?可以在to_embeddings()前加一行print(openai.api_base)确认。.env里的OPENAI_API_KEY是否为 TaoToken 创建的 Key?不要混入其他渠道的旧 Key。- 是否不小心把
api_base写成了https://taotoken.net/api/v1?
5.2 如果还报 404 或模型不存在
404通常意味着请求到达了 TaoToken 通道,但路径或模型名不对。检查 Base URL 末尾是否多了/v1;检查模型 ID 是否与模型广场完全一致。不要凭记忆写,去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content= 上复制模型 ID 最保险。
5.3 在 Qdrant 里确认 collection 已经写入
导入完成后,可以用只读命令看看 Qdrant 里的文档数量。假设 collection 名是data_collection,在本地执行:
curl http://localhost:6333/collections/data_collection响应里的points_count应该大于 0。这一步只是确认结果,不需要让 AI 去连你的生产库。向量数据在本地,查询服务也在本地,你完全掌握整个流程。
6. 跑通之后去控制台对一下这次调用
6.1 用同一把 Key 在模型对话里测一条消息
为了确认 Key 本身还有效、模型 ID 没填错,建议先用 TaoToken 的模型对话页面发一条测试消息。打开 模型对话,选择你打算在知识库查询里使用的gpt-3.5-turbo,输入同一把 Key,随便问一句“你好”。如果这里能正常返回,说明 Key 和模型都没问题,接下来再排查自己脚本里的配置就更有方向。
6.2 查看用量并决定是否升级 Coding Plan
导入几百个文件、调用几千次 embedding,这些都会产生 token 消耗。你可以去 控制台 API Keys 查看刚才几次调用的记录,确认text-embedding-ada-002和gpt-3.5-turbo的请求都成功计费。如果打算把知识库做成长期服务,可以考虑 Coding Plan,按自己的调用量选择套餐,避免按量付费到月底才发现超支。至于 Claude Code 或其他工具的接入方式,TaoToken 也提供了对应的 接入文档,不过那是另一条路,先把手头这套知识库脚本跑通再说。