1. Streamlit 点「开始对比」报 401 和 KeyError:先别急着改代码
你在 chapter5code 目录下配好了 .env,streamlit run app.py 也成功了,可在页面上输入古诗、点「开始对比」,控制台立刻给出三连击:HTTP 401 Unauthorized、KeyError: 'choices'、ConnectionError。这三个错在原文 5.2.3 的清单里都有,但你明明是照着 5.5.3 一步一步配的,为什么还会翻车?
原因通常不在 Streamlit,而在「Key 和 Base URL 是不是同一套系统」。你在硅基流动开的 Key,配的却是另一个服务的地址;或者 Key 有效,但请求路径返回的不是 Chat Completions JSON,于是result["choices"]直接 KeyError。TaoToken 在这里可以当一条兼容通道:把 Key 换成从 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建的新 Key,把 app.py 里的 API URL 换成 TaoToken 的 Base URL,原来为硅基流动写的 try-except 错误处理完全不用动,401 和 KeyError 基本就消失了。
注意,这里不是让你学习什么高深网络知识,而是把「四件套」里的 URL 和 Key 两件换成稳定的来源。原文 5.2.1 说过,调用 API 就是一次 HTTP POST,URL、Headers、Body、Timeout 四件事做对就行。现在 URL 从 https://api.siliconflow.cn/v1/chat/completions 变成 TaoToken 的接入地址,Key 从官网控制台复制,Body 保持 messages 结构不变,Timeout 继续用 90 秒,这三连错就从源头被掐断。
2. 对照 5.2.3 的报错清单:HTTP 401 和 KeyError 的伪装
2.1 HTTP 401:Key 没配对,或没按 Bearer 传
原文 5.2.3 的表格里,认证错误对应的典型报错就是 HTTP 401。它的直接含义是服务端不认识你带过来的身份。常见的情况有三种:第一种,Key 复制少了尾巴,或者 .env 文件里值后面多了一个空格,读取出来的 Key 是错的;第二种,Key 是 A 服务商的,却填到了 B 服务商的接口上;第三种,Headers 里没有写Authorization: Bearer <Key>,而是只写了Authorization: <Key>,或者 Key 被硬编码成了占位符没替换。
在 Streamlit 界面里,401 的表现通常是点击按钮后页面下方直接红字API 错误:401 Client Error: Unauthorized,因为 call_model 里的raise_for_status()会把这个异常抛给调用方,然后被except requests.exceptions.HTTPError捕获。你看到的是友好提示,但根源还是 Key 没配对。
2.2 KeyError: 'choices':请求通了,但返回 JSON 结构不对
如果说 401 是「门卫拦你」,KeyError 则是「门开了但屋内布局变了」。response.json()成功解析出 JSON,但 JSON 里没有choices字段,于是result["choices"][0]["message"]["content"]抛 KeyError。这是 5.2.3 里最容易被忽略的一类错误,因为请求本身是成功的,HTTP 状态码是 200,可返回体是{"error": {"message": "model not found", "type": "invalid_request_error"}}之类,你按正常结构去取当然取不到。
为什么会这样?通常是模型 ID 不存在,或者 Base URL 指向的是一种不兼容 Chat Completions 的服务。比如你把硅基流动的模型 ID 填到另一家平台上,而这家平台的命名规则不同,服务端返回错误 JSON,你的代码却还在找choices。
2.3 ConnectionError:网络层失败,经常和超时轮流出现
ConnectionError 在原文 5.2.3 里对应网络错误,原因包括 DNS 解析失败、连接被重置、代理干预等。但很多同学遇到的是:第一次点按钮,转圈 90 秒后报 ConnectionError;再点一次,却成功了。这说明服务端负载高,或者本地网络到目标地址不稳定,与 Key 无关。
当 401、KeyError、ConnectionError 交织出现时,最佳做法不是反复重试,而是先确认「Base URL 和 Key 是不是同一套体系」。硅基流动的 Key 只能用它的 URL;TaoToken 的 Key 要用 TaoToken 的 URL。混用就会出现上面三种错。
3. 把 app.py 的 Base URL 切到 TaoToken 兼容通道
3.1 先到 TaoToken 官网拿一把自己的 Key
打开 TaoToken,注册后进入控制台,在 API Keys 页面创建一把新 Key。创建后先复制到剪贴板,然后回到项目根目录,打开 .env 文件,把SILICONFLOW_API_KEY=sk-你的真实Key这一行替换成你自己的 Key,注意不要加引号,也不要在值后面留空格。如果你愿意,也可以把变量名改成TAOTOKEN_API_KEY,但为了少改代码,保留SILICONFLOW_API_KEY也没问题,因为 Python 代码只负责读取这个环境变量,并不关心它叫什么。
这里要特别强调:千万别把 Key 写在 app.py 里。原文 5.2.2 已经说了三个风险——泄露、难管理、教学不友好。即使你只是本地 demo,也建议用 .env 加 python-dotenv 的方式加载。你可以在 .env 旁边放一个 .env.example,里面写YOUR_API_KEY,提交 Git 时把 .env 忽略掉。
3.2 修改 API_URL:工具填 Base URL,requests 填完整 URL
打开chapter5code/app.py,找到定义 API URL 的位置。原文 5.2.1 说 URL 是https://api.siliconflow.cn/v1/chat/completions,现在换成 TaoToken 的接入地址:
API_URL = "https://taotoken.net/api/chat/completions"如果你用的是 OpenAI SDK、Claude Code、Codex 这类工具,Base URL 只填https://taotoken.net/api,末尾不要加/v1。这是因为 TaoToken 的兼容通道会自动处理版本路径;但你在 requests 里拼完整 URL 时,一般就是 Base URL 加/chat/completions。假如请求返回 404 Not Found,说明路径需要带/v1,把API_URL改成https://taotoken.net/api/v1/chat/completions再试一次。
修改后的 call_model 函数核心部分保持不变:
headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", } payload = { "model": model, "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt} ], "temperature": 0.7, "max_tokens": 1500, } response = requests.post(API_URL, json=payload, headers=headers, timeout=90) response.raise_for_status() result = response.json() content = result["choices"][0]["message"]["content"] tokens = result.get("usage", {}).get("total_tokens", "未知")3.3 .env 里的 Key 没读进来?先打印确认
很多 401 其实不是 TaoToken 的问题,而是 .env 没被正确加载。看下面这段代码:
import os from dotenv import load_dotenv load_dotenv() API_KEY = os.getenv("SILICONFLOW_API_KEY")如果 .env 文件和 app.py 不在同一目录,load_dotenv()默认不会被加载到。你可以在load_dotenv()中传入路径,或者先在命令里打印一下:
print("KEY_PREFIX:", API_KEY[:6] if API_KEY else "EMPTY")只要能看到KEY_PREFIX: sk-之类的前缀,说明 Key 读取成功。如果输出EMPTY,检查 .env 是否在项目根目录,以及变量名是否正确。注意 Windows PowerShell 里运行 Streamlit 时,如果 .env 文件是 UTF-8 编码,一般没问题;但如果你用记事本编辑并选择了其他编码,可能读到隐藏字符,也会导致 401。
4. 复用 5.2.3 的 try-except:错误处理一行不用改
4.1 raise_for_status 继续拦 HTTP 错误
TaoToken 的兼容通道返回的是标准 Chat Completions 结构,所以原文 5.2.3 里的错误处理逻辑可以直接沿用。response.raise_for_status()会在 HTTP 状态码不是 200 时抛出 HTTPError,比如 Key 错误时返回 401,请求过快时返回 429。调用方已有的except requests.exceptions.HTTPError分支会自动接手,把错误显示在对应模型的栏位里,不会让整个页面崩溃。
4.2 KeyError 消失的关键:返回结构一致
只要 Base URL 正确、Key 有效、模型 ID 存在于 TaoToken 模型广场,result["choices"][0]["message"]["content"]就一定能取到内容。你不需要为了兼容 TaoToken 去改任何提取逻辑,这也意味着「多模型对比工具」里三个模型并行调用的代码保持原样,只是换了一个后端。
4.3 模型 ID 以模型广场为准,不要照抄旧 ID
原文 5.3.1 推荐了 Qwen2.5-7B、Qwen3-8B、DeepSeek-R1 三个模型,并给了类似Qwen/Qwen2.5-7B-Instruct的 ID。但模型 ID 会随着平台更新而变化。切到 TaoToken 后,打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 的模型广场,找到你想对比的模型,复制它当前的 ID,再填进 app.py 的AVAILABLE_MODELS字典。如果模型广场上已经没有某些旧 ID,就不要把原文表格里的 ID 硬写进代码,否则会得到KeyError: 'choices'的提示。
5. 验证与排障:跑通之后去控制台核对调用
5.1 在 Streamlit 界面再点一次「开始对比」
完成上面的修改,重新运行streamlit run app.py,在输入框里粘贴一首古诗,点「开始对比」。正常情况是三个模型依次返回内容,性能对比表格里显示耗时、Token 消耗和成功状态。如果某个模型仍失败,错误信息会以「API 错误」「网络错误」「未知错误」的格式显示在栏位中,这正是 5.2.3 的异常分级在起作用。
5.2 还是失败?对照下面的检查清单
- 401:确认 Key 是从 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 控制台复制的,而不是从别的网站粘贴;确认 .env 里值前后没有空格;确认 headers 里
Authorization前面有Bearer前缀。 - KeyError: 'choices':在 call_model 的
result = response.json()后面加一行print(response.text),看原始返回是不是模型抛出的错误 JSON。如果是"model not found",去模型广场核对模型 ID;如果是 404,把 API_URL 改成带/v1的版本。 - ConnectionError:先确认本地网络能访问
https://taotoken.net/api。注意,这是接口地址,不要在这里加utm_source参数;UTM 参数只用于官网页面,不能混进 API 地址。 - 页面打开但点按钮无反应:多数是模型调用时间太长,检查 timeout 是否设置成 90 秒,以及是否选中了过多的模型。
5.3 去控制台看这几次调用有没有被记录
打开 TaoToken 控制台的用量页面,你会看到刚才 Streamlit 发出的几次请求记录,包括模型、Token 数、耗时和状态。这一步能确认你的请求真的经过 TaoToken 通道,而不是走了某个本地缓存或代理。以后你每次跑对比工具,都可以回到这里核对,这也是判断 Key 是否有效的最直接方式。
6. 下一步:从对比工具到 Coding Plan,把这把 Key 用起来
现在你已经把 Streamlit 的 API 调用稳定在了 TaoToken 通道上,接下来可以用同一把 Key 解锁更多场景。如果你在做 AI 编程,可以在 模型对话 里试试同一把 Key 能不能跑通聊天;需要持续跑模型对比或自动化任务,可以看一眼 Coding Plan 里的额度是否够用;创建和管理 Key 永远在 控制台 API Keys。至于 Claude Code 的接入方式,官方文档也给出了环境变量对照:Base URL 写https://taotoken.net/api,模型 ID 从模型广场复制。这四步做完,你的本地环境和 AI 工具就真正统一了。
回到本章最初的问题:HTTP 401 和 KeyError 反复出现,不是因为你不会写 Python,而是因为 Key 和 URL 没有构成同一套体系。TaoToken 提供的兼容通道让这套体系变得简单:去官网拿 Key,把 Base URL 填对,剩下的调用代码、错误处理、对比逻辑都能从原文原封不动搬过来。以后你再跑 Streamlit 多模型对比,至少这三连错不会再是你的绊脚石。