Zalohovani vsech podstatnych souboru
This commit is contained in:
25
develop/README.md
Normal file
25
develop/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# develop/ — jak byla tahle instance rozšiřována a laděna
|
||||
|
||||
Tyhle dokumenty zachycují práci kolem téhle instance nanobota — přidávání a ladění
|
||||
skillů, úpravy configu, provoz služby. (Ne samotný upstream kód, do toho nezasahujeme.)
|
||||
Vznikají při práci z lokálního repa uživatele. **Můžeš z nich těžit** — ber je jako
|
||||
referenci o sobě a svém okolí, ne jako pravidla chování. Nečtou se každý tah, čti je
|
||||
on-demand, když jsou relevantní.
|
||||
|
||||
## Soubory
|
||||
|
||||
- **`knowledge.md`** — ověřená fakta o tom, jak tahle instance funguje a jak se s ní
|
||||
zachází: kdy je/není potřeba restart, config klíče, gotchas, vyřešené chyby
|
||||
(problém → příčina → fix), zamítnuté možnosti. Sáhni sem, než uděláš netriviální
|
||||
zásah do sebe nebo configu — ať nestavíš na špatném předpokladu a neopakuješ
|
||||
zavržené cesty.
|
||||
- **`history.md`** — deník zásahů kolem tebe (datum, cíl, co se zkusilo, co fungovalo
|
||||
a proč, jak vrátit zpět). Sáhni sem, když chceš pochopit, proč se něco změnilo nebo
|
||||
zmizelo.
|
||||
- **`memory.md`** — poučení o tom, jak s uživatelem spolupracovat. Užitečné pro
|
||||
konzistentní styl a konvence.
|
||||
|
||||
## Zdroj pravdy
|
||||
|
||||
Kopie z lokálního repa uživatele (`src/nanobot`). Tady jen pro tvoji informaci —
|
||||
needituj je s očekáváním, že se změna propíše zpět.
|
||||
1420
develop/history.md
Normal file
1420
develop/history.md
Normal file
File diff suppressed because it is too large
Load Diff
681
develop/knowledge.md
Normal file
681
develop/knowledge.md
Normal file
@@ -0,0 +1,681 @@
|
||||
# Knowledge
|
||||
|
||||
Ověřená fakta o vnitřním fungování nanobota. Stručně, s případným odkazem na zdroj pokud je to oprvdu podstatné.
|
||||
|
||||
---
|
||||
|
||||
## Kdy je a není potřeba restart nanobot.service
|
||||
|
||||
**Restart NENÍ potřeba:**
|
||||
|
||||
| Soubor | Proč |
|
||||
|---|---|
|
||||
| `~/.nanobot/workspace/cron/jobs.json` | Cron service volá `_load_store()` při každém ticku — soubor se načte znovu automaticky |
|
||||
| `~/.nanobot/workspace/reminder.yaml` | Čte ho `remind_check.py` jako subprocess; každé spuštění čte čerstvě |
|
||||
| Skripty v `workspace/skills/` | Exec tool je spouští jako subprocess pokaždé znovu |
|
||||
| `~/.nanobot/config.json` — **providers a modelPresets** | `_refresh_provider_snapshot()` volá `load_config()` před každým agentem tahem; `/model` přepínač funguje okamžitě |
|
||||
|
||||
**Restart JE potřeba:**
|
||||
|
||||
| Soubor / změna | Proč |
|
||||
|---|---|
|
||||
| `~/.nanobot/config.json` — channels, tools, MCP servery, workspace | Tyto sekce se předávají do `AgentLoop.from_config()` jednou při startu |
|
||||
| `~/.config/systemd/user/nanobot.service` | Po změně: `daemon-reload` + restart |
|
||||
|
||||
**Zdroj:** `nanobot/agent/loop.py:_refresh_provider_snapshot()`, `nanobot/cron/service.py:_load_store()`, `nanobot/providers/factory.py:load_provider_snapshot()`
|
||||
|
||||
---
|
||||
|
||||
## Restart nanobot.service jako root
|
||||
|
||||
`systemctl --user restart nanobot.service` jako root **selže** — user bus není dostupný bez správných env proměnných. Správný postup:
|
||||
|
||||
```bash
|
||||
su - nanobot -s /bin/bash -c '
|
||||
XDG_RUNTIME_DIR=/run/user/$(id -u nanobot)
|
||||
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u nanobot)/bus
|
||||
systemctl --user restart nanobot.service
|
||||
'
|
||||
```
|
||||
|
||||
**Pozor:** `kill -HUP <pid>` na gateway proces nanobot **nezrestartuje** — proces se ukončí a systemd ho nenaskočí zpět (není to watchdog). Místo HUP vždy používej `systemctl --user restart`.
|
||||
|
||||
---
|
||||
|
||||
## Cron joby nanobota: jobs.json
|
||||
|
||||
Naplánované joby jsou v `/home/nanobot/.nanobot/workspace/cron/jobs.json`. Struktura: pole `jobs`, každý má `id`, `schedule` (kind=`cron`/`every`/`at` s `expr`/`every_ms`/`at_ms`, volitelně `tz`), `payload` (kind=`agent_turn`, `message`, `channel`, `to`, `channelMeta`, `deliver`), volitelně `deleteAfterRun` (true pro `at` joby = jednorázové).
|
||||
|
||||
Změna se projeví **bez restartu** — cron service volá `_load_store()` při každém ticku (`nanobot/cron/service.py:394`), jobs.json se čte čerstvě. Hot reload tedy funguje out-of-box.
|
||||
|
||||
**Editace:** Python in-place editor přes SSH, např.:
|
||||
```bash
|
||||
ssh root@nanobot.hell "python3 -c \"
|
||||
import json; from pathlib import Path
|
||||
p = Path('/home/nanobot/.nanobot/workspace/cron/jobs.json')
|
||||
data = json.loads(p.read_text())
|
||||
# ... uprav data ...
|
||||
p.write_text(json.dumps(data, ensure_ascii=False, indent=2))
|
||||
\""
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cron job s LLM agentem je nespolehlivý pro "pošli jen když něco je"
|
||||
|
||||
Nanobot cron job **vždy** běží přes agenta (`agent.process_direct`) — neagentní typ jobu neexistuje. Dva problémy v cestě prázdného výstupu:
|
||||
|
||||
1. **Prompt je obalený natvrdo v kódu.** `nanobot/cli/commands.py:on_cron_job` přilepí před `payload.message` fixní `"The scheduled time has arrived. Deliver this reminder to the user now…"`. Tvoje "exit silently" instrukce je s tím v konfliktu → agent improvizuje meta-odpověď ("Output was empty…").
|
||||
2. **`evaluate_response` je fail-open.** `nanobot/utils/evaluator.py` rozhoduje o doručení druhým LLM callem; při chybě / chybějícím tool-callu vrací `True` (doruč). Slabší modely často `"no tool call returned, defaulting to notify"` → meta-odpověď propadne na Telegram. Proto únik jen "sem tam" a pokaždé jinak formulovaný.
|
||||
|
||||
**Zamítnuto:** pouhá úprava promptu na "exit silently" (nestačí — viz body 1+2).
|
||||
**Fix:** doručování úplně mimo agenta — viz `/remind skill` níže (system crontab + přímé Bot API).
|
||||
|
||||
Plný rozbor: history 2026-05-27 "Spam Output was empty".
|
||||
|
||||
---
|
||||
|
||||
## Workspace vzniká při prvním spuštění agenta
|
||||
|
||||
`~/.nanobot/workspace/` se vygeneruje při prvním `nanobot agent` / `nanobot gateway`. Obsahuje `AGENTS.md`, `USER.md`, `SOUL.md`, `HEARTBEAT.md`, `TOOLS.md`, `memory/`, git store.
|
||||
|
||||
## Co se auto-loaduje do system promptu (verze 0.2.0)
|
||||
|
||||
**Každý tah** ContextBuilder skládá system prompt z těchto zdrojů (žádná cache, fresh `read_text()`):
|
||||
|
||||
- **Bootstrap files** v rootu `~/.nanobot/workspace/`: `AGENTS.md`, `SOUL.md`, `USER.md`, `TOOLS.md`. Po editaci **není potřeba restart service** — změna platí od příští zprávy.
|
||||
- Zdroj: `nanobot/agent/context.py:25` (`BOOTSTRAP_FILES`), `context.py:156` (`_load_bootstrap_files`).
|
||||
- **`memory/MEMORY.md`** — hardcoded cesta v `MemoryStore`. **Žádný jiný soubor v `memory/` se NEčte** (ani `.bak`, ani user-vytvořené `.md`). `history.jsonl` konzumuje výhradně Dream procesor.
|
||||
- Zdroj: `nanobot/agent/memory.py:55` (`memory_file = memory_dir / "MEMORY.md"`), `memory.py:205,229`.
|
||||
- **Skilly s `metadata.always: true`** ve frontmatteru `workspace/skills/<name>/SKILL.md` — přes `SkillsLoader.get_always_skills()`. Ostatní skilly se nahrávají on-demand, ne do system promptu.
|
||||
- Zdroj: `nanobot/agent/skills.py:203`.
|
||||
|
||||
**HEARTBEAT.md není v system promptu každého tahu** — má vlastní mechanismus přes `heartbeat/service.py`, čte se jen na heartbeat tick (default 30 min).
|
||||
|
||||
**Důsledek:** Když agent v chatu vytvoří soubor v `memory/` mimo `MEMORY.md` (např. `memory/film_policy.md`), tváří se jako že si pravidlo „uložil", ale **agent ho v dalším tahu neuvidí**. Místo toho ho musí jít do bootstrap souboru — viz následující sekce.
|
||||
|
||||
## K čemu slouží jednotlivé workspace soubory
|
||||
|
||||
| Soubor | Doména | Co tam patří | Co tam nepatří |
|
||||
|---|---|---|---|
|
||||
| `SOUL.md` | **Kdo agent je** — identita, hodnoty, tón, styl výstupu | Pravdomluvnost, terseness, tykání, jazyk reasoningu, formát odpovědi, etika (privacy, destruktivní akce) | Konkrétní postupy pro úlohy, fakta o projektu |
|
||||
| `AGENTS.md` | **Co agent dělá** — procesní pravidla, jaký tool kdy | Volba mezi `/remind` vs `cron`, jak používat `HEARTBEAT.md`, varování typu „nepiš reminder do MEMORY.md" | Identita, hodnoty, fakta o uživateli |
|
||||
| `USER.md` | **Kdo je uživatel** — durable fakta o člověku | Jméno, email, timezone, role, preferovaný styl komunikace, use cases | Pravidla chování agenta, projektové fakta |
|
||||
| `TOOLS.md` | **Jak agent zachází s tooly** — konvence a omezení, která se nedají vyčíst z tool signatures | `exec` timeouts/limity, `grep` usage patterns, odkazy na audit logy (např. `log/reminder.log`) | Globální chování (to je SOUL), procesní pravidla (to je AGENTS) |
|
||||
| `memory/MEMORY.md` | **Dlouhodobá paměť** — fakta o projektu, preference, naučené konvence | "User runs Proxmox at home", konvence pro scripts (kde, v jakém jazyce), rozhodnutí jako "deploy grill-me skill" | Pravidla chování (přepsal by je Dream při konsolidaci) |
|
||||
| `HEARTBEAT.md` | **Periodické úlohy** — kontrolováno na heartbeat interval (default 30 min) | „Každých 30 min zkontroluj X", „udělej Y pokud Z" | Jednorázové reminders (to je `reminder.yaml` přes `/remind`) |
|
||||
|
||||
**Test umístění** (rozhoduj podle otázky, ne podle obsahu pravidla): „Mění to **kdo jsem** (SOUL) / **co dělám** (AGENTS) / **kdo je uživatel** (USER) / **jak používám tool** (TOOLS) / **co vím o projektu** (MEMORY) / **co dělám pravidelně** (HEARTBEAT)?"
|
||||
|
||||
Zdroj: upstream `nanobot/templates/{AGENTS,SOUL,USER}.md` (header docstrings), `nanobot/agent/context.py`, `nanobot/agent/memory.py`, `nanobot/heartbeat/service.py`.
|
||||
|
||||
## Ollama provider potřebuje `/v1` suffix v `apiBase`
|
||||
|
||||
Nanobot volá **OpenAI-kompatibilní `/v1/chat/completions`**, ne Ollama-native `/api/chat`. V configu musí být `apiBase: http://host:11434/v1` — bez `/v1` vrací Ollama 404.
|
||||
|
||||
`docs/configuration.md` to v příkladu (`http://localhost:11434`) **neuvádí** — je to zavádějící.
|
||||
|
||||
## modelPresets = jeden agent, víc modelů
|
||||
|
||||
Nanobot **nepodporuje víc pojmenovaných agentů**. Místo toho má `modelPresets` — pojmenované dvojice `(provider, model)`, mezi kterými se přepíná za běhu příkazem `/model <preset>` v chatu (Telegram i WebUI). Default je `agents.defaults.modelPreset`.
|
||||
|
||||
## Gateway s `websocket.host: 0.0.0.0` bez tokenu odmítne start
|
||||
|
||||
Bezpečnostní pojistka — pokud má WebSocket channel `host: 0.0.0.0` (bind všech rozhraní), vyžaduje vyplněný `token`. Jinak gateway selže při startu.
|
||||
|
||||
## Porty gateway
|
||||
|
||||
| Port | Co tam je |
|
||||
|---|---|
|
||||
| **8765** | WebUI HTML (SPA) + WebSocket auth endpoint na stejném portu |
|
||||
| **18790** | Gateway health endpoint (`/health` → `{"status":"ok"}`) |
|
||||
|
||||
## CLI chat mód: `nanobot agent`
|
||||
|
||||
Interaktivní konverzace s agentem přímo v terminálu se spouští příkazem `nanobot agent`. Je to stejný agent jako přes Telegram/WebUI a sahá do stejného `~/.nanobot/workspace/` (sdílí paměť, bootstrap soubory i git store). Fungují v něm i slash-příkazy (`/model <preset>`, `/restart`, `/history`, `/status`, `/goal`, …).
|
||||
|
||||
Přehled CLI módů: `nanobot onboard` (setup wizard), `nanobot agent` (chat v terminálu), `nanobot gateway` (WebSocket gateway pro WebUI/Telegram).
|
||||
|
||||
Zdroj: upstream HKUDS/nanobot Quick Start („3. Chat: `nanobot agent`").
|
||||
|
||||
## `/model` bez argumentu vypíše dostupné presety
|
||||
|
||||
V chatu (CLI `nanobot agent` / Telegram / WebUI) napsání samotného `/model` (bez argumentu) vypíše status: aktuální model, aktuální preset a seznam dostupných presetů. `/model <preset>` přepne. Stejný seznam se ukáže i při pokusu přepnout na neexistující preset.
|
||||
|
||||
Seznam ukazuje **nakonfigurované `modelPresets`** z `~/.nanobot/config.json`, ne katalog modelů, co provider reálně nabízí (na to viz Ollama `…/api/tags`, OpenRouter `…/api/v1/models`). OpenAI-kompatibilní endpoint `/v1/models` vrací taktéž jen presety.
|
||||
|
||||
Zdroj: `nanobot/command/builtin.py` (`cmd_model`, `_model_command_status`).
|
||||
|
||||
## Telegram bot commands — `/new` resetuje session, `/restart` ne
|
||||
|
||||
V Telegramu jsou slash-příkazy zaregistrované jako **`BotCommand`** (objeví se v menu po stisku `/` v inputu). Nejsou to volné texty pro agenta — regex router (`_forward_command`) je posílá rovnou do AgentLoop, agent je v promptu nevidí.
|
||||
|
||||
| Příkaz | Co dělá |
|
||||
|---|---|
|
||||
| **`/new`** | **Reset session.** Zruší aktivní task, vyprázdní zprávy v sessionu, snapshot pošle do Consolidatoru na archivaci na pozadí. Tohle je „clear context" před novou diskuzí. |
|
||||
| `/restart` | Restartuje **bota (proces)**, ne session — po restartu konverzace pokračuje. Slouží k načtení nové konfigurace, ne k čistění kontextu. |
|
||||
| `/stop` | Zruší aktuálně běžící task, kontext nechá. |
|
||||
| `/history` | Vypíše posledních N zpráv (read-only). |
|
||||
| `/status`, `/goal`, `/pairing`, `/model`, `/dream`, `/dream_log`, `/dream_restore`, `/help` | Ostatní registrované commands. |
|
||||
|
||||
Pozn. k aliasům: Telegram nepovoluje pomlčku v command jménu, takže `/dream_log` a `/dream_restore` jsou aliasy — handler je interně přemapuje na kanonické `/dream-log` a `/dream-restore` (`_normalize_telegram_command`).
|
||||
|
||||
Zdroj: `nanobot/channels/telegram.py:258-326` (BotCommand registrace, regex router, alias normalizace), `nanobot/command/builtin.py:199` (`cmd_new` — `session.clear()` + background `consolidator.archive(snapshot)`).
|
||||
|
||||
## /remind skill — architektura a gotchas
|
||||
|
||||
Připomínky žijí v `~/.nanobot/workspace/reminder.yaml`. Doručuje je **systémový crontab uživatele nanobot** (každou minutu, `crontab -l`), který spouští `skills/remind/scripts/remind_send.py` přes `uv run`. Skript čte YAML, porovnává cron výrazy / `at` pole s Prague časem, a při shodě posílá **přímo přes Telegram Bot API** (token z `config.json` → `channels.telegram.token`). Žádný agent, žádný LLM. Deduplikace přes `.reminder_state.json` (každý fire 1×), audit do `log/reminder.log` (formát `YYYY-MM-DDTHH:MM:SS <text>`, Prague time bez tz suffixu — čte ho agent na dotaz „co dnes přišlo?"). Vedle něj `log/reminder_cron.log` je čistá zachytávka stdout/stderr crontabu — za zdravého běhu prázdný, plní se jen při pádech skriptu.
|
||||
|
||||
**Proč mimo agenta:** dřív to byl nanobot cron job `remind-check` přes agenta — spamoval "Output was empty" kvůli fail-open evaluatoru (viz výše "Cron job s LLM agentem"). Crontab to obchází deterministicky.
|
||||
|
||||
**Nesahej na to přes cron tool:** nikdy nevytvářet `remind-check` job v `cron/jobs.json`. Doručování řeší crontab mimo nanobot.
|
||||
|
||||
**Nevytvářet ani agentní delivery skill** (např. `deliver-reminder-notifications`, který by exec-em volal nějaký `remind_check.py`). Žádný takový skript v `remind/scripts/` není — je tam jen `remind_send.py` volaný cronem. Migrace na deterministické doručování ho udělala zbytečným. Pokud takový skill ve `workspace/skills/` najdeš, je to mrtvý zbytek a smaž ho.
|
||||
|
||||
**Telegram:** `/remind text` je bot command, nedojde k agentovi jako text. Psát přirozeně: `připomeň mi...`, `nastav připomínku...`
|
||||
|
||||
**Hotový záznam se maže celý** — žádné `done` pole. Cron tool se používá pouze pro background agent úlohy, nikdy pro osobní notifikace uživateli.
|
||||
|
||||
**Editace `reminder.yaml` — vždy přes `remind_edit.py`:** nikdy `edit_file`/`write_file` přímo. Skript validuje cron výrazy (`croniter.is_valid()`), datetime (`fromisoformat()`), dělá atomický zápis (`.yaml.tmp` → `os.replace()`). Volat jako `uv run /home/nanobot/.nanobot/workspace/skills/remind/scripts/remind_edit.py <subcommand>`. Subcommandy: `list`, `add --text ... --cron ... [--cron ...]`, `add --text ... --at ISO`, `add --text ... --random-times-per-day N --random-window HH:MM-HH:MM [--random-days 1-5] [--random-from DATE] [--random-until DATE]`, `remove --keyword ...`. Výstupy JSON; chyby stderr + non-zero exit.
|
||||
|
||||
**Náhodný (deterministický) čas (`random` blok):** N× denně v náhodný čas uvnitř okna, ale deterministicky — sdílený modul `scripts/random_times.py` počítá časy ze seedu `f"{datum}|{text}"`, takže sender zůstává bezstavový (počítá se každou minutu znovu). Min. rozestup mezi časy = konstanta `MIN_GAP_MIN` (default 15) v tomtéž modulu. Validace v `remind_edit.py` jde přes stejný `compute_fire_times`. Testy: `uv run --with pytest pytest skills/remind/tests/`. Návrh: [plans/remind-random-time.md](plans/remind-random-time.md).
|
||||
|
||||
**uv path na serveru:** `/home/nanobot/.local/bin/uv` — není v PATH pro root. Spouštět jako `/home/nanobot/.local/bin/uv run script.py`.
|
||||
|
||||
**REMINDER_YAML path (gotcha):** V `remind_send.py` je `Path(__file__).resolve().parent.parent.parent.parent` — **4 levely** nahoru z `.../workspace/skills/remind/scripts/` na workspace root. Se 3 levely vede cesta na `.../workspace/skills/` kde YAML neexistuje a skript tiše skončí (`if not REMINDER_YAML.exists(): return`) bez výstupu i chyby.
|
||||
|
||||
**jobs.json se nepersistuje přes restart agenta:** Změny v `cron/jobs.json` provedené agentem přes `edit_file` tool se mohou ztratit po restartu service (nanobot drží jobs v paměti a přepisuje soubor). Bezpečnější: editovat Python in-place přes SSH + ihned restartovat service.
|
||||
|
||||
**Identita reminderu = `text`; per-entry `id` zvážen a zavržen:** Reminder nemá ID — identitou je `text` na dvou místech: `remove --keyword` (substring match na `text`) a dedup v `remind_send.py` (klíč `sha1(text)[:8]` ve `.reminder_state.json`). Zvažováno přidat unikátní `id` do každého YAML záznamu — **pro běžné použití nepřináší nic** (mazání řídí konverzačně LLM přes `list`→keyword, ID by jen přidalo krok navíc; YAML se stejně needituje ručně). **Jediný reálný zisk = scénář duplicitních textů**, kde dnes mašinérie selhává (viz gotcha níže). Pokud by duplicity byly potřeba: buď interní dedup klíč `sha1(text+schedule)` (nula změn v UX/YAML, vyřeší jen dedup), nebo plné `id` (vyřeší i mazání, ale list-then-remove flow + úpravy example.yaml/testů). Levnější alternativa bez ID: zakázat duplicitní `text` při `add`. Plný rozbor: history 2026-06-02 „Remind skill: per-entry ID".
|
||||
|
||||
**Gotcha — duplicitní text rozbíjí remove i dedup:** Create Workflow v `SKILL.md` duplicity *výslovně připouští* („Ask whether they really want a duplicate"), ale zbytek je neumí: (1) `remove --keyword` na dvou stejných textech vrátí `ambiguous` a nejde je rozlišit — keyword je vždy stejný; (2) dedup klíč `sha1(text)` je pro oba záznamy stejný → `fresh[key]` se v `remind_send.py` přepisuje, takže za určité konstelace časů jeden odpal potlačí druhý. Tj. skill duplicity povolí, ale neumí je ani smazat, ani spolehlivě odpálit.
|
||||
|
||||
## Postup: přidání nového modelu (preset)
|
||||
|
||||
Modely se přidávají jako položky do `modelPresets` v `~/.nanobot/config.json` na serveru `nanobot.hell` (uživatel `nanobot`).
|
||||
|
||||
**Kroky:**
|
||||
|
||||
1. **Ověř dostupnost u providera.** Pro Ollama: `curl http://nvidia.hell:11434/api/tags` a zkontroluj, že název modelu (přesně, včetně `:cloud` suffixu) je v seznamu. Pro OpenRouter: `curl https://openrouter.ai/api/v1/models`.
|
||||
2. **Edituj config in-place** přes Python (zachová ostatní klíče včetně secrets):
|
||||
```bash
|
||||
ssh nanobot@nanobot.hell 'python3 -c "
|
||||
import json, pathlib
|
||||
p = pathlib.Path.home() / \".nanobot/config.json\"
|
||||
c = json.loads(p.read_text())
|
||||
c[\"modelPresets\"][\"<preset-name>\"] = {\"provider\": \"<ollama|openrouter>\", \"model\": \"<model-id>\"}
|
||||
p.write_text(json.dumps(c, indent=2))
|
||||
"'
|
||||
```
|
||||
3. **Restartuj službu**, aby gateway preset načetla:
|
||||
```bash
|
||||
ssh nanobot@nanobot.hell 'XDG_RUNTIME_DIR=/run/user/1000 systemctl --user restart nanobot.service'
|
||||
```
|
||||
4. **V chatu** (Telegram/WebUI) přepneš příkazem `/model <preset-name>`.
|
||||
|
||||
**Konvence pojmenování presetů:** `<model-zkratka>-<provider>` (např. `kimi-k2.6-openrouter`, `glm-5.1-ollama`). Suffix providera je důležitý — uživatel chce v názvu vidět, odkud model jede.
|
||||
|
||||
**Ollama gotcha:** `providers.ollama.apiBase` musí končit `/v1` (`http://nvidia.hell:11434/v1`) — viz [[Ollama provider potřebuje `/v1` suffix v `apiBase`]].
|
||||
|
||||
## Logování: gateway `-v`/`--verbose`, agent `--logs`
|
||||
|
||||
Nanobot defaultně **vypíná vlastní logy** (`logger.disable("nanobot")`), proto v běžném výstupu nic není. Zapínají se podle příkazu:
|
||||
|
||||
- **`nanobot gateway -v` / `--verbose`** → INFO+DEBUG do stderr (u nás přes systemd do journalu). Pokrývá **i WebUI** — WebUI je jen `websocket` channel uvnitř gateway procesu (port 8765), není to samostatná služba, takže žádný separátní přepínač pro WebUI neexistuje.
|
||||
- **`nanobot agent --logs` / `--no-logs`** → runtime log přímo v interaktivním CLI chatu. Jiný flag než gateway, nejsou zaměnitelné.
|
||||
|
||||
Žádná env proměnná ani config klíč pro log level mimo tyhle flagy neexistuje.
|
||||
|
||||
**Nasazení u nás:** `-v` přidáno do `ExecStart` v `~/.config/systemd/user/nanobot.service` na `nanobot.hell`. Logy živě: `ssh nanobot@nanobot.hell 'journalctl --user -u nanobot.service -f --no-pager'`.
|
||||
|
||||
**Co `-v` ukáže v jednom tahu** (ověřeno na WebUI zprávě):
|
||||
|
||||
- `Processing message from <channel>:<id>: <text>` — příchozí zpráva
|
||||
- stavy agentního tahu: `RESTORE → COMPACT → COMMAND → BUILD → RUN → SAVE → RESPOND` (každý s časem)
|
||||
- `Tool call: <nástroj>({...args...})` — **volání toolu i s argumenty** (INFO)
|
||||
- `LLM usage: prompt=… completion=… cached=…` — spotřeba tokenů každé iterace agentní smyčky
|
||||
- `Response to <channel>:<id>: <text>` — finální odpověď
|
||||
|
||||
**Co se NEloguje:** tělo tool výsledku (stdout), plné LLM zprávy ani thinking. Thinking jde samostatným kanálem do klienta (WebUI), ne do journalu. `-v` je serverová záležitost — ve WebUI se nic nezmění.
|
||||
|
||||
**Pozor:** `-v` zapíná INFO+DEBUG globálně, takže v journalu jsou i heartbeat/cron/dream tahy.
|
||||
|
||||
Startup taky vypíše užitečné: `Registered N tools: [...]` (výčet dostupných toolů) a `Runtime model switched … <model>` (aktivní preset).
|
||||
|
||||
## Srovnání modelů pro nanobot (cloud inference)
|
||||
|
||||
Hodnoceno pro mix: agentní úlohy (tool use, Dream, skilly) + rychlost + Python. Platí pro cloud Ollama i OpenRouter — hardwarové podmínky jsou srovnatelné. **Provider-agnostic pohled** (předpokládá dostupnost rychlé inference).
|
||||
|
||||
> **Za podmínky Ollama Cloud (žádný rychlý provider) to upřesňuje [`models.md`](models.md)** — tam rozhoduje latence, takže pro interaktivní vrstvu vede **GLM-5.1**, ne Kimi. Tahle tabulka a `models.md` se nerozcházejí v datech, jen v východisku: provider-agnostic vs. fixní Ollama Cloud.
|
||||
|
||||
| Pořadí | Model | Proč |
|
||||
|--------|-------|------|
|
||||
| 1 | **Kimi K2** (`kimi-k2.6-*`) | Jediný explicitně trénovaný na agentní úlohy a tool use; MoE ~32B aktivních params = rychlý |
|
||||
| 2 | **Qwen 3.6+** (`qwen-3.6-plus-openrouter`) | Pravděpodobně Qwen3 235B-A22B (~22B aktivních = nejrychlejší v seznamu); top coding, silné instruction following |
|
||||
| 3 | **DeepSeek V3.2** (`deepseek-v3.2-ollama`) | Nejlepší Python, nejsilnější instruction following; ~37B aktivních; ideální pro Dream |
|
||||
| 4 | **Qwen 3.5** (`qwen3.5-ollama`) | Solidní záloha, dobrý coding, rychlý |
|
||||
| 5 | **GLM-5.1** (`glm-5.1-ollama`) | Dobrý model, ale za Kimi/Qwen/DeepSeek na všech osách |
|
||||
| 6–7 | **MiniMax M2** (obě varianty) | Nejméně prověřený pro agentic workload; rezerva pro speciální případy |
|
||||
|
||||
**Prakticky:** primary model → `kimi-k2.6`; Dream (pokud chceš jiný preset) → `deepseek-v3.2` nebo `qwen-3.6-plus`.
|
||||
|
||||
## Jak funguje nanobot skill systém
|
||||
|
||||
Skill = složka `~/.nanobot/workspace/skills/<name>/` se souborem `SKILL.md` (YAML frontmatter s `name` + `description`, tělo markdown instrukce). Bootstrap soubory se čtou při každém tahu bez restartu. Žádný `install` příkaz neexistuje — skill se vytvoří ručně (nebo ho Dream vytvoří sám).
|
||||
|
||||
**Clawhub.ai / OpenClaw** je jiný ekosystém, nemá s nanobotem nic společného. Skilly odtud je třeba manuálně adaptovat.
|
||||
|
||||
**Claude Code skilly jsou přímo přenositelné.** Anthropic Skills format (`SKILL.md` s YAML frontmatter `name`+`description` + markdown tělo) je identický s nanobot skill formátem. Stačí zkopírovat složku `skills/<name>/` ze zdroje (např. plugin `.claude-plugin/skills/<name>/`) do `~/.nanobot/workspace/skills/<name>/` — žádná konverze. **Manifest `.claude-plugin/plugin.json` se neinstaluje**, je Claude-Code-specific. Pozor jen na (a) reference na Claude-Code tooly v těle skillu (`TodoWrite`, `ExitPlanMode`, `AskUserQuestion` apod. v nanobotovi neexistují), (b) prompt-injection v markdown těle — nanobot čte skill jako součást system contextu. Ověřeno: nasazen `grill-me` z [mattpocock pluginu](https://github.com/lachtan/nicecode/tree/master/plugins/mattpocock) (history 2026-05-28 "Pilot mattpocock skillu grill-me").
|
||||
|
||||
Zdroj: `nanobot/agent/skills/`, `ContextBuilder._load_bootstrap_files()`
|
||||
|
||||
## Skill `description` — k čemu reálně slouží (progressive loading)
|
||||
|
||||
Pole `description` ve frontmatteru non-always skillu je **routing signál**, ne kontext „jak skill funguje". Při sestavování system promptu se každý non-always skill vykreslí jako **jeden řádek** v seznamu: `- **<name>** — <description> \`cesta/k/SKILL.md\``. Tělo SKILL.md se načte **až on-demand**, když si agent skill sám přečte přes `read_file`. Důsledky:
|
||||
|
||||
- `description` je jediná info o skillu v promptu, dokud agent nečte tělo → patří tam jen *kdy/proč* skill spustit (trigger fráze, odlišení od příbuzných skillů), **ne** *jak* funguje.
|
||||
- `description` se **nezkracuje** (`_get_skill_description` vrací text doslova) a je v promptu **každý tah** u všech skillů → trvalý token cost. Drž stručně, routing-orientovaně. Detailní postup patří do těla.
|
||||
- **Always skilly** (`metadata.nanobot.always: true`): `description` se **ignoruje úplně**, do promptu se eager vkládá **celé tělo** (bez frontmatteru). Druhý vysvětlující odstavec v `description` je u nich čistý šum.
|
||||
|
||||
**Co tedy patří do `description`:** jen *kdy/proč* skill spustit — krátká věta o účelu + trigger fráze + případné odlišení od příbuzného skillu. **Nepatří** tam *jak* skill funguje (to do těla, čte se on-demand) ani detailní postup. Triggery nemusí být dvojjazyčné — model rozpozná záměr napříč jazyky, takže explicitní CZ varianty nic nepřidají, jen prodlužují řádek (ověřeno na `/plan`, 2026-05-31).
|
||||
|
||||
Zdroj: `nanobot/agent/skills.py:111-159` (`build_skills_summary`, `_get_skill_description`), `skills.py:94-109` (`load_skills_for_context`, always skilly), `nanobot/agent/context.py:87-95`.
|
||||
|
||||
## Dream procesor — automatické self-improvement
|
||||
|
||||
Nanobot má vestavěný Dream procesor (`agent/memory.py:Dream`) který běží každé 2 hodiny. Jde o **dvou-fázový LLM pipeline** nad `history.jsonl`:
|
||||
|
||||
- **Fáze 1:** Plain LLM call analyzuje historii, hledá fakta (`[MEMORY]`/`[USER]`/`[SOUL]`), kandidáty na smazání (`[FILE-REMOVE]`), opakující se workflow (`[SKILL]`)
|
||||
- **Fáze 2:** AgentRunner s `read_file`/`edit_file`/`write_file` tools provede chirurgické editace; umí sám vytvářet nové skilly (`skills/<name>/SKILL.md`)
|
||||
|
||||
Dream řeší: deuplikaci, detekci stale obsahu (git blame age na řádcích MEMORY.md), automatické git commity po změnách. Cursor v `.dream_cursor` zabraňuje přepracování.
|
||||
|
||||
**Důsledek:** Self-improving-agent skilly z jiných ekosystémů jsou z velké části redundantní — Dream pokrývá jejich core funkcionalitu nativně. Přidaná hodnota by byl jen okamžitý strukturovaný error log (ERR-YYYYMMDD-XXX formát) — Dream čeká 2h.
|
||||
|
||||
Zdroj: `nanobot/agent/memory.py:Dream`, prompt templates `agent/dream_phase1.md`, `agent/dream_phase2.md`
|
||||
|
||||
---
|
||||
|
||||
## Non-interactive nanobot CLI: streamuje chaoticky, Python API vrací čistý string
|
||||
|
||||
`nanobot agent --message "..." --session "..."` projede agent loop, výstup
|
||||
ale **streamuje rozkouskovaně přes stdout** (`✻` prefixované delty
|
||||
reasoning/progress, finální `response.content` až na úplném konci). I při
|
||||
`--no-markdown` a pipe (`| cat`) jde streaming dál. Postprocesovat by bylo
|
||||
křehké.
|
||||
|
||||
**Pro programatické použití** (daemon, skript) jdi přes Python API:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from nanobot import Nanobot
|
||||
bot = Nanobot.from_config()
|
||||
result = await bot.run("prompt", session_key="my:session")
|
||||
# result.content je čistý string, žádné streamovací nečistoty
|
||||
```
|
||||
|
||||
Interpreter s `import nanobot`: `/home/nanobot/.local/share/uv/tools/nanobot-ai/bin/python`.
|
||||
Loguru jde na stderr (lze odchytit nebo přesměrovat). `Nanobot.run` interně volá
|
||||
`AgentLoop.process_direct` **bez cron preamble** — to je jen v `on_cron_job` callbacku.
|
||||
|
||||
Zdroj: `nanobot/cli/commands.py:1204-1231` (CLI), `nanobot/nanobot.py:71-102` (`Nanobot.run`).
|
||||
|
||||
---
|
||||
|
||||
## Agent vidí `Channel` a `Chat ID` v runtime contextu zprávy
|
||||
|
||||
ContextBuilder každý tah příchozí zprávy obaluje runtime context blokem,
|
||||
ve kterém je `Channel: <name>` a `Chat ID: <id>` (kromě `Current Time` a
|
||||
volitelně `Sender ID`). Skill nebo prompt si je tedy **může přečíst** —
|
||||
nemusí mít vlastní tool ani contextvars přístup.
|
||||
|
||||
```
|
||||
Channel: telegram
|
||||
Chat ID: 8826147089
|
||||
```
|
||||
|
||||
V CLI / SDK session bez channel kontextu se blok nezobrazí (`Chat ID`
|
||||
chybí). Skill na to musí umět reagovat (např. `detach` v takovém
|
||||
případě nabídne synchronní vykonání).
|
||||
|
||||
Zdroj: `nanobot/agent/context.py:123-139` (`ContextBuilder._build_runtime_context`).
|
||||
|
||||
---
|
||||
|
||||
## Cron preamble je hardcoded — pro non-reminder background úlohy obejít
|
||||
|
||||
`nanobot/cli/commands.py:891-897` (`on_cron_job`) obaluje payload natvrdo:
|
||||
|
||||
```
|
||||
The scheduled time has arrived. Deliver this reminder to the user now,
|
||||
as a brief and natural message in their language. Speak directly to them —
|
||||
do not narrate progress, summarize, include user IDs, or add status reports
|
||||
like 'Done' or 'Reminded'.
|
||||
|
||||
Reminder: <payload.message>
|
||||
```
|
||||
|
||||
Pro reminders je to správné chování. Pro background **úlohy** (deep research,
|
||||
ingest, multi-step research) je to v přímém rozporu — agent má provést úkol,
|
||||
zapsat výsledek do souboru, vrátit informativní větu. Preamble ho stáhne
|
||||
do meta-statusu.
|
||||
|
||||
**Cesta okolo:** zahodit cron tool i `at` jednorázové joby, orchestraci řešit
|
||||
**externím daemonem mimo agent loop** — viz "Detach skill" níže. Stejný pattern
|
||||
už používá `/remind` (viz "Cron job s LLM agentem je nespolehlivý…" výše).
|
||||
|
||||
---
|
||||
|
||||
## Detach skill — background úlohy přes externí daemon (mimo agent loop)
|
||||
|
||||
Architektura podobná `/remind` — orchestrace mimo agent loop, žádný cron preamble.
|
||||
|
||||
**Tok:**
|
||||
1. Skill `detach` v chatu → `exec skills/detach/scripts/create-task.py --goal … --slug … --channel … --chat-id …`. Skript vygeneruje timestamp + frontmatter, zajistí fronty (`mkdir -p`), atomicky zapíše do `tasks/tmp/` a přesune do `tasks/inbox/` (atomický rename, partial-write race neexistuje). Agent dělá jen LLM části (přeformulovat goal, vybrat slug, přečíst channel/chat_id z runtime contextu) — žádný ruční `write_file`/`mv`/`date`.
|
||||
2. Systemd user unit `tasks-daemon.path` (`DirectoryNotEmpty=…/tasks/inbox`) přes inotify spustí `tasks-daemon.service` (`Type=oneshot`).
|
||||
3. `tasks-daemon.py` (Python, shebang na uv venv interpreter) projede inbox: `mv → running/`, parse frontmatter (`chat_id` povinný), zavolá `Nanobot.from_config().run(goal, session_key=f"detach:<stem>")` s 45-min timeoutem, appendne `## Result` sekci, `mv → done/` nebo `failed/`, pošle Telegram zprávu přes Bot API (urllib + token z `~/.nanobot/config.json["channels"]["telegram"]["token"]`).
|
||||
|
||||
**Volba modelu pro task (od 2026-06-07):** Detach umí task spustit na explicitně zvoleném presetu (background = latence nebolí, vyplatí se silnější model). Uživatel model jen zmíní ve větě („na kimi") → agent předá token jako `create-task.py --model "<token>"` → skript ho **při captue** fuzzy-resolvne proti `config.json` (`resolve_preset`: exact case-insensitive → unikátní substring; jinak `KeyError` se seznamem, exit 1, fail-fast v chatu) a uloží přesný preset do frontmatteru `model:`. Bez `--model` jede default (`agents.defaults.modelPreset`). Daemon přečte `fm["model"]` a před `run()` přepne `bot._loop.set_model_preset(preset)` — stejný switch jako `/model` v chatu (ověřeno e2e s nainstalovaným balíčkem, history 2026-06-07). **Gotcha:** klíč presetů je v serverovém `config.json` na disku **snake_case `model_presets`** (ne camelCase `modelPresets`), zatímco `agents.defaults.modelPreset` je camelCase — `load_preset_names()` proto čte oba tvary.
|
||||
|
||||
**Soubory:**
|
||||
- `~/.nanobot/workspace/skills/detach/SKILL.md` — definice + triggery (EN-only)
|
||||
- `~/.nanobot/workspace/skills/detach/scripts/tasks_common.py` — sdílené čisté helpery (TASKS, FILENAME_RE, parse_frontmatter, parse_kv, format_*, build_task_*), importují ho ostatní skripty
|
||||
- `~/.nanobot/workspace/skills/detach/scripts/create-task.py` — capture skript (frontmatter + atomický tmp→inbox)
|
||||
- `~/.nanobot/workspace/skills/detach/scripts/{list-tasks,read-task}.py` — list / read subactions
|
||||
- `~/.nanobot/workspace/skills/detach/tests/` — pytest čisté logiky (lokálně v repu, ne na serveru)
|
||||
- `~/.nanobot/workspace/skills/detach/scripts/tasks-daemon.py` — daemon
|
||||
- `~/.nanobot/workspace/skills/detach/systemd/tasks-daemon.{path,service}` — user systemd unity (symlinkované do `~/.config/systemd/user/`)
|
||||
- `~/.nanobot/workspace/tasks/{tmp,inbox,running,done,failed}/` — fronty
|
||||
- `~/.nanobot/workspace/log/tasks-daemon.{log,stdout.log,stderr.log}` — append-only logy
|
||||
|
||||
**Souběh:** systemd serializuje (`Type=oneshot` se nespustí podruhé, dokud první běh trvá; level-triggered `.path` ho restartne po doběhu pokud inbox stále není prázdný). Žádný flock není potřeba.
|
||||
|
||||
**Notifikační target — Telegram s fallback chat_id (single-user setup):** Skill v frontmatteru zapíše `channel` + `chat_id` z runtime contextu (`Channel: telegram` → numeric ID, `Channel: websocket` → session UUID, atd.). Daemon `resolve_telegram_chat_id(fm)`:
|
||||
|
||||
- pokud `channel == "telegram"` → použij `chat_id` z frontmatteru (multi-user ready)
|
||||
- jinak → čti `channels.telegram.allowFrom[0]` z `~/.nanobot/config.json`
|
||||
|
||||
Tím Telegram vždy doručí, i když úkol přišel z WebUI / CLI. Daemon log: `NOTIFY chat=<id> source=<frontmatter|fallback>`. Bez tohoto fallbacku selhával Telegram Bot API s HTTP 400 pro non-telegram channel (history 2026-05-28 18:37).
|
||||
|
||||
**Subactions `list` a `read`:** detach skill umí i číst zpět hotové úkoly. „výsledky?" → markdown tabulka tasks/{running,done,failed}/. „výsledek <slug-nebo-pattern>" → `read_file` přes match v done/+failed/, předlož `# Result` sekci. Identifier match: slug substring (`*foo*`), timestamp fragment (`*T175451*`), nebo prázdný = nejnovější.
|
||||
|
||||
**Zdroj:** [skills/detach/](skills/detach/) v tracking repu, history 2026-05-28 „Skill detach + daemon" + iterace #2 + iterace #3.
|
||||
|
||||
**uv-native invokace (iterace #3):** Shebang přepnut na `#!/usr/bin/env -S uv run --script` s PEP 723 inline metadata (`requires-python = ">=3.11"`, `dependencies = ["nanobot-ai"]`). `uv run --script` samo vytvoří/cachuje izolované venv — skript přežije `uv tool uninstall/install` i přesun na jiný stroj. První spuštění po PEP 723 změně trvá ~5-10s (budování venv), další jsou instantní (cache v `~/.cache/uv/`). Systemd user unit musí mít `Environment=PATH=%h/.local/bin:/usr/bin:/bin`, jinak `uv` v PATH chybí.
|
||||
|
||||
---
|
||||
|
||||
## Detach notifikace do origin kanálu (WebUI/CLI) — záměrně nepodporováno
|
||||
|
||||
Daemon notifikuje **jen Telegram** (přes Bot API, deterministicky). Když task přišel z WebUI nebo CLI, do toho kanálu se notifikace nepošle — uživatel si výsledek vyzvedne přes `výsledek <slug>` (detach subaction `read`).
|
||||
|
||||
**Architektonický důvod:** WebSocket spojení vlastní gateway proces; daemon je samostatný systemd oneshot. Nanobot nemá HTTP endpoint pro vstřikování zpráv do WS sessions (`nanobot/channels/websocket.py:673-782` — všechny `/api/sessions/...` jsou read-only). Sdílí jen filesystem, žádné IPC.
|
||||
|
||||
**Zvážené a zamítnuté možnosti:**
|
||||
|
||||
- **Samostatný `Nanobot.run()` jen kvůli notifikaci** — LLM jako IPC proxy. Pomalé (10–30 s), drahé, nedeterministické (model může prompt překroutit nebo `message` tool nezavolat). Stejná třída problému jako [[Cron job s LLM agentem je nespolehlivý]].
|
||||
- **Přibalit `message` tool call k existujícímu agent turnu tasku** — žádný extra LLM call, ale stále LLM-mediated; nepokrývá timeout/exception (agent se k toolu nedostane).
|
||||
- **Patch upstream + nový HTTP endpoint na gatewayi** — čisté řešení (daemon dělá prostý POST, žádný LLM), ale udržovat patch napříč upgrady `nanobot-ai`. Pokud někdy ano, místo je `nanobot/channels/websocket.py` (přidat handler vedle stávajících `/api/sessions/...`, vytvořit `OutboundMessage(channel="websocket", chat_id=..., content=...)` a `bus.publish_outbound(msg)`).
|
||||
|
||||
**Rozhodnutí 2026-05-29:** status quo — Telegram fallback stačí, `výsledek <slug>` je dokumentovaný způsob pro WebUI/CLI.
|
||||
|
||||
---
|
||||
|
||||
## Skill `exec` běží z workspace rootu, ne ze skill adresáře
|
||||
|
||||
Když skill volá `exec` bez explicitního `working_dir`, příkaz běží s **CWD = workspace root** (`~/.nanobot/workspace`), **ne** v adresáři skillu. Cesty na skripty skillu proto musí být buď workspace-relativní (`skills/<name>/scripts/x.py`) nebo absolutní — **skill-dir-relativní `scripts/x.py` se rozbije** (resolvuje na `workspace/scripts/x.py`).
|
||||
|
||||
Zdroj: upstream `nanobot/agent/tools/shell.py:148` (`working_dir=ctx.workspace`) + `:370` (`cwd = working_dir or workspace_root`). Pozn.: remind SKILL.md používá `scripts/remind_edit.py` — v tomto ohledu zavádějící; detach používá korektní `skills/detach/scripts/…`.
|
||||
|
||||
---
|
||||
|
||||
## Python skripty na serveru — uv-native pattern (PEP 723)
|
||||
|
||||
Preferovaný způsob pro libovolný stand-alone Python skript v `~/.nanobot/workspace/`:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = ["nanobot-ai", "requests", ...]
|
||||
# ///
|
||||
```
|
||||
|
||||
`uv run --script` vytvoří a cachuje izolované venv per skript (`~/.cache/uv/`). Skript přežije `uv tool uninstall/install`, upgrade Pythonu i přesun stroje — bez přímé cesty do `~/.local/share/uv/tools/<tool>/bin/python`. První spuštění po vytvoření hlavičky trvá ~5-10s (build venv), další jsou instantní.
|
||||
|
||||
**Gotcha pro user systemd:** unit musí mít explicitní PATH, jinak shebang `uv` nenajde:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
Environment=PATH=%h/.local/bin:/usr/bin:/bin
|
||||
ExecStart=%h/path/to/script.py
|
||||
```
|
||||
|
||||
Bez `Environment=PATH` selže s `/usr/bin/env: 'uv': No such file or directory`. Aplikace pravidla na všechny budoucí user systemd unity spouštějící uv skripty (nejen detach).
|
||||
|
||||
Zdroj: [PEP 723](https://peps.python.org/pep-0723/), [uv docs `uv run --script`](https://docs.astral.sh/uv/guides/scripts/), ověřeno deployem detach skillu iterace #3.
|
||||
|
||||
---
|
||||
|
||||
## Systemd `.path` unit s `DirectoryNotEmpty=` — event-driven workspace daemon
|
||||
|
||||
Pattern pro libovolný daemon, který má reagovat na soubory v workspace **bez polling**:
|
||||
|
||||
```ini
|
||||
# tasks-daemon.path
|
||||
[Path]
|
||||
DirectoryNotEmpty=%h/.nanobot/workspace/tasks/inbox
|
||||
Unit=tasks-daemon.service
|
||||
|
||||
[Install]
|
||||
WantedBy=paths.target
|
||||
```
|
||||
|
||||
```ini
|
||||
# tasks-daemon.service
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=%h/path/to/daemon.py
|
||||
```
|
||||
|
||||
`%h` = user home. `.path` unit je jen watcher (přes inotify), reálnou akci dělá `.service`. **Level-triggered:** dokud kondice `DirectoryNotEmpty=` platí, systemd po každém doběhnutí service spustí novou instanci. Daemon by měl drenovat celý inbox v jednom běhu (sériově).
|
||||
|
||||
Install: `systemctl --user enable --now <unit>.path`. Lingering musí být zapnutý (`loginctl enable-linger nanobot`), jinak user units po odhlášení padnou. Pro reminders se to nepoužívá — ty mají cron výrazy, .path není vhodný (kondice se nemění minutu po minutě). Pro file-driven queue (jako detach) ano.
|
||||
|
||||
**Gotcha — level-triggered `.path` + startup crash = permanentní latch:** Když oneshot daemon spadne **ve startup fázi** (před vyprázdněním inboxu), inbox zůstane neprázdný → `.path` ho hned znovu spustí → další pád → … Na manager defaultu (`StartLimitIntervalSec=10s`, `Burst=5`) to za <2 s narazí na rate-limit a systemd zalatchuje **`.service` i `.path`** do `failed (unit-start-limit-hit)`. Z toho se **sám nezotaví** — nutný `systemctl --user reset-failed <unit>.service <unit>.path` + `restart <unit>.path`. (Stalo se 7.6., když daemon padal na `NameError`.)
|
||||
|
||||
**Hardening (ověřeno, nasazeno na tasks-daemon):** v `.service` přidat
|
||||
```ini
|
||||
[Unit]
|
||||
StartLimitIntervalSec=1800
|
||||
StartLimitBurst=20
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
RestartSec=60
|
||||
```
|
||||
`Restart=on-failure` + `RestartSec` dá **delay mezi pokusy** (nezávisle na `.path` retriggeru); čistý `exit 0` (inbox vyprázdněn) ani SIGTERM od systemd nerestartují. Širší okno (`30min`/`20`) zajistí, že se latch po posunu okna sám pustí dál. **`man systemd.service`: pro `Type=oneshot` jsou zakázané jen `Restart=always`/`on-success`, `on-failure` je povolený.**
|
||||
|
||||
Zdroj: `man systemd.path` + `man systemd.service`, ověřeno smoke testem před deployem detach skillu; latch+hardening history 2026-06-07 19:33.
|
||||
|
||||
---
|
||||
|
||||
## `nvm` je shell funkce, ne binárka
|
||||
|
||||
`nvm` je definován jako bash funkce v `.bashrc` — **není to spustitelný soubor**. Proto ho systemd service nevidí, ani když má správně nastavenou `PATH` s nvm node cestou.
|
||||
|
||||
| Příkaz | Typ | Dostupný v systemd service? |
|
||||
|---|---|---|
|
||||
| `node`, `npm`, `npx` | skutečné binárky v `.nvm/.../bin/` | ano, pokud je PATH nastavena explicitně |
|
||||
| `nvm` | shell funkce v `.bashrc` | **ne nikdy** — `.bashrc` se nesourcuje |
|
||||
|
||||
Pro správu verzí Node.js z shellu → přihlásit se jako `nanobot` a volat `nvm` interaktivně. Z agenta nebo daemonu → volat `node`/`npx` přímo (fungují přes PATH).
|
||||
|
||||
---
|
||||
|
||||
## Skill `/keep` — explicit immediate memory
|
||||
|
||||
On-demand skill pro okamžitou explicitní paměť. Uživatel řekne „keep X" → agent reformuluje na terse fact → zapíše jako bullet do `workspace/keep.md`. Bez datumů. Dedup, compaction při >150 řádcích.
|
||||
|
||||
**Persistent awareness:** `keep.md` není v `BOOTSTRAP_FILES` (ty jsou hardcoded). Trvalé povědomí zajišťuje krátká reference `## workspace/keep.md` na konci `USER.md` (auto-loadovaný každý tah). Skill je tedy čistě write endpoint — neplýtvá context window každou session.
|
||||
|
||||
**Kde žije:** `workspace/keep.md` v rootu workspace (vedle `USER.md`, `MEMORY.md`). Edituje ho výhradně `/keep` skill; ostatní agent paths smí číst. **Odděleno od Dream / MEMORY.md** — Dream o `keep.md` neví, needituje ho.
|
||||
|
||||
**Dedup pokrývá `keep.md` i `MEMORY.md`:** Write protocol (krok 4) před appendem přečte `workspace/memory/MEMORY.md` a pokud tam je sémanticky podobný fakt (Dream ho mohl destilovat), upozorní uživatele a defaultně přeskočí. `MEMORY.md` je read-only — `/keep` do něj nikdy nezapisuje.
|
||||
|
||||
**Ukládá i *why*, ne jen *what* (od 2026-06-06):** Krok 2 Write protokolu rozlišuje typ záznamu — plain fakt (alergie, deploy window, jméno) jde bez důvodu; **rozhodnutí / preference / dead-end** dostane důvod inline na stejném řádku (`<fakt> — because <terse why>`). Pokud je vstup rozhodnutí/dead-end *bez* uvedeného důvodu, model se **jednou doptá** na why (decline/self-evident → uloží bez něj). Záměrně úzká varianta Claude memory.md vzoru, který why přidává jen u feedback/project, ne u reference/faktu. Žádné `Why:` bloky ani few-shot příklady — silné Ollama Cloud / OpenRouter modely zvládnou hranici fakt-vs-rozhodnutí zero-shot. Plný kontext: history.md 2026-06-06.
|
||||
|
||||
**Gotcha — BOOTSTRAP_FILES jsou hardcoded:** `nanobot/agent/context.py:25` má `BOOTSTRAP_FILES = ["AGENTS.md", "SOUL.md", "USER.md", "TOOLS.md"]` — nelze přidat vlastní soubor bez patche. Vše, co má být vidět každý tah bez on-demand loadingu, musí být reference v existujícím bootstrap souboru (USER.md, SOUL.md, …).
|
||||
|
||||
---
|
||||
|
||||
## Skill `/note` — explicit notes (no auto-load)
|
||||
|
||||
On-demand skill pro ukládání poznámek do `workspace/notes.md`. Uživatel řekne „note X" → agent reformuluje na terse fact → zapíše jako bullet. Bez dedup, bez kompakce, bez dat.
|
||||
|
||||
**Klíčový rozdíl od `/keep`:** `notes.md` nemá referenci v `USER.md` ani jiném bootstrap souboru — nikdy nevstupuje do context window automaticky. Maže se výhradně přes `/note delete <pattern>` (by index nebo substring).
|
||||
|
||||
**Kde žije:** `workspace/notes.md`. Edituje ho výhradně `/note` skill. Odděleno od `/keep`, Dream, MEMORY.md — žádný cross-read ani cross-write.
|
||||
|
||||
---
|
||||
|
||||
## MCP servery v nanobotu — skrytá tokenová zátěž
|
||||
|
||||
Každý nakonfigurovaný MCP server přidává do system promptu svůj tool schema popis. Pro sqlite MCP to jsou ~1–2k tokenů, a to **každý tah** — bez ohledu na to, jestli tool vůbec použiješ.
|
||||
|
||||
U menších modelů s omezeným kontextovým oknem (typicky cloud MoE modely s efektivními ~32B params) je to zbytečné plýtvání. Přitom přímá alternativa (CLI `sqlite3` přes `exec`, nebo Python `sqlite3` stdlib přes `uv run`) **tuto zátěž nemá** a pro 95 % use-cases je dostatečná.
|
||||
|
||||
**Pravidlo:** MCP server zapojit jen pokud přidaná hodnota nad přímým přístupem výrazně převáží tokenovou cenu. Pro sqlite typicky nepřeváží.
|
||||
|
||||
---
|
||||
|
||||
## Reasoning stream (`✻`) na konzoli — `channels.showReasoning`
|
||||
|
||||
Řádky prefixované `✻`, streamované token po tokenu (`✻ The`, `✻ user wants`, …) v `nanobot agent` CLI chatu nejsou debug ani chyba — je to **reasoning/thinking stream** modelu. Řídí ho jediný config klíč `channels.show_reasoning` (default `true`, camelCase alias `showReasoning`).
|
||||
|
||||
**Vypnout:** `channels.showReasoning = false` v `~/.nanobot/config.json`. Sourozenec `telegram`/`websocket` uvnitř `channels`, ne uvnitř konkrétního kanálu.
|
||||
|
||||
- **Je to globální flag, ne per-channel.** Gate čte globální `channels_config.show_reasoning` (`nanobot/cli/commands.py:345,354`), ne per-kanálový config. Nelze vypnout jen pro konzoli a nechat zapnuté ve WebUI — buď všude, nebo nikde. (Trade-off: ve WebUI se reasoning hodí při ladění „proč něco jde/nejde".)
|
||||
- **Restart:** CLI (`nanobot agent`) čte config čerstvě při startu → stačí restart sezení. Gateway/WebUI/Telegram dostávají `channels` přes `AgentLoop.from_config()` jednou při startu → restart service.
|
||||
- **Žádný runtime flag** `nanobot agent` na to není; `--logs/--no-logs` řídí jen loguru runtime log, ne reasoning stream.
|
||||
- Příbuzné knoby v témže bloku: `sendProgress` (default `true`, progress řádky `↳`), `sendToolHints` (default `false`, tool-call hinty). Vykreslení `✻` na `commands.py:301`.
|
||||
|
||||
Zdroj: `nanobot/config/schema.py:37-39`, `nanobot/cli/commands.py:301,345,354`. Plný záznam: history 2026-06-01 „Vypnutí reasoning streamu".
|
||||
|
||||
---
|
||||
|
||||
## Context window presetů: default 65k, přepis přes `contextWindowTokens`
|
||||
|
||||
Nanobot má **hardcoded default `context_window_tokens = 65_536`** pro `ModelPresetConfig` i `AgentDefaults` (`nanobot/config/schema.py:101,124`). Pokud preset v `config.json` tuto hodnotu nepřepíše, jede model na 65k **bez ohledu na to, co reálně umí**. Klíč v JSON: `contextWindowTokens` (Base má `alias_generator=to_camel` + `populate_by_name=True`, `schema.py:24` → projde camelCase i snake_case). Sourozenec `maxTokens` (max output) má default jen `8192`.
|
||||
|
||||
Nastaveno 2026-06-02 per-preset na reálné limity modelů (kimi-k2.6 / qwen3.5 / nemotron-3-super 262144, minimax-m2.7 204800, glm-5.1 196608, deepseek-v4-flash 1048576) + `maxTokens` 16384. **Bez restartu** — `modelPresets` se hot-reloadují (viz sekce „Kdy je a není potřeba restart"). U `:cloud` modelů hostí kontext Ollama cloud, takže `contextWindowTokens` reálně rozšíří budget — není to lokální `num_ctx` žeroucí RAM. Plný záznam: history 2026-06-02.
|
||||
|
||||
**Důsledky (trade-off, ne čistá výhra):**
|
||||
- **+** Méně ořezávání/komprese historie → lepší návaznost v dlouhých sezeních. Delší souvislé odpovědi (16k vs 8k output).
|
||||
- **−** „Lost in the middle": LLM neudrží kvalitu rovnoměrně přes celý kontext; info zahrabané uprostřed ~200k se vybavuje hůř. Propad je výraznější u slabších MoE modelů (glm/qwen/nemotron) než u špičkových (Kimi K2.6). Roste latence i protečené tokeny úměrně naplnění.
|
||||
- Při běžném (nízkém) naplnění se kvalita **nemění** — efekt nastává až když sezení přeroste 65k.
|
||||
|
||||
**Otevřená otázka** (todo.md): nenechat slabším modelům kontext spíš na ~128k? Menší okno může dát lepší kvalitu „per token" než maximální naplnění.
|
||||
|
||||
---
|
||||
|
||||
## Rozpad input contextu (co zabírá tokeny každý tah)
|
||||
|
||||
Detailní naměřený rozpad ~15k baseline input contextu (system prompt po částech + tool definitions 18 toolů) je v samostatném souboru [`tokens-explain.md`](tokens-explain.md) — k 0.2.1, preset glm-5.1. Stručně: ~8,7k system prompt (největší `MEMORY.md`, `skills_section`, `SOUL.md`), ~5,2k tool defs, zbytek session zprávy.
|
||||
|
||||
## Optimalizovat skilly kvůli tokenům se nevyplatí
|
||||
|
||||
Celý blok skillů (~2,5k: `skills_section` 1,56k + always-skilly 0,95k) je při okně 196k jen **~1,3 % okna**. Smazat on-demand skill ušetří jen popis + framing (~40–75 tok/kus) → fakticky neměřitelné. **Description neškrtat** — je to trigger pro progressive loading (model podle něj pozná, kdy skill načíst); bez něj skill přestane fungovat, ušetříš desítky tokenů a přijdeš o funkčnost. Jediná páka jsou `always: true` (jdou celým tělem), ale `my`+`memory` mají být always. Větší blok jsou tool defs (5,2k, jen vypnutím toolů v configu). **Závěr:** skilly maž podle užitečnosti, ne kvůli tokenům; reálný strop je `contextWindowTokens`, ne baseline. Začalo by to dávat smysl až u desítek–stovek skillů nebo velkého těla jako `always`. Plný rozbor: [`tokens-explain.md`](tokens-explain.md).
|
||||
|
||||
---
|
||||
|
||||
## `maxTokens` se počítá dvakrát; prompt caching má 3 háčky
|
||||
|
||||
**`maxTokens`** jde jednak přímo do API jako strop výstupu (`runner.py:621`), jednak se **odečítá z input budgetu** jako rezerva na výstup — u snipu historie i u konsolidace: `budget = contextWindowTokens − maxTokens − 1024` (`runner.py:1262`, `memory.py:619`). Vyšší `maxTokens` tedy zmenšuje prostor pro kontext a uspíší konsolidaci → držet skromně (16k OK), u reasoning modelů víc (reasoning tokeny se počítají taky).
|
||||
|
||||
**Prompt caching** nanobot zapíná jen pro providery s `supports_prompt_caching=True` = **openrouter, anthropic, bedrock** (`registry.py:149,278`); `ollama` a `gemini` ne → aktivní `glm-5.1` přes ollama od nanobota **žádné cache breakpointy nedostává**. Háčky tam, kde caching jede:
|
||||
1. **TTL 5 min** — holé `{"type": "ephemeral"}` (`anthropic_provider.py:400`). U sporadického chatu cache mezi tahy obvykle vyprší → platí se plný vstup; write navíc 1,25× base (read 0,1×).
|
||||
2. **Konsolidace/snip rozbíjí prefix** — breakpoint sedí na system + `messages[-2]` + tools (`openai_compat:453`); jakmile Dream/`_snip_history` změní začátek pole, prefix se invaliduje.
|
||||
3. **Interakce s kontextem:** vyšší `contextWindowTokens` = méně časté konsolidace = stabilnější cachovaný prefix → argument cachingem podporuje velké okno, ale jen na cachujících presetech.
|
||||
|
||||
---
|
||||
|
||||
## MiniLoop — změřená čísla `/remind add` parseru (PoC)
|
||||
|
||||
Samostatný `.NET` PoC v `src/MiniLoop/` (prompt-only parse text→JSON, `Microsoft.Extensions.AI` nad OpenAI SDK, swap providera přes config). Test `test` mód protáčí 17 párů × všechny modely **souběžně** (modely paralelně, příklady uvnitř modelu sekvenčně; NOW fixní `2026-06-03T14:30:00`). Souběžnost vůči provideru je omezená `maxConcurrency` v configu (`SemaphoreSlim` per provider) — ollama=3 dle kvóty předplatného. Běh 5 modelů s gate=3 (2026-06-03):
|
||||
|
||||
| Model | Provider | Úspěšnost | Wall median / avg | Tokeny in / out |
|
||||
|---|---|---|---|---|
|
||||
| glm-5.1 | ollama (nvidia.hell) | 17/17 | 1690 / 1927 ms | 21118 / 2896 |
|
||||
| deepseek-v4-flash | ollama (nvidia.hell) | 17/17 | 4894 / 6118 ms | 21654 / 3487 |
|
||||
| minimax-m2.7 | ollama (nvidia.hell) | 17/17 | 4815 / 4604 ms | 21969 / 2317 |
|
||||
| claude-haiku-4.5 | openrouter | 16/17 | 1064 / 1135 ms | 23668 / 691 |
|
||||
| gpt-5.4-nano | openrouter | 17/17 | 3034 / 4003 ms | 20987 / 553 |
|
||||
|
||||
**Hrdlo souběhu = kvóta paralelních dotazů providera, ne sdílený výpočet ani počet spojení.** Ollama předplatné povoluje **max 3 paralelní dotazy** ([ollama.com/pricing](https://ollama.com/pricing)). Když běží víc ollama modelů než 3 naráz, přebytečné dotazy čekají ve frontě a to čekání spadne do wall-clocku (stopky obalují jen HTTP call). Dřív (4 ollama modely bez stropu): glm median 3240 ms, jednotlivá volání qwen až 59 s. Po zavedení `maxConcurrency=3` (a redukci na 3 ollama modely, takže strop zatím ani nepřekáží): glm median zpět na **1690 ms**, žádné odlehlé hodnoty. `SemaphoreSlim` slot se navíc získává **mimo stopky**, takže i kdyby strop překážel, čekání na slot se do měřené latence nezapočte. OpenRouter běží na vlastní infře, strop nemá. Každý model má vlastní `OpenAIClient` (spojení se nesdílí) — víc spojení by nepomohlo.
|
||||
|
||||
**Reasoning = pomalé + drahé na out tokeny:** deepseek out=3487, minimax out=2317 — proto ~5 s. (Dříve zavržený qwen3.5 byl extrém: out=22671 tok ≈ jako input, volání i 59 s — proto vyhozen.) haiku/gpt-nano out 550–700 tok = přímý parse bez reasoningu. glm rychlý (out~2,9k, ale median 1,7 s).
|
||||
|
||||
**FAILy:** jediný „FAIL" haiku = **false negative v test datech** (`zkontrolovat pečení` vs `pečeni`; JsonCompare porovnává `text` přesně, ordinálně). Ostatní modely 17/17. (Z dřívějška: gemma dělala skutečnou chybu data `příští pondělí`→`06-09` místo `06-08`; gemma teď v reálném configu není.)
|
||||
|
||||
**Teze PoC potvrzena:** ~1,2k input tokenů na `add` (vs ~28–32k přes nanobot agent loop, ~30× méně) a ~1 s wall-clock u rychlých modelů (vs ~10 s u `/remind list` přes agenta). Sedí s odhady z [plans/remind-standalone-bot.md](plans/remind-standalone-bot.md). Plný záznam: history 2026-06-03 „MiniLoop paralelizace".
|
||||
|
||||
### Levné / OSS modely z OpenRouteru (změřeno 2026-06-03)
|
||||
|
||||
Test 5 levných OpenRouter modelů (cena $/M tok in/out), gate=3 na ollam(ě) se netýká — vše OpenRouter:
|
||||
|
||||
| Model | Cena | Úspěšnost | Wall median / avg | out tok |
|
||||
|---|---|---|---|---|
|
||||
| `mistralai/mistral-small-3.2-24b-instruct` | 0.075/0.20 | 17/17 | **934 / 1050 ms** | 610 |
|
||||
| `google/gemma-3-27b-it` | 0.08/0.16 | 17/17 | 1413 / 1611 ms | 608 |
|
||||
| `z-ai/glm-4-32b` | 0.10/0.10 | 16/17* | 1905 / 2072 ms | 519 |
|
||||
| `qwen/qwen3-30b-a3b-instruct-2507` | 0.043/0.17 | 16/17* | 1991 / 1866 ms | 588 |
|
||||
| `openai/gpt-oss-120b` | levný | 16/17 | 7238 / 12164 ms | 3769 |
|
||||
|
||||
\* false negative (slovosled `se protáhnout`/`protáhnout se`, resp. `pečení`/`pečeni`).
|
||||
|
||||
**Závěr:** **`mistral-small-3.2` má nejlepší poměr ze všech dosud měřených** — 934 ms median (rychlejší než glm-5.1 1690 ms i haiku 1064 ms), 17/17, out 610 tok, cena pakatel. Evropská/česká stopa Mistralu se potvrdila. `gemma-3-27b` těsně za ním. **`gpt-oss-120b` je jediný propadák — reasoning → 7 s a 6× víc out tokenů**, gpt-5.4-nano pokrývá OpenAI rychleji. Potvrzení teze o thinkingu: `qwen3-30b-a3b-instruct` 1991 ms vs cloud `qwen3.5` (thinking) 10852 ms + out=22671 — past byl režim thinking, ne qwen. Plný záznam: history 2026-06-03 „MiniLoop levné OSS".
|
||||
|
||||
### Malé ollama modely — ministral-3, nemotron-3-nano (změřeno 2026-06-03)
|
||||
|
||||
`:cloud` varianty registrované na nvidia.hell přes `POST /api/pull` (cloud pointer, žádný GB download):
|
||||
|
||||
| Model | Úspěšnost | Wall median / avg | out tok |
|
||||
|---|---|---|---|
|
||||
| `ministral-3:8b-cloud` | 15/17 | 1043 / 1169 ms | 653 |
|
||||
| `nemotron-3-nano:30b-cloud` | 16/17 | 2015 / 2277 ms | 7805 |
|
||||
|
||||
**Ani jeden nepřekonal mistral-small-3.2 — oba zavrženy.** `ministral-3-8b` udělal **skutečnou chybu dne v týdnu** (`každý pátek` → cron `* * 6` sobota místo `* * 5`) — u připomínek vážné, na 8b je to znát; přitom **není ani rychlejší** než mistral-small (1043 vs 934 ms). `nemotron-3-nano-30b` má **reasoning sklony (out=7805 tok, ~12× víc než mistral)**, je 2× pomalejší a jeho jediný FAIL byl rozsekání `1,3,5` na tři cron výrazy (rozvrh ekvivalentní, formát ne). Závěr: pod ~24b instruct (mistral-small, gemma-3-27b) klesá spolehlivost cronu a malé „nano" modely buď chybují, nebo zbytečně reasonují. Plný záznam: history 2026-06-03 „MiniLoop ministral/nemotron-nano".
|
||||
|
||||
## Rychlost: glm-5.1 vs minimax-m3 (Ollama nativní streaming, 2026-06-07)
|
||||
|
||||
Měřeno přímo proti Ollamě na `nvidia.hell` (stejný endpoint jako nanobot), streaming `/api/chat`, identický `/remind list` prompt, 3 běhy/model. **`:cloud` modely nevracejí sub-durations** (`eval_duration` ap. = `None`) — tok/s nutno měřit přes streaming (TTFT = čas 1. content chunku).
|
||||
|
||||
| Model | TTFT (medián) | Total wall (medián) | Out tok | End-to-end průtok (out/total) |
|
||||
|---|---|---|---|---|
|
||||
| glm-5.1 | ~5,9 s | ~7,5 s | 1200–1730 | **~198 tok/s** |
|
||||
| minimax-m3 | ~6,8 s | ~10,8 s | 420–460 | **~40 tok/s** |
|
||||
|
||||
**minimax-m3 je výrazně línější:** TTFT mají srovnatelný (start není problém), ale minimax má **~50 % delší celkovou dobu i přes 3–4× MÉNĚ vygenerovaných tokenů**. Čistá generace minimaxu ~95–120 tok/s (streamuje plynule); glm ~5× vyšší end-to-end průtok. Pozn.: glm „1300 tok/s" z post-TTFT okna NEbrat doslovně — cloud buffer flushne dávku, proto měřit `out/total`. Na interaktivní úkoly je glm-5.1 jednoznačně svižnější. Plný záznam + per-run čísla: history 2026-06-07 „Měření rychlosti glm-5.1 vs minimax-m3".
|
||||
|
||||
**Širší rozhodovací rozbor** (GLM-5.1 vs MiniMax M3 vs Kimi K2.6 — kdy který za podmínky Ollama Cloud, capability cliffs, use-case mřížka): [`models.md`](models.md).
|
||||
|
||||
### Doplněk: minimax-m2.7 vs glm-5.1 (2026-06-07, prokládaně 5 kol)
|
||||
|
||||
`minimax-m2.7:cloud` zmizel z `/api/tags` (Ollama Cloud ho nahradila m3), ale `POST /api/pull` ho dotáhne (cloud pointer). Mediány (cloud byl vytížený → absolutní čísla vyšší než ranní m3 měření, ber jen poměr):
|
||||
|
||||
| Model | TTFT | Total wall | Out tok | e2e (out/total) |
|
||||
|---|---|---|---|---|
|
||||
| glm-5.1 | 15,4 s | 18,3 s | 1558 | **~91 tok/s** |
|
||||
| minimax-m2.7 | 9,2 s | 11,3 s | 351 | **~28 tok/s** |
|
||||
|
||||
**m2.7 má decode ~3× pomalejší než glm (a horší než m3 ~40 tok/s).** Nižší wall-clock (11 vs 18 s) je **jen díky terseness** (~4,5× méně tokenů), ne rychlejším generováním. Pro delší agentní výstupy (tool args, kód) je pomalý decode handicap. Plný záznam: history 2026-06-07 17:51.
|
||||
|
||||
---
|
||||
|
||||
## minimax-m3 je pro nanobot agenta nepoužitelný (BLOCKED)
|
||||
|
||||
**Verdikt: nenasazovat `minimax-m3` jako agent model.** Vedle pomalosti (~40 tok/s end-to-end, viz sekce výše) má fatální slabinu v **agentní recovery** — neumí přečíst chybovou hlášku toolu a vystoupit ze smyčky.
|
||||
|
||||
Konkrétně (detach deep-research `ollama-cloud-models-research`, 2026-06-07): web_fetch velké stránky se perzistoval do souboru, parsování přes `exec` blokoval `restrictToWorkspace` guard, a minimax-m3 místo aby přesunul soubor / použil `read_file` (guard to doslova radil) **opakoval identický blokovaný příkaz s kosmetickými obměnami**, prokládal ho triviálními `print('ok')` sanity-checky (četl failure jako rozbitý interpreter) a jednou vystřelil 10× tentýž grep v jednom tahu → **spálil všech 200 `maxToolIterations` bez výsledku**. Stejný úkol s `kimi` doběhl za ~456 s.
|
||||
|
||||
K tomu už dřív známé: tool-result bug + výrazná pomalost. **Zkouší se náhrada `minimax-m2.7`** (starší MiniMax). Pro background deep-research drž GLM-5.1 / Kimi, ne MiniMax. Plný rozbor smyčky: session `detach_2026-06-07T170344-ollama-cloud-models-research.jsonl`.
|
||||
33
develop/memory.md
Normal file
33
develop/memory.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Memory
|
||||
|
||||
## feedback: při deploymentu skillu synchronizovat celý adresář, ne jen scripts/
|
||||
|
||||
Při kopírování skillu na server vždy rsyncovat **celý adresář skillu** (např. `skills/remind/`), nikoli jen podadresář `scripts/`.
|
||||
|
||||
**Why:** 2026-05-29 — fix `remind_edit.py` byl rsyncem nasazen, ale `SKILL.md` s novou instrukcí ne. Agent proto stále četl starý `SKILL.md` a chybné chování přetrvávalo. Druhé kolo čištění dat bylo nutné zbytečně.
|
||||
|
||||
**How to apply:** `rsync -av skills/remind/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/remind/` — cílový rsync pokrývá vše (SKILL.md i scripts/). Nikdy nekopírovat jen podadresář, pokud si nejsi jistý, že ostatní soubory jsou beze změny.
|
||||
|
||||
## feedback: Python skripty na serveru spouštět přes uv (PEP 723 + `uv run --script`)
|
||||
|
||||
Nové Python skripty v `~/.nanobot/workspace/` psát s shebang `#!/usr/bin/env -S uv run --script` a PEP 723 inline metadata (`# /// script` blok). Žádné přímé cesty do `~/.local/share/uv/tools/<tool>/bin/python` jako shebang, žádné `python3` s předpokladem správného venv.
|
||||
|
||||
**Why:** Uživatel explicitně řekl „python veci se maji spoustet pres uv" (2026-05-28, iterace #3 detach skillu). Důvod: uv-managed venv má izolaci, cache, a skript je portable — přežije reinstalaci tooly, upgrade Pythonu i přesun stroje.
|
||||
|
||||
**How to apply:** Pro každý nový stand-alone Python skript na serveru → PEP 723 hlavička. Pokud skript poběží přes user systemd unit, doplnit `Environment=PATH=%h/.local/bin:/usr/bin:/bin` do `.service`, jinak `uv` nebude v PATH. Detail pattern viz [[knowledge.md]] sekce „Python skripty na serveru".
|
||||
|
||||
## feedback: zálohy serverových configů ukládat do `~/.nanobot/backup/`
|
||||
|
||||
Před editací jakéhokoli configu na serveru (zejména `~/.nanobot/config.json`) ukládej zálohu do adresáře **`~/.nanobot/backup/`**, ne vedle původního souboru. Pojmenování s timestampem (např. `config.json.bak-YYYYMMDD-HHMMSS`).
|
||||
|
||||
**Why:** Uživatel to vyžádal 2026-06-02 po editaci context window presetů — záloha vedle configu (`config.json.bak-*`) zaneřáďuje `.nanobot/` root. Centrální `backup/` drží root čistý a zálohy pohromadě.
|
||||
|
||||
**How to apply:** `mkdir -p ~/.nanobot/backup` a `cp config.json ~/.nanobot/backup/config.json.bak-$(date +%Y%m%d-%H%M%S)` před in-place editem. Platí pro všechny serverové configy, které trackujeme/měníme.
|
||||
|
||||
## feedback: po změně knowledge/history/memory synchronizovat do serverového develop/
|
||||
|
||||
Po každém commitu, který mění `knowledge.md`, `history.md` nebo `memory.md`, rsyncni daný soubor i do `nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/develop/`, aby měl serverový nanobot agent aktuální verzi (čte je on-demand jako referenci „jak byla instance rozšiřována a laděna").
|
||||
|
||||
**Why:** Uživatel 2026-06-02 chtěl agentovi zpřístupnit develop kontext a zvolil průběžnou synchronizaci (ne jednorázovou kopii) — jinak agent časem uvidí zastaralý stav.
|
||||
|
||||
**How to apply:** `rsync -av <soubor> nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/develop/`. `README.md` v `develop/` je statický popis, ten se nesynchronizuje. Owner zůstává `nanobot:nanobot` (jdeme jako `nanobot`).
|
||||
Reference in New Issue
Block a user