PRZEWODNIK · INFRASTRUKTURA

Zarejestruj heterogeniczną flotę, routuj po wolnym VRAM-ie.

machines-server traktuje każdą stację roboczą, host aplikacyjny i węzeł brzegowy jako pełnoprawny byt z profilem sprzętowym, dostępnymi modelami i telemetrią na żywo. Ten przewodnik pokazuje, jak dodać drugą maszynę i routować agentów do tej, która ma model załadowany i zapas, żeby go pociągnąć.

Po co routować po maszynach

Jedna stacja robocza z 24 GB GPU wystarcza do rozwoju w pojedynkę. Dwie maszyny zaczynają się liczyć w chwili, gdy chcesz model 70B-klasy rezydujący na jednej, a mały szybki model rezydujący na drugiej, albo gdy agent-recenzent ma siedzieć na hoście CPU-only, a agent-pisarz zostaje na maszynie z GPU. machines-server jest tym, przez co ci agenci się znajdują.

1. Opisz każdą maszynę w YAML

Wrzuć po jednym pliku na maszynę do katalogu machines/ w roocie checkoutu consciousness-server. Format to zwykły YAML; właścicielem jest operator.

machines/workstation.yaml
# machines/workstation.yaml
name: workstation
role: gpu-host
hardware:
  cpu: 16-core x86_64
  ram_gb: 128
  gpu:
    - model: 24GB consumer GPU
      vram_gb: 24
    - model: 24GB consumer GPU
      vram_gb: 24
storage_gb: 8000
network:
  hostname: workstation.lan
  port: 11434          # Ollama
models:
  - llama3.1:70b
  - qwen2.5:32b
  - nomic-embed-text
tags: [primary, gpu, training]

I drugi plik, dla hosta CPU-only:

machines/app-host.yaml
# machines/app-host.yaml
name: app-host
role: cpu-host
hardware:
  cpu: 16-core x86_64
  ram_gb: 64
  gpu: []              # bez GPU, sam CPU
storage_gb: 2000
network:
  hostname: app-host.lan
  port: 11434
models:
  - phi3:mini
  - qwen2.5:3b
tags: [secondary, cpu]

Wymagane klucze to name, role, hardware, network.hostname i models. Reszta jest opcjonalna i pojawia się niezmodyfikowana w odpowiedzi API — wygodne do tagowania ("primary", "training", "edge"), grupowania po lokalizacji albo przypinania warunków licencyjnych.

2. Sprawdź, że machines-server je widzi

machines-server hot-reloaduje przy odczycie. Bez restartu, bez compose rebuild. Po wrzuceniu YAML-a:

terminal
# machines-server hot-reloaduje przy odczycie: wrzucasz YAML i curl
# zobaczy maszynę przy następnym /api/infrastructure. Sanity check:
curl -s http://127.0.0.1:3038/api/machines | jq '.machines[].name'

# Output:
# "workstation"
# "app-host"

3. Obserwuj telemetrię na żywo

Każda maszyna okresowo raportuje CPU, RAM, VRAM GPU (przez nvidia-smi) i które modele są aktualnie załadowane przez Ollamę. Endpoint zbiorczy:

terminal
curl -s http://127.0.0.1:3038/api/infrastructure | jq '.'

Co dostajesz w odpowiedzi:

response
{
  "machines": [
    {
      "name": "workstation",
      "role": "gpu-host",
      "status": "online",
      "telemetry": {
        "cpu_percent": 12.4,
        "ram_used_gb": 38.1,
        "gpu": [
          { "vram_used_gb": 18.2, "vram_free_gb": 5.8, "utilisation": 47 },
          { "vram_used_gb":  0.5, "vram_free_gb": 23.5, "utilisation":  0 }
        ]
      },
      "loaded_models": ["llama3.1:70b"]
    },
    {
      "name": "app-host",
      "role": "cpu-host",
      "status": "online",
      "telemetry": { "cpu_percent": 4.0, "ram_used_gb": 12.3 },
      "loaded_models": ["phi3:mini"]
    }
  ]
}

Telemetrię zbiera proces agentowy działający na każdym hoście (sidecar machines-server). Jeśli maszyna spadnie z sieci, status przechodzi na "offline" w następnym cyklu pollingu; agenci jej szukający powinni mieć fallback do peera.

4. Routuj agentów do właściwej maszyny

Najprostszy router wybiera maszynę z największą wolną ilością VRAM-u, która ma już załadowany żądany model. Załadowanie modelu na zimno potrafi zająć 10–60 s na dużym modelu; użycie hosta, gdzie już rezyduje, to dominująca optymalizacja.

router.py
import requests

def pick_machine_for(model: str, min_free_vram_gb: float = 0):
    """Znajdź maszynę, która ma model załadowany i wystarczający zapas VRAM."""
    infra = requests.get("http://127.0.0.1:3038/api/infrastructure").json()
    candidates = []
    for m in infra["machines"]:
        if m["status"] != "online":
            continue
        if model not in m.get("loaded_models", []):
            continue
        gpu = m.get("telemetry", {}).get("gpu", [])
        free = max((g["vram_free_gb"] for g in gpu), default=0)
        if free >= min_free_vram_gb:
            candidates.append((free, m["name"], m["network"]["hostname"]))
    if not candidates:
        raise RuntimeError(f"no machine has {model} with {min_free_vram_gb} GB free")
    candidates.sort(reverse=True)        # najwięcej wolnego pierwszy
    _, name, host = candidates[0]
    return name, host

# Użycie: kierujesz ciężki job tam, gdzie GPU ma zapas.
name, host = pick_machine_for("llama3.1:70b", min_free_vram_gb=4)
print(f"sending request to {name} at {host}")

Wynik wrzuć w żądanie agenta — Cortex, własny klient albo dispatcher, który przekazuje robotę przez czat (@workstation-agent please run this).

Dalej