← Back to Agentic AI map
Lesson 6.2 · CrewAI

CrewAI + Ollama Setup

Install CrewAI, point it at a local llama3, and run a one-agent crew. Then make sure nothing leaves your machine: CrewAI sends telemetry by default, and the switch most tutorials give no longer works - we watched the network to find the one that does.

crewai

What you will be able to do

  • Install CrewAI in its own virtual environment and check the version
  • Connect every agent to a local Ollama model with LLM()
  • Run a one-agent crew and read the CrewOutput
  • Turn off CrewAI’s telemetry - and prove it is off
  • Recognise the errors for a missing prefix, a missing model and a stopped Ollama

The idea, in plain English

Lesson 6.1 showed what CrewAI does. Now we set it up properly on your own machine, with the same local model as the rest of this course: llama3 through Ollama.

The goal of a local setup is that no data leaves your machine. The model runs locally - but CrewAI itself sends usage data (telemetry) to its servers by default. We watched the network to find out exactly how to stop it.

Everything below was run with CrewAI 1.15.27 on Python 3.11, with llama3 on Ollama.

Worked example: A one-agent "friendly teacher" crew on llama3, run with and without telemetry, with the four setup mistakes beginners make.

request flowWhere the requests gostep 1 / 3

1 - Default settings

The model call stays on your machine. But CrewAI also tried to connect to its telemetry server 8 times.

model calls
localhost only
telemetry attempts
8

Network connections we recorded while running the same one-agent crew. Real counts.

Words you will see in this lesson

A few setup words.

Small dictionary
LLM()CrewAI’s object for a model: which model, and where it runs.
Provider prefixThe part before the slash: "ollama/llama3". It tells CrewAI which kind of server to talk to.
base_urlThe address of the model server. For Ollama: http://localhost:11434.
OpenAI-compatibleA server that accepts the same requests as OpenAI’s API. Ollama offers one at /v1.
TelemetryUsage data a program sends to its makers.
CrewOutputWhat kickoff() returns: the final text, each task’s output, and token counts.

An everyday example: a new phone

  • You buy a phone (install CrewAI) and insert your SIM card (connect it to your model).
  • The phone works at once - but some apps send usage data in the background (telemetry).
  • A settings page says "turn off sharing". On some phones that old switch no longer does anything; the real one moved. (CREWAI_TELEMETRY_OPT_OUT vs CREWAI_DISABLE_TELEMETRY.)
  • The only way to be sure is to check what the phone actually sends - which is what we did.

Step 1 - install

  • CrewAI 1.15.27 needs Python 3.10, 3.11, 3.12 or 3.13 (Requires-Python: <3.14,>=3.10).
  • Use its own virtual environment. CrewAI has many dependencies; keep them away from your LangGraph projects.
  • Ollama must be running, with llama3 pulled (ollama pull llama3).
  • Older tutorials install litellm for Ollama. Version 1.15.27 did not need it - it talks to Ollama’s OpenAI-compatible endpoint directly.
Install and check
python3.11 -m venv crew source crew/bin/activate pip install crewai python -c "import importlib.metadata as m; print('Requires-Python:', m.metadata('crewai')['Requires-Python'])" crewai --version
Output
Requires-Python: <3.14,>=3.10 crewai, version 1.15.27

Step 2 - a one-agent crew on llama3

The smallest useful crew - one agent, one task:

  • LLM(model="ollama/llama3", base_url=..., temperature=0) - the "ollama/" prefix says "this is an Ollama model".
  • Pass llm=llm to EVERY agent. An agent without llm uses CrewAI’s default, gpt-4.1-mini - and failed with "OPENAI_API_KEY is required".
  • Agent: role, goal, backstory - becomes the system prompt (Lesson 6.1).
  • Task: description and expected_output - becomes the user prompt.
  • crew.kickoff() runs it; result.raw is the final text.
hello.py
import time from crewai import LLM, Agent, Crew, Task llm = LLM(model="ollama/llama3", base_url="http://localhost:11434", temperature=0) teacher = Agent( role="Friendly teacher", goal="Explain technical ideas to beginners in simple words", backstory="You teach programming to students who are new to the topic.", llm=llm, ) task = Task( description="Explain what an API is in two short sentences.", expected_output="Two short, simple sentences.", agent=teacher, ) crew = Crew(agents=[teacher], tasks=[task]) start = time.perf_counter() result = crew.kickoff() print("---") print(result.raw) print(f"{time.perf_counter() - start:.1f} s")
Output - first run (llama3 had to load)
--- Here is the explanation of what an API is in two short sentences: An API, or Application Programming Interface, is a set of rules that allows different computer programs to talk to each other and share information. Think of it like a messenger between different apps, allowing them to request and receive specific data or services from each other. 9.3 s

Reading the output

  • It worked: two simple sentences, as asked.
  • 9.3 s on the first run; later runs took about 4 s. The first call loads llama3 into memory (Lesson 5.6).
  • Again llama3 started with "Here is the explanation ..." - a sentence for us, not for the student. A rule in the backstory or a check in code can remove it.

Step 3 - see what the agent does: verbose=True

By default CrewAI printed nothing but our own lines. Add verbose=True to the agent to see each step in a box:

  • "Agent Started" shows which agent took which task.
  • "Agent Final Answer" shows its answer.
  • Useful while you learn and debug; turn it off for real runs.
Output - with verbose=True on the agent
╭────────────────────────────── 🤖 Agent Started ──────────────────────────────╮ │ │ │ Agent: Friendly teacher │ │ │ │ Task: Explain what an API is in two short sentences. │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ [Finalize] todos_count=0, todos_with_results=0 ╭─────────────────────────── ✅ Agent Final Answer ────────────────────────────╮ │ │ │ Agent: Friendly teacher │ │ │ │ Final Answer: │ │ Here is the explanation of what an API is in two short sentences: │ │ │ │ An API, or Application Programming Interface, is a set of rules that │ │ allows different computer programs to talk to each other and share │ │ information. Think of it like a messenger between different apps, allowing │ │ them to request and receive specific data or services from each other. │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯

Step 4 - what kickoff() returns

kickoff() returns a CrewOutput. The useful parts:

  • raw - the final text.
  • tasks_output - one entry per task, with the agent that did it.
  • token_usage - how many tokens were read and written. Useful for cost and speed (Lesson 5.6).
  • cached_prompt_tokens=94 of 95: Ollama reused the start of the prompt from the run before - the cache Lesson 5.6 measured.
Reading the result
result = Crew(agents=[teacher], tasks=[task]).kickoff() print("type:", type(result).__name__) print("raw:", result.raw[:60], "...") print("tasks_output:", len(result.tasks_output), "| agent:", result.tasks_output[0].agent) print("token_usage:", result.token_usage)
Output
type: CrewOutput raw: Here is the explanation of what an API is in two short sente ... tasks_output: 1 | agent: Friendly teacher token_usage: total_tokens=161 prompt_tokens=95 cached_prompt_tokens=94 completion_tokens=66 reasoning_tokens=0 cache_creation_tokens=0 successful_requests=1

Step 5 - keep your data on your machine

The model ran locally. But does anything else leave the machine? We sent all outgoing HTTPS traffic through a small logging proxy and ran the same crew three times:

  • Default settings: 8 connection attempts to telemetry.crewai.com:4319.
  • CREWAI_TELEMETRY_OPT_OUT=true (the switch in many tutorials): still 8. Nothing in CrewAI 1.15.27 reads that name.
  • CREWAI_DISABLE_TELEMETRY=true: 0 attempts. The answer was the same.
  • The code also accepts CREWAI_DISABLE_TRACKING=true and OTEL_SDK_DISABLED=true. We checked these names in CrewAI’s source code.
  • Set the switch for every run - in your shell profile, a .env file, or your service’s settings.
How we checked - a proxy that only logs where a program tries to go
# A tiny HTTPS proxy that only logs which hosts a program tries to reach (CONNECT), then refuses. import socketserver, sys class H(socketserver.StreamRequestHandler): def handle(self): line = self.rfile.readline().decode(errors="replace").strip() with open("proxy.log", "a") as f: f.write(line + "\n") self.wfile.write(b"HTTP/1.1 403 Forbidden\r\n\r\n") socketserver.ThreadingTCPServer.allow_reuse_address = True socketserver.ThreadingTCPServer(("127.0.0.1", 8899), H).serve_forever() # then, for each setting: # HTTPS_PROXY=http://127.0.0.1:8899 NO_PROXY=localhost,127.0.0.1 python hello.py
Output - what each run tried to reach
== no flag: 8 CONNECT telemetry.crewai.com:4319 == CREWAI_TELEMETRY_OPT_OUT=true: 8 CONNECT telemetry.crewai.com:4319 == CREWAI_DISABLE_TELEMETRY=true: (nothing)

Watch out: Do not trust a setting name from a blog post - or from this lesson in a year. Libraries rename things. When privacy matters, check what really leaves the machine, as we did here.

Step 6 - the setup mistakes, and their errors

We made each common mistake on purpose:

  • No prefix (model="llama3"): CrewAI thought it was an OpenAI model - "OPENAI_API_KEY is required". Add "ollama/".
  • A model that is not pulled (ollama/llama3.1): a clear 404 - "model 'llama3.1' not found". Run ollama pull, or fix the name.
  • Ollama not running (wrong port): "Failed to connect to OpenAI API: Connection error." The message says OpenAI because CrewAI uses Ollama’s OpenAI-compatible endpoint. Check that Ollama runs.
  • No base_url, but OLLAMA_HOST=127.0.0.1:11434 set: it worked. CrewAI reads Ollama’s own variable and adds the missing parts (http://, /v1).
  • Before each error CrewAI also printed an internal warning ("Event pairing mismatch"). It is noise - read the last line.
Output - four runs of the same one-agent crew
== model="llama3" (no prefix) ERROR: ValueError: OPENAI_API_KEY is required == model="ollama/llama3.1" (not pulled) ERROR: ValueError: Model llama3.1 not found: Error code: 404 - {'error': {'message': "model 'llama3.1' not found", 'type': 'not_found_error', 'param': None, 'code': None}} == base_url="http://localhost:11999" (nothing listening) ERROR: ConnectionError: Failed to connect to OpenAI API: Connection error. == model="ollama/llama3", no base_url, OLLAMA_HOST=127.0.0.1:11434 result: Hello!

The recipe

  • 1. A separate virtual environment with Python 3.10-3.13; pip install crewai.
  • 2. Ollama running, model pulled.
  • 3. llm = LLM(model="ollama/llama3", base_url="http://localhost:11434", temperature=0), passed to every agent.
  • 4. export CREWAI_DISABLE_TELEMETRY=true before you run anything.
  • 5. verbose=True while learning; read result.raw, result.tasks_output and result.token_usage.

Setup at a glance

Install

Python 3.10-3.13.

pip install crewai
Local model

Prefix + address.

LLM(model="ollama/llama3", base_url="http://localhost:11434")
Address from env

Ollama’s own variable.

OLLAMA_HOST=127.0.0.1:11434
Telemetry off

Checked on the network.

CREWAI_DISABLE_TELEMETRY=true
See the steps

On the agent.

Agent(..., verbose=True)
Token counts

After kickoff().

result.token_usage

Try it yourself

The code does not change. Swap the content string and the program does something else entirely.

Use gemma3

“Change the model to "ollama/gemma3". Is the answer different? Is it faster?”

Remove the preamble

“Add "Never introduce your answer; start with the answer itself." to the backstory. Does "Here is..." disappear?”

Check your machine

“Run the proxy check yourself, with and without CREWAI_DISABLE_TELEMETRY.”

Break it

“Stop Ollama and run the crew. Compare the error with the one in this lesson.”

What usually goes wrong

Forgetting the provider prefix

Without "ollama/" CrewAI asks for an OpenAI key.

✗ LLM(model="llama3")
✓ LLM(model="ollama/llama3", base_url="http://localhost:11434")
An agent without llm=

It uses CrewAI’s default model (gpt-4.1-mini) and fails with "OPENAI_API_KEY is required". Give every agent your local llm.

The old telemetry switch

CREWAI_TELEMETRY_OPT_OUT did nothing in 1.15.27 - 8 connection attempts still happened.

✗ CREWAI_TELEMETRY_OPT_OUT=true
✓ CREWAI_DISABLE_TELEMETRY=true
Reading "OpenAI" in the error literally

"Failed to connect to OpenAI API" here meant Ollama was not reachable.

Key points

  • CrewAI 1.15.27 needs Python 3.10-3.13; install it in its own virtual environment.
  • LLM(model="ollama/llama3", base_url=...) connects to a local model; pass it to every agent.
  • It talks to Ollama’s OpenAI-compatible endpoint - no litellm needed in this version.
  • CrewAI sends telemetry by default; CREWAI_DISABLE_TELEMETRY=true stopped it, CREWAI_TELEMETRY_OPT_OUT did not.
  • Know the errors: no prefix -> OPENAI_API_KEY required; missing model -> 404; Ollama down -> "Failed to connect to OpenAI API".

Quick check before you move on

What does the "ollama/" in "ollama/llama3" do?
It tells CrewAI the model runs on Ollama, so it uses Ollama’s endpoint instead of OpenAI.
How many telemetry connection attempts did the default run make?
8, to telemetry.crewai.com:4319.
Which setting stopped them?
CREWAI_DISABLE_TELEMETRY=true (the code also accepts CREWAI_DISABLE_TRACKING and OTEL_SDK_DISABLED).
Why was the first run 9.3 s and later ones about 4 s?
The first call had to load llama3 into memory.

Quiz

  1. 1.

    You see "OPENAI_API_KEY is required" but you use Ollama. What is the likely cause?

  2. 2.

    The error says "Failed to connect to OpenAI API". What do you check first?

  3. 3.

    Why check the network instead of trusting a documented setting?

  4. 4.

    Where do you find token counts after a run?

Interview questions

How do you run CrewAI fully locally?

Point every agent at a local model via LLM(model="ollama/<model>", base_url=...), disable telemetry with the current environment variable, avoid hosted tools and defaults, and verify outbound traffic.

How would you verify that an AI library does not send data externally?

Route its traffic through a logging proxy or firewall, run it with and without the opt-out setting, and compare the connections - rather than trusting documentation alone.

Comments

Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.

Loading comments...