
1. crewai 里 Litellm 报 BadRequestError先别急着改源码如果你在用 crewai 搭多智能体流程模型层走的是 Litellm然后某天突然抛出一个litellm.exceptions.BadRequestError大概率不是 crewai 本身坏了也不是 Litellm 不支持国内模型服务而是模型名和 base_url 的写法没对上 Litellm 的路由规则。我自己第一次遇到这个报错时第一反应是「Litellm 是不是不认国内这些 OpenAI 兼容端点」甚至去翻 Litellm 的 provider 源码想加分支。结果折腾半天发现问题根本不在支持不支持而在于模型名前缀少写了一个openai/。Litellm 看到没有前缀的模型名会按它内置的 provider 映射去猜猜不到就走到默认分支参数拼出来不对服务端直接返回 400于是包装成BadRequestError抛给你。这篇就围绕这个场景展开crewai 调用 Litellm 时出现BadRequestError的排查路径覆盖 openai 兼容接口、模型名与 base_url 配置给出可复制的config.toml/settings.json骨架以及 TaoToken 统一 Key 通道的接入步骤。适合正在用 crewai Litellm 接国内模型、被 400 卡住的人。核心检索词就三个crewai、Litellm、BadRequestError。先说结论最容易被忽略的一行改动是# 报错写法 llm LLM(modelqwen3-235b-a22b-instruct-2507, base_url..., api_keysk-xxx) # 正确写法模型名前加 openai/ llm LLM(modelopenai/qwen3-235b-a22b-instruct-2507, base_url..., api_keysk-xxx)openai/这个前缀不是让你去调 OpenAI 官方而是告诉 Litellm这是一个 OpenAI 兼容端点请走/chat/completions那条路由。下面把原理、配置、验证、排障一步步拆开。2. 为什么加openai/前缀就能解决 BadRequestError2.1 Litellm 的 provider 路由逻辑Litellm 处理一个模型名时会先做一次「provider 解析」。它的规则大致是如果模型名里带/斜杠前面那段就被当作 provider 标识如果没有斜杠就拿整个字符串去匹配内置的模型清单。你写qwen3-235b-a22b-instruct-2507没有斜杠Litellm 在内置清单里找不到完全匹配项就会 fallback 到默认 provider 推断。推断出来的调用方式和你的 base_url 不匹配请求体里可能缺字段、或者 endpoint 拼错服务端返回 400。你写openai/qwen3-235b-a22b-instruct-2507斜杠前是openaiLitellm 立刻知道走 OpenAI 兼容协议用 openai-client 发请求自动补/chat/completions。模型名斜杠后的部分原样传给服务端。这样 base_url 指向哪个兼容服务都行。2.2 base_url 不要画蛇添足官方文档里有一句很关键的话不要在 base_url 上添加任何额外内容比如/v1/embedding。LiteLLM 用 openai-client 发请求会自动补上相关端点路径。也就是说你的 base_url 应该停在版本号那一层https://your-endpoint.example.com/v1而不是https://your-endpoint.example.com/v1/chat/completions # 错多写一段路径openai-client 再拼一次就变成/v1/chat/completions/chat/completions服务端当然 400。2.3 两种前缀对应两种端点你要调的端点模型名前缀说明/chat/completionsopenai/对话补全最常用/completionstext-completion-openai/传统文本补全通过/v1/completions路由调 openai 端点不需要额外前缀走路由时自动识别crewai 里的 LLM 调用基本都是对话补全所以记住openai/就够了。3. TaoToken 统一 Key 通道的前置准备3.1 为什么用统一 Key 通道crewai 项目里往往不止一个模型规划用一个大模型执行用另一个评审再用一个。如果每个模型都单独配 key、单独记 base_url配置会散得到处都是排查BadRequestError时你甚至分不清是哪个通道出的问题。统一 Key 通道的思路是所有 OpenAI 兼容请求都走同一个入口模型名区分具体模型key 只有一份。这样配置集中、报错集中、切换模型只改一个字符串。3.2 拿到 Key 和入口地址进入控制台创建 API Key入口在这里控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteAPI Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite创建后你会得到一串sk-开头的 key。base_url 用 API 地址https://taotoken.net/api注意这里不要在后面加/v1/chat/completions原因见 2.2。如果你用的客户端要求带版本号就写到/api这一层让 openai-client 自己补。3.3 环境变量先落地在动手改 crewai 代码前先把 key 放进环境变量避免硬编码export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell$env:TAOTOKEN_API_KEYsk-你的key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api这样后面 config.toml 和代码里都引用变量名换 key 不用改文件。4. 可复制的 config.toml 与 settings.json 骨架4.1 crewai 的 config.toml 骨架crewai 新版本支持用config.toml管理 LLM 配置。下面这份可以直接抄重点是model字段带openai/前缀# config.toml [llm] # 统一走 OpenAI 兼容通道前缀 openai/ 不能省 model openai/qwen3-235b-a22b-instruct-2507 base_url https://taotoken.net/api api_key env:TAOTOKEN_API_KEY temperature 0.3 max_tokens 2048 [llm.fallback] model openai/qwen3-235b-a22b-instruct-2507 base_url https://taotoken.net/api api_key env:TAOTOKEN_API_KEYapi_key env:TAOTOKEN_API_KEY这种写法让 crewai 从环境变量读不把 key 写进仓库。4.2 settings.json 骨架如果你的项目用 JSON 配置等价写法{ llm: { model: openai/qwen3-235b-a22b-instruct-2507, base_url: https://taotoken.net/api, api_key: env:TAOTOKEN_API_KEY, temperature: 0.3, max_tokens: 2048 } }4.3 代码里直接构造 LLM不走配置文件、直接在 Python 里构造也可以import os from crewai import LLM llm LLM( modelopenai/qwen3-235b-a22b-instruct-2507, base_urlos.environ[TAOTOKEN_BASE_URL], api_keyos.environ[TAOTOKEN_API_KEY], ) if __name__ __main__: response llm.call( Analyze the following messages and return the name, age, and breed. Meet Kona! She is 3 years old and is a black german shepherd. ) print(response)对比一下报错版本和修复版本唯一区别就是model字段# 报 BadRequestError modelqwen3-235b-a22b-instruct-2507 # 正常 modelopenai/qwen3-235b-a22b-instruct-25074.4 多模型场景的配置crewai 里不同 agent 用不同模型时每个都带前缀[llm.planner] model openai/qwen3-235b-a22b-instruct-2507 base_url https://taotoken.net/api api_key env:TAOTOKEN_API_KEY [llm.executor] model openai/qwen3-235b-a22b-instruct-2507 base_url https://taotoken.net/api api_key env:TAOTOKEN_API_KEY模型名不同就换斜杠后面那段前缀和 base_url 保持不变。5. 最小复现请求与验证动作5.1 先用 curl 验证通道本身在改 crewai 之前先用 curl 确认 key 和 base_url 是通的把变量隔离出来curl -sS https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: qwen3-235b-a22b-instruct-2507, messages: [{role: user, content: reply with ok}], max_tokens: 16 }注意这里 curl 直接打/chat/completions因为 curl 不会自动补路径。如果这一步返回正常 JSON说明 key 和通道没问题问题在 Litellm 的模型名写法上。5.2 再用 Litellm 单独验证绕开 crewai直接调 Litellm确认前缀规则import os from litellm import completion resp completion( modelopenai/qwen3-235b-a22b-instruct-2507, base_urlos.environ[TAOTOKEN_BASE_URL], api_keyos.environ[TAOTOKEN_API_KEY], messages[{role: user, content: reply with ok}], max_tokens16, ) print(resp.choices[0].message.content)如果这个能通而 crewai 里不通那就是 crewai 的配置没把model字段传对。5.3 最后跑 crewai 最小示例import os from crewai import LLM llm LLM( modelopenai/qwen3-235b-a22b-instruct-2507, base_urlos.environ[TAOTOKEN_BASE_URL], api_keyos.environ[TAOTOKEN_API_KEY], ) response llm.call(Say hello in one word.) print(response)三步验证的顺序很重要curl 验通道 → Litellm 验前缀 → crewai 验集成。哪一步断掉问题就锁定在哪一层。5.4 成功结果长什么样正常返回是一段文本比如Hello。如果返回里带choices、usage这些字段说明走的是标准 OpenAI 兼容响应结构。crewai 拿到这个结构后自己解析不会再抛BadRequestError。6. 本篇常见错排查清单6.1 模型名没加openai/前缀这是最高频的原因。症状是BadRequestError堆栈里能看到get_llm_provider相关调用。修复就是加前缀。判断方法把模型名打印出来看斜杠前是不是openai。6.2 base_url 多写了路径症状同样是 400但错误信息里可能带Not Found或路径重复。检查 base_url 是不是写成了.../api/v1/chat/completions。正确写法停在https://taotoken.net/api。6.3 前缀和端点不匹配调/chat/completions用了text-completion-openai/或者反过来。对照 2.3 的表格改。crewai 场景基本都是openai/。6.4 key 没读到环境变量症状是 401 而不是 400但有时会被包装成BadRequestError。检查os.environ.get(TAOTOKEN_API_KEY)是否有值。config.toml 里写env:TAOTOKEN_API_KEY时确认变量名拼写一致。6.5 模型名斜杠后拼错前缀对了但斜杠后的模型名写错服务端找不到模型也可能返回 400。把模型名复制到 curl 里单独测一次确认服务端认这个名字。6.6 crewai 版本差异老版本 crewai 的 LLM 构造参数和新版本略有不同。如果base_url传了不生效检查是不是要用api_base。看 crewai 的 LLM 类签名或者打印llm.__dict__确认参数进去了。6.7 排查顺序建议遇到BadRequestError别一上来就改源码。按这个顺序走先看模型名有没有openai/前缀 → 再看 base_url 有没有多写路径 → 再用 curl 验通道 → 再用 Litellm 验前缀 → 最后看 crewai 配置。九成问题在前两步就解决了。7. 接入文档与后续动作配置和排查都跑通后建议把 key 管理、模型切换这些动作固定下来。需要看更细的接入说明可以走接入文档接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteAPI Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite如果你只是想快速验证某个模型名能不能通用模型对话页面直接试模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite如果你在搭长期的编码 Agent 或者多智能体流水线模型调用量大、需要稳定通道可以看 Coding PlanCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite回到这篇的核心crewai 下 Litellm 的BadRequestError绝大多数情况就是模型名少了个openai/前缀加上就好。base_url 停在版本层别多写路径。先用 curl 验通道再用 Litellm 验前缀最后跑 crewai问题定位会快很多。