第 2 课

不花钱跑通:接本地 Ollama,学会无头模式

把本机 Ollama 的 qwen3:1.7b 配成 Custom Provider;用无头模式直接提问、管道喂文件、输出 JSON 给 jq 解析;关掉自动记忆让一次性调用从 1 分 43 秒降到 14 秒,并看清小模型改不了代码的原因。

图文25 分钟Qwen Code 官方文档 ↗

课程目录第 2 / 3 课

学习位置仅保存在当前浏览器,有效期 180 天。

01 / 图文教材

不花钱跑通:接本地 Ollama,学会无头模式

本课对照 Qwen Code 官方 Model Providers(本地自托管模型一节)与 Headless Mode 文档编写;在 8 核无显卡的 Debian 13 测试机上实跑,Qwen Code 0.27.3 + Ollama 0.40.13 + qwen3:1.7b(2026-11-14)。

本课目标

  • 不花钱、不注册,用本机 Ollama 跑的千问小模型当 Qwen Code 的「Custom Provider」
  • 学会无头模式的三种用法:直接带提示词、用管道喂文件、输出 JSON 给脚本解析
  • 亲眼看到小模型的能力边界,知道改代码这类任务该换什么模型

前置条件

  • 已完成第 1 课,qwen --version 能输出版本号
  • 已安装 Ollama,ollama --version 能输出版本号,服务监听在默认端口 11434
  • 内存建议 4 GB 以上空闲;本课用的 qwen3:1.7b 下载约 1.4 GB
  • 安装了 jq(Debian / Ubuntu 用 sudo apt install jq,macOS 用 brew install jq)

第 1 步:拉取本地模型

ollama pull qwen3:1.7b

对照结果(结尾几行):

pulling 3d0b790534fe: 100% ▕██████████████████▏ 1.4 GB
verifying sha256 digest
writing manifest
success

再确认 Ollama 的 OpenAI 兼容接口能看到这个模型:

curl -s http://localhost:11434/v1/models | jq -r '.data[].id'

对照结果里应当有一行 qwen3:1.7b。

第 2 步:把 Ollama 写进 settings.json

官方文档说明,Ollama、vLLM、LM Studio 这类本地推理服务都提供 OpenAI 兼容接口,按 openai 协议加一个本地 baseUrl 即可。Ollama 不校验 Key,OLLAMA_API_KEY 随便填一个值就行。

如果第 1 课已经写过 ~/.qwen/settings.json,先备份一份:

mkdir -p ~/.qwen
cp ~/.qwen/settings.json ~/.qwen/settings.json.bak 2>/dev/null

然后写入下面的内容:

cat > ~/.qwen/settings.json <<'EOF'
{
  "env": {
    "OLLAMA_API_KEY": "ollama"
  },
  "modelProviders": {
    "openai": [
      {
        "id": "qwen3:1.7b",
        "name": "Qwen3 1.7B (Ollama)",
        "envKey": "OLLAMA_API_KEY",
        "baseUrl": "http://localhost:11434/v1",
        "generationConfig": {
          "timeout": 300000,
          "contextWindowSize": 32768
        }
      }
    ]
  },
  "security": {
    "auth": {
      "selectedType": "openai"
    }
  },
  "model": {
    "name": "qwen3:1.7b"
  }
}
EOF

timeout 是单次请求的超时(毫秒)。CPU 上跑模型很慢,官方示例给本地模型配的就是 300000,也就是 5 分钟。

第 3 步:准备一个练习项目

mkdir -p ~/hello-qwen && cd ~/hello-qwen
git init -q
printf 'def add(a, b):\n    return a + b\n' > calc.py
git add calc.py
cat calc.py

对照结果:

def add(a, b):
    return a + b

git add 先把文件放进暂存区,之后智能体改了什么,用 git diff 一眼就能看出来。

第 4 步:无头模式问第一句

把提示词直接写在 qwen 后面,就是一次性的无头调用,答完自动退出:

time qwen "用一句中文介绍你自己"

对照结果(测试机实跑):

我是一名助手,旨在为您提供帮助和支持。

real	2m18.000s

官方文档里的写法是 qwen -p "…",效果相同;不过 0.27.3 的 qwen --help 已经把 -p 标记为 deprecated,推荐直接写提示词。

第 5 步:用管道把文件喂给它

标准输入的内容会和提示词拼在一起发给模型:

time (cat calc.py | qwen "用两句中文解释这段代码")

对照结果:

这段代码定义了一个名为`add`的函数,接收两个参数`a`和`b`,
通过`+`运算符将它们相加后返回结果。函数本质上是一个简单的
加法操作,用于计算两个数值的总和。

real	1m2.204s

终端里这段话是一整行,这里为了好读手动折了行。小模型每次的措辞都会不同,意思对上即可。

第 6 步:输出 JSON,交给脚本解析

加 --output-format json 后,输出是一个 JSON 数组:第一项是 system 初始化消息(含模型名、工具列表、审批模式),中间是 assistant 消息,最后一项是 result。用 jq 只取最终答案:

time (qwen "只回答一个数字:3 乘以 7 等于几" --output-format json \
  | jq -r '.[] | select(.type == "result") | .result')

对照结果:

3乘以7等于21。

real	1m43.840s

总耗时 1 分 43 秒。可我们在同一台机器上另跑同一句时,JSON 里记录模型作答耗时的 duration_ms 只有 17408,也就是 17 秒。多出来的时间花在答完以后:官方 Headless 文档说明,一次性调用输出结果后,会等后台的自动记忆整理(auto-memory)做完才退出。CPU 上的小模型整理得很慢,做练习时可以先关掉:

python3 - <<'PYEOF'
import json, pathlib
p = pathlib.Path.home() / ".qwen/settings.json"
s = json.loads(p.read_text())
s["memory"] = {"enableManagedAutoMemory": False,
               "enableManagedAutoDream": False}
p.write_text(json.dumps(s, indent=2, ensure_ascii=False))
PYEOF

这段脚本只是在 settings.json 里加上一段 "memory" 配置,手动编辑也一样。再跑一次,顺便取几个写脚本常用的字段:

time (qwen "只回答一个数字:3 乘以 7 等于几" --output-format json \
  | jq -c '.[] | select(.type == "result")
           | {result, num_turns, duration_ms}')

对照结果:

{"result":"21","num_turns":1,"duration_ms":13289}

real	0m14.159s

总耗时从 1 分 43 秒降到 14 秒。写脚本时用 .is_error 和进程退出码判断成败:is_error 为 false、退出码为 0 就是成功。

第 7 步:试试让小模型改代码

time qwen "在 calc.py 里新增函数 multiply(a, b),返回 a * b" \
  --approval-mode auto-edit
cat calc.py

--approval-mode auto-edit 表示改文件自动批准、执行 Shell 命令仍要确认。测试机上的真实结果(模型回复较长,这里按句折行,结尾的 JSON 示例从略):

The error occurred because the `tool_call` function required
the `name` parameter, which was missing in the parameters
provided. To resolve this, ensure the `name` is included as a
required field when invoking the tool. ……

real	2m16.544s
def add(a, b):
    return a + b

模型没有改文件,反而解释起了自己的报错,calc.py 还是原样。

我们另外用 --output-format json 复跑了两次(其中一次用 --core-tools read_file edit 只开放读文件和改文件两个工具),记录显示 qwen3:1.7b 每次都没按工具格式调用,Qwen Code 返回 [tool_call bridge refused] params must have required property 'name',calc.py 一行都没改。这说明 1.7B 的模型能聊天、能读管道进来的文本,却撑不起「读文件、改文件」这种多步工具调用。

要真正改代码,就换第 1 课配好的云端模型,用 --model 临时切换;也可以在本地换参数量更大的模型。想让两种模型都能选,把两课的 modelProviders 条目合进同一个 openai 数组,env 里两个 Key 都保留:

qwen "在 calc.py 里新增函数 multiply(a, b),返回 a * b" \
  --approval-mode auto-edit --model qwen3-coder-plus
git diff

本课的测试机没有配云端 Key,这条命令没有实跑。跑完以后用 git diff 确认 calc.py 里多了 multiply 函数即可。

常见坑

  • 答完了进程还不退出:这不是卡死,是在等自动记忆整理,见第 6 步。关掉 memory.enableManagedAutoMemory 和 memory.enableManagedAutoDream 后,测试机上一次问答从 1 分 43 秒降到 14 秒。换回云端大模型时可以再打开,跨会话记忆是很有用的功能
  • 连接被拒绝:先 curl http://localhost:11434/v1/models 确认 Ollama 在运行;baseUrl 末尾的 /v1 不能漏
  • model.name 对不上:model.name 必须和 modelProviders 里某一项的 id 一字不差,Ollama 的模型名带冒号,例如 qwen3:1.7b
  • CPU 上太慢:Qwen Code 每次都会带上很长的系统提示词和工具说明,测试机上一句简单问答要 1 到 2 分钟,有显卡的机器会快很多
  • 覆盖了第 1 课的配置:本课的 cat > 会整个覆盖 settings.json,要恢复就把 settings.json.bak 复制回去

小结

只靠一份 settings.json,Qwen Code 就能接上任何 OpenAI 兼容的本地服务。无头模式的直接提问、管道输入和 JSON 输出,足够把它塞进 shell 脚本和 CI。小模型适合练手和做问答,改代码要换更强的模型。下一课给项目立规矩:写 QWEN.md、选审批模式、挂 MCP 服务。