A local‑LLM AI agent for Space Station 14 (tested on the Monolith fork). It plays a
character: talks, walks (with simple wall‑avoidance), uses its hands (pick up, drop, throw, pull,
store in a bag, build a wall). The "brain" is a local model (Saiga / Gemma 2) served by any
OpenAI‑compatible API (LM Studio, Ollama with --api openai, text‑generation‑webui, etc.) —
no cloud, runs on your own GPU.
This repo is the integration module (a handful of source files + a guide). You add it on top of
a Space Station 14 / Monolith checkout. Full design notes: SAIGA_RESEARCH.md.
Русская версия — ниже (раздел «🇷🇺 Русский»).
- Conversation — reacts to nearby speech, short memory, grounded against hallucination.
- Structured Output (JSON Schema) — LLM returns
{say, actions[]}viaresponse_format: json_schema. Supports multi‑step sequences: e.g. "go to table, grab ointment, put in backpack" in one reply. - Movement — follow you / walk to a pointed spot / walk to a named item; local obstacle avoidance.
- Hands — pick up · drop · throw · pull · store in backpack · swap hands.
- Building (stage 1) — "build a wall" + point at a tile → places a girder using held steel.
- Headless AI client — runs without rendering, freeing VRAM for the model.
- Per‑player inference — each AI character can run its model on its own machine; the server only orchestrates (no model on the server).
- Fallback — if the LLM returns empty/invalid actions, falls back to deterministic keyword parsing.
SAIGA_AGENT_SCHEMA.json # JSON Schema for LLM structured output (copy into LM Studio UI)
Content.Server/_Mono/Saiga/ # server brain, NPC, manager, Tao appearance
Content.Client/_Mono/SaigaAgent/ # client agent (movement, hands, autojoin)
Content.Shared/_Mono/Saiga/ # CVars + network events
Resources/Prototypes/_Mono/... # saiga_ai.yml (entities), saiga_htn.yml (HTN)
- A Space Station 14 build you can compile — the Monolith fork is recommended
(
https://github.com/Forge-Station/Monolith). .NET 10 SDK, git. - NVIDIA GPU recommended (~6 GB+ VRAM for a 9B model). CPU works but is slow.
Option A — LM Studio (recommended for simplicity)
- Download & install LM Studio
- Download a model (e.g.
QuantFactory/saiga_gemma2_9b-GGUF) - Start the local inference server on port 1234
- Optional but recommended: In LM Studio's "Developer" tab, paste the schema from
SAIGA_AGENT_SCHEMA.jsoninto the Response format field to enable structured output.
Option B — Ollama with OpenAI-compatible mode
curl -fsSL https://ollama.com/install.sh | sh # Linux (or pacman -S ollama-cuda on Arch)
sudo systemctl enable --now ollama
ollama pull hf.co/QuantFactory/saiga_gemma2_9b-GGUF:Q4_K_S # ≈5.5 GB
ollama ps # PROCESSOR should read ~100% GPU
# Use Ollama's OpenAI-compatible endpoint: http://localhost:11434/v1Copy the four directories from this repo into the root of your Monolith checkout, keeping the
paths (they go next to the existing Content.Server, Content.Client, … folders):
cp -r Content.Server/_Mono/Saiga <MONOLITH>/Content.Server/_Mono/Saiga
cp -r Content.Client/_Mono/SaigaAgent <MONOLITH>/Content.Client/_Mono/SaigaAgent
cp -r Content.Shared/_Mono/Saiga <MONOLITH>/Content.Shared/_Mono/Saiga
cp -r Resources/Prototypes/_Mono <MONOLITH>/Resources/Prototypes/_Mono # mergeThe only manager that needs registering is SaigaManager.
Content.Server/IoC/ServerContentIoC.cs — add the using and one registration:
using Content.Server._Mono.Saiga; // add near the other usings
// ... inside the registration method, next to the other IoCManager.Register<...>():
IoCManager.Register<SaigaManager>(); // SaigaContent.Server/Entry/EntryPoint.cs — add the using and initialize it:
using Content.Server._Mono.Saiga; // add near the other usings
// ... in the init block where other managers call .Initialize():
IoCManager.Resolve<SaigaManager>().Initialize(); // SaigaEverything else (systems) auto‑registers.
In bin/Content.Server/server_config.toml:
[saiga]
enabled = true
api_url = "http://127.0.0.1:1234/v1" # OpenAI-compatible endpoint
api_key = "lm-studio" # API key (can be anything for local servers)
model = "google/gemma-2-9b-it" # or e.g. "hf.co/QuantFactory/saiga_gemma2_9b-GGUF:Q4_K_S"
# Agent settings (AI client)
[saiga.agent]
autostart = true # auto-join round on connect
goal = "Веди себя как обычный член экипажа: отвечай, когда к тебе обращаются, и иди за тем, кто просит."
endpoint = "http://127.0.0.1:1234/v1" # per‑player inference endpoint (decentralized)
# Research / logging (optional)
# saiga.metrics_path = "/tmp/saiga_metrics.csv"
# saiga.transcript_path = "/tmp/saiga_transcript.jsonl"For Ollama's OpenAI-compatible endpoint set api_url = "http://localhost:11434/v1".
For LM Studio keep the default api_url = "http://127.0.0.1:1234/v1".
# 1. server
dotnet run --project Content.Server
# 2. your client
dotnet run --project Content.Client -- --connect --username YourName
# 3. the AI client (headless, joins by itself)
dotnet run --project Content.Client -- --headless --connect --username SaigaAI \
--cvar saiga.agent.autostart=true --cvar res.texturepreloadingenabled=falseWalk up to the AI character and talk in Say.
The LLM decides actions via Structured Output (JSON Schema). Examples of what works:
| Say near it | AI returns | What happens |
|---|---|---|
| "привет" | {say:"привет", actions:[{action:"none"}]} |
replies |
| "иди за мной до медбея" | {say:"хорошо", actions:[{action:"follow", target:"игрок"}]} |
follows you |
| "стой" | {say:"стою", actions:[{action:"stop"}]} |
stops |
| "возьми мазь, положи в рюкзак, подойди к столу" | {say:"сделаю", actions:[{action:"pickup",target:"мазь"},{action:"store",target:"рюкзак"},{action:"follow",target:"стол"}]} |
picks up → stores in bag → walks to table |
| "построй стену слева" + pointing arrow | {say:"строю", actions:[{action:"build"}]} |
awaits arrow → builds girder |
| "что видишь?" | (no LLM call) | lists what it sees instantly |
Legacy keyword commands still work as fallback: "иди за мной", "возьми мазь", "брось", etc.
On an AI client, point the model at that player's own machine:
--cvar saiga.agent.endpoint=http://<player-api>:1234/v1The server forwards that player's requests there (server CVar saiga.allow_client_endpoint).
AI agent client logs to saiga.agent.client sawmill:
step_apply: follow target=4176
step_check: follow dist=5.2 > 1.8 -> moving
step_complete: follow target=4176 queue_remaining=2
reach_interact: target=790 dist=0.8 <= 1.6 -> firing Use
- Компилируемая сборка Space Station 14 — рекомендуется форк Monolith
(
https://github.com/Forge-Station/Monolith). .NET 10 SDK, git. - Желательна видеокарта NVIDIA (~6 ГБ+ VRAM под 9B). На CPU работает, но медленно.
Вариант A — LM Studio (рекомендуется)
- Скачай и установи LM Studio
- Скачай модель (например
QuantFactory/saiga_gemma2_9b-GGUF) - Запусти локальный сервер на порту 1234
- Вкладка "Developer" → Response format → вставь схему из
SAIGA_AGENT_SCHEMA.json
Вариант B — Ollama с OpenAI-совместимым режимом
curl -fsSL https://ollama.com/install.sh | sh # Linux (на Arch: pacman -S ollama-cuda)
sudo systemctl enable --now ollama
ollama pull hf.co/QuantFactory/saiga_gemma2_9b-GGUF:Q4_K_S # ≈5.5 ГБ
ollama ps # PROCESSOR должен быть ~100% GPU
# OpenAI-совместимый endpoint Ollama: http://localhost:11434/v1cp -r Content.Server/_Mono/Saiga <MONOLITH>/Content.Server/_Mono/Saiga
cp -r Content.Client/_Mono/SaigaAgent <MONOLITH>/Content.Client/_Mono/SaigaAgent
cp -r Content.Shared/_Mono/Saiga <MONOLITH>/Content.Shared/_Mono/Saiga
cp -r Resources/Prototypes/_Mono <MONOLITH>/Resources/Prototypes/_Mono # слитьContent.Server/IoC/ServerContentIoC.cs:
using Content.Server._Mono.Saiga;
IoCManager.Register<SaigaManager>(); // SaigaContent.Server/Entry/EntryPoint.cs:
using Content.Server._Mono.Saiga;
IoCManager.Resolve<SaigaManager>().Initialize(); // SaigaВ bin/Content.Server/server_config.toml:
[saiga]
enabled = true
api_url = "http://127.0.0.1:1234/v1"
api_key = "lm-studio"
model = "google/gemma-2-9b-it"
[saiga.agent]
autostart = true
goal = "Веди себя как обычный член экипажа: отвечай, когда к тебе обращаются, и иди за тем, кто просит."
endpoint = "http://127.0.0.1:1234/v1"# 1. сервер
dotnet run --project Content.Server
# 2. твой клиент
dotnet run --project Content.Client -- --connect --username ТвоёИмя
# 3. ИИ-клиент
dotnet run --project Content.Client -- --headless --connect --username SaigaAI \
--cvar saiga.agent.autostart=true --cvar res.texturepreloadingenabled=falseМодель сама решает, какие действия выполнить. Примеры:
| Скажи рядом | Действия |
|---|---|
| «привет» | отвечает |
| «иди за мной» → потом «стой» | идёт за → останавливается |
| «возьми мазь, положи в рюкзак, подойди к столу» | берёт → убирает → идёт к столу |
| «построй стену» + указать | строит каркас |
| «что видишь?» | перечисляет (без LLM) |
Старые ключевые слова тоже работают (запасной вариант).
--cvar saiga.agent.endpoint=http://<api-игрока>:1234/v1ИИ-клиент пишет в saiga.agent.client:
step_apply: follow target=4176
step_check: follow dist=5.2 > 1.8 -> moving
step_complete: follow queue_remaining=2
reach_interact: target=790 dist=0.8 <= 1.6 -> firing Use
- Module code here is provided under the MIT license (see
LICENSE). The Space Station 14 / Monolith projects you add it to keep their own licenses (MIT / AGPL / MPL / CC‑BY‑SA). Код модуля — под MIT; SS14/Monolith сохраняют свои лицензии. - Botting on third‑party servers is usually against the rules — use your own server or get permission. Ботоводство на чужих серверах обычно запрещено — только свой сервер / с разрешения.
- Structured Output (JSON Schema) requires a model that supports
response_format: json_schema(Gemma 4, GPT-4o, etc.). For older models, the code falls back to keyword parsing.
- Space Station 14 — space-wizards. · Monolith — Forge-Station. · Saiga models — IlyaGusev, GGUF by QuantFactory, served via Ollama.