工程实践

让 Raft Agent 通过 Pi 使用本地大模型(Ollama)

技术笔记 · good fun 谷放

Raft 平台上的 AI agent 默认走云端模型 API。如果你自己运行 Ollama 提供本地大模型,可以让 agent 通过 runtime = pi 直接连上它——零 API 成本、低延迟、数据不出内网。

这篇基于真实的本地部署与局域网部署复盘整理,覆盖从配置到踩坑的完整流程。

01它怎么工作

先理解一件事:runtime = pi 并不会启动一个独立的 Pi CLI 子进程,而是在 raft-computer daemon 进程内加载内置的 Pi SDK 来运行 agent。Pi SDK 通过 OpenAI 兼容接口(/v1)调用 Ollama,模型与 provider 的配置读取自 ~/.pi/agent/models.json

Raft Agent 通过 Pi SDK 调用 Ollama 的架构图

前置条件:

02第一步:让 Ollama 可被访问(服务端)

如果 Ollama 与 raft-computer 在同一台机器上,用 localhost 即可,跳过本节。

Ollama 默认只监听 localhost,局域网内的其他机器访问不到。需要让它监听所有网卡:

# 设置 Ollama 监听所有接口
launchctl setenv OLLAMA_HOST "0.0.0.0:11434"

# 重启 Ollama 使配置生效

# 验证 LAN 可达(返回模型列表即成功)
curl http://<studio-ip>:11434/v1/models

03第二步:配置 Pi 的模型 provider(客户端)

编辑 ~/.pi/agent/models.json,添加一个 Ollama provider:

{
  "providers": {
    "local-ollama": {
      "baseUrl": "http://<ollama-host>:11434/v1",
      "api": "openai-completions",
      "apiKey": "ollama",
      "models": [
        {
          "id": "qwen3:27b",
          "name": "Local Ollama / qwen3:27b",
          "reasoning": false,
          "input": ["text"],
          "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
          "contextWindow": 262144,
          "maxTokens": 32768,
          "compat": {
            "supportsStore": false,
            "supportsDeveloperRole": false
          }
        }
      ]
    }
  }
}

几个字段说明:

04第三步:在 Raft 中选择模型

在 agent 设置中:

切换模型后必须开 New session。如果选择 Continue / Migrate,会复用旧 session,把之前超时留下的错误状态带进来,导致 agent 反复崩溃。

05第四步:解决大模型超时(关键坑)

如果你用的是 27B 以上的大模型,大概率会遇到这个问题。

现象:agent 每次 API 调用约 30 秒后报 Request timed out.,session 日志显示 input: 0, output: 0(一个 token 都没收到就断了)。8B 小模型正常,但 27B 大模型一调就超时;本地 Pi CLI 和 curl 直连 Ollama 都正常。

根因:这是 raft-computer daemon 的一个超时配置缺陷。daemon 为 Pi runtime 创建隔离的 HTTP 客户端时,设置了 bodyTimeoutMs: 0(不限制 body 传输,适合 streaming),却漏传了 headersTimeoutMs,于是 fallback 到默认值 30 秒。这个值是 undici 的 headersTimeout,即"从发出请求到收到第一个响应头"的最大等待时间。而 27B 模型冷加载时(把 18GB 模型从磁盘读入 GPU),处理大 system prompt 需要 60 秒以上才出第一个 token,超过 30 秒就被判定超时。本地 Pi CLI 不受影响,是因为它启动时把全局 dispatcher 的 headersTimeout 设成了 300 秒,而 daemon 内的 Pi runtime 用的是隔离客户端,不吃全局设置。

修复:通过环境变量把超时提高到 300 秒,并正确重启 daemon。

# 写入 shell profile 持久化
echo 'export SLOCK_DAEMON_FETCH_PRE_RESPONSE_TIMEOUT_MS=300000' >> ~/.bash_profile

# 必须 stop + start(不能用 restart,原因见下)
source ~/.bash_profile
raft-computer stop <server>
raft-computer start <server>
不要用 raft-computer restartrestart 由旧 service 进程 fork 出新 daemon,新进程继承旧环境,读不到新设置的环境变量。必须 stop 完全终止旧进程后,从 source 过 profile 的终端重新 start。

另外:stop daemon 会连同运行在其上的 agent 一起终止。如果你自己就是这个 daemon 上的 agent,请由人工在终端执行,而不是让 agent 自己重启自己。

验证环境变量已注入:

# 确认 daemon PID
ps aux | grep "raft-computer __run" | grep -v grep

# 确认环境变量已注入(替换 <PID>)
ps eww -p <PID> | tr ' ' '\n' | grep SLOCK_DAEMON_FETCH_PRE_RESPONSE_TIMEOUT_MS
# 应输出:SLOCK_DAEMON_FETCH_PRE_RESPONSE_TIMEOUT_MS=300000

06第五步(推荐):让模型常驻 GPU

Ollama 默认在模型空闲 5 分钟后自动从 GPU 卸载,下次请求需要重新冷加载(27B 约 89 秒)。如果 Ollama 是从 macOS 应用(/Applications/Ollama.app)启动的,shell 里的 OLLAMA_KEEP_ALIVE=-1 不会生效,需要用:

launchctl setenv OLLAMA_KEEP_ALIVE -1
# 然后重启 Ollama 应用

这样模型常驻 GPU,每次请求都是热状态,首 token 只需 2–3 秒。

07常见问题排查

现象原因解决
Request timed out.(约 30 秒) 环境变量未设置,headersTimeout 默认 30s 设置 SLOCK_DAEMON_FETCH_PRE_RESPONSE_TIMEOUT_MS=300000
环境变量设了但没生效 daemon 继承了旧环境 从 source 过 profile 的终端 stop + start
首次请求很慢(~90s)之后正常 模型冷加载 设置 OLLAMA_KEEP_ALIVE=-1 让模型常驻
Pi model not found: <provider>/<model> models.json 格式错误或名称不一致 检查 JSON 语法、provider 名与 model id,重启 agent(新 session)
input too large contextWindow 设得太小 ollama show <model> 取实际值填入
切换模型后仍崩溃 复用了旧 session Restart agent 并选择 New session
小模型(8B)不超时但 agent 不回消息 8B 指令遵循能力不足,不会调用 raft message send 使用 27B 或以上模型
重启后 pi 命令找不到 / Pi 报 Node.js 版本低 Node 版本不符 source ~/.bash_profile && nvm use default;Pi 要求 Node.js ≥ 22.19.0

08回滚

如果你在搭建本地 AI 工作流、或者想把 agent 跑在自己的机器上,可以聊聊。

聊聊你的想法 →