CrewAI 接入 EXAIV:把多智能体协作组接到 OpenAI 兼容的国产模型上
CrewAI 用 LiteLLM 作为非原生供应商的回退路径,而 LiteLLM 接受任意 OpenAI 兼容端点——这正是 EXAIV 的入口。本篇覆盖两种配置方式(LLM 类与环境变量)、默认模型 gpt-4o-mini 的陷阱、按 agent 分配模型、本地模型对照写法与按现象排查表。
CrewAI 接入 EXAIV:把多智能体协作组接到国产模型上
CrewAI 是一个用 Python 编排多个智能体、让它们分工完成同一件事的框架。它对几大厂商使用原生 SDK 集成,其余供应商回退到 LiteLLM —— 而 LiteLLM 接受任意 OpenAI 兼容端点,这就是 EXAIV 的入口。
有两个点值得在抄代码之前先搞清楚。第一,CrewAI 有一个默认模型(不配置时是 gpt-4o-mini),所以配置做了一半的项目会去请求 OpenAI,哪怕你以为已经指向别处了。第二,每个 agent 有自己的 llm 参数,一个协作组可以混用便宜快的模型和更强的模型 —— 这恰恰是通过中转站跑多智能体最主要的理由。
接入步骤
安装带 LiteLLM 扩展的 CrewAI
uv add 'crewai[litellm]'(或等价的 pip 命令)。官方文档明确:没有原生集成的供应商走 LiteLLM 回退。在 EXAIV 控制台创建 API Key
这把 Key 用于向中转站表明身份。配置端点
从下面两种方式里选一种。不要只配一半——很多「玄学故障」来自教程里残留的OPENAI_MODEL_NAME。先用单 agent 验证
一个 agent + 一个 task 跑通并打印结果。同时调试编排逻辑和端点,只会让你分不清是谁的问题。
方式一:LLM 类
显式写法把配置留在代码里,便于评审和按环境区分。
import os
from crewai import Agent, Task, Crew, Process, LLM
llm = LLM(
model="openai/kimi-k3", # openai/ 前缀 = 走 OpenAI 兼容路径
base_url="https://exaiv.com/api/ai/v1",
api_key=os.environ["EXAIV_API_KEY"],
)
researcher = Agent(
role="研究员",
goal="收集简报所需的事实",
backstory="你负责找资料,并且绝不编造引用来源。",
llm=llm,
)
writer = Agent(
role="撰稿人",
goal="把研究笔记写成可读的简报",
backstory="你写得直白,并且避免不可证实的说法。",
llm=LLM(
model="openai/deepseek-v4.1-flash",
base_url="https://exaiv.com/api/ai/v1",
api_key=os.environ["EXAIV_API_KEY"],
),
)
gather = Task(
description="列出 {topic} 的关键事实与待确认问题",
expected_output="条目化的事实清单,每条注明来源。",
agent=researcher,
)
write = Task(
description="为 {topic} 写一页简报",
expected_output="带标题与三个小节的结构化简报。",
agent=writer,
)
crew = Crew(
agents=[researcher, writer],
tasks=[gather, write],
process=Process.sequential,
verbose=True,
)
print(crew.kickoff(inputs={"topic": "开放权重模型发布"}))
关于 openai/ 前缀
CrewAI 官方文档在指向 OpenAI 兼容端点时,同时出现过裸模型名与 openai/<model> 两种写法(前缀用于把请求归到 OpenAI 兼容路径)。如果你装的那个版本用裸名解析不到,就改成带前缀的形式 —— 请对照你所用版本的文档,而不是更早的教程。
方式二:环境变量
环境变量方式一次性配置整个进程,适合容器部署与 CI。
import os
from crewai import Agent, Task, Crew, Process
os.environ["OPENAI_API_KEY"] = os.environ["EXAIV_API_KEY"]
os.environ["OPENAI_API_BASE"] = "https://exaiv.com/api/ai/v1"
os.environ["OPENAI_MODEL_NAME"] = "openai/glm-5.3"
agent = Agent(
role="分析师",
goal="解释给定选项之间的取舍",
backstory="你把假设写在明处。",
)
task = Task(
description="比较 {notes} 里的选项并给出一个建议",
expected_output="简短对比 + 单一建议。",
agent=agent,
)
crew = Crew(agents=[agent], tasks=[task], process=Process.sequential)
print(crew.kickoff(inputs={"notes": "方案 A 更快,方案 B 更省"}))
默认模型陷阱
官方文档写明默认模型是 gpt-4o-mini,由 OPENAI_MODEL_NAME 决定。如果你配了 base URL 和 Key,却没设置模型名,协作组仍会按默认值去请求 —— 端点会拒绝一个它没有的模型。这个失败看起来像端点问题,实际原因只是少了一行配置。
按 agent 分配模型
因为每个 agent 都能独立指定 llm,协作组的形态决定了你需要几种模型。以一个「调研 + 写作」的组为例:
| agent 角色 | 高频动作 | 选型倾向 |
|---|---|---|
| 调研 / 检索 | 大量短的结构化调用 | 快模型——这是整个组里调用次数最多的一环 |
| 写作 / 总结 | 次数少、单次输出长 | 更强的模型,调用频率低 |
| 审阅 / 批评 | 中等长度校验 | 中档;它要读写作的输出,上下文长度要够 |
| 路由 / 分类 | 每步一次短决策 | 能稳定分类的最小模型 |
所有模型 ID 都必须取自 EXAIV 侧的实时目录(/models);从上游供应商记下来的名字,经中转站不会解析成功。
同一套组跑在本地模型上
多智能体框架里让人困惑的通常是框架本身,不是模型。所以更稳的做法是:先用本地 OpenAI 兼容服务把编排逻辑跑通,再换端点。
from crewai import LLM
local = LLM(
model="ollama/llama3.2", # 官方文档给出的本地写法
base_url="http://localhost:11434", # Ollama 默认端口
)
# 切到中转站只改这两处:model 与 base_url
relay = LLM(
model="openai/deepseek-v4.1-flash",
base_url="https://exaiv.com/api/ai/v1",
api_key="YOUR_EXAIV_KEY",
)
这也是隐私敏感场景的实际答案:编排逻辑完全不变,只在数据不能离开自己硬件时换掉端点。
按现象排查
| 现象 | 可能原因 | 处理 |
|---|---|---|
| 端点返回 401 | Key 缺失;或环境变量方式设了 OPENAI_API_KEY,代码却走 LLM 类 | 选定一种方式,把 Key 配在同一处 |
报模型不存在且提到 gpt-4o-mini | 没有覆盖默认模型 | 给 LLM 设 model=,或环境变量方式设 OPENAI_MODEL_NAME |
| 报模型不存在且提到你的模型名 | ID 不在实时目录里,或用了 HuggingFace 权重名当 API ID | 重新看 /models,用目录里的准确 ID |
| 404 且错误里带路径 | base_url 少了 /api/ai/v1 后缀 | 用 https://exaiv.com/api/ai/v1 |
| 长任务卡住 | 串行调用太多,每次各自超时 | 先只用 1 个 agent + 1 个 task 验证;重试策略要有意设计,而不是把超时都调大 |
| 本机能跑、CI 失败 | 环境变量没有注入容器 | 用部署配置传入同样三个变量 |
这张表反复说明的一件事:协作组里的「端点问题」,多数其实是协作组的配置问题。先用单 agent 验证,再去调编排。
接入检查清单
- 单 agent + 单 task 能通过 EXAIV 返回真实结果
- 模型 ID 与实时目录一致
- 没有残留的
OPENAI_*变量指向别的供应商 - 每个 agent 的
llm都显式设置,包括你忘记的那个 - 组中途失败时给出可读错误,而不是静默返回空结果
- 若在容器里运行,三个配置变量都已注入