进阶 📋 7 个步骤 第 481 / 481 篇

用 CrewAI + Ollama 搭全本地多智能体:零 API 账单跑通层级协作

CrewAI 1.15 + Ollama 本地实操:环境与模型准备、遥测关闭、角色 Agent 定义、sequential 与 hierarchical 双形态跑通、Ollama 本地记忆嵌入、自定义工具与防跑飞护栏。

2026.09.27· 24 分钟阅读· 约 2690 字· 🧠 CrewAI / 🦙 Ollama

多智能体框架的教程大多默认你接云端大模型 API——好用,但每一轮 agent 循环都在向外发数据、产生账单。如果你处理的数据不适合出本地,或者只想低成本把多智能体协作的机制跑通,CrewAI + Ollama 是社区验证充分的组合:CrewAI 负责角色分工与任务编排,Ollama 在本机提供 OpenAI 兼容接口的模型服务,CrewAI 用 ollama/ 前缀直连,全程零 API 账单、数据不出机器。本篇从装环境到跑通 sequential 与 hierarchical 两种协作形态,再到本地记忆与工具,给你一条可复现的完整路径。

🎯 适合人群:想体验/落地多智能体协作、且要求数据本地化的开发者。需要一台显存尚可的机器(跑 14B 级模型建议 16GB+ 显存或统一内存;资源紧张可换 7B 级小模型,代价见正文)。

先理解:本地多智能体的取舍

维度本地 Ollama 模型云端 API
数据边界不出机器,敏感场景友好提示与工具结果都发往供应商
成本电费;无按 token 计费按 token 计费,多智能体轮次多、放大明显
指令遵循小模型对角色/格式指令遵循明显偏弱,需收紧提示前沿模型可靠得多
速度与并发受本机算力限制,多 agent 串行时明显偏慢弹性并发

结论很实际:本地组合适合练机制、跑原型、处理不出本地的数据;对输出质量要求高的生产任务,让关键角色走云端、辅助角色走本地,是常见的混合形态。CrewAI 两种都支持,切换只改模型配置那一行。

版本信息以官方发布为准。本文写作时参照 CrewAI 1.15.x(Python 3.10-3.13)与 Ollama v0.34.x,两者迭代都很快;类名、参数、模型名随版本变动,装出来的版本若与文中不同,以你本地 crewai --version 与 Ollama 官方文档为准。另外 CrewAI 默认发送匿名使用遥测,本教程 Step 2 会关掉它——本地优先就贯彻到底。

Step 1:环境准备

1 Python 侧装 CrewAI,模型侧装 Ollama
# 1) Python 侧(推荐用 uv 管理项目与依赖)
uv init crew-local && cd crew-local
uv add crewai crewai-tools

# 2) 模型侧:安装 Ollama 后拉取两个模型
ollama pull qwen3:14b          # 主力推理模型(按显存换 7b/32b)
ollama pull nomic-embed-text   # 本地记忆用的嵌入模型

# 3) 验证 Ollama 服务在跑
ollama list
curl http://localhost:11434    # 返回 Ollama is running 即正常

模型选择的量力原则:显存够就上 14B 级,多智能体的角色扮演与工具调用对模型能力有实打实的要求;资源紧张用 7B 级起步,但要把任务拆得更细、提示写得更死(原因见 Step 5 的 warn)。嵌入模型 nomic-embed-text 很小,随便装。

💡 先花两分钟单测模型本身:命令行里 ollama run qwen3:14b 聊两句,确认回答速度可接受。模型层慢,CrewAI 层怎么调都白搭。

Step 2:关掉遥测,锁定本地边界

2 一行环境变量,把使用统计也留在本地
# 在运行 crew 的 shell 里设置(或写进 .env / 项目启动脚本)
export CREWAI_DISABLE_TELEMETRY=true

# 验证:设置后运行一次 crew,确认无联网上传的遥测请求
# (本地防火墙日志里不应出现 CrewAI 相关外联)

这一步的意义不只是隐私洁癖:既然选择本地化的动机是数据边界,那么框架自身的遥测通道就是同一条边界上的口子。把开关写进项目启动脚本而不是靠记忆手工设置,边界才稳定。

Step 3:定义角色 Agent

3 role / goal / backstory 三件套 + 本地模型绑定
from crewai import Agent, LLM, Task, Crew, Process

# 绑定本地 Ollama:ollama/ 前缀直连本机 11434 的
# OpenAI 兼容端点,无需 API Key
local_llm = LLM(
    model="ollama/qwen3:14b",
    base_url="http://localhost:11434",
    temperature=0.2,
)

researcher = Agent(
    role="市场调研员",
    goal="围绕给定主题收集并列出关键事实,不编造数据",
    backstory="十年行业研究经验,坚持只写有来源的事实,"
              "查不到就明确说查不到。",
    llm=local_llm,
    allow_delegation=False,   # 执行者不委派,职责单一
)

writer = Agent(
    role="技术写手",
    goal="把调研事实整理成 300 字以内的中文小结",
    backstory="资深编辑,文风克制,绝不添加调研未提供的信息。",
    llm=local_llm,
)

role/goal/backstory 不是装饰——它们会拼进提示词塑造模型行为,本地小模型对此尤其敏感。写法上两个要点:负面约束写具体(「不编造」「查不到就说」比「认真负责」有用得多);每个 Agent 职责单一,别让一个角色既调研又审校,小模型撑不住复合角色。

Step 4:sequential 流程跑通双 Agent 协作

4 任务接力:调研的产出是写作的输入
task_research = Task(
    description="调研「2026 年 AI 智能体在制造业的落地场景」,"
                "列出至少 3 个有事实支撑的场景。",
    expected_output="带要点的中文事实清单,每条一句话",
    agent=researcher,
)

task_write = Task(
    description="基于上一任务的调研结果写一段 300 字以内小结。",
    expected_output="300 字以内中文小结,不含调研之外的信息",
    agent=writer,
    context=[task_research],   # 接收上游产出作为上下文
)

crew = Crew(
    agents=[researcher, writer],
    tasks=[task_research, task_write],
    process=Process.sequential,   # 顺序执行
)

result = crew.kickoff()
print(result)

预期效果:researcher 先跑,输出事实清单;writer 拿着清单(context 显式指定依赖)产出小结。expected_output 是质量闸门——它告诉模型「什么算做完了」,对输出格式不稳的本地模型尤其重要,每个任务都要写清楚。初次运行要等两轮模型推理,14B 模型在消费级显卡上单轮十几秒到几十秒属正常,别当成卡死。

💡 打开 verbose=True(Crew 或 Agent 级)能看到每轮的思考与工具调用轨迹。排查「产出跑偏」时,先看轨迹判断是哪一环跑偏,再定向收紧那一环的提示。

Step 5:升级 hierarchical,让 manager 动态派活

5 固定接力 vs 经理派活,按任务复杂度选
crew = Crew(
    agents=[researcher, writer],
    tasks=[task_write],          # hierarchical 下任务目标交给经理拆解
    process=Process.hierarchical,
    manager_llm=local_llm,       # 经理也用本地模型;配置形态随版本而定
)

result = crew.kickoff()

hierarchical 模式引入一个 manager 角色:不再按固定顺序执行,而是由经理把任务目标拆解、动态分配给 worker、汇总结果。它比 sequential 灵活,代价是推理调用更多、行为更难预测——经理本身也是一轮模型调用,拆解质量取决于模型能力。

本地小模型当经理容易翻车。社区经验一致:小模型在 hierarchical 模式下常出现重复派活、提前收工、任务描述失真。如果 7B 级模型做经理不稳,两个选择:把经理换成你本地最大的模型;或经理走云端、worker 留本地——CrewAI 支持按角色配不同模型,混合形态就是为此准备的。

Step 6:开本地记忆,让 Crew 越用越顺

6 嵌入模型指到本地,记忆数据不出机器
crew = Crew(
    agents=[researcher, writer],
    tasks=[task_research, task_write],
    process=Process.sequential,
    memory=True,                          # 打开 CrewAI 内置记忆
    embedder={
        "provider": "ollama",
        "config": {"model": "nomic-embed-text",
                   "url": "http://localhost:11434"},
    },
)

CrewAI 的记忆分短期(会话内上下文)、长期(跨次运行的实体与结论)、实体记忆几层,默认需要嵌入模型做向量化——配置指向 Ollama 后,记忆的存储与检索都在本机完成。记忆的价值在重复性任务上最明显:同一 Crew 第二次跑相似主题时,能带上上次的结论与偏好,减少重复劳动。跑完一轮后到项目目录看生成的存储文件,确认记忆确实落盘在本地。

💡 记忆不是越多越好:长期记忆会沉淀上一次的错误结论。定期翻一眼记忆存储内容,发现过时或错误的条目手动清理——这是本地文件,直接可编辑。

Step 7:加工具与运行护栏

7 自定义工具 + 校验 + 超时,防跑飞三件套
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

class PriceRangeInput(BaseModel):
    product: str = Field(description="产品名称")

class LocalPriceTool(BaseTool):
    name: str = "local_price_query"
    description: str = "查询本地价格库中某产品的近期价格区间"
    args_schema: type = PriceRangeInput     # Pydantic 校验入参

    def _run(self, product: str) -> str:
        # 只读查询你自己的数据库/文件,返回结构化文本
        return f"{product} 近期价格区间:……"

researcher.tools = [LocalPriceTool()]

# 运行护栏:单任务超时与总轮次上限(参数名随版本而定,
# 以所用版本文档为准),并给任务设 expected_output 兜底

给 agent 挂工具时,本地模型对「何时调用工具、传什么参数」的判断弱于云端模型,所以 args_schema 的 Pydantic 校验与工具 description 一定要写到位——description 就是模型的工具使用说明书。另外两个社区高频提醒:CodeInterpreterTool 已从 crewai-tools 中移除,老教程里还在用它做代码执行的要换成外部方案;工具只给真正需要的角色,不给全员。

防死循环与失控成本。本地模型更容易陷入「反复调用同一工具」或「任务永不完成」的循环。上线前给 Crew 配好最大迭代次数与单任务超时,跑批场景在外层加失败重试上限——多智能体 × 死循环 = 一晚上的电费和日志,别问是怎么知道的。

常见问题速查

你遇到的现象大概率原因 & 解决
连接报错 Connection refusedOllama 服务没起。先跑 ollama list 与 curl 验证 11434 端口
模型输出格式混乱、无视角色设定本地小模型能力上限。收紧 goal/backstory 负面约束、写细 expected_output,或换更大模型
hierarchical 模式重复派活/提前结束经理模型太弱。经理换本地最大模型或走云端,worker 可留小模型
跑得极慢模型超出显存走了内存交换。换小模型或量化版本;多 agent 任务本身轮次多,属正常
工具从不被调用 / 参数乱传description 与 args_schema 描述不清。把工具说明当「给模型看的文档」来写
crew 卡住不结束循环上限/超时没配。加上限后重跑,并检查是否工具反复调用
想做代码执行工具CodeInterpreterTool 已从 crewai-tools 移除。改用自建沙箱执行方案,别再按老教程 import 它
← 返回教程中心