Files
nanobot-runtime/skills/reflect/README.md
2026-09-02 15:22:39 +02:00

9.5 KiB
Raw Permalink Blame History

reflect — jak to funguje

Skill hledá v mých vlastních session logách opakující se chyby, pojmenuje je a navrhne opravu. Nálezy pak procházíš ty, jeden po druhém, a rozhoduješ, co se použije. Sám od sebe nikdy nic nezmění.

Dva režimy

ANALÝZA — denně 03:30, bez tebe        REVIEW — jen když napíšeš /reflect
cron → reflect_auto.py                   ├─ vezme 1 otevřený nález
  ├─ destiluje session z okna            ├─ ukáže diagnózu, důkazy, diff + šanci
  ├─ LLM tah → nálezy                    ├─ čeká na tvoje rozhodnutí
  ├─ zapíše do findings.jsonl            ├─ aplikuje → git commit → audit
  └─ Telegram (jen když je co)           └─ další nález
NEEDITUJE NIC                          edituje jen to, co schválíš

Nálezy jsou o nedávném chování, ne o celé historii

Běh se dívá jen na posledních 21 dní (--window-days). Není to úspora — je to podmínka, aby nález něco znamenal: korpus má 3,5 měsíce a jedna dávka 200 kB, takže dohánění celé historie po jedné dávce za noc znamenalo, že cursor tři noci stál na konci května a nálezy z něj se předkládaly jako aktuální.

Dvě hranice, každá jinak:

Co dělá
cursor podlaha — co se jednou analyzovalo, se neanalyzuje znovu (jinak by se zdvojily počty)
okno strop — co je starší než 21 dní, se přeskočí a cursor to mine natrvalo

Kdybys někdy potřeboval archeologii, vrať cursor ve state.json a spusť --window-days 0.

Proč tě to neotravuje každý den

Denně přibude jen pár session — v nich se vzor nepozná, jedna chyba je náhoda. Proto:

Kdy Status Telegram
vzor poprvé, 1 výskyt watch ne, jen se počítá
vzor podruhé (≥2× a ve ≥2 session) open ano
vzor už jednou opravený se vrátí po opravě open + regrese ano
vzor už jednou opravený, ale důkazy jsou starší než oprava watch ne
vzor jsi zamítl watch napořád ne, už nikdy

Zamítnutí je rozhodnutí, ne odklad — zamítnutý vzor se znovu neotevře.

Co ti /reflect nabídne

Napíšeš Stane se
ok / aplikuj patch se použije a commitne
uprav: <text> přepíšeš návrh vlastními slovy, ukáže se nový diff
přeskoč nález zůstane otevřený na příště
zamítni nález se zavře natrvalo
konec konec review

Rozhoduješ vždycky nad hotovým diffem, ne nad větou o něm. Nález, který od analýzy patch nedostal (většina), si ho složí ještě předtím, než ti ho předloží: agent napíše návrh do JSON a nechá ho ověřit (--set-patch). Skript ho nejdřív ověří proti souboru a teprve pak uloží, takže nepoužitelný pokus ve findings.jsonl nezůstane — a agent do store nesahá vůbec. Nález, který se editací souboru opravit nedá („míň se překotně přizvukovat"), diff nedostane — to ti agent řekne rovnou a nabídne jen přeskočení nebo zamítnutí.

Vždy jen jeden nález najednou. Nálezy se číslují 1..N podle pořadí, ne podle interního id. Nahoře jsou regrese, teprve pak se řadí podle závažnosti: závažnost odhaduje model znovu v každém běhu a u téhož vzoru kolísá, kdežto „tohle už jednou opravené bylo a vrátilo se" je fakt z auditu.

Šance, že oprava zabere

Pod diffem je odhad typu Šance, že zabere: ~40 %. Nejde o změřenou úspěšnost, ale o zařazení do jednoho ze čtyř pásem (~80 / ~60 / ~40 / ~20 %) podle toho, co patch dělá:

Co patch mění Pásmo
mechaniku — skript nebo hradlo, které nejde ukecat ~80 %
tvrdý zákaz do souboru, který je v kontextu ve chvíli, kdy chyba vzniká (SOUL.md, AGENTS.md, SKILL.md dotčeného skillu) ~60 %
přeformulování existujícího pokynu v takovém souboru ~40 %
soubor, který v tu chvíli v kontextu není, nebo rozhodnutí nechává na úvaze agenta ~20 %

Hlavní osa je jestli je opravovaný text vůbec v kontextu, když chyba nastává — sebelíp formulovaná věta v souboru, který se v tu chvíli nenačítá, chování změnit nemůže. O pásmo dolů jde nález, který už jednou opravený byl a vrátil se (regrese), a nález, jehož důkazy pocházejí z nesouvisejících situací. Zastaralý nález odhad nedostane vůbec — vzor už možná dávno zmizel, takže není co předpovídat. Ke každému číslu patří věta, co ho tam zařadilo.

Kde co leží

Cesta Co
reflect/findings.jsonl všechny nálezy, jeden JSON na řádek
reflect/state.json kam se došlo (cursor), použité okno + statistiky běhů
results/<datum>_reflect.md plný report jednoho běhu + okno, které pokryl, a kolik výskytů na 100 session mají už známé vzory
log/reflect.log audit — každé rozhodnutí, i zamítnutí a přeskočení

„Naposledy" u nálezu je nejnovější datum v důkazech, ne datum, kdy se záznam naposledy přepsal. Když je starší než okno posledního běhu, je nález zastaralý: analýza tam už nedohlédne, takže se sám nikdy neobnoví — spíš než patch si zaslouží zamítnutí. /reflect ti to u něj řekne.

Počty v nálezu (7× ve 4 session) jsou kumulativní součet napříč běhy — sečtené z toho, co model napočítal v jednotlivých oknech, ne měření nad celým korpusem. Skript u nich hlídá jen to, co hlídat umí: vzor nemůže zasáhnout víc session, než kolikrát nastal, ani víc, než kolik jich v dávce vůbec bylo.

Co se o tvém rozhodnutí zapíše

Rozhodnutí Do findings.jsonl Do log/reflect.log
složení patche (u každého nálezu bez patche, ještě před předložením) patch + patch_drafted_at (= složeno při review, ne modelem) DRAFTED <id> [vzor] <soubor>
ok applied: {at, sha, file}, patch = návrh modelu APPLIED <id> [vzor] <soubor> — <sha>
uprav: navíc applied.new_text = tvoje verze (návrh modelu zůstává v patch) APPLIED-EDITED …
zamítni rejected: {at, reason}důvod je povinný REJECTED <id> [vzor] — <důvod>
přeskoč skipped: {count, last}, status zůstává open SKIPPED <id> [vzor] ×N

Povinný důvod u zamítnutí není otravování: je to jediná zpětná vazba na kvalitu analýzy. Z prvních osmi nálezů jsi čtyři zamítl — bez důvodů se z toho čísla nedá poznat, co v analytickém promptu změnit.

Na dotaz „co jsem už rozhodl" ti to /reflect vypíše z logu včetně revert příkazu.

Jak vrátit změnu zpět

Workspace je git, takže každá aplikovaná oprava je samostatný commit:

git revert <sha>

SHA najdeš v log/reflect.log nebo u nálezu ve findings.jsonl (applied.sha).

Změna žije jen na serveru — do trackovacího repa (src/nanobot) se musí dotáhnout zvlášť, jinak ji příští rsync skillu přepíše zpátky.

Ruční spuštění

cd ~/.nanobot/workspace

# normální běh (to dělá cron)
~/.local/bin/uv run --script skills/reflect/scripts/reflect_auto.py

# jiné okno než výchozích 21 dní
~/.local/bin/uv run --script skills/reflect/scripts/reflect_auto.py --window-days 7

# jen prompty do tmp/, nevolat model; s --all přes celý korpus
~/.local/bin/uv run --script skills/reflect/scripts/reflect_auto.py --dry-run --all

# jak velký je korpus a na kolik dávek vyjde (nevolá model)
~/.local/bin/uv run --script skills/reflect/scripts/reflect_distill.py --stats > /dev/null

--all ignoruje cursor, takže by znovu přečetl už spočítané session a nafoukl jim počty. V ostrém běhu ho skript odmítne — je jen na --dry-run.

Přerušený běh o hotovou práci nepřijde. Nálezy i cursor se zapisují po každé dávce a nová dávka nezačne po 20 minutách (--deadline-minutes). S 21denním oknem to vyjde zpravidla na jednu dávku, takže se tenhle strop ani neuplatní; kdyby zbyla nezpracovaná dávka, Telegram to řekne i když nálezy nejsou žádné.

Plná cesta k uv je tu proto, že v neinteraktivním SSH není v PATH. Crontab si PATH nastavuje sám, takže tam stačí uv run ….

Záruky

Nejsou to sliby v promptu, ale kód:

  • Analýza (reflect_auto.py) nemá v sobě žádnou cestu k zápisu do cizího souboru. Navíc se před a po tahu porovná git status celého workspace — kdyby agent přesto něco zapsal, nálezy se zahodí.
  • Editaci dělá výhradně reflect_apply.py, vždy jeden nález. Agent soubor needituje sám. Skript odmítne patch, jehož původní text v souboru není nebo je tam vícekrát — nehádá, kam patřil. Odmítne i nález, který není open (tj. nebyl ti předložen).
  • Do findings.jsonl píše taky jen reflect_apply.py. I patch složený při review jde přes něj (--set-patch) a projde stejnou kontrolou; agent auditní stopu needituje.
  • Commituje se jen ten jeden dotčený soubor (git add -- <file>), nikdy git add -A. Rozdělaná práce Dreamu a jiných skillů se do commitu nedostane; když je rozdělaný přímo ten soubor, udělá se nejdřív checkpoint commit, aby byl revert přesný.
  • Skill vynechává vlastní session, takže neanalyzuje sám sebe.
  • Každé rozhodnutí nechá záznam, i to, které nic nezmění: zamítnutí s důvodem, přeskočení s počítadlem, uprav: odděleně od původního návrhu modelu.

Ověřeno testy v tests/ — včetně toho, že git revert vrátí soubor do původního stavu.