DESIGN: grounded loop + merit-based autonomy; reprioritize TODO (7 steps)

- DESIGN.md: три контура (сделки / заземление / методы), модель данных,
  правила нормализации метрик, тиры автономии по заслугам, энергия от результата
- Судья = метрики разного типа: охват, позиции, вовлечённость, конверсии,
  продажи, деньги, стоимость, скорость, качество (ручной вердикт)
- Автономия по областям (seo/ads_budget/content_publish/client_comms/...),
  +1 тир за 3 успеха, -1 за провал, затухание 90 дней, границы persona непреодолимы
- P7 сужен до тонкого Channel (console/telegram/file); harness-реестр отложен
- TODO.md переписан под 7 шагов с критериями приёмки
This commit is contained in:
n1kt4r committed 2026-09-23 14:37:17 +05:00
1 parent 288d05825f
commit 436dd5fa31
3 files changed
+298 -56

No files matched your search

+226
View File
@@ -0,0 +1,226 @@
# spark-mind — DESIGN: заземлённый контур и автономия по заслугам
Обновление плана от 2026-09-23. Заменяет «шесть пунктов P1–P6» на три контура
плюс механизм автономии. Источник идей — разбор iLands (см. `ilands-razbor.txt`
в workspace), но реализация — своя.
## 1. Тезис
iLands для «судьи без автора» строит общество на 80 000 агентов. Нам это не нужно:
у нас есть **мир, который отвечает сам** — Метрика, GSC, Директ, Вордстат,
реакция клиентов. Это последствия, которые не сбрасываются и которых никто не
авторит.
Отсюда два принципа, которых нет у iLands в рабочем виде:
1. **Заземление.** Действие агента оценивается не его же рассуждением,
а замером метрики в реальном мире до/после, с отложенным вердиктом.
2. **Автономия по заслугам.** Агент получает права не сразу, а по подтверждённому
трек-рекорду. Провал — права сужаются. Границы из persona `values` непреодолимы
всегда, независимо от уровня.
## 2. Три контура
```
A. КОНТУР СДЕЛОК «кому можно верить»
counterparties → deals → deal_events
B. КОНТУР ЗАЗЕМЛЕНИЯ «стало ли лучше на самом деле» ← ядро уникальности
outcomes (метрики до/после) → capabilities (автономия) → ledger (энергия)
C. КОНТУР МЕТОДОВ «как делать хорошо»
skills (методы, retrieval по эмбеддингу) + reflection (ночная консолидация)
```
Плюс сквозная микро-правка P5 (`noAction`) и тонкий `Channel` (бывший P7).
## 3. Модель данных (SQLite, тот же файл БД)
### A. Контур сделок
```sql
CREATE TABLE counterparties (
id TEXT PRIMARY KEY,
kind TEXT NOT NULL CHECK(kind IN ('client','contact','platform','external')),
name TEXT NOT NULL,
notes TEXT,
trust_score REAL NOT NULL DEFAULT 0, -- derived, не задаётся руками
created_at INTEGER NOT NULL
);
CREATE TABLE deals (
id TEXT PRIMARY KEY,
counterparty_id TEXT NOT NULL REFERENCES counterparties(id),
task TEXT NOT NULL,
price REAL,
currency TEXT DEFAULT 'RUB',
status TEXT NOT NULL CHECK(status IN
('offered','accepted','delivered','paid','refused','defaulted')),
created_at INTEGER NOT NULL,
closed_at INTEGER
);
-- append-only: нельзя переписать задним числом
CREATE TABLE deal_events (
id TEXT PRIMARY KEY,
deal_id TEXT NOT NULL REFERENCES deals(id),
type TEXT NOT NULL, -- offered|accepted|delivered|paid|refused|note
payload_json TEXT,
created_at INTEGER NOT NULL
);
```
### B. Контур заземления
```sql
-- Метрики РАЗНОГО ТИПА. Один outcome = одно действие + один замер.
CREATE TABLE outcomes (
id TEXT PRIMARY KEY,
deal_id TEXT REFERENCES deals(id), -- nullable: действие без сделки
domain TEXT NOT NULL, -- seo|ads_budget|content_publish|client_comms|research|code
metric_kind TEXT NOT NULL CHECK(metric_kind IN
('reach','position','engagement','conversion','sale','revenue','cost','speed','quality')),
metric_name TEXT NOT NULL, -- 'ctr', 'avg_position', 'cpa', 'leads', 'revenue_rub'...
unit TEXT, -- '%','руб','шт','сек','место'
direction TEXT NOT NULL CHECK(direction IN ('up_good','down_good')),
value_before REAL,
value_after REAL,
source TEXT NOT NULL CHECK(source IN ('metrika','gsc','direct','wordstat','manual','client')),
horizon_days INTEGER NOT NULL, -- через сколько замеряем
due_at INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'pending'
CHECK(status IN ('pending','measured','missed','unavailable')),
measured_at INTEGER,
score REAL -- нормализованный вердикт [-1..+1]
);
-- Автономия по заслугам: уровень НА КАЖДУЮ ОБЛАСТЬ, не общий.
CREATE TABLE capabilities (
domain TEXT PRIMARY KEY,
tier INTEGER NOT NULL DEFAULT 0 CHECK(tier BETWEEN 0 AND 4),
positive_outcomes INTEGER NOT NULL DEFAULT 0,
negative_outcomes INTEGER NOT NULL DEFAULT 0,
updated_at INTEGER NOT NULL
);
-- Энергия (бывший P4): не деньги, а единица активности.
CREATE TABLE ledger (
id TEXT PRIMARY KEY,
ts INTEGER NOT NULL,
delta REAL NOT NULL,
reason TEXT NOT NULL,
balance_after REAL NOT NULL
);
```
### C. Контур методов
```sql
CREATE TABLE skills (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
method_md TEXT NOT NULL,
source TEXT NOT NULL CHECK(source IN ('builtin','learned')),
validated INTEGER NOT NULL DEFAULT 0,
uses INTEGER NOT NULL DEFAULT 0,
success_rate REAL,
embedding BLOB -- bge-m3, dim=1024
);
```
## 4. Правила
### 4.1 Нормализация метрики в score
Один вердикт на outcome, диапазон `[-1..+1]`:
```
raw = (value_after - value_before) / max(|value_before|, eps)
score = clamp(raw * (direction == 'up_good' ? 1 : -1), -1, 1)
```
Для `quality` (ручной вердикт клиента) — дискретно: `+1 / 0 / -1`.
Для `speed` — `direction = 'down_good'` (быстрее = лучше).
Если `status != 'measured'` → score не участвует в трек-рекорде.
### 4.2 Автономия по заслугам
Уровни (по области, отдельно для каждой):
| tier | Имя | Что разрешено |
|------|-----|----------------|
| 0 | observer | только думает и пишет в память, внешних действий нет |
| 1 | suggest | предлагает действия человеку, сам не делает |
| 2 | ask | делает после подтверждения (кроме своих каналов) |
| 3 | act+report | делает и отчитывается; человек может откатить |
| 4 | autonomous | делает без подтверждения в пределах границ persona |
Переходы:
- `+1 тир` за **3 нетто-положительных** outcome в области (score > 0).
- `-1 тир` за **1 нетто-отрицательный** (score < 0) — асимметрия: падать легче.
- Затухание: outcome старше 90 дней весит 0.5.
- Новая область всегда стартует с tier 0.
- **Границы persona `values` непреодолимы на любом тире.** Тир расширяет
свободу внутри границ, но не снаружи.
### 4.3 Энергия
- Расход: каждый тик, каждое внешнее действие, каждый вызов саб-агента.
- Приход: **подтверждённые положительные outcomes** (особенно `revenue`), плюс
донат человека.
- Низкий баланс → реже просыпается (интервал растёт), затем Deep Rest.
- Ключевое отличие от iLands: энергия восполняется **результатом**, а не покупкой.
Дефицит мотивирует делать полезное, а не имитировать активность.
## 5. Триггеры heartbeat (добавляются к существующим)
| Триггер | Что делает |
|---------|------------|
| `{type:'measure'}` | наступил `due_at` → тянет метрику из источника → пишет `value_after`, `score` |
| `{type:'reflection'}` | ночной цикл: обобщает deals + outcomes + thoughts → в long-term и `self_observations` |
| `{type:'task'}` | **наконец используется** — cron-планировщик (нужен для measure/reflection) |
Плюс в `decision` появляется `noAction: true` (P5) — агент может решить,
что действие не требуется, и не писать thought.
## 6. Тонкий Channel (бывший P7)
Не строим plugin-реестр рантаймов. Только:
```ts
interface Channel { send(text: string): Promise<void>; }
```
Реализации: `ConsoleChannel`, `TelegramChannel`, `FileChannel` (лог для заземления).
`LLMClient` уже есть — движок меняли Spark→Ornith без переписывания Mind.
Полный harness-реестр откладываем: абстракция окупается с ≥2 реализациями, у мира
пока одна. `Channel` станет дверью к BYOA, если решишь подключаться к чужим мирам.
## 7. Порядок внедрения
| Шаг | Что | Критерий приёмки |
|-----|-----|------------------|
| 0 | P5 `noAction` + `Channel` (console/telegram/file) | агент умеет не действовать; вывод через интерфейс |
| 1 | Планировщик (`task` + `measure`): проверка due-записей в SQLite | задача с датой выполняется в срок |
| 2 | Контур A: counterparties + deals + deal_events | сделка проходит offered→paid, события в журнале |
| 3 | Контур B: outcomes + `measure` | метрика замеряется через horizon, score пишется |
| 4 | Контур B: capabilities (автономия по заслугам) | тир растёт после 3 успехов, падает после провала |
| 5 | Контур C: reflection | ночью опыт уходит в self_observations, мёртвый слой оживает |
| 6 | Контур C: skills + retrieval по bge-m3 | нужный метод подгружается по задаче, не всё сразу |
| 7 | Контур B: ledger (энергия) | результат пополняет энергию, дефицит замедляет тики |
Зависимости: 3 требует 1 (замер по расписанию) и 2 (deal_id). 4 требует 3.
7 требует 3. Остальное независимо.
## 8. Что НЕ делаем
- **Соцсеть/ленту/подписки** внутри агента — бессмысленно без массы участников.
- **Токены как валюту** — Тамагочи-монетизация, не наша польза.
- **Полный harness-реестр** (Codex/Claude Code/OpenClaw) — оверинжиниринг для соло.
- **«Суперразум из общества»** — нарратив без измерений.
## 9. Открытые вопросы
- Источник метрик по умолчанию для каждой области (Метрика/GSC/Директ — какие API уже есть).
- Кто ставит `horizon_days` — агент или человек (по умолчанию предлагаю агент, человек правит).
- Как считать `quality`-вердикт: явный ответ клиента или оценка Ника вручную.
+6 -6
View File
@@ -94,13 +94,13 @@ const telegram = new TelegramMessenger({
## Roadmap (P6+) ## Roadmap (P6+)
Полный список незакрытых задач — в [TODO.md](./TODO.md). - **План** — [DESIGN.md](./DESIGN.md): заземлённый контур + автономия по заслугам
- **Чек-лист** — [TODO.md](./TODO.md)
Кратко: Суть программы: агент оценивается не своими же рассуждениями, а замером метрик
- P6: sleep & energy, cron-планировщик, рабочие слои памяти (notepads / core self) в цикле mind, tool-runtime для под-агентов реального мира (охват, конверсии, продажи, деньги, скорость) до/после действия,
- P7: Web UI (persona setup), конфиг/.env, retry+timeout, тесты с отложенным вердиктом. Права (автономия) выдаются по подтверждённому трек-рекорду,
- Каналы: Telegram — обход SSL-проблемы bot.qw4.ru а не сразу; границы persona непреодолимы на любом уровне.
- Движок: Spark X2.5-4B (unknown architecture в Ollama)
## License ## License
+66 -50
View File
@@ -1,63 +1,79 @@
# spark-mind — Оставшиеся TODO # spark-mind — Оставшиеся TODO
Статус: **P0–P5 завершены** (heartbeat→mind, JSON output, embeddings, sub-agents, Полный план — в [DESIGN.md](./DESIGN.md) (заземлённый контур + автономия по заслугам).
SQLite, Telegram). Ниже — реально незакрытые пункты, проверенные по коду Здесь — рабочий чек-лист. Порядок обязателен: зависимости между шагами.
(2026-09-15). Порядок — по приоритету.
## P6 — Функциональная полнота Статус: **P0–P5 базовые завершены** (heartbeat→mind, JSON output, bge-m3
embeddings, sub-agents, SQLite, Telegram+fallback). Далее — новая программа.
- [ ] **Sleep & energy (циркадный ритм).** Сейчас heartbeat тикает всегда ## Шаг 0 — Основа (дёшево, чистит память)
с фиксированным интервалом 5 мин. Нужно: окна активности/сна, накопление - [ ] P5: `decision.noAction: true` — агент может решить, что действие не нужно,
«энергии», снижение частоты тиков в пассивные периоды. и не писать thought. Сейчас heartbeat всегда «работает» → шум в памяти.
- [ ] **Планировщик задач (cron).** Тип триггера `{ type: 'task' }` объявлен - [ ] Тонкий `Channel`-интерфейс: `ConsoleChannel`, `TelegramChannel`, `FileChannel`.
в `heartbeat.ts`, но **ничто его не эмитит** — планировщика нет `sendReply` сейчас жёстко зашит на Telegram+console.
(`scheduleNext` — это idle-таймер, не cron). Нужен движок отложенных задач.
- [ ] **Рабочая память (notepads) в цикле mind.** Таблица `notepads` и методы
`updateNotepad()`/`getNotepad()` реализованы, но **вне memory.ts не
вызываются** — слой не участвует в `handleTick`.
- [ ] **Core self (self_observations) в цикле mind.** Аналогично: `addSelfObservation()` /
`getAllSelfObservations()` реализованы, но из mind не вызываются.
Самосознательность не накапливается автоматически.
- [ ] **Реальное исполнение инструментов под-агентов.** `subagent.ts` объявляет
список tools (web_search, terminal, file_write…), но это **строки-декларации** —
исполнения нет. Нужен tool-runtime для под-агентов.
## P7 — Инфраструктура ## Шаг 1 — Планировщик (нужен для measure/reflection)
- [ ] Cron/otlozhennye задачи: триггер `{type:'task'}` объявлен, но никто не эмитит.
- [ ] Проверка due-записей в SQLite → эмит `{type:'measure'}`.
- [ ] Ночной `{type:'reflection'}`.
- Приёмка: задача с датой выполняется в срок.
- [ ] **Web UI (persona setup).** Настройка 8 слоёв persona через интерфейс ## Шаг 2 — Контур A: сделки и контрагенты
вместо `DEFAULT_PERSONA` в коде. - [ ] Таблицы `counterparties`, `deals`, `deal_events` (append-only).
- [ ] **Конфиг / .env.** URL-ы Ollama (`localhost:11434`), dbPath, Telegram - [ ] `trust_score` выводится из истории, не задаётся руками.
захардкожены в `index.ts`. Вынести в конфиг/`.env`. - [ ] Mind умеет открывать/двигать сделку (offered→accepted→delivered→paid|refused|defaulted).
- [ ] **Retry + timeout для LLM-запросов.** `fetch` в `mind.ts` и `embedding.ts` - Приёмка: сделка проходит полный цикл, все переходы в журнале.
без retry и без `AbortController` — один сетевой сбой роняет тик.
- [ ] **Тесты.** Их нет вообще. Минимум: unit на `parseSparkOutput` (nested JSON, ## Шаг 3 — Контур B: outcomes (ядро уникальности)
fences, fallback), на `Database` CRUD, на cosine-поиск. - [ ] Таблица `outcomes`: метрики разного типа (reach, position, engagement,
conversion, sale, revenue, cost, speed, quality), `direction`, `horizon_days`, `due_at`.
- [ ] Замер через `{type:'measure'}`: тянем метрику из источника (Метрика/GSC/Директ/Вордстат/manual).
- [ ] Нормализация в `score` [-1..+1] (формула в DESIGN.md §4.1).
- Приёмка: метрика замеряется через horizon, score пишется.
## Шаг 4 — Контур B: автономия по заслугам
- [ ] Таблица `capabilities` (уровень НА ОБЛАСТЬ: seo, ads_budget, content_publish,
client_comms, research, code).
- [ ] Тиры 0..4 (observer → suggest → ask → act+report → autonomous).
- [ ] +1 тир за 3 нетто-положительных; −1 за 1 отрицательный; затухание 90 дней.
- [ ] Границы persona `values` непреодолимы на любом тире.
- Приёмка: тир растёт после 3 успехов и падает после провала.
## Шаг 5 — Контур C: reflection
- [ ] Ночной цикл: обобщает deals + outcomes + thoughts → long-term.
- [ ] Пишет в `self_observations` — **сейчас мёртвый слой**, оживляется здесь.
- [ ] Дешёвая модель (не основная).
## Шаг 6 — Контур C: skills registry
- [ ] Таблица `skills` (name, method_md, source, validated, uses, success_rate, embedding).
- [ ] Retrieval нужного метода по эмбеддингу bge-m3 (не грузить всё сразу).
- [ ] Заменяет 4 захардкоженные роли sub-agents.
- [ ] Tool-runtime для под-агентов: tools сейчас — только строки-декларации.
## Шаг 7 — Контур B: энергия (бывший P6 sleep & energy)
- [ ] Таблица `ledger`.
- [ ] Расход: тик, внешнее действие, саб-агент. Приход: положительные outcomes + донат.
- [ ] Низкий баланс → интервал тиков растёт → Deep Rest.
- Приёмка: результат пополняет энергию, дефицит замедляет тики.
## Инфраструктура (по мере надобности)
- [ ] Конфиг / `.env`: URL-ы Ollama, dbPath, Telegram захардкожены в `index.ts`.
- [ ] Retry + timeout (AbortController) для LLM/embedding fetch — один сбой роняет тик.
- [ ] Тесты: `parseSparkOutput`, `Database` CRUD, cosine-поиск, нормализация score.
- [ ] Web UI (persona setup) вместо `DEFAULT_PERSONA` в коде.
## Каналы вывода ## Каналы вывода
- [ ] Telegram: обход SSL-проблемы bot.qw4.ru (`tlsv1 unrecognized name`, серверная).
- [ ] **Telegram: обход SSL-проблемы bot.qw4.ru.** Сервер отдаёт Сейчас console-only fallback. Варианты: починить SNI на qw4.ru, локальный
`tlsv1 unrecognized name` (SSL alert 112) при TLS-handshake — проблема MAX-релей, или Telegram Bot API напрямую.
на стороне сервера, не клиента (проверено curl-ом и Node https).
Агент сейчас работает в console-only fallback. Варианты: починить
SNI/сертификат на qw4.ru, поднять локальный MAX-релей, или другой канал
(Telegram Bot API напрямую).
## Движок ## Движок
- [ ] Spark X2.5-4B: не грузится в Ollama (`unknown architecture 'spark2_5'`).
- [ ] **Spark X2.5-4B.** Не грузится в текущей Ollama: Варианты: совместимый GGUF / llama.cpp напрямую / остаться на Ornith-1.5-9B.
`error loading model: unknown model architecture: 'spark2_5'`.
Варианты: собрать совместимый GGUF, поднять через llama.cpp напрямую
(OpenAI-сервер на :8080), или оставить Ornith-1.5-9B как основной движок.
**Важно:** локальные модели пумолкают (пустой ответ), если `subAgent` **Важно:** локальные модели пумолкают (пустой ответ), если `subAgent`
присутствует в JSON-схеме structured output — учитывать при правках промпта. в JSON-схеме structured output.
## Косметика ## Косметика
- [ ] Устаревший doc-комментарий в `mind.ts` (класс `SparkLLMClient` описан как
- [ ] Устаревший doc-комментарий в `mind.ts`: класс `SparkLLMClient` всё ещё «клиент для Spark X2.5-4B, в MVP заглушка»).
описан как «Реальный LLM-клиент для Spark X2.5-4B. В MVP — заглушка», - [ ] Дефолт модели в конструкторе `SparkLLMClient` = `'spark-x2.5-4b'` (вводит в заблуждение).
хотя работает с Ornith-9B и без заглушки.
- [ ] Дефолтное имя модели в конструкторе `SparkLLMClient` = `'spark-x2.5-4b'`
(index.ts переопределяет на `ornith-1.5-9b:latest`, но дефолт вводит в заблуждение).
---
Проверено `grep`-ом по коду: dead-code слои, отсутствие cron/retry/tests/.env, SSL-ошибка Telegram.