Agent Skills: Remote Ollama GPU Scheduler

调度远程 Ollama GPU 资源执行批量 embedding 或推理任务,提升多机环境下的算力利用率。

UncategorizedID: aaaaqwq/claude-code-skills/remote-ollama-gpu-scheduler

Install this agent skill to your local

pnpm dlx add-skill https://github.com/aAAaqwq/AGI-Super-Team/tree/HEAD/skills/remote-ollama-gpu-scheduler

Skill Files

Browse the full folder contents for remote-ollama-gpu-scheduler.

Download Skill

Loading file tree…

skills/remote-ollama-gpu-scheduler/SKILL.md

Skill Metadata

Name
remote-ollama-gpu-scheduler
Description
调度远程 Ollama GPU 资源执行批量 embedding 或推理任务,提升多机环境下的算力利用率。

Remote Ollama GPU Scheduler

高效调度远程 Ollama GPU 算力进行批量 embedding 的技能。

核心问题

Node.js fetch 不尊重 NO_PROXY,导致 Tailscale 流量被本地代理拦截:

# ❌ 这样没用!Node 忽略 NO_PROXY
NO_PROXY=100.0.0.0/8 node -e "fetch('http://100.x.x.x:11434')..."

# ✅ 必须清空全局代理变量
unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy
# 或用 undici 的 setGlobalDispatcher 绕过

架构选择

方案1: Ollama Remote Backend(稳定但慢)

┌─────────┐    Tailscale     ┌────────────┐
│  Linux  │ ───────────────▶ │ Mac Studio │
│  QMD    │   NO_PROXY       │  Ollama    │
│         │   100.0.0.0/8    │  0.6b/8b   │
└─────────┘                  └────────────┘

配置:

export QMD_EMBED_BACKEND=ollama
export QMD_OLLAMA_EMBED_URL=http://100.65.110.126:11434
export QMD_OLLAMA_EMBED_MODEL=qwen3-embedding:0.6b  # 或 :8b

性能:

  • 0.6b: ~2.5 chunks/s (1024-dim)
  • 8b: ~1.1 chunks/s (4096-dim)
  • 73k chunks: 8h (0.6b) / 19h (8b)

方案2: llama-server Backend(快但不稳定)⚠️

┌─────────┐    Tailscale     ┌────────────┐
│  Linux  │ ───────────────▶ │ Mac Studio │
│  QMD    │   /v1/embeddings │llama-server│
│         │   OpenAI格式     │  Flash Attn│
└─────────┘                  └────────────┘

配置:

export QMD_EMBED_BACKEND=llamaserver
export QMD_LLAMASERVER_URL=http://100.65.110.126:8081
export QMD_LLAMASERVER_CONCURRENCY=4

已知问题:

  • llama.cpp b8352 + Qwen3-0.6b embedding 在 parallel>1 时崩溃
  • 错误: GGML_ASSERT(i01 >= 0 && i01 < ne01) failed
  • 临时方案: 用 --parallel 1 或换 llama.cpp b8200

Mac Studio llama-server 启动命令

# 0.6b 模型路径
MODEL=/Users/daniel/.ollama/models/blobs/sha256-06507c7b42688469c4e7298b0a1e16deff06caf291cf0a5b278c308249c3e439

# 稳定配置(parallel=1)
/tmp/llama/llama-b8352/llama-server \
  -m $MODEL \
  --embedding \
  --port 8081 \
  --host 0.0.0.0 \
  -ngl 99 \
  --parallel 1 \
  -c 4096 \
  -b 512

# 激进配置(可能崩溃)⚠️
/tmp/llama/llama-b8352/llama-server \
  -m $MODEL \
  --embedding \
  --port 8081 \
  --host 0.0.0.0 \
  -ngl 99 \
  --parallel 8 \
  -c 16384 \
  -b 2048

性能基准(Mac Studio M4 Max)

| 后端 | 模型 | 并行度 | 速度 | 维度 | 稳定性 | |------|------|--------|------|------|--------| | Ollama | qwen3:0.6b | 1 | 2.5 c/s | 1024 | ✅ | | Ollama | qwen3:8b | 1 | 1.1 c/s | 4096 | ✅ | | llama-server | 0.6b | 1 | ~15 c/s* | 1024 | ⚠️ | | llama-server | 0.6b | 8 | ~120 c/s* | 1024 | ❌ crash |

*估算值,实际测试中 parallel>1 会崩溃

QMD Embedding 代码修改

/path/to/qmd/dist/llm.js 添加 llamaserver 后端:

// --- llama-server Backend (OpenAI-compatible) ---
const _LLAMASERVER_URL = process.env.QMD_LLAMASERVER_URL || "";
const _LLAMASERVER_CONCURRENCY = parseInt(process.env.QMD_LLAMASERVER_CONCURRENCY || "4", 10);

async function _llamaserverEmbed(text) {
    const url = `${_LLAMASERVER_URL}/v1/embeddings`;
    const resp = await fetch(url, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ model: "qwen3-embedding", input: text }),
        signal: AbortSignal.timeout(300000),
    });
    const data = await resp.json();
    const vec = data?.data?.[0]?.embedding;
    return vec ? { embedding: vec, model: "llamaserver:qwen3" } : null;
}

async function _llamaserverEmbedBatch(texts) {
    const CONC = _LLAMASERVER_CONCURRENCY;
    const subSize = Math.ceil(texts.length / CONC);
    const subBatches = [];
    for (let i = 0; i < texts.length; i += subSize) {
        subBatches.push(texts.slice(i, i + subSize));
    }
    
    const results = await Promise.all(subBatches.map(async (batch) => {
        const resp = await fetch(`${_LLAMASERVER_URL}/v1/embeddings`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ model: "qwen3-embedding", input: batch }),
            signal: AbortSignal.timeout(600000),
        });
        const data = await resp.json();
        return (data?.data || []).sort((a,b) => a.index - b.index)
            .map(d => d.embedding ? { embedding: d.embedding, model: "llamaserver:qwen3" } : null);
    }));
    
    return results.flat();
}

故障排查

1. fetch failed / connection refused

# 检查代理
env | grep -i proxy

# 检查 Tailscale
tailscale status | grep studio

# 测试直连
curl --noproxy "*" http://100.65.110.126:11434/api/tags

2. embedding 维度不匹配

Error: dimension mismatch (expected 4096, got 1024)

解决: qmd embed -f 强制重建索引

3. llama-server 崩溃

# 查看崩溃日志
tail -20 /tmp/llama-server.log | grep -E 'ASSERT|exception|abort'

# 降级参数
--parallel 1 -c 4096 -b 512

推荐配置

稳定优先: 用 Ollama backend + 0.6b 模型

export QMD_EMBED_BACKEND=ollama
export QMD_OLLAMA_EMBED_URL=http://100.65.110.126:11434
export QMD_OLLAMA_EMBED_MODEL=qwen3-embedding:0.6b

速度优先: 等待 llama.cpp 修复后使用 llamaserver backend


创建日期: 2026-03-15 适用场景: 批量 embedding、知识库构建、向量搜索