342 lines
16 KiB
Markdown
Executable File
342 lines
16 KiB
Markdown
Executable File
# Балда → Callee + Prism: делегирование
|
|
|
|
> Обновлён 2026-09-29. Статус: **C0 закрыта, C1-C5 не начаты.**
|
|
> Часть [[personal/projects/balda/balda-mm-delegation-plan]].
|
|
|
|
---
|
|
|
|
## 1. Целевая архитектура
|
|
|
|
```
|
|
Mattermost (Alex)
|
|
│ «добавь CSV-экспорт в отчёт»
|
|
▼
|
|
Балда-оркестратор (Kraken, DeepSeek, balda-mm:0.1.13)
|
|
│ приём сообщения, чтение Obsidian, контекст, ответ в чат
|
|
│ MCP-инструмент executor_run
|
|
▼
|
|
MCP-сервер balda-executor-mcp (Kraken, :8890, сеть mattermost_mm-net)
|
|
│ HTTP + X-Bridge-Secret
|
|
▼
|
|
Мост agents-bridge (Mac, :8790, launchd com.agents.bridge)
|
|
│ запускает `callee agent run <resource> --message <prompt>`
|
|
▼
|
|
callee CLI (контейнер balda-executor, Mac)
|
|
│ компилирует граф → валидирует ЦЕЛИКОМ до запуска
|
|
│ общий state-объект через прогон → ОДИН артефакт в stdout
|
|
├→ Role / DynamicRole → ACP-процесс → DeepSeek (generic_acp)
|
|
├→ Script → локальные детерминированные проверки
|
|
└→ Human → точка одобрения
|
|
▼
|
|
Beads (`bd`) — durable-состояние Story/Epic, переживает сессии
|
|
▼
|
|
артефакт → Балда → Mattermost
|
|
```
|
|
|
|
### Разделение ролей
|
|
|
|
| Слой | Владелец |
|
|
|---|---|
|
|
| Граф воркфлоу, валидация, state | **callee** |
|
|
| Durable-состояние + одобрение | **coding agent** |
|
|
| Исполнение модели | ACP-процесс |
|
|
|
|
### Разделение расходов
|
|
|
|
| Роль | Где | Провайдер |
|
|
|---|---|---|
|
|
| Оркестратор верхнего уровня | Kraken | DeepSeek `deepseek-chat` |
|
|
| Исполнитель-роли callee | Mac, контейнер | DeepSeek |
|
|
|
|
📌 «DeepSeek с обоих концов» = обе стороны на DeepSeek. Подписочный Codex подключается
|
|
позже заменой `State.provider` в `DynamicRole` — без правки графа.
|
|
|
|
---
|
|
|
|
## 2. Провайдер DeepSeek для callee
|
|
|
|
### 2.1 ACP-адаптер — выбран `@openma/deepseek-harness-acp@0.4.33`
|
|
|
|
Свой адаптер писать не надо.
|
|
|
|
| | Значение |
|
|
|---|---|
|
|
| Пакет | `@openma/deepseek-harness-acp` |
|
|
| Версия | **`0.4.33`** |
|
|
| `bin` | **`dsh-acp`** → `dist/bin.js` — запускаемый ACP-сервер |
|
|
| Родословная | обёртка вокруг официального DeepSeek Harness |
|
|
| Node | **`>= 22.15`** (в контейнере 24.21.0 ✅) |
|
|
| Лицензия | Apache-2.0 |
|
|
| Зависимости | `@agentclientprotocol/sdk 1.4.0`, `tar`, `zod` |
|
|
|
|
Установка (standalone-вариант, подходит для `generic_acp`):
|
|
|
|
```bash
|
|
npm i -g @openma/deepseek-harness-acp
|
|
# запуск: dsh-acp
|
|
```
|
|
|
|
### 2.2 Креды
|
|
|
|
Приоритет: **флаги → переменные окружения → дефолты**.
|
|
|
|
| Способ | Значение |
|
|
|---|---|
|
|
| Флаг | `api-key`, либо `api-key:<provider>` при нескольких провайдерах |
|
|
| Env | **`DEEPSEEK_API_KEY`** |
|
|
| Env | **`DEEPSEEK_BASE_URL`** — OpenAI-совместимый endpoint override |
|
|
|
|
🔴 `DEEPSEEK_BASE_URL` — это OpenAI-совместимый override, значит переезд на Colex
|
|
сводится к смене одной переменной, без правки графа.
|
|
|
|
📌 Ключ `DEEPSEEK_API_KEY` лежит в `~/.hermes/hermes-whale/.env` (длина 35).
|
|
|
|
📌 Креды не попадают в конфиг клиента: адаптер переиспользует ключ из credential store
|
|
(`dsh web` / `dsh-acp login`), либо берёт `DEEPSEEK_API_KEY` из окружения процесса.
|
|
|
|
### 2.3 Конфигурация Role
|
|
|
|
Тип провайдера — **`generic_acp`**, потому что список поддерживаемых типов закрытый:
|
|
`codex`, `claude`, `opencode`, `copilot`, `grok`, `cursor`, `generic_acp`.
|
|
DeepSeek в нём отсутствует.
|
|
|
|
```yaml
|
|
provider:
|
|
type: generic_acp
|
|
cmd: dsh-acp # одно имя исполняемого файла, НЕ shell-строка
|
|
model: deepseek-chat
|
|
timeout: 20m
|
|
```
|
|
|
|
| Поле | Контракт |
|
|
|---|---|
|
|
| `cmd` | одно имя исполняемого файла; для `generic_acp` обязателен |
|
|
| `extraArgs` | упорядоченный argv-список; каждый элемент непустой |
|
|
| `model` / `reasoning` / `mode` | опциональны; пустые → дефолт бэкенда |
|
|
| `timeout` | Go duration, **по умолчанию `15m`** |
|
|
| Конфиг провайдера | обязан быть **вложенным** (`spec.provider.*`), плоские поля не поддержаны |
|
|
|
|
### 2.4 DynamicRole — провайдер из состояния
|
|
|
|
Все поля провайдера — шаблоны, рендерятся из состояния в момент визита:
|
|
|
|
```yaml
|
|
kind: DynamicRole
|
|
spec:
|
|
provider:
|
|
type: '{{ .State.provider }}'
|
|
model: '{{ .State.model }}'
|
|
timeout: '{{ .State.timeout }}'
|
|
state:
|
|
provider: generic_acp
|
|
model: deepseek-chat
|
|
timeout: 20m
|
|
```
|
|
|
|
| Факт | Значение |
|
|
|---|---|
|
|
| Что рендерится | `type`, `cmd`, `model`, `reasoning`, `mode`, `timeout`, каждый `extraArgs` |
|
|
| Из чего | `.Prompt`, текущий `.Input`, один согласованный post-node снапшот `.State`, `.Params` |
|
|
| Чего нельзя | `.Output`, env, файловая система, сеть, часы, рандом, криптография, мутаторы |
|
|
| Когда валидируется | до поиска и старта процесса; ошибка → визит падает, провайдер не стартует |
|
|
| Периметр визита | конфиг фиксирован на весь визит, включая REPL-ходы; новый визит Loop рендерит заново |
|
|
| Переиспользование процесса | одинаковые effective type+cmd → один процесс; сессия всегда новая |
|
|
|
|
🔴 **Безопасность:** state-driven `cmd` и `extraArgs` могут выбирать исполняемый код.
|
|
Не подавать туда вывод модели без ограничения через авторскую шаблонную логику.
|
|
|
|
---
|
|
|
|
## 3. Prism — таск-трекинг
|
|
|
|
### 3.1 Callee и prism — независимые установки
|
|
|
|
Установка одного плагина не ставит другой.
|
|
|
|
| | `baldaworks/prism` | `baldaworks/prism-callee` |
|
|
|---|---|---|
|
|
| Что это | плагин в coding agent (Codex/Claude/Grok/Copilot/Cursor) | callee-агенты для тех же воркфлоу |
|
|
| Требования | coding agent + `bd` (Beads) | coding agent + `bd` + callee ≥ 0.19.0 |
|
|
| Команды | `$prism:story`, `$prism:epic`, `$prism:lifecycle` | `$prism-callee:lifecycle` |
|
|
| Состояние | Beads (`bd`) | Beads (`bd`) |
|
|
|
|
Импорт callee-агентов:
|
|
|
|
```bash
|
|
callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix prism
|
|
```
|
|
|
|
🔴 **Проверка импорта:** `callee agent view prism/lifecycle --json` → **kind `Router`**.
|
|
`Sequential` = устаревший импорт, обновлять `--force`.
|
|
|
|
### 3.2 Жизненный цикл
|
|
|
|
**Story:** Specify → Design → Breakdown → **Human approval** → Apply → Verify.
|
|
Фазы: `phase:story:specify` → `design` → `breakdown` → `human` → `apply` → `verify`.
|
|
|
|
**Epic:** Frame → Architecture → Roadmap → **approval** → Deliver Stories → Validation.
|
|
|
|
| Правило | Значение |
|
|
|---|---|
|
|
| Дети Epic | только Stories |
|
|
| Дети Story | только Tasks |
|
|
| Вложенные Epic / Task напрямую в Epic | невалидно |
|
|
| Фиксированного диапазона числа детей | нет — оценка качественная |
|
|
| Apply | каждый переход требует явной авторизации человека |
|
|
| Одобрение Epic → Story | не переносится |
|
|
|
|
---
|
|
|
|
## 4. Одобрения в Mattermost — работают
|
|
|
|
Одобрение проходит **ответом в треде**, как в Slack.
|
|
|
|
| Механизм | Статус |
|
|
|---|---|
|
|
| Вопрос с опциями (markdown-нумерация) | ✅ `questionfmt` + `questioncmd` + `deliveryfmt`, покрыто тестами |
|
|
| Приём ответа реплаем | ✅ `BuildInboundReply` через `RootID` (`channel/mattermost/inbound.go`) |
|
|
| `ask` как режим разрешений | ✅ `mattermost` в списке каналов (`permissions/service.go`) |
|
|
|
|
📌 `ask` — это режим политики разрешений агента; механизм вопросов — отдельный слой.
|
|
Оба в MM работают.
|
|
|
|
⛔ **Инлайн-кнопок и реакций в MM нет** — только Telegram. Событие `reaction`
|
|
сознательно отбрасывается в `channel/mattermost/ingress.go`.
|
|
|
|
---
|
|
|
|
## 5. Фазы
|
|
|
|
### C0 — ✅ ЗАКРЫТА 2026-09-29
|
|
|
|
Разведка npm: готовый ACP-адаптер к DeepSeek существует (§2.1). Свой адаптер не нужен.
|
|
|
|
**Метод поиска:** прямые URL `registry.npmjs.org/<scope>%2f<name>` и
|
|
`/-/v1/search?text=...`. ⛔ Поиск по `scope:normahq` и `scope:baldaworks` даёт пусто —
|
|
scoped-пакеты так не индексируются.
|
|
|
|
### C1 — установка в контейнер `balda-executor` (1-2 ч)
|
|
|
|
Ни `callee`, ни `bd` нет ни в контейнере, ни на хосте.
|
|
|
|
| # | Шаг | Проверка |
|
|
|---|---|---|
|
|
| 1 | `npm i -g @baldaworks/callee` | `callee --version` |
|
|
| 2 | `npm i -g @openma/deepseek-harness-acp` | `dsh-acp --help` |
|
|
| 3 | `callee setup codex` → стартовые ресурсы | `callee agent list` содержит `workflows/investigate` |
|
|
| 4 | Установить Beads `bd` | `bd where` |
|
|
| 5 | Импорт prism-пака | `callee agent view prism/lifecycle --json` → `Router` |
|
|
| 6 | Прогон стартового `investigate` | артефакт в stdout, exit 0 |
|
|
|
|
📌 Персистентность: установки живут в слое контейнера. Чтобы переживали пересборку —
|
|
том или `Dockerfile`.
|
|
|
|
### C2 — Role к DeepSeek (1 ч)
|
|
|
|
Описать `Role` по §2.3 и проверить один визит вживую.
|
|
|
|
**Гейт:** в логе видны `role_model` и `role_reasoning`. До успешного рендера провайдера
|
|
эти поля опускаются — их появление означает, что рендер прошёл.
|
|
|
|
### C3 — подключение к мосту (2-3 ч)
|
|
|
|
1. Бэкап `~/Developer/agents-bridge/executor.py`.
|
|
2. Замена argv-таблицы `PROVIDERS`:
|
|
|
|
```python
|
|
PROVIDERS = {
|
|
"callee": ["callee", "agent", "run", "prism/lifecycle", "--message"],
|
|
}
|
|
```
|
|
|
|
3. `EXECUTOR_PROVIDER=callee` в `~/Docker/balda-executor/.env`.
|
|
4. Перезапуск моста: `launchctl bootout` + `bootstrap`.
|
|
5. Прогон задачи через мост, проверка `exit_code`.
|
|
|
|
### C4 — таск-трекинг (1-2 ч)
|
|
|
|
1. `bd` инициализируется в `/agents`.
|
|
2. Прогон `prism:story` на реальной задаче.
|
|
3. Проверить durable: прервать сессию, возобновить Story — состояние в Beads на месте.
|
|
|
|
### C5 — сквозная приёмка (1 ч)
|
|
|
|
От **учётки Alex**, не подставным аккаунтом:
|
|
|
|
```
|
|
@balda добавь CSV-экспорт в отчёт
|
|
→ Specify → Design → Breakdown → Human approval → Apply → Verify
|
|
→ ответ в Mattermost + артефакт
|
|
```
|
|
|
|
Доказательство — лента `qentra:balda-test`, автор поста `cbwsorzwoigbtrdnteugnx4aqo`.
|
|
|
|
---
|
|
|
|
## 6. Решения
|
|
|
|
| # | Вопрос | Решение | Почему |
|
|
|---|---|---|---|
|
|
| 1 | Провайдер обеих сторон | DeepSeek | команда Alex |
|
|
| 2 | Оркестрация | callee | документированная архитектура вместо самодельной |
|
|
| 3 | Таск-трекинг | prism + Beads | задание Alex |
|
|
| 4 | Host coding agent | callee CLI в контейнере `balda-executor` | Node 24 — родная среда; там смонтирован `~/agents`; мост уже умеет `docker exec` |
|
|
| 5 | Мост `:8790` | сохранить, сменить argv | идемпотентность, таймауты, лимит 1 задача уже проверены; выбрасывать работающий слой ради эстетики незачем |
|
|
| 6 | Тип провайдера | `generic_acp` | список типов закрытый, DeepSeek в нём нет |
|
|
|
|
---
|
|
|
|
## 7. Питфоллы
|
|
|
|
1. ⚠️ **`PROVIDERS` в мосте — argv-список.** Промпт должен остаться **одним**
|
|
последним элементом, иначе передача рвётся.
|
|
2. ⚠️ **`.env` не перебивает plist.** `--host` жёстко в `ProgramArguments`; смена
|
|
аргументов требует `bootout` + `bootstrap`. Проверка:
|
|
`lsof -nP -iTCP:8790 -sTCP:LISTEN` → должно быть `*:8790`.
|
|
3. ⚠️ **`prism/lifecycle` должен быть `Router`** — `Sequential` означает устаревший
|
|
импорт, лечится `--force`.
|
|
4. ⚠️ **Provider `cmd` — одно имя файла, не shell-строка.** Аргументы — только
|
|
в `extraArgs`.
|
|
5. ⚠️ **Никогда не маршрутизировать вывод модели в `cmd`/`extraArgs`.**
|
|
6. ⚠️ **Установки в контейнере не переживают пересборку** — том или `Dockerfile`.
|
|
7. ⚠️ **`docker-compose` (дефис) на Mac, `docker compose` (пробел) на Kraken.**
|
|
8. ⚠️ **Node `>= 22.15`** требуется адаптером (в образе 24.21.0).
|
|
|
|
---
|
|
|
|
## 8. Риски
|
|
|
|
| Риск | Оценка | Митигация |
|
|
|---|---|---|
|
|
| Импорт `prism/*` устаревает при обновлении | средний | проверять `kind: Router` после каждого импорта |
|
|
| callee обновляется и меняет контракт | средний | пиннить версию в ресурсах |
|
|
| Мост теряет работоспособность после смены argv | низкий | бэкап `executor.py`, откат одной правкой |
|
|
| Стоимость DeepSeek растёт | низкий | исполнитель-роли — короткие визиты |
|
|
| Установка в контейнер не переживёт пересборку | низкий | том или `Dockerfile` |
|
|
|
|
---
|
|
|
|
## 9. Порядок и запреты
|
|
|
|
**Порядок:** C1 → C2 → C3 → C4 → C5.
|
|
|
|
**НЕ трогать в `~/Developer/balda`:** `go.mod`, `Dockerfile.release`, `go.sum`,
|
|
`internal/apps/balda/app.go`, `internal/apps/balda/preflight.go` — вне PR-дерева,
|
|
нужны для сборки стенда. `git checkout -- .` / `stash` / `reset --hard` их снесёт.
|
|
См. [[personal/plans/balda-mm-pr-plan]] §5.
|
|
|
|
**НЕ начинать работу** без явного «поехали».
|
|
**Ничего не удалять** без отдельного подтверждения.
|
|
|
|
---
|
|
|
|
## Связанные заметки
|
|
|
|
- [[personal/tech/callee-prism]] — **справочник по контрактам callee и prism**
|
|
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план Фаз 1-5
|
|
- [[personal/plans/balda-mm-phase4-agents]] — Фаза 4 (мост :8790, приёмка)
|
|
- [[personal/plans/balda-mm-pr-plan]] — Фаза 2 (PR #8)
|
|
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3
|
|
- [[personal/tech/agents-bridge]] — мост :8790
|
|
- [[personal/tech/balda-executor-mcp]] — MCP-сервер делегирования
|