云端模型有一个绕不开的性质:你的代码和数据要离开本机。对很多团队来说这不是洁癖,是硬要求。好在 2026 年的本地推理已经足够可用——Ollama 现在既能提供 OpenAI 兼容接口,也能用一条命令把本地模型直接接进主流编码 Agent。这篇教程带你搭一个真正会调用工具的本地 Agent:模型在你机器上跑,工具在你机器上执行,数据不出本机。
先理解:本地 Agent 的三个组成部分
别把它想复杂,本地 Agent 就是三件事拼起来:
| 组件 | 在本教程里是什么 | 作用 |
|---|---|---|
| 推理引擎 | Ollama(默认监听 11434 端口) | 把模型跑起来,并提供 API |
| 模型 | Qwen3 8B 这类支持工具调用的开源模型 | 理解任务、决定调用哪个工具 |
| 工具循环 | 你自己写的那几十行代码 | 执行工具、把结果回填、判断是否继续 |
很多人以为「本地模型不行」,其实是第三块没搭好。工具循环写对了,小模型也能干成事。
Step 1:装 Ollama 并拉一个会调工具的模型
# macOS / Linux
curl -fsSL https://ollama.com/install.sh | sh
# Windows(PowerShell)
irm https://ollama.com/install.ps1 | iex
# 也可以直接用官方 Docker 镜像 ollama/ollama
# 拉一个支持工具调用的模型(8B 在 16GB 机器上是甜点区)
ollama pull qwen3:8b
# 确认拉好了
ollama list
不是所有本地模型都支持工具调用协议。选模型前先确认它声明了 tool calling 能力。另外 Qwen3 属于混合推理模型,每次工具调用前都会先「想一遍」,这会明显拖慢循环速度——后面 Step 7 会讲怎么关掉它。
Step 2:确认 OpenAI 兼容端点可用
Ollama 默认在 11434 端口提供两种接口:原生 /api/chat,以及 OpenAI 兼容的 /v1/chat/completions。用后者意味着你以后想切回云端,代码几乎不用改。
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3:8b",
"messages": [{"role": "user", "content": "用一句话介绍你自己"}]
}'
ollama serve。端口被占用的话,检查是不是已经有一个实例在跑。Step 3:跑通最小对话,并验证工具调用真的可用
pip install ollama
# minimal.py
from ollama import chat
r = chat(
model='qwen3:8b',
messages=[{'role': 'user', 'content': '用 Python 写一个快速排序'}],
)
print(r.message.content)
对话能跑通不代表工具调用能跑通——这是两件事。用一个带工具定义的最小请求确认一次:
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询某个城市的当前天气",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
r = chat(model='qwen3:8b',
messages=[{'role': 'user', 'content': '北京现在天气怎么样?'}],
tools=tools)
print(r.message.tool_calls) # 期望:返回一个 get_weather 调用,而不是普通文本
如果 tool_calls 是空的,说明这个模型或这个量化版本没走通工具协议。换个模型标签重试,别急着往下写循环——循环写得再漂亮,模型不发起调用也是白搭。
Step 4:手写一个工具调用循环
循环的逻辑只有五步:让模型决策 → 执行工具 → 把结果追加回消息 → 再问模型 → 直到模型给出最终回答。关键是要有步数上限,否则模型可能反复调同一个工具。
import json, ollama
TOOLS_SCHEMA = [/* 同 Step 3 的工具定义 */]
MAX_STEPS = 12
def execute(name, args):
if name == "get_weather":
return {"city": args["city"], "temp_c": 24, "condition": "晴"}
raise ValueError("未授权的工具: " + name) # 白名单兜底
def run(goal):
messages = [{"role": "user", "content": goal}]
for step in range(MAX_STEPS):
r = ollama.chat(model='qwen3:8b', messages=messages, tools=TOOLS_SCHEMA)
msg = r.message
if not msg.tool_calls: # 没有工具调用 = 这是最终回答
return msg.content
messages.append(msg)
for call in msg.tool_calls:
name = call.function.name
args = call.function.arguments or {}
print("[step %d] 调用 %s(%s)" % (step, name, args))
result = execute(name, args)
messages.append({
"role": "tool",
"content": json.dumps(result, ensure_ascii=False),
})
return "达到最大步数上限,已中止(请检查是否陷入循环)"
print(run("查一下北京的天气,再用一句话总结"))
messages.append(msg) 这一步——它把模型的工具调用意图也写进了历史。少了这行,下一轮模型就看不到自己刚才发起了什么调用,会一直重复同一个动作。这是手写循环最常见的 Bug。Step 5:加三条护栏
| 护栏 | 做法 | 为什么必要 |
|---|---|---|
| 工具白名单 | execute() 里对未声明工具直接抛错 | 防止模型编造出一个你没实现的工具名 |
| 只读优先 | 先只给读类工具;写类工具要求显式确认 | 小模型对「该不该动手」的判断不如大模型稳 |
| 全程留痕 | 每步记录工具名、参数、结果、步数 | 出错时你能复盘的东西就只有这份日志 |
别让本地模型直接操作生产系统。「本地」只解决了数据不出机器的问题,不解决判断力问题。删库、转账、发邮件这类不可逆动作,无论模型跑在哪里,都应该走人工确认。
Step 6:一条命令把本地模型接进编码 Agent
这是 2026 年本地部署最实用的新玩法:Ollama 内置了 launcher,一条命令就能把本地模型接进主流编码 Agent,不用手工配环境变量。
ollama launch claude --model qwen3:8b # Claude Code
ollama launch opencode --model qwen3:8b # OpenCode
ollama launch openclaw --model qwen3:8b # OpenClaw
# 支持的集成还包括 Codex、Copilot CLI、Droid 等
结果就是:编码 Agent 的交互体验不变,但推理跑在你自己的机器上——不按 token 计费,代码也不出本机。
Step 7:手工配置与性能调优
如果你想手动接 Claude Code,核心是让请求指向本地端点。有三个细节新手最容易踩:
export ANTHROPIC_BASE_URL=http://127.0.0.1:11434
export ANTHROPIC_API_KEY=""
export ANTHROPIC_AUTH_TOKEN=ollama # 必须写 ollama,留空会连不上
export ANTHROPIC_MODEL=qwen3:8b
export ANTHROPIC_DEFAULT_HAIKU_MODEL=qwen3:8b # 摘要也用同一个模型,
# 避免同时加载两个模型占满显存
export MAX_THINKING_TOKENS=0 # 关掉 Qwen3 的思考过程,
# 否则每次工具调用前都要等它"想"
export DISABLE_TELEMETRY=1
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
想让它长期生效,就把这些写进 ~/.claude/settings.json 的 env 段。跑起来之后用这条命令看性能:
ollama ps
# NAME SIZE PROCESSOR UNTIL
# qwen3:8b 5.2 GB 100% GPU 4 minutes from now
# PROCESSOR 显示 100% GPU = 加速生效
# 若 CPU 占比很高,说明没吃上显卡加速,速度会差好几倍
设定正确预期,比调参更重要。① 本地模型的多步推理能力仍不如云端前沿模型,跨文件的复杂架构调整是它的弱项;② 小模型更容易给出「看起来对但其实错」的答案,用在大仓库上尤其要谨慎;③ Ollama 的强项是简单,不是吞吐——多人并发场景请换 vLLM 之类的方案;④ 改端点意味着所有代码都流经本机,如果这台机器是共享的,隐私优势就不成立了。
常见问题速查
| 你遇到的现象 | 大概率原因 & 解决 |
|---|---|
| 模型返回 404 / not found | 模型标签写错。先 ollama list 看准确名称再填 |
| 连不上 11434 | 服务没起。dmg 版会自动起;Homebrew 版要手动 ollama serve |
| 工具调用一直不触发 | 模型不支持工具协议,或量化版本丢了该能力。换模型标签重试 |
| Agent 反复调同一个工具 | 忘记把模型的 tool_calls 消息追加进历史;或步数上限设得太高 |
| 回答慢到无法忍受 | 关掉 thinking(MAX_THINKING_TOKENS=0),或换更小一档模型 |
| 速度远低于预期 | ollama ps 看 PROCESSOR 列,确认 GPU 加速是否生效 |