CrewAI with EXAIV: Pointing a Multi-Agent Crew at OpenAI-Compatible Domestic Models
CrewAI orchestrates multiple agents and routes model calls through LiteLLM, which accepts any OpenAI-compatible endpoint. This guide covers the two configuration routes (LLM class and environment variables), the default-model trap, per-agent model assignment, a local-model contrast, and a troubleshooting table.
CrewAI with EXAIV: Pointing a Multi-Agent Crew at OpenAI-Compatible Domestic Models
CrewAI is a Python framework for orchestrating multiple agents that divide a job between them. It connects to models through native SDK integrations for the largest providers and falls back to LiteLLM for everything else — and LiteLLM accepts any OpenAI-compatible endpoint, which is the door EXAIV comes through.
Two things make this integration worth reading before you copy code. First, CrewAI has a default model (gpt-4o-mini unless you configure otherwise), so a partially configured project will try to reach OpenAI even when you thought you pointed it elsewhere. Second, because each agent takes its own llm argument, a crew can mix a cheap fast model with a stronger one — which is the reason to run a crew through a relay in the first place.
Setup steps
Install CrewAI with the LiteLLM extra
uv add 'crewai[litellm]'(or the pip equivalent). The docs note LiteLLM is the fallback for providers without a native integration.Create an EXAIV API key
Generate one in the EXAIV console; the key identifies your account to the relay.Configure the endpoint
Pick one of the two routes below — LLM class or environment variables. Do not mix them halfway; a leftoverOPENAI_MODEL_NAMEfrom a tutorial is a common cause of confusing failures.Verify with a single agent before building the crew
Run one agent with one task and print the result. Debug an orchestrator and an endpoint at the same time only if you enjoy guesswork.
Route 1: the LLM class
The explicit route keeps configuration in code, which is easier to review and to keep per-environment.
import os
from crewai import Agent, Task, Crew, Process, LLM
llm = LLM(
model="openai/kimi-k3", # openai/ prefix = the OpenAI-compatible path
base_url="https://exaiv.com/api/ai/v1",
api_key=os.environ["EXAIV_API_KEY"],
)
researcher = Agent(
role="Researcher",
goal="Collect the facts needed for the brief",
backstory="You gather sources and never invent a citation.",
llm=llm,
)
writer = Agent(
role="Writer",
goal="Turn the research notes into a readable brief",
backstory="You write plainly and avoid unverifiable claims.",
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="List the facts and open questions for: {topic}",
expected_output="A bulleted fact list with sources named for each item.",
agent=researcher,
)
write = Task(
description="Write a one-page brief for: {topic}",
expected_output="A structured brief with a heading and three sections.",
agent=writer,
)
crew = Crew(
agents=[researcher, writer],
tasks=[gather, write],
process=Process.sequential,
verbose=True,
)
print(crew.kickoff(inputs={"topic": "open-weight model releases"}))
About the openai/ prefix
The CrewAI documentation shows both a bare model name and the openai/<model> form when targeting an OpenAI-compatible endpoint (the prefix is added for providers that are reached through the OpenAI path). If a bare name does not resolve in your installed version, use the prefixed form — and check the version-specific docs rather than an older tutorial.
Route 2: environment variables
The environment route configures the whole process at once, which suits container deployments and CI runs.
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="Analyst",
goal="Explain the trade-offs in the provided options",
backstory="You state assumptions explicitly.",
)
task = Task(
description="Compare the options in {notes} and recommend one.",
expected_output="A short comparison plus a single recommendation.",
agent=agent,
)
crew = Crew(agents=[agent], tasks=[task], process=Process.sequential)
print(crew.kickoff(inputs={"notes": "option A: faster, option B: cheaper"}))
The default-model trap
CrewAI's documentation states the default model is gpt-4o-mini, set through OPENAI_MODEL_NAME. If you configure the base URL and the key but leave the model unset, the crew still asks for that default — and the endpoint will refuse a model it does not have. The failure looks like an endpoint problem but the cause is a missing line of configuration.
Assigning models per agent
Because every agent accepts its own llm, the crew's shape determines which models you need. A practical split for a research-and-write crew:
| Agent role | What it does a lot of | Model characteristic to pick |
|---|---|---|
| Researcher / retriever | Many short structured calls | A fast model — this is the highest call count in the crew |
| Writer / summariser | Fewer, longer generations | A stronger model, called less often |
| Reviewer / critic | Medium-length checks | Mid-tier; it reads the writer's output, so context length matters |
| Router / classifier | One short decision per step | The smallest model that classifies reliably |
All model identifiers must come from the live catalog on the EXAIV side (/models); a name memorised from an upstream provider will not resolve through the relay.
Running the same crew against a local server
The confusing part of multi-agent frameworks is usually the framework, not the model. That is an argument for testing the orchestration logic against a local OpenAI-compatible server first, then switching the endpoint:
from crewai import LLM
local = LLM(
model="ollama/llama3.2", # documented local-model form
base_url="http://localhost:11434", # Ollama default port
)
# switching to the relay changes only these two lines: model and base_url
relay = LLM(
model="openai/deepseek-v4.1-flash",
base_url="https://exaiv.com/api/ai/v1",
api_key="YOUR_EXAIV_KEY",
)
This is also the practical answer for privacy-sensitive runs: the crew logic stays identical, and only the endpoint changes when the data cannot leave your own hardware.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 401 from the endpoint | Key missing, or the env route set OPENAI_API_KEY while the code uses the LLM route | Pick one route and set the key there |
Model-not-found naming gpt-4o-mini | The default model was never overridden | Set model= on the LLM, or OPENAI_MODEL_NAME for the env route |
| Model-not-found naming your model | ID not in the live catalog, or a weight name from HuggingFace used as an API id | Re-read /models and use the exact API identifier |
| 404 with the path in the message | base_url missing the /api/ai/v1 suffix | Use https://exaiv.com/api/ai/v1 |
| Crew hangs on long runs | Many sequential calls, each with its own timeout | Test with one agent and one task first; add retry policy deliberately rather than raising every timeout |
| Works locally, fails in CI | Environment variables not injected into the container | Pass the same three variables through the deployment configuration |
The recurring lesson in this table: most "endpoint problems" in a crew are configuration problems in the crew. Verify with a single agent before debugging the orchestration.
Verification checklist
- One agent with one task returns a real answer through EXAIV
- The model identifier matches the live catalog
- No leftover
OPENAI_*variable points at a different provider - Each agent's
llmis set explicitly, including the ones you forgot - A failure in the middle of a crew produces a readable error, not a silent empty result
- If the crew runs in a container, all three configuration variables are injected