v0.3.0-rc.1: live skill registry + fast advisor scaffold
Roots out the v0.2.x failure mode: Pi-extracted lessons routinely named
hallucinated skill ids (relocate.surface, choose.safe.surface,
survive.shelter, gather.visible_log, …). All 47 Pi-lessons in the live DB
had applied_count=0 because normalisePreferSkill couldn't find them.
Fix:
1. runtime/skill-registry.js — single source of truth derived from
skills/index.js. Exports listSkillIds, isRegistered, and a
prompt-ready block (skillRegistryPrompt) grouped by namespace.
2. Pi prompts (coach/postmortem, coach/reflect) embed the live registry
with a "USE ONLY THESE, never invent" instruction. Lessons are
filtered at write-time too — anything not in the registry and not a
known mode name gets dropped.
3. coach/advice.js — normalisePreferSkill now returns null for unknown
ids, hardening consult() against any hallucinations that slip
through. Warn-logged for visibility.
Also lays the LLM substrate for the rest of v0.3.0:
- runtime/llm/provider.js — OpenAI-compatible chat client. Configured
via PEPA_FAST_LLM_{BASE_URL,API_KEY,MODEL,TIMEOUT_MS}. Safe no-op
unless API_KEY is set. Supports JSON-mode.
- runtime/coach/fast-advisor.js — tactical advisor tier (scaffold).
Exposes advise() that asks the fast LLM what to do RIGHT NOW when
the reflex is wedged/stuck. Rejects hallucinated skill ids using the
registry. Rate-limited 6/h, 30s cooldown. Not auto-triggered yet —
wired into reflex in rc.3 (awareness layer).
Tests: 279 green (+24 vs rc.3): 5 registry, 9 provider, 10 advisor.
See dev/v0.3.0/PLAN.md for the full iteration design (manifesto needs
ladder, event-driven awareness, skill pre-emption) and STATUS.md for
shipped/pending tracking.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# pepa v0.3.0 — Maslow + Awareness
|
||||
|
||||
Concept: needs-based hierarchical agent with real-time event awareness and a
|
||||
fast LLM tactical advisor. Pi (CLI) stays for slow deep analytics
|
||||
(postmortem, reflection). A second, fast LLM tier (TimeWeb / OpenAI-
|
||||
compatible) handles "what to do right now" decisions when the reflex
|
||||
detects wedged/stuck/changed environment.
|
||||
|
||||
This is a major behavioural rewrite over v0.2.x:
|
||||
|
||||
- v0.2.x had a flat reflex chain (modes → defend → eat → sleep →
|
||||
curriculum → idle) — curriculum was just "next mode". The bot had no
|
||||
internal concept of "do I have a pickaxe?" let alone "do I have a
|
||||
shelter?" Lessons from Pi were hallucinated skill names (47/47
|
||||
Pi-extracted lessons applied_count=0 as of v0.2.0-rc.3).
|
||||
- v0.3.0 introduces a **needs ladder** (Maslow-like) that drives the
|
||||
bot's intent end-to-end, an **awareness layer** that reacts to env
|
||||
changes within ~100ms instead of waiting for the next tick boundary,
|
||||
and a **fast LLM** tier that closes the loop when the bot is stuck.
|
||||
|
||||
## Concept terms
|
||||
|
||||
This pattern is called variously in the AI literature:
|
||||
- **Hierarchical Task Network (HTN)** planning — Voyager uses this
|
||||
- **Needs-based / utility AI** — game-AI mainstream
|
||||
- **BDI agent** (Beliefs-Desires-Intentions) — academic AI
|
||||
- **Subsumption architecture** (Brooks) — reactive layers preempt
|
||||
deliberative layers when conditions trigger
|
||||
|
||||
Pepa v0.3.0 is essentially **Maslow-stack curriculum + Brooks-style
|
||||
preemption + dual-tier LLM (fast tactical + slow analytical)**.
|
||||
|
||||
## Manifesto / Needs ladder
|
||||
|
||||
```
|
||||
L0 alive HP>5, не тонет, не горит, не падает с фатальной высоты
|
||||
L1 food ≥6 насыщения (готов кушать на месте)
|
||||
L2 tools_wood wooden_pickaxe + wooden_axe + wooden_sword
|
||||
L3 shelter_basic 4 стены + крыша + кровать в радиусе 8 от спавн-base
|
||||
L4 tools_stone stone_pickaxe + stone_axe + stone_sword
|
||||
L5 armor_basic хотя бы один кусок (predпочтительно нагрудник)
|
||||
L6 food_security ≥16 еды + источник (ферма / стая коров рядом)
|
||||
L7 tools_iron iron_pickaxe + iron_axe + iron_sword
|
||||
L8 armor_iron полный iron set
|
||||
L9 village_seed 2+ постройки, забор/оградка, базовая ферма
|
||||
L10 village_full глобальная цель (ферма + дом + сосед-NPC мечта)
|
||||
```
|
||||
|
||||
На каждом тике reflex выбирает **самую нижнюю неудовлетворённую** нужду.
|
||||
Эта нужда становится **активной**. Curriculum.next() и Pi-coach подсказки
|
||||
дальше выбираются **внутри** активной нужды. Если нужда сменилась
|
||||
(например, HP упало → L0 проснулся), текущий skill прерывается.
|
||||
|
||||
## v0.3.0 release plan (3 rc)
|
||||
|
||||
### rc.1 — Skill registry hardening + Fast advisor scaffold
|
||||
**Цель**: убрать главную проблему v0.2.x — Pi инвентит skill names.
|
||||
Не вводим манифест ещё, но строим инфраструктуру для него.
|
||||
|
||||
- `runtime/skill-registry.js` — exported `listSkillIds()`, `isRegistered(id)`,
|
||||
`skillRegistryPrompt()` (готовый блок текста для LLM-промпта со списком
|
||||
валидных id, по группам)
|
||||
- Все Pi-промпты (`coach/postmortem.js`, `coach/reflect.js`) передают
|
||||
реестр в system prompt
|
||||
- `runtime/coach/advice.js`: `normalisePreferSkill` строго отбрасывает
|
||||
всё, что не в реестре (raise log, не дрейфит на fuzzy)
|
||||
- `runtime/llm/provider.js` — OpenAI-совместимый клиент, конфигурируется
|
||||
через env (`PEPA_FAST_LLM_BASE_URL`, `_API_KEY`, `_MODEL`); graceful
|
||||
fallback "no-op" если env не задан (бот не падает)
|
||||
- `runtime/coach/fast-advisor.js` — функция `advise({snapshot, reason})`
|
||||
с rate-limit (макс. 6 вызовов/час), таймаут 8с, JSON-парсинг ответа
|
||||
через тот же `extractJson()` что у Pi. **Пока не подключаем к reflex** —
|
||||
scaffold + тесты
|
||||
- Тесты для каждого нового модуля + регрессионный тест:
|
||||
`advice.test.js` проверяет что hallucinated `relocate.surface` falls
|
||||
through (никакой override)
|
||||
- Минимум 270+ зелёных тестов
|
||||
|
||||
### rc.2 — Manifesto / Needs ladder + curriculum integration
|
||||
**Цель**: bot acts toward concrete needs, not toward "explore further".
|
||||
|
||||
- `runtime/manifesto/needs.js` — каталог 11 нужд, каждая со схемой:
|
||||
```
|
||||
{ id, level, detect(snapshot) → boolean satisfied, prefer_skill_for_pursuit, ... }
|
||||
```
|
||||
- `runtime/manifesto/state.js` — `pickActiveNeed(snapshot)` возвращает
|
||||
самую нижнюю неудовлетворённую. Кеширует на 5с.
|
||||
- `runtime/reflex.js`:
|
||||
- В `curriculumReflex` сначала `activeNeed = pickActiveNeed(...)`
|
||||
- Skill подбирается в первую очередь по `activeNeed.prefer_skill_for_pursuit`
|
||||
- Fallback на curriculum.next() только если нужда не дала однозначного skill
|
||||
- `runtime/coach/advice.js`: `consult()` теперь принимает `activeNeed` и
|
||||
отбрасывает lessons чьи trigger_situation противоречит текущей нужде
|
||||
(например, "избегай ночью гулять" не применяется когда L0=alive в опасности)
|
||||
- Pi-промпты (postmortem, reflect) получают `currentNeed: "L2 tools_wood"`
|
||||
и просят Pi дать совет именно для этого уровня
|
||||
- Новый персонаж reflex hook: при смене activeNeed бот произносит в чате
|
||||
"пора заняться X" (Russian narration tying into chatter.js)
|
||||
- Тесты: каждая нужда имеет 2-3 теста (detect satisfied/unsatisfied,
|
||||
правильный prefer_skill)
|
||||
|
||||
### rc.3 — Event-driven awareness + skill pre-emption
|
||||
**Цель**: bot reacts within ~100ms to env changes (fall, teleport,
|
||||
damage, hostile spawn near).
|
||||
|
||||
- `runtime/awareness/events.js` — установка listeners:
|
||||
- `bot.on('move')` — детект position-jump >5 блоков за тик → событие
|
||||
`forced-move` → инвалидация current dispatch context
|
||||
- `bot.on('health')` — снижение HP > 2 за тик → reflex.preempt()
|
||||
- `bot.on('entitySpawn')` — враждебный <12 блоков → reflex.preempt()
|
||||
- `bot.on('blockUpdate')` около бота (manhattan <4) → пометка
|
||||
`environment_changed=true`
|
||||
- `runtime/awareness/state.js` — храним flags `(forcedMove, lastDamage,
|
||||
hostileAdded, envChanged)`, expose `consumeFlags()` для reflex
|
||||
- Skill protocol extended: `execute(ctx, args)` теперь получает
|
||||
`ctx.abortSignal` (AbortSignal). Длинные операции (pathfinder.goto,
|
||||
collectBlock loops) проверяют `signal.aborted` между шагами и сразу
|
||||
возвращают `{ok: false, code: 'preempted'}`
|
||||
- `runtime/reflex.js`: при срабатывании preempt-флагов вызывается
|
||||
`currentDispatch?.abort()`, и reflex запускает следующий тик
|
||||
немедленно (не ждёт `DISPATCH_INTERVAL_MS`)
|
||||
- `recovery.tunnel-out`, `survive.pillar-up`, `gather.logs`, `explore.far`
|
||||
адаптируются под AbortSignal (минимальное — `if (signal.aborted)
|
||||
return { ok:false, code:'preempted' }` после каждого `await`)
|
||||
- **Связка с fast advisor**: когда preempt сработал из-за `forcedMove`
|
||||
или environment_changed, и reflex не находит очевидный skill, вызывает
|
||||
`fastAdvisor.advise(...)` чтобы получить тактический совет (rc.1
|
||||
scaffolding активируется здесь)
|
||||
|
||||
### Acceptance signals (после rc.3)
|
||||
|
||||
- В живой БД: `lessons WHERE applied_count > 0` растёт (сейчас 3,
|
||||
должно стать 20+ за сутки)
|
||||
- Bot движется к конкретным целям: видимый прогресс инвентаря (wood →
|
||||
pickaxe → stone → axe), а не "блуждание в одном квадранте"
|
||||
- При forcedMove бот меняет план в течение секунды, не продолжает
|
||||
старый skill
|
||||
- Fast advisor пакетно срабатывает <10 раз/час, каждый раз приводит к
|
||||
смене skill (логируется)
|
||||
|
||||
## Что отложено в v0.3.1
|
||||
|
||||
- **Vision** (multimodal LLM на скриншотах) — требует prismarine-viewer
|
||||
pipeline + multimodal model в провайдере; не в первом релизе
|
||||
- **Vector memory of scenarios** — embeddings от похожих ситуаций
|
||||
- **Auto-curriculum from wiki** — фоновый паук minecraft.wiki
|
||||
|
||||
## Workflow notes
|
||||
|
||||
- Каждый rc — отдельный PR, мержим после approve
|
||||
- main защищён, auto-patch открывает PR с тегом `auto-patch`
|
||||
- Если что-то ломается в проде (живой бот в петле >30 мин), откатываем
|
||||
на v0.2.0-rc.3 commit `865aae1` через `git checkout <commit>` на
|
||||
ветке `revert/v0.3.0-stability`
|
||||
- Тестовые данные строго в `/tmp/pepa-test-state-*` (исправлено в v0.2.0-rc.2)
|
||||
@@ -0,0 +1,86 @@
|
||||
# pepa v0.3.0 — status
|
||||
|
||||
Live tracking document for the v0.3.0 iteration ("Maslow + Awareness").
|
||||
See [`PLAN.md`](./PLAN.md) for the full design.
|
||||
|
||||
## Shipped
|
||||
|
||||
### rc.1 — Live skill registry + Fast advisor scaffold
|
||||
**Root problem solved**: 47/47 Pi-extracted lessons in v0.2.x had
|
||||
`applied_count = 0` because Pi was hallucinating skill ids
|
||||
(`relocate.surface`, `choose.safe.surface`, `survive.shelter`,
|
||||
`gather.visible_log`, …) that don't exist in the registry. Both halves
|
||||
fixed: (a) Pi now sees the real registry in its system prompt,
|
||||
(b) anything that still slips through gets rejected at consult time.
|
||||
|
||||
- [`runtime/skill-registry.js`](../../runtime/skill-registry.js) —
|
||||
single source of truth wrapping `skills/index.js`. Exports:
|
||||
- `listSkillIds()` — live id list
|
||||
- `isRegistered(id)` — bool check
|
||||
- `describeSkill(id)` — id/title/timeoutMs
|
||||
- `skillRegistryPrompt({ limit })` — prompt-ready block grouped by
|
||||
namespace, with "USE ONLY THESE, never invent" instruction
|
||||
- [`runtime/llm/provider.js`](../../runtime/llm/provider.js) —
|
||||
OpenAI-compatible chat client, env-driven:
|
||||
- `PEPA_FAST_LLM_BASE_URL` (default `https://api.openai.com/v1`)
|
||||
- `PEPA_FAST_LLM_API_KEY` (required to enable; safe no-op otherwise)
|
||||
- `PEPA_FAST_LLM_MODEL` (required)
|
||||
- `PEPA_FAST_LLM_TIMEOUT_MS` (default 8000)
|
||||
- Supports JSON-mode via `response_format: { type: "json_object" }`
|
||||
- Surfaces `not_configured`, `no_model`, `http_<status>`,
|
||||
`network_error`, `timeout`, `bad_json` codes
|
||||
- [`runtime/coach/fast-advisor.js`](../../runtime/coach/fast-advisor.js)
|
||||
— tactical "what now?" tier. Scaffold only in rc.1; auto-trigger
|
||||
comes in rc.3.
|
||||
- `advise({snapshot, reason, recentSkillIds, lessonsTail})` →
|
||||
`{action: 'switch_skill'|'continue'|'wait', skillId?, rationale}`
|
||||
- Rejects any returned `skill_id` not in the live registry
|
||||
- Rate-limit: 6 calls/hour, 30s cooldown between calls
|
||||
- System prompt embeds registry; user prompt carries snapshot + trigger
|
||||
- [`runtime/coach/advice.js`](../../runtime/coach/advice.js):
|
||||
- `normalisePreferSkill()` now returns `null` for anything not in
|
||||
registry/mode-map (was: passed through unchanged → dispatcher
|
||||
crashed at `runSkill()`)
|
||||
- Logs `warn` line when a hallucinated prefer_skill is dropped
|
||||
- [`runtime/coach/postmortem.js`](../../runtime/coach/postmortem.js):
|
||||
- Pi prompt includes the live registry block (`skillRegistryPrompt`)
|
||||
with a "CRITICAL: USE ONLY THESE" instruction
|
||||
- On insert, drops `prefer_skill`/`avoid_skill` that's neither a
|
||||
registered id nor a known mode name; warn-logs the count
|
||||
- [`runtime/coach/reflect.js`](../../runtime/coach/reflect.js) — same
|
||||
treatment as postmortem (registry in prompt + write-time filter)
|
||||
|
||||
Tests: 279 green (was 257 on rc.3). Added:
|
||||
- `runtime/skill-registry.test.js` — 5 tests
|
||||
- `runtime/llm/provider.test.js` — 9 tests
|
||||
- `runtime/coach/fast-advisor.test.js` — 10 tests
|
||||
|
||||
### rc.2 — (pending) Manifesto / Needs ladder
|
||||
### rc.3 — (pending) Event-driven awareness + skill pre-emption
|
||||
|
||||
## Next session quick start
|
||||
|
||||
1. **Read PLAN.md** for the full design and per-rc breakdown.
|
||||
2. **Check live DB** to see if Pi-lesson application is improving:
|
||||
```bash
|
||||
sqlite3 state/play.xmatic.team_25565/knowledge.db \
|
||||
"SELECT source, COUNT(*) AS n, SUM(applied_count > 0) AS applied
|
||||
FROM lessons GROUP BY source ORDER BY n DESC;"
|
||||
```
|
||||
After rc.1 deploys, expect Pi-coach/Pi-reflect `applied` count to
|
||||
start growing as the registry feedback closes the loop.
|
||||
3. **Set fast-advisor env when ready to test**:
|
||||
```bash
|
||||
export PEPA_FAST_LLM_BASE_URL="https://<timeweb-endpoint>/v1"
|
||||
export PEPA_FAST_LLM_API_KEY="<key>"
|
||||
export PEPA_FAST_LLM_MODEL="gpt-5-mini"
|
||||
```
|
||||
The advisor still isn't auto-triggered in rc.1 — it's wired in rc.3.
|
||||
4. **Pick the next rc** from PLAN.md.
|
||||
|
||||
## Workflow notes
|
||||
|
||||
- main is protected — only operator merges PRs
|
||||
- Tests: `npm test` (279 green at last check), isolated under `/tmp/`
|
||||
- The bot supervisor hot-restarts on file changes in `runtime/**/*.js`
|
||||
- If something regresses badly, revert to v0.2.0-rc.3 commit `865aae1`
|
||||
Reference in New Issue
Block a user