news 2026/8/28 11:03:07

Claude Code+Ollama+调度层:打造局域网NUC推理集群

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Claude Code+Ollama+调度层:打造局域网NUC推理集群

在实际的私有化开发环境里,最理想的状态是既保留 Claude Code 这种 Agent 编码工具的体验,又不必把所有请求都发到远程 API。标题里的 Yeschef 项目就是这种思路的一种落地:Claude Code 作为任务入口,局域网里跑 3 台 NUC,每台 NUC 上都装 Ollama,中间再由一个调度层把 Claude Code 发出的推理请求分发到不同节点,最终在局域网内部获得 627 tok/s 级别的生成速度。

这套组合的核心价值,是把“控制流”和“模型推理”解耦。Claude Code 负责读取代码、生成工具调用、维护多轮上下文;Ollama 只负责加载模型并输出 token;Yeschef 这个调度层则负责协议转换、节点选择和流量分配。三者各干各的,任何一个环节都可以单独替换。对正在做本地模型网关、私有化编码助手或多节点推理集群的人来说,这套架构可以直接作为参考。

这篇文章会先拆解整套链路里三个角色的职责,再说明为什么选择“局域网 + NUC 集群”而不是单台服务器,然后从零搭建一个最小可运行系统:三台 NUC 装 Ollama、Claude Code 连接本地端点、调度层完成请求转换和节点路由,最后给出吞吐验证方法和一份针对三层架构的排错手册。

1. 先理解这套架构的三个角色:Claude Code、Ollama 和调度层

1.1 Claude Code 的定位:它是一个 Agent 控制器,不只是聊天客户端

Claude Code 是 Anthropic 推出的命令行编程 Agent,能够在终端里读取项目文件、执行命令、调用工具,并根据上下文生成代码改动。它的核心能力不是“生成一段文本”,而是维护一套完整的工具调用循环:用户发出需求,Claude Code 决定调用哪个工具、读取哪些文件、执行什么命令,然后根据结果继续推进。

但这个 Agent 本身不运行大模型,它需要把对话历史和工具结果发送给一个模型推理端点,拿到模型的下一步决策。默认情况下,这个端点是 Anthropic 的云 API。也就是说,Claude Code 和模型之间是标准的 HTTP 请求关系,而这也意味着:只要请求格式能被正确解释,模型推理发生在哪里并不重要。

这正是整套架构能被改造的基础。Claude Code 并不强制请求必须发到某个固定地址,它读取环境变量或配置来确认 API 地址,然后发送 Anthropic Messages API 格式的请求。

1.2 Ollama 的定位:本地模型运行时,通过 REST API 暴露推理能力

Ollama 是一个面向本地部署的模型运行工具,它把模型下载、量化管理、显存/内存调度和 REST API 封装在一起。开发者只需要拉取模型,就能在本地起一个 HTTP 服务。

Ollama 默认监听127.0.0.1:11434,提供两个最常用的接口:

  • POST /api/chat:发送多轮对话,支持流式返回。
  • POST /api/generate:输入一段 prompt,直接生成补全。

它还提供GET /api/tags查看本机已下载的模型列表,GET /api/ps查看当前正在运行的模型和加载状态。这些接口对后面做健康检查和负载估算非常有用。

Claude Code 默认发送的是 Anthropic Messages API 格式,而 Ollama 的原生接口不是这种格式,因此不能直接把 Claude Code 的ANTHROPIC_BASE_URL指向 Ollama 的11434端口。两者之间必须有一个格式转换层。

1.3 调度层 Yeschef 的职责:格式转换、模型映射、节点路由

题目里的 Yeschef 承担的就是这个“中间人”角色。它的职责可以拆成三件事:

第一,格式转换。接收 Claude Code 发来的 Anthropic Messages 请求,转换成 Ollama/api/chat请求;再把 Ollama 返回的 NDJSON 流转换成 Anthropic SSE 流返回给 Claude Code。

第二,模型映射。Claude Code 传入的模型名是 Anthropic 体系的名称,例如claude-sonnet-4-20250514。Ollama 可用的模型名则是qwen3:32bllama3.2:3b这样的格式。调度层维护一张路由表,把“Claude Code 请求的模型名”映射到“某台 NUC 上的某个 Ollama 模型”。

第三,节点路由。一台 NUC 跑一个模型,三台 NUC 就构成一个小型模型集群。调度层根据请求的模型名、节点健康状态、当前并发数和响应速度决定把请求发给哪一台。

下面用表格总结三者边界:

组件职责直接对外暴露的接口
Claude Code任务编排、工具调用、上下文管理命令行入口,消费 API
Yeschef 调度层协议转换、模型映射、节点分发对外提供/v1/messages
Ollama 节点加载模型、执行推理、返回流式结果对内提供/api/chat/api/tags

理解了这个分层,后面每一步操作就都围绕同一个目标:把三层之间的协议和地址打通,然后让流量按预期路由。

2. 为什么是“局域网 + NUC 集群”:选型逻辑和 627 tok/s 的解读

2.1 NUC 集群解决什么问题

单台性能很强的服务器当然能直接跑 Ollama,但如果只是做个人编码助手,成本偏高,而且大规模 GPU 服务器不一定适合放在办公环境。NUC 是 Intel 的迷你主机,体积小、功耗低、噪音小,多台叠加起来可以形成一个低成本的家庭或办公室推理集群。

三台 NUC 的典型配置常见于 8 到 16 核 CPU、16GB 到 64GB 内存、NVMe SSD,部分型号带 Iris Xe 核显。这个配置跑量化后的 7B 到 32B 模型是可行的,关键是模型文件和内存要匹配。如果单台机器只有 16GB 内存,跑 32B 模型会比较吃力;更适合的方案是每台 NUC 各跑一个不同的中小模型,由调度层按任务类型分发。

集群的意义在于“分工”:三台 NUC 可以分别加载三个模型,也可以让同一模型在多个节点上各放一份副本。前者扩大可用的模型种类,后者提升并发吞吐。标题中的场景选择了三台 NUC,本质上是用横向扩展换单机性能不足。

2.2 627 tok/s 应该怎么理解

627 tok/s 是一个很亮眼的数字,但要放在特定条件下去看,不能简单理解为“单次回复每秒生成 627 个 token”。这个数值通常来自两种可能:

  • 多请求并发时的聚合吞吐。例如同时发起多个流式请求,三台 NUC 各自以 150 到 250 tok/s 的速度生成,叠加后总吞吐接近 627 tok/s。
  • 加载了参数量较小的量化模型。比如 3B 或 7B 的 Q4 量化模型,在内存带宽充足的 NUC 上可以达到很高的单流速度。

对实际开发体验来说,单流速度影响“第一个 token 多久出现、回复多快打满”,聚合吞吐影响“同时处理多少个请求不排队”。验证 627 tok/s 时,先明确是单流还是并发聚合,否则压测结果没有可比性。

2.3 网络和硬件准备

局域网是这个架构的默认前提。所有推理流量都在内网传输,不依赖外网带宽,延迟低且数据不出内网。

对于网络,千兆以太网以内是够用的。Ollama 返回的文本 token 数据量并不大,瓶颈一定在模型推理,不在网络。2.5G 网口属于可选项,只有在大量并发请求、大量工具结果回传时才有明显收益。WiFi 也可以跑通,但更推荐有线连接,因为长时间推理会产生持续流量,无线网卡在弱信号下可能抖动。

硬件准备清单如下:

项目建议说明
CPU8 核以上决定 CPU 推理吞吐上限
内存16GB 起步,32B 模型建议 32GB模型权重、KV Cache 都要占内存
存储NVMe SSD,预留模型空间32B Q4 模型大约需要 20GB 左右
网络千兆有线稳定性和延迟都优于无线
散热NUC 底部架空,避免叠放长时间推理会持续发热,降频会直接影响 tok/s

2.4 NUC 选型时最容易忽略的点

NUC 型号很多,不同代际的 CPU 性能和核显能力差异很大。选择时先确认三个问题:

  • 准备跑多大的模型。如果只跑 3B 到 8B,16GB 到 32GB 内存足够;如果要跑 32B,至少 32GB 内存。
  • 是否依赖核显。部分 NUC 的核显能被 Ollama 调用,但更稳妥的做法是先把它当成 CPU 推理环境,再单独测试核显是否被识别。
  • 是否长期高负载。多台 NUC 放在同一个封闭机柜里,散热必须考虑。连续推理半小时后如果 CPU 降频,627 tok/s 会掉到原速率的 60% 甚至更低。

3. 在每台 NUC 上部署 Ollama 并暴露到局域网

3.1 安装 Ollama

Ollama 支持 Linux、macOS 和 Windows。NUC 通常安装 Linux,这里以 Ubuntu/Debian 类系统为例。官方提供安装脚本:

curl -fsSL https://ollama.com/install.sh | sh

执行前先确认脚本来源可信,安装完成后检查服务状态:

systemctl status ollama ollama --version

如果系统没有自动启动服务,可以手动启动:

sudo systemctl enable ollama sudo systemctl start ollama

3.2 修改监听地址,让 Ollama 能被局域网访问

Ollama 默认只监听本机127.0.0.1,其他机器访问不到。要让调度层访问每台 NUC 的 Ollama,需要修改环境变量OLLAMA_HOST

编辑 systemd 服务文件:

sudo systemctl edit ollama

追加配置:

[Service] Environment="OLLAMA_HOST=0.0.0.0:11434"

然后重启:

sudo systemctl daemon-reload sudo systemctl restart ollama

检查是否监听在局域网地址上:

ss -tlnp | grep 11434

此时从调度层所在机器执行以下命令,如果返回 JSON 列表,说明局域网访问已经打通:

curl http://<nuc-ip>:11434/api/tags

3.3 常用 Ollama 环境变量

Ollama 除OLLAMA_HOST外,有几个环境变量在集群场景很常用:

环境变量作用建议值
OLLAMA_HOST监听地址,默认 127.0.0.1:114340.0.0.0:11434
OLLAMA_MODELS模型存放目录按磁盘分区单独指定
OLLAMA_KEEP_ALIVE模型在内存中驻留时间高频使用建议5m或更长
OLLAMA_MAX_LOADED_MODELS最多同时加载几个模型按内存大小设置 1 到 3
OLLAMA_NUM_PARALLEL单个模型并发请求数默认 1,性能测试时可调大
OLLAMA_DEBUG输出调试日志排错时设为 1

需要注意:调大OLLAMA_NUM_PARALLEL会增加内存占用,因为多个并发请求需要更多 KV Cache 空间。如果节点内存不够,并发请求会被 Ollama 排队,实际吞吐不会随并发增加而线性增长。

3.4 模型规划:三台 NUC 如何分工

在拉模型之前,先想清楚每个节点跑什么模型。下面是一份可行的规划:

节点建议模型适合场景内存需求参考
nuc-01qwen3:32b复杂代码修改、长文分析约 24GB 到 32GB
nuc-02llama3.2:3bqwen3:8b快速回复、简短问答8GB 到 16GB
nuc-03qwen2.5:14bllama3.1:8b通用任务、中档负载16GB 到 24GB

具体选型要结合机器内存和实际任务来判断。如果三台都是 32GB 内存,可以都跑 32B 模型形成三副本,提升并发能力;如果内存差异较大,就按模型大小分配。

拉取模型命令:

ollama pull qwen3:32b ollama pull llama3.2:3b

拉取完成后确认模型存在:

ollama list

如果网络不稳定导致拉取很慢,不要反复删除重试。Ollama 支持断点续传,重复执行ollama pull会从已有进度继续。另外,如果团队内有多台机器需要相同的模型文件,可以在一台机器上拉取完成后,把整个模型目录拷贝到其他节点,绕开重复下载。

3.5 确认每个节点健康状态

节点上线后,先逐个验证两个接口:

# 查看可用模型 curl http://<nuc-01-ip>:11434/api/tags # 查看当前加载模型和并发情况 curl http://<nuc-01-ip>:11434/api/ps

/api/ps返回每个模型当前是否加载、加载进程的 CPU/内存占用、是否在处理请求等字段。这个接口适合作为调度层的健康检查路径。

4. 安装并配置 Claude Code,把请求指到本地调度层

4.1 安装 Claude Code

Claude Code 以 npm 包形式分发,需要先准备 Node.js 环境。安装命令:

npm install -g @anthropic-ai/claude-code

完成检查版本:

claude --version

不同版本对 API 端点、模型名和登录策略的支持有差异。如果安装后无法登录或提示当前地区不可用,先确认版本和官方文档要求的支持范围,再考虑是否需要降级或升级。

有一种常见报错是:

"deepseek-v4-pro" is not a model this version of claude code recognizes

这说明该版本 Claude Code 会校验模型名,遇到不认识的模型名会直接拒绝启动。解决办法不是去更换模型名,而是去修改 Claude Code 侧配置,让它使用一个能识别的模型名,再由调度层把这个名字映射成本地模型。

4.2 通过环境变量覆盖 API 端点

Claude Code 支持通过环境变量或 settings 配置自定义 API 地址。常见的做法是在~/.claude/settings.json里写入:

{ "env": { "ANTHROPIC_BASE_URL": "http://<scheduler-ip>:8787", "ANTHROPIC_AUTH_TOKEN": "local-test-token" } }

其中:

  • ANTHROPIC_BASE_URL指向调度层的地址,而不是直接指向某台 NUC。
  • ANTHROPIC_AUTH_TOKEN是调度层定义的令牌,用于区分请求来源。Claude Code 不管这个令牌是不是 Anthropic 签发的,它只负责带在请求头里。

如果使用命令行验证,也可以临时注入:

export ANTHROPIC_BASE_URL="http://<scheduler-ip>:8787" export ANTHROPIC_AUTH_TOKEN="local-test-token" claude

4.3 模型名的处理策略

Claude Code 启动后会读取模型配置。在本地场景里,最稳妥的方式是让 Claude Code 使用一个合法的 Anthropic 模型名,例如:

claude-sonnet-4-20250514

然后在调度层把该名称映射为某个 Ollama 模型。这样 Claude Code 端不会报模型名无法识别,调度层也能准确知道自己该把请求路由到哪里。

如果通过/model命令切换模型,同样保持 Claude Code 侧的模型名在它认识的范围内,调度层再决定这个名称对应哪个本地模型。

4.4 验证通路

此时 Claude Code 启动后会向http://<scheduler-ip>:8787/v1/messages发请求。如果调度层还没有实现,启动后会出现连接失败或 404。这是正常现象,下一步就来实现调度层。

5. 实现 Yeschef 调度层:协议转换和节点路由

5.1 调度层要处理的核心问题

调度层本质上是一个带路由能力的协议适配器。它需要处理四件事:

  1. 接收 Claude Code 发来的 Anthropic Messages 请求。
  2. 根据模型名映射到具体节点和 Ollama 模型名。
  3. 把 Anthropic 请求体转换成 Ollama/api/chat请求。
  4. 把 Ollama 的 NDJSON 流式响应转换成 Anthropic SSE 流返回。

下面用 Python 和 FastAPI 实现最小版本。这个示例用于说明核心思路,实际项目要结合自己的包名、路径和版本调整,并补充异常处理、认证、日志和配置外置化。

5.2 路由表与模型映射

路由表用一个独立文件管理,方便不重启服务就调整映射关系:

# routes.py ROUTE_TABLE = { # Claude Code 认识的模型名 -> (Ollama 节点地址, 本地模型名) "claude-sonnet-4-20250514": ("http://nuc-01:11434", "qwen3:32b"), "claude-3-5-haiku-latest": ("http://nuc-02:11434", "llama3.2:3b"), "claude-3-opus-latest": ("http://nuc-03:11434", "qwen2.5:14b"), }

路由表是关键配置。它把 Claude Code 侧的模型名和局域网内的 Ollama 节点解耦。以后想换本地模型,只需要修改这张表,不需要改 Claude Code 配置。

5.3 FastAPI 入口接收 Anthropic 请求

# main.py import json import httpx from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse from routes import ROUTE_TABLE app = FastAPI() def select_node(model_name: str): if model_name in ROUTE_TABLE: base_url, local_model = ROUTE_TABLE[model_name] return base_url, local_model # 未匹配时回退到默认节点,避免直接报错 return "http://nuc-01:11434", "qwen3:32b" @app.post("/v1/messages") async def dispatch(request: Request): body = await request.json() base_url, local_model = select_node(body.get("model", "")) ollama_payload = build_ollama_payload(body, local_model) # 转发到目标 Ollama 节点 async with httpx.AsyncClient(timeout=300) as client: upstream = await client.post( f"{base_url}/api/chat", json=ollama_payload, ) if upstream.status_code != 200: return StreamingResponse( iter([json.dumps({"error": upstream.text})]), status_code=502, media_type="application/json", ) # 这里应当改为流式转发,下面单独说明 return StreamingResponse( upstream.aiter_text(), media_type="text/event-stream", )

上面的代码只完成了转发,但 Ollama 返回的是 NDJSON,不是 Anthropic SSE,直接转发 Claude Code 无法解析。所以必须做格式转换。

5.4 请求体转换:Anthropic 到 Ollama

Anthropic Messages 和 Ollama Chat 请求结构不同,需要转换的主要字段如下:

Anthropic 请求字段Ollama 请求字段说明
modelmodel由路由表映射后的本地模型名
messages[].content字符串或块数组messages[].content文本需要拍平拆块
systemsystem系统提示单独传
max_tokensoptions.num_predict控制最大生成 token 数
streamstream两个体系都有流式模式
不直接对应options.num_ctx控制上下文窗口,按模型能力设置

转换函数:

def build_ollama_payload(body: dict, local_model: str): system_parts = [] messages = [] for item in body.get("messages", []): role = item.get("role") content = item.get("content", "") if role == "system": if isinstance(content, str): system_parts.append(content) continue if isinstance(content, str): messages.append({"role": role, "content": content}) continue # content 是块数组时,只提取文本块,工具块按需处理 text_parts = [] for block in content: if block.get("type") == "text": text_parts.append(block.get("text", "")) messages.append({"role": role, "content": "\n".join(text_parts)}) options = { "num_predict": body.get("max_tokens", 2048), "num_ctx": 8192, } return { "model": local_model, "messages": messages, "system": "\n".join(system_parts), "stream": True, "options": options, }

注意:如果 Claude Code 的请求里有工具调用块,真实项目需要把tool_usetool_result保留或转换为 Ollama 模型支持的工具格式。不同模型对工具调用的 JSON 结构要求不同,这一层往往是适配工作量最大的地方。

5.5 流式响应转换:Ollama NDJSON 转 Anthropic SSE

Ollama 流式返回的是 JSON Lines,每行一个 JSON 对象,文本在message.content字段里。Anthropic SSE 则要求按content_block_deltamessage_deltamessage_stop等事件返回。

def convert_stream(upstream): async def generator(): async for line in upstream.aiter_lines(): if not line.strip(): continue data = json.loads(line) delta = data.get("message", {}).get("content", "") if delta: yield "event: content_block_delta\n" yield f"data: {json.dumps({'type': 'content_block_delta', 'delta': {'type': 'text_delta', 'text': delta}})}\n\n" if data.get("done"): yield "event: message_delta\n" yield f"data: {json.dumps({'type': 'message_delta', 'delta': {'stop_reason': 'end_turn'}})}\n\n" yield "event: message_stop\n" yield "data: [DONE]\n\n" return generator()

然后在dispatch中使用:

async with httpx.AsyncClient(timeout=300) as client: async with client.stream( "POST", f"{base_url}/api/chat", json=ollama_payload ) as resp: if resp.status_code != 200: ... return StreamingResponse( convert_stream(resp), media_type="text/event-stream", )

这里最关键的是保持流式特性。不要在调度层把整个响应读完再返回,否则用户会感觉“要等全部生成完才开始输出”,丢失了流式体验。

5.6 健康检查与简单的负载选择

多节点路由要避免把请求发给已经挂掉或过热的节点。调度层可以定期访问每个节点的/api/ps/api/tags做健康检查。

async def check_health(base_url: str): try: async with httpx.AsyncClient(timeout=3) as client: resp = await client.get(f"{base_url}/api/ps") return resp.status_code == 200 except Exception: return False

完整的健康检查还要考虑模型是否加载、是否处于饱和状态、连续请求延迟是否变高等指标。一个简单策略是:每 10 秒检查一次节点可用性,并在路由选择时跳过不健康的节点;如果所有节点都不可用,调度层返回 503,并在响应头里带上诊断信息。

5.7 启动调度层

pip install fastapi uvicorn httpx uvicorn main:app --host 0.0.0.0 --port 8787

调度层启动后,先用curl做一次非流式验证:

curl http://<scheduler-ip>:8787/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: local-test-token" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 64, "messages": [{"role": "user", "content": "你好,回复一句话"} ]}'

如果返回的是 SSE 格式文本,并且能看到content_block_delta事件,说明链路已经打通。

6. 跑通最小演示并测量吞吐

6.1 端到端验证

调度层启动后,进入 Claude Code:

claude

在交互界面里输入一个简单问题,例如:

请列出当前目录下的文件,并解释每个文件的作用。

正常情况下,Claude Code 会先调用工具读取目录,然后把结果发送给调度层,调度层转换后交给某台 NUC 上的 Ollama 模型,生成文本后再经调度层返回。这个链路如果通了,说明三层架构基本成立。

6.2 用压测脚本计算聚合吞吐

627 tok/s 的验证不能靠人工观察,需要脚本统计。思路是并发发送多个流式请求,统计每个请求生成的 token 数总和,再除以总耗时。

import asyncio import time import json import httpx async def send_one(client, index): payload = { "model": "claude-sonnet-4-20250514", "max_tokens": 256, "messages": [{"role": "user", "content": "写一段 100 字左右的技术说明,介绍 HTTP 和 WebSocket 的区别"}] } tokens = 0 start = asyncio.get_event_loop().time() async with client.stream("POST", "http://127.0.0.1:8787/v1/messages", json=payload) as resp: async for line in resp.aiter_lines(): if not line.startswith("data: "): continue data = line[6:] if data == "[DONE]": break try: obj = json.loads(data) except json.JSONDecodeError: continue delta = obj.get("delta", {}) if isinstance(delta, dict) and delta.get("type") == "text_delta": tokens += len(delta.get("text", "")) cost = asyncio.get_event_loop().time() - start return tokens, cost async def main(): total_tokens = 0 max_cost = 0.0 async with httpx.AsyncClient(timeout=300) as client: results = await asyncio.gather(*[send_one(client, i) for i in range(8)]) for tokens, cost in results: total_tokens += tokens max_cost = max(max_cost, cost) print(f"总 token 数: {total_tokens}") print(f"最快请求耗时: {max(results[0][1] for results in [results]):.2f}s") print(f"聚合吞吐: {total_tokens / max_cost:.2f} tok/s") asyncio.run(main())

这里用字符数近似 token 数,只用于整体估算。更精确的做法是读取 Ollama 每个流式响应末尾的eval_count字段,把它累加起来。

6.3 627 tok/s 的结果解读

如果得到的聚合吞吐在 600 tok/s 左右,说明三台 NUC 的推理资源基本被用起来了。此时单次请求的响应速度可能仍然不算快,因为单个请求只落在某一台节点上,聚合吞吐是多节点并行带来的。

测试时要注意变量一致。模型相同、量化相同、并发数相同,数据才有可比性。影响吞吐的常见因素包括:

  • 模型参数量和量化级别。
  • 并发请求数,OLLAMA_NUM_PARALLEL是否调大。
  • 生成长度。短文本生成无法充分体现吞吐。
  • 机器是否降频。连续压测时散热不足会导致吞吐逐步下降。

7. 常见问题排查:从三层链路定位故障

7.1 先确定问题在哪一层

整套系统有三层:Claude Code、调度层、Ollama 节点。排查问题的顺序应该是先看 Claude Code 到调度层是否通,再看调度层到 Ollama 是否通,最后看模型本身是否正常工作。

问题现象优先排查层检查项
Claude Code 启动就报模型名无法识别Claude Code模型名是否在支持范围内
请求发出后长时间无响应调度层是否有请求进入日志,路由表是否匹配
调度层返回 404Ollama 节点/api/chat是否存在,模型名是否写错
连接被拒绝Ollama 节点OLLAMA_HOST是否设为 0.0.0.0
返回内容乱码调度层和终端编码是否为 UTF-8,流式拼接是否有误

7.2 Ollama 连接被拒绝

现象:

curl: (7) Failed to connect to 192.168.x.x port 11434: Connection refused

检查顺序:

ss -tlnp | grep 11434 curl http://127.0.0.1:11434/api/tags

如果本机能访问但其他机器不能,检查OLLAMA_HOST是否仍是127.0.0.1,以及防火墙是否放行 11434 端口。

7.3 模型名无法识别

Claude Code 报"xxx" is not a model this version of claude code recognizes时,本质是 Claude Code 在本地做模型名校验,请求还没到达调度层。

解决方式:不要试图让 Claude Code 接受一个本地模型名,而是让 Claude Code 使用它能识别的模型名,然后在调度层路由表里做映射。

7.4 调度层返回 502 或 404

如果 Claude Code 能看到错误码,排查调度层日志。常见原因:

  • 路由表里的节点地址写错。
  • Ollama 节点上没有该模型,/api/chat返回 404。
  • 请求体转换后缺少必填字段。

在调度层临时加上日志,把select_node的结果和 Ollama 返回的状态码打出来,能快速定位。

7.5 请求被 Killed 或进程退出

Ollama 日志里如果出现:

plugindaemoninternalservererror: killed

通常说明内存不足,模型加载时被系统 OOM Killer 终止。检查本机可用内存和模型大小:

free -h ollama list

解决方案是使用更小的量化模型、减少OLLAMA_NUM_PARALLEL,或增大机器内存。不要为了并发把内存耗尽。

7.6 中文乱码

如果 Claude Code 终端或调度层返回的中文乱码,先确认终端编码是 UTF-8:

locale

再检查调度层转发 SSE 时是否有截断或分块拼接问题。Ollama 流式返回的每一行都是一个完整 JSON,不要在调度层按固定字节切分,也不要尝试把多个 chunk 手动拼接后再编码,否则容易出现半个字被截断的乱码。

7.7 局域网访问不稳定

如果 NUC 使用无线网卡,长时间推理时吞吐波动大,优先换成有线网络。部分 Realtek 无线网卡在功率管理开启时会出现延迟抖动,可以在系统层面检查无线网卡驱动和电源管理策略,但最省事的方案还是网线直连交换机。

8. 生产化和扩展方向

8.1 从演示到可用的三个步骤

当前示例只是最小链路,真实使用还需要补齐三件事:

第一,认证。调度层不应该对所有局域网请求开放。可以在请求头校验自定义 token,并在多次认证失败后记录日志。

第二,日志和监控。为三个环节分别打日志:Claude Code 请求到达时间、调度层转发节点、Ollama 返回状态码和耗时。用 Prometheus 收集/api/ps的模型加载状态也不错,但要先保证基础日志能帮助定位问题。

第三,配置外置化。路由表、模型名映射、节点地址不要写死在代码里。建议用 YAML 管理:

routes: - claude_model: claude-sonnet-4-20250514 node: http://nuc-01:11434 ollama_model: qwen3:32b max_tokens: 4096

调度层启动时加载这份配置,变更后重新加载即可,不需要改代码。

8.2 模型策略:同一模型多副本还是多个模型

如果开发团队主要做通用编码任务,建议让三台 NUC 都加载同一个能力较强的模型,形成三副本,调度层按负载分发。这种方式能最大化并发吞吐,缺点是模型种类单一。

如果团队经常需要在“快速回复”和“复杂分析”之间切换,更适合不同节点跑不同模型。调度层根据请求模型名选择节点,小任务落到小模型,复杂任务落到大模型。

8.3 学习环境和生产环境差异

学习环境可以一台 NUC 一台机器地扩展:先两台节点跑通,再加第三台。生产环境必须从一开始就考虑:

  • 每个节点监控内存、温度、吞吐。
  • 调度层部署为 systemd 服务,异常自动重启。
  • 路由变更走配置发布,而不是直接改代码。
  • 增加熔断逻辑:某节点连续超时后自动摘除。

8.4 可复用的部署检查清单

检查项命令或位置预期结果
Ollama 监听局域网地址ss -tlnp | grep 11434显示 0.0.0.0:11434
节点模型列表可用curl http://<nuc-ip>:11434/api/tags返回 models 数组
调度
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/28 11:02:23

隐式高斯解码突破大基线:单目视图合成的新范式

大基线单目视图合成这个方向&#xff0c;这几年一直处于“能看但没完全能用”的状态。InfiniSplat 这个项目标题里最值得注意的&#xff0c;不是“Gaussian”也不是“View Synthesis”&#xff0c;而是“Implicit Gaussian Decoding”和“Large-Baseline”这两个修饰词。简单说…

作者头像 李华
网站建设 2026/8/28 11:02:13

写论文到底用哪个AI?我从开题到答辩帮你捋了一遍

又到开学季秋招季叠加论文季&#xff0c;后台被问最多的一句话就是&#xff1a;“学姐&#xff0c;写论文到底用哪个AI啊&#xff1f;” 说实话&#xff0c;2026年了&#xff0c;这个问题的答案早就不是"用ChatGPT就行"。现在的工具已经分化成好几个流派&#xff1a;…

作者头像 李华
网站建设 2026/8/28 10:54:44

SPMA定点分析法:突破FFT分辨率限制的频谱超分辨技术

简介&#xff1a;频谱分析是信号处理领域的核心基础&#xff0c;用于将时域信号转换到频域以观察其频率成分。传统方法如快速傅里叶变换&#xff08;FFT&#xff09;虽应用广泛&#xff0c;但受限于频率分辨率和栅栏效应&#xff0c;难以精确估计密集或接近的频率分量。其原理在…

作者头像 李华
网站建设 2026/8/28 10:54:07

具身智能技术栈拆解与仿真环境开发实践

黄仁勋、李飞飞、林斌&#xff0c;三个名字放在一起&#xff0c;很容易让人先切到“八卦视角”。但如果从技术视角看&#xff0c;他们重合在同一家机器人公司的投资方里&#xff0c;其实是在给具身智能画一条比较清晰的路径&#xff1a;训练算力、AI 算法、消费电子量产&#x…

作者头像 李华
网站建设 2026/8/28 10:52:32

10分钟跑通一个私有AI聊天界面:Open WebUI 部署实操

10分钟跑通一个私有AI聊天界面&#xff1a;Open WebUI 部署实操 【免费下载链接】open-webui User-friendly AI Interface (Supports Ollama, OpenAI API, ...) 项目地址: https://gitcode.com/GitHub_Trending/op/open-webui 模型厂商自带的 Demo 页面很简陋&#xff0…

作者头像 李华
网站建设 2026/8/28 10:50:34

AI情感陪伴产品实战:从大模型调用到多轮记忆与内容安全的工程链

近两年&#xff0c;“和 AI 谈恋爱”成了社交平台上的高频话题&#xff1a;有人把它当树洞&#xff0c;有人把它当虚拟伴侣&#xff0c;也有人靠聊天记录剪辑成短视频来获取流量。很多人只看到“话术甜、回复快、随时在线”&#xff0c;但落到工程里&#xff0c;这类产品并不是…

作者头像 李华