CrewAI 里两个 Agent 一跑就 401?TaoToken 官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 注册并创建一把 Key,把 CrewAI 的 Base URL 填成 https://taotoken.net/api,研究员和编辑两个 Agent 的角色、任务、流程一行都不用动。很多人照着 Manus、MetaGPT、CrewAI 的对比手册抄代码,抄完发现只有 CrewAI 那段在自己机器上挂了,报错却只有一行 401。这类问题九成不在业务逻辑上,而在模型通道:Key 是哪家发的、请求打去了哪个端点、模型 ID 在不在对方的列表里,这三件事只要错一件,Agent 就起不来。
下面按排障顺序走:先看清 401 到底是谁拒绝了你,再决定改哪几行;然后把 CrewAI 的 LLM 指到统一入口;最后跑一次最小验证,确认账记在了正确的 Key 上。
1. CrewAI 报 401 时,先分清是谁在拒绝
1.1 litellm 的报错文本,其实已经把端点写出来了
CrewAI 自己没有 HTTP 客户端,它底层用 litellm 转发请求。所以你在终端看到的不是openai.error.AuthenticationError,而是带 litellm 前缀的一长串:
litellm.AuthenticationError: AuthenticationError: OpenAIException - Error code: 401 - {'error': {'message': 'Invalid API key provided', 'type': 'invalid_request_error'}}这行字里有三个信息值得抠:OpenAIException说明走的是 openai 这条 provider 分支;401是身份没被端点认出来;Invalid API key provided是服务端在说"这把 Key 我不认识"。注意最后一句话的主语是服务端,不是你的代码。也就是说,代码语法没问题,是请求落地的那台服务器不认这把钥匙。
常见的三种成因由此展开。第一种,Key 确实是从别处申请的,但 Base URL 没改,请求照样打到默认的api.openai.com,那边当然不认。第二种,Base URL 改了,但改写方式不对,路径被拼成了/api/v1/chat/completions之类,端点回 404,litellm 有时会把状态码包装成鉴权失败往上抛。第三种,模型 ID 不在对方列表里,某些通道对未知模型返回的也是 401 而不是 404。
还有一种容易被忽略的情况:.env文件写了但没生效。CrewAI 的默认 LLM 在初始化那一刻就把环境变量读进去了,如果你在load_dotenv()之前就构造了 Agent,读到的还是空字符串。表现同样是 401,但根因跟通道无关。
1.2 为什么对比手册里的 Manus、MetaGPT 没这么容易踩
回到那篇对比手册的语境。Manus 是托管形态,模型通道由平台自己管,你连 Key 都不用碰,代价是 Agent 行为被框在它的产品界面里;MetaGPT 走的是配置文件路线,config2.yaml里有base_url和api_key两个字段,写错位置会直接读不到;CrewAI 走的是 Python 进程内的环境变量加 LLM 对象,灵活度最高,也最容易出现"我明明改了却没生效"。
这不是谁好谁坏的问题,而是排障路径不同。托管平台你只能等或者换产品,配置文件类你能 diff 出差异,代码内配置类你需要确认三件事的执行顺序:环境变量加载、LLM 对象构造、Agent 绑定 LLM。顺序错了,改十遍 Key 都没用。
1.3 401 和 404、429 的分工,别混着查
把状态码当路标用,效率会高很多。401指向身份与端点不匹配,优先查 Key 和 Base URL;404指向路径不存在,优先查 Base URL 末尾有没有多写/v1;429指向频率或额度触顶,优先查用量面板;超时指向网络可达性,跟 Key 无关。排障最怕的是把 404 当成 401 去反复换 Key,一晚上就过去了。
如果你想先确认端点是否可达,可以用最小脚本探一下,别一上来就跑整条 Crew:
curl -s -o /dev/null -w "%{http_code}\n" https://taotoken.net/api能返回状态码,说明网络这一层通了,剩下的问题在鉴权和参数上。
2. 把 CrewAI 的模型通道接上 TaoToken
2.1 先创建 YOUR_API_KEY,顺手记下模型 ID
打开 TaoToken 官网 注册登录,进控制台创建 API Key,复制出来的那串就是后面所有配置里的YOUR_API_KEY。创建页上通常会给出可用模型的列表,把你要用的那个 ID 原样记下来,后面的YOUR_MODEL_ID就填它。这一步不要凭记忆写模型名,像gpt-5这种带猜测性质的名字,或者自己加日期后缀拼出来的 ID,基本都是 401 或 404 的来源。模型 ID 一律以 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 模型广场里当时列出的为准。
Key 生成后建议立刻做两件事:一是把它写进.env并确认.env在.gitignore里;二是别把 Key 硬编码进crew.py,Agent 代码将来要提交、要给别人跑,硬编码等于把钥匙贴在门上。
2.2 两种接法:环境变量兜底,显式 LLM 更稳
CrewAI 的默认 LLM 会去读一组环境变量,历史上常用的有OPENAI_API_KEY、OPENAI_API_BASE、OPENAI_MODEL_NAME。不同版本对 Base URL 变量的拼写略有差异,OPENAI_API_BASE和OPENAI_BASE_URL都有人见到过能生效。所以如果你的项目只是想快速跑通,改环境变量够用;但只要涉及多模型、多供应商,建议改成显式传参,把不确定性锁死在一个对象里。
显式写法的好处是:Agent 拿到的是同一个 LLM 实例,研究员和编辑不会各自去猜通道;出了问题只需要看一段配置,不用翻遍环境变量。
2.3 研究员和编辑的角色定义,一个字都不用改
排障时最容易犯的错是顺手改业务代码。401 是通道问题,跟role、goal、backstory没有关系。原来手册里研究员负责收集资料、编辑负责润色成文的那套分工,照原样保留就行。你要动的只有 LLM 的构造部分:把base_url指到https://taotoken.net/api,把api_key从环境变量里取,把model换成模型广场上确认过的那个 ID。改完先别加新功能,跑通再谈优化。
3. crew.py 和 .env 的可复制写法
3.1 .env 里放 Key,不放逻辑
# .env 放在项目根目录,和 crew.py 同级 OPENAI_API_KEY=YOUR_API_KEY OPENAI_API_BASE=https://taotoken.net/api OPENAI_MODEL_NAME=YOUR_MODEL_ID注意OPENAI_API_BASE的末尾不要加/v1,写了就会变成/api/v1/...,端点找不到。也不要在这三个变量里塞任何查询参数,Base URL 就是干净的https://taotoken.net/api。
3.2 crew.py 用显式 LLM,把通道钉死
import os from dotenv import load_dotenv from crewai import Agent, Task, Crew, LLM load_dotenv() # 一定要在构造 LLM 之前调用 llm = LLM( model="openai/YOUR_MODEL_ID", # 前缀保留,ID 以模型广场为准 base_url="https://taotoken.net/api", # 末尾不要加 /v1 api_key=os.environ["OPENAI_API_KEY"], ) researcher = Agent( role="行业研究员", goal="围绕指定主题收集可核查的事实与数据", backstory="你习惯把零散资料整理成结构化要点,并标注不确定的部分。", llm=llm, verbose=True, ) editor = Agent( role="内容编辑", goal="把研究员给出的要点改写成通顺的中文短文", backstory="你负责删掉冗余表述,保留数据与结论。", llm=llm, verbose=True, ) collect = Task( description="调研 {topic} 的现状,列出至少五条可核查的事实。", expected_output="条目式事实清单,每条附来源说明。", agent=researcher, ) polish = Task( description="把事实清单改写成 500 字以内的中文短文。", expected_output="结构清晰、无夸大表述的短文。", agent=editor, ) crew = Crew(agents=[researcher, editor], tasks=[collect, polish], verbose=True)model前面的openai/是 litellm 的 provider 前缀,不是因为你在用 OpenAI 的官方服务,而是告诉 litellm 走 openai 兼容的那套请求格式。删掉这个前缀,litellm 可能识别不出 provider,报错会变成另一种模样。
3.3 研究员用大模型、编辑用快模型,各配一个 LLM
两个 Agent 不一定要共用实例。研究员要读长材料,编辑只做改写,完全可以让它们走不同模型:
llm_research = LLM( model="openai/YOUR_STRONG_MODEL_ID", base_url="https://taotoken.net/api", api_key=os.environ["OPENAI_API_KEY"], ) llm_edit = LLM( model="openai/YOUR_FAST_MODEL_ID", base_url="https://taotoken.net/api", api_key=os.environ["OPENAI_API_KEY"], )两个 ID 都从模型广场取,别自己拼。哪个模型擅长长上下文、哪个响应快,以当时的列表说明为准。
3.4 参数对照表,填之前扫一眼
| 配置项 | 应该填什么 | 常见错误 |
|---|---|---|
base_url | https://taotoken.net/api | 末尾多写/v1,或误填成官网落地页 |
api_key | YOUR_API_KEY,从控制台创建 | 把别处申请的 Key 直接拿来用 |
model | openai/+ 模型广场上的 ID | 凭记忆写模型名、自己加日期后缀 |
.env加载时机 | 在构造LLM之前 | 先建 Agent 再load_dotenv() |
4. 验证:先单量 Key,再放整条 Crew
4.1 用一段最小脚本确认 Key 和端点匹配
别急着crew.kickoff()。先用十行代码确认这把 Key 在这个端点上能用:
from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://taotoken.net/api", ) resp = client.chat.completions.create( model="YOUR_MODEL_ID", messages=[{"role": "user", "content": "只回复两个字:收到"}], ) print(resp.choices[0].message.content)能打印出内容,说明 Key、Base URL、模型 ID 三件套是对齐的。这时再回头跑 Crew,如果还报 401,问题就落在.env加载顺序或者 Agent 没绑到llm上,范围一下子缩小了。
4.2 跑 crew.kickoff() 看两个 Agent 的交接
python crew.pyverbose=True会把每个 Agent 的思考过程和工具调用打出来。正常的话你会先看到研究员产出事实清单,再看到编辑拿着清单改写。如果研究员正常、编辑报错,那多半是任务交接时的上下文格式问题,不是通道问题;如果两个都在第一步就挂,回到 4.1 重查。
4.3 回到控制台核对这次调用
跑通之后,打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 进控制台,看这次调用有没有记上账、用的是哪个模型、消耗了多少。这一步很关键:如果控制台里没有记录,说明请求根本没到这条通道,你看到的"成功"可能是缓存或者本地 mock。
5. 401 修完还会撞上的几个坑
5.1 Base URL 写成官网落地页
最容易犯的一类错,是把浏览器里打开的页面地址直接粘进base_url。工具里要填的是接口地址https://taotoken.net/api,官网地址是给人点的,用来注册、看模型列表、看用量,两者不要混用。混用的结果是路径完全对不上,报错五花八门。
5.2 模型 ID 带了自己的后缀
有人习惯把-latest、-0613、-2024xx之类的后缀往模型名上挂,以为能自动映射。在统一通道里,模型 ID 是精确匹配的,多一个字符就是另一个名字。以模型广场列出的写法为准,复制粘贴比手打靠谱。
5.3 Key 泄露后的处理顺序
如果 Key 不小心提交进了仓库,正确顺序是:先去控制台撤销这把 Key,再重新创建一把,然后清理 Git 历史。只清理历史不撤销 Key,等于把旧钥匙留在门外。
5.4 请求超时和并发
Agent 数量一多,并发会上来。CrewAI 默认是顺序执行任务的,两个 Agent 问题不大;如果你后面加了Process.hierarchical或者更多角色,可能会遇到超时。这时先降并发、加超时参数,再去控制台确认是不是撞上了速率限制。
6. 跑通之后,把通道配置从代码里抽出来
6.1 让 crew.py 保持干净
业务代码里只留LLM(...)的构造,具体值全从环境变量读。这样换模型、换通道都不用改 Agent 定义,研究员和编辑的role、goal也能长期稳定。将来你要把同一个 Crew 部署到别的地方,只需要改环境变量。
llm = LLM( model=os.environ.get("CREW_MODEL", "openai/YOUR_MODEL_ID"), base_url=os.environ.get("CREW_BASE_URL", "https://taotoken.net/api"), api_key=os.environ["OPENAI_API_KEY"], )6.2 下一步:用同一把 Key 试更多场景
最小验证跑完后,可以拿这把 Key 去 TaoToken 模型对话 手动发几条消息,感受一下不同模型在同一段提示词下的差异,再决定研究员和编辑分别用哪个。如果这个 Crew 要长期跑、每天调用量不小,可以看看 Coding Plan 的套餐是否合适,避免按次计费到最后不好估。新 Key 统一在 控制台 API Keys 创建,方便区分项目和用途。想把同一把 Key 也接到终端里的编码工具,环境变量对照可以看 Claude Code 接入文档。
排障这件事,顺序比技巧重要。401 出现时先别动业务代码,把"Key 从哪来、请求去哪、模型叫什么"这三句话各写一遍贴在自己屏幕上,对着改,通常十分钟内就能定位。