[2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/tech/balda-executor-mcp.md
This commit is contained in:
1 parent
2a8706d450
commit
0c7b1f6466
2 files changed
+204
-34
No files matched your search
@@ -16,7 +16,7 @@
|
||||
| 4 | Секция «Agents» в Dashboard | ✅ активирована и проверена |
|
||||
| 5 | Связка Балда → мост | ✅ конфиг `type: http`, образ 0.2.0, `tools/list` из docker-сети |
|
||||
| 6 | Obsidian MCP для Балды | ✅ контейнер `obsidian-mcp` на Kraken + `type: sse` в конфиге (транспорт проверен) |
|
||||
| 6a | 🔴 Инструменты MCP в сессии Балды | 🔴 **блокер:** снапшот каталога в `state.db` содержит только `builtin` → Балда «MCP не вижу». См. [[personal/tech/balda-executor-mcp]] §10g |
|
||||
| 6a | 🔴 Инструменты MCP в сессии Балды | 🔴 **корень: `session_memory` выключен; при включении на Mattermost падает `/reset`** (`no session-memory classifier for "mattermost"`). См. §9.3 |
|
||||
| 7 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI |
|
||||
|
||||
---
|
||||
@@ -293,16 +293,58 @@ lsof -nP -iTCP:8790 -sTCP:LISTEN
|
||||
- obsidian
|
||||
```
|
||||
См. §11 ниже.
|
||||
3. 🔴 **MCP-инструменты не видны в сессии Балды.** Транспорты и сети проверены живьём
|
||||
(оба сервера отвечают 200 из docker-сети), Балда стартует без ошибок и отвечает в чате —
|
||||
но говорит «MCP не вижу». **Корень найден:** снапшот каталога в `state.db`
|
||||
(`balda.appruntime_catalog_snapshot:*`) содержит **только `builtin`** — источники
|
||||
`configured` не скомпилированы; плюс сессия закреплена за старым `RuntimeSnapshotID`.
|
||||
Разбор и механика — [[personal/tech/balda-executor-mcp]] §10g.
|
||||
⏳ Ждёт решения Alex: **A)** вычистить ключи снапшотов из `state.db` (рекомендовано),
|
||||
**B)** полный сброс `state.db`.
|
||||
3. 🔴 **MCP-инструменты не видны в сессии Балды. КОРЕНЬ НАЙДЕН (2026-09-26).**
|
||||
Оказалось **не** снапшот каталога — снапшот в порядке. Самый свежий (`scope: effective`,
|
||||
`runtime_catalog_snapshot:895d7905…`) содержит всё как надо:
|
||||
```json
|
||||
"sources":[{"kind":"builtin","name":"balda"},
|
||||
{"kind":"configured-mcp","name":"executor-mcp"},
|
||||
{"kind":"configured-mcp","name":"obsidian"}]
|
||||
"mcp_servers":[{"name":"executor-mcp","transport":"streamable-http"},
|
||||
{"name":"obsidian","transport":"sse"}]
|
||||
```
|
||||
|
||||
**Две настоящие причины (обе исправлены в конфиге):**
|
||||
|
||||
**3a. Провайдер не ссылался на MCP-серверы.** Доки Balda:
|
||||
`runtime.providers.<id>.mcp_servers` — серверы подключаются **через секцию провайдера**;
|
||||
`balda.mcp_servers` лишь добавляет ID в сессию. Без провайдерской секции инструменты
|
||||
не грузятся. Формула: `effective = bundled(balda) + runtime.providers.<p>.mcp_servers + balda.mcp_servers`.
|
||||
```yaml
|
||||
runtime:
|
||||
providers:
|
||||
deepseek:
|
||||
type: openai
|
||||
openai: {…}
|
||||
mcp_servers: # 🔴 обязательная секция
|
||||
- executor-mcp
|
||||
- obsidian
|
||||
```
|
||||
|
||||
**3b. 🔴 `balda.session_memory.enabled` — выключатель MCP-биндера, но на Mattermost
|
||||
он ломает `/reset`.** Биндер создаётся как
|
||||
`SessionMCPBinderAdapter{Enabled: cfg.Balda.SessionMemory.Enabled}`
|
||||
(`sessionapp/module.go:191-196`). Значит MCP-инструменты в сессии включаются именно
|
||||
`session_memory`, а не `balda.memory` (это разные секции!).
|
||||
|
||||
НО: при `session_memory.enabled: true` сброс сессии падает —
|
||||
```
|
||||
warn … "observe reset boundary: no session-memory classifier is registered for \"mattermost\""
|
||||
→ в чате: "Could not reset this session."
|
||||
```
|
||||
`ScopeResolver` регистрирует классификаторы только для **telegram / slackagent / zulip**
|
||||
(`preflight.go`), для Mattermost — нет. Итог: сессия не пересоздаётся, MCP не подключаются.
|
||||
|
||||
**Вывод:** MCP-инструменты в сессии Балды на Mattermost сейчас **недостижимы** —
|
||||
упирается в отсутствие Mattermost-классификатора в `ScopeResolver`.
|
||||
`session_memory` оставлен **выключенным** (иначе `/reset` не работает).
|
||||
Для разблокировки нужно либо добавить `ClassifyLocatorScope` для Mattermost в апстриме,
|
||||
либо перейти на канал с поддержкой (telegram/slackagent/zulip).
|
||||
|
||||
⚠️ Тест-пост должен идти **от админского токена** — посты от имени самого бота Балда
|
||||
игнорирует (правильно): `docker exec mattermost mmctl --local token generate admin …`.
|
||||
игнорирует (правильно), в логе `dropping posted event: bot not mentioned`:
|
||||
`docker exec mattermost mmctl --local token generate admin …`.
|
||||
И **не забывать `@balda`** в тексте поста.
|
||||
4. 🔴 Логин CLI (Alex вручную) — без него сквозная приёмка невозможна.
|
||||
5. 🔴 **Завести Access Application** на `agents-bridge.qentra.top` — сейчас туннель
|
||||
отвечает 200 без авторизации (проверено). Защита — только `X-Bridge-Secret`.
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
> Создан 2026-09-26. Шаг 5 плана [[personal/plans/balda-mm-phase4-agents]].
|
||||
> Статус: **собран, задеплоен на Kraken, образ `0.2.0`; конфиг Балды — `type: http` +
|
||||
> `obsidian: sse`. Балда стартует без ошибок и отвечает в чате.**
|
||||
> 🔴 **Блокер:** MCP-инструменты не попадают в сессию — корень в снапшоте каталога
|
||||
> (`state.db`), см. **§10g**. Ждёт решения Alex (вариант A/B).
|
||||
> 🔴 **Блокер:** MCP-инструменты не попадают в сессию на Mattermost. Корень найден —
|
||||
> **§10h** (секция провайдера + `session_memory` ломает `/reset`). Ждёт решения Alex (A/B).
|
||||
> ⚠️ Прежняя версия заметки указывала на снапшот каталога — **диагноз был неверным**, §10g.
|
||||
> Связка: Балда (Kraken) → MCP → мост (Mac :8790) → контейнер-исполнитель.
|
||||
|
||||
---
|
||||
@@ -278,37 +279,56 @@ docker exec balda-executor-mcp python3 -c "import uvicorn, fastapi, httpx; print
|
||||
|
||||
---
|
||||
|
||||
## 10g. 🔴🔴 ГЛАВНЫЙ КОРЕНЬ: MCP-серверы не попадают в сессию из-за снапшота каталога
|
||||
## 10g. 🔴 Диагностика «MCP не вижу»: снапшот каталога — ПРОВЕРИТЬ, но это НЕ корень
|
||||
|
||||
Симптом: **все транспорты верны, оба сервера отвечают 200 из docker-сети, а Балда в чате
|
||||
говорит «MCP не вижу, ни одного сервера».** Ошибок в логах нет. `/reset` при этом
|
||||
**перечисляет** серверы — то есть ID прочитаны из YAML, но инструменты не подключены.
|
||||
|
||||
### Диагностика (порядок)
|
||||
> ⚠️ **Важно.** В первой редакции этой заметки корнем был объявлен снапшот каталога
|
||||
> («в `state.db` только `builtin` → чистить снапшоты»). **Это оказался неверный диагноз.**
|
||||
> Проверка показала, что снапшот в порядке и оба источника в нём есть. Удалять данные
|
||||
> НЕ нужно. Настоящий корень — §10h ниже.
|
||||
|
||||
### Шаг 1. Подтвердить, что дело не в транспорте
|
||||
|
||||
```bash
|
||||
# 1. Есть ли вообще слова obsidian / executor-mcp в логе Балды?
|
||||
docker logs balda-mm 2>&1 | grep -ci "obsidian" # → 0
|
||||
docker logs balda-mm 2>&1 | grep -ci "executor-mcp" # → 0
|
||||
```
|
||||
|
||||
Если **0** — проблема не в транспорте и не в сети: каталог не публикует эти источники.
|
||||
### Шаг 2. Прочитать снапшот каталога (ожидаемо — он ЗДОРОВ)
|
||||
|
||||
⚠️ Только `sqlite3` **с хоста Kraken** (в контейнере Балды нет ни shell, ни sqlite3).
|
||||
`strings` для разбора структуры НЕ годится.
|
||||
|
||||
```bash
|
||||
# 2. Что лежит в снапшотах каталога (state.db)
|
||||
STATE=/srv/dev-disk-by-uuid-49e8f586-…/docker/balda/state
|
||||
strings "$STATE/state.db" | grep -o 'balda.appruntime_catalog_snapshot:[a-f0-9]*' | head
|
||||
strings "$STATE/state.db" | grep -A1 'catalog_snapshot' | grep -o '"kind":"[a-z]*","name":"[a-z-]*"' | sort -u
|
||||
DB="$STATE/state.db"
|
||||
sqlite3 "$DB" "SELECT key, substr(updated_at,1,25) FROM balda_app_kv
|
||||
WHERE key LIKE '%catalog_snapshot%' ORDER BY updated_at DESC LIMIT 1;"
|
||||
sqlite3 "$DB" "SELECT value_json FROM balda_app_kv WHERE key LIKE '%<hash>%';" | jq '.sources, .mcp_servers'
|
||||
```
|
||||
|
||||
**Показательный результат (баг):**
|
||||
**Наблюдаемый (исправный) результат:**
|
||||
|
||||
```json
|
||||
"sources":[{"id":{"kind":"builtin","name":"balda"}, …}]
|
||||
"sources":[{"kind":"builtin","name":"balda"},
|
||||
{"kind":"configured-mcp","name":"executor-mcp"},
|
||||
{"kind":"configured-mcp","name":"obsidian"}]
|
||||
"mcp_servers":[{"name":"executor-mcp","transport":"streamable-http"},
|
||||
{"name":"obsidian","transport":"sse"}]
|
||||
```
|
||||
|
||||
В снапшоте **только `builtin`**. Источников `configured` (наши `executor-mcp`/`obsidian`)
|
||||
нет — значит `PreparePluginCandidate` их не скомпилировала.
|
||||
Если источники и транспорты видны — каталог исправен, идти в §10h.
|
||||
|
||||
### Схема таблиц (пригодится)
|
||||
|
||||
| Что | Где |
|
||||
|---|---|
|
||||
| KV-хранилище снапшотов | `balda_app_kv` (`namespace='balda.app'`, `key='runtime_catalog_snapshot:<hash>'`, значение в `value_json`) |
|
||||
| Сессии | `balda_runtime_sessions` (`session_id`, `state_json`, `updated_at`) — **поля snapshot в `state_json` нет** |
|
||||
| Ревизии каталога на диске | `state/catalog-revisions/` — `drwx------ root`, обычным пользователем **не читается** (`Permission denied`) |
|
||||
|
||||
### Механика (по коду, `internal/apps/balda/`)
|
||||
|
||||
@@ -334,23 +354,128 @@ strings "$STATE/state.db" | grep -A1 'catalog_snapshot' | grep -o '"kind":"[a-z]
|
||||
### Правило для будущих правок MCP в Балде
|
||||
|
||||
1. Залить конфиг.
|
||||
2. Убедиться, что `PublishCandidate` отработает (новый каталог) — иначе снапшот остаётся старым.
|
||||
3. Создать **новую** сессию (не переиспользовать старую — она прибита к снапшоту).
|
||||
2. **Прописать серверы в секции ПРОВАЙДЕРА** — см. §10h, это обязательное условие.
|
||||
3. Создать **новую** сессию.
|
||||
4. Проверить **постом от админского токена** (не от бота — свои посты Балда игнорирует):
|
||||
|
||||
```bash
|
||||
docker exec mattermost mmctl --local token generate admin whale-check --json | jq -r '.[0].token'
|
||||
```
|
||||
|
||||
### Открытый вопрос на конец сессии
|
||||
---
|
||||
|
||||
Требует решения Alex (удаление данных → нужно подтверждение):
|
||||
- **A)** точечно вычистить ключи `balda.appruntime_catalog_snapshot:*` из `state.db`
|
||||
(owner/настройки целы, снапшоты пересоберутся) — рекомендовано;
|
||||
- **B)** полный сброс `state.db` (заново `bootstrap-admin` + owner-авторизация).
|
||||
## 10h. 🔴🔴 НАСТОЯЩИЙ КОРЕНЬ (найден 2026-09-26): две причины, обе исправлены
|
||||
|
||||
⚠️ `catalog-revisions/` на хосте — `drwx------ root`, обычным пользователем не читается
|
||||
(`Permission denied`). Смотреть только через `strings state.db`.
|
||||
### Причина 1 — провайдер не ссылался на MCP-серверы
|
||||
|
||||
Официальная дока (`docs/reference/configuration.md`):
|
||||
|
||||
> MCP servers are configured in `runtime.mcp_servers` and **referenced by providers via
|
||||
> `runtime.providers.<id>.mcp_servers`**.
|
||||
|
||||
Формула из той же доки (стр. 555):
|
||||
|
||||
```
|
||||
effective MCP IDs = bundled defaults + runtime.providers.<provider_id>.mcp_servers + balda.mcp_servers
|
||||
```
|
||||
|
||||
Я добавлял ID **только** в `balda.mcp_servers` — этого мало: секция провайдера подключает
|
||||
серверы к рантайму, а `balda.mcp_servers` лишь дополняет список в сессии.
|
||||
|
||||
**Исправлено:**
|
||||
|
||||
```yaml
|
||||
runtime:
|
||||
providers:
|
||||
deepseek:
|
||||
type: openai
|
||||
openai:
|
||||
base_url: "https://api.deepseek.com/v1"
|
||||
api_key: "${DEEPSEEK_API_KEY}"
|
||||
model: "deepseek-chat"
|
||||
mcp_servers: # 🔴 ОБЯЗАТЕЛЬНАЯ секция
|
||||
- executor-mcp
|
||||
- obsidian
|
||||
```
|
||||
|
||||
### Причина 2 — 🔴 `session_memory` включает MCP-биндер, но на Mattermost ломает `/reset`
|
||||
|
||||
**Включатель MCP-инструментов в сессии — это `balda.session_memory.enabled`, НЕ `balda.memory`.**
|
||||
Две разные секции, легко перепутать.
|
||||
|
||||
Механика (`internal/apps/balda/sessionapp/module.go:191-196`):
|
||||
|
||||
```go
|
||||
fx.Annotate(
|
||||
func(broker *sessionmemorymcp.ContextBroker, enabled bool) baldaagent.SessionMCPBinder {
|
||||
return SessionMCPBinderAdapter{Broker: broker, Enabled: enabled}
|
||||
},
|
||||
fx.ParamTags(``, `name:"balda_session_memory_enabled"`),
|
||||
)
|
||||
```
|
||||
|
||||
`enabled` = `cfg.Balda.SessionMemory.Enabled`. Далее в `agent/runtime_manager.go:308-311`:
|
||||
|
||||
```go
|
||||
bindingEnabled := binder != nil && registry != nil
|
||||
if status, ok := binder.(sessionMCPBinderStatus); ok && !status.SessionMCPEnabled() {
|
||||
bindingEnabled = false // ← MCP-инструменты отключаются целиком
|
||||
}
|
||||
```
|
||||
|
||||
**НО** при `session_memory.enabled: true` сброс сессии падает:
|
||||
|
||||
```json
|
||||
{"level":"warn","component":"balda.session_manager",
|
||||
"error":"observe reset boundary: no session-memory classifier is registered for \"mattermost\"",
|
||||
"boundary_reason":"reset"}
|
||||
```
|
||||
|
||||
В чате: **`Could not reset this session.`**
|
||||
|
||||
Причина: `ScopeResolver` (`preflight.go`) регистрирует классификаторы **только** для
|
||||
`telegram`, `slackagent`, `zulip`:
|
||||
|
||||
```go
|
||||
sessionmemoryapp.NewScopeResolver(map[string]sessionmemoryapp.ScopeClassifier{
|
||||
baldatelegram.ChannelType: baldatelegram.ClassifyLocatorScope,
|
||||
baldaslackagent.ChannelType: baldaslackagent.ClassifyLocatorScope,
|
||||
baldazulip.ChannelType: baldazulip.ClassifyLocatorScope,
|
||||
})
|
||||
```
|
||||
|
||||
**Mattermost в этом списке НЕТ.**
|
||||
|
||||
### Итоговый вывод (замкнутый круг)
|
||||
|
||||
| `session_memory` | MCP-инструменты | `/reset` на Mattermost |
|
||||
|---|---|---|
|
||||
| `false` (текущее) | ❌ не подключаются | ✅ работает |
|
||||
| `true` | ✅ подключились бы | ❌ «Could not reset this session.» |
|
||||
|
||||
**MCP-инструменты в сессии Балды на Mattermost сейчас НЕДОСТИЖИМЫ** — ограничение апстрима,
|
||||
не ошибка конфигурации. `session_memory` оставлен **выключенным** (иначе ломается `/reset`).
|
||||
|
||||
### Что осталось для разблокировки (нужно решение Alex)
|
||||
|
||||
- **A)** Добавить `ClassifyLocatorScope` для Mattermost в `ScopeResolver` (патч апстрима,
|
||||
~20 строк по образцу zulip) → MCP заработает в Mattermost. **Рекомендовано** — вся работа
|
||||
Alex идёт в Mattermost.
|
||||
- **B)** Перевести бота на канал, где классификатор есть (`telegram` / `slackagent` / `zulip`)
|
||||
→ MCP заработает без правок кода.
|
||||
|
||||
⛔ **НЕ делать:** чистить `state.db` / ключи снапшотов. Каталог исправен (§10g),
|
||||
данные трогать не нужно.
|
||||
|
||||
### Питфоллы тестирования (сожрали время)
|
||||
|
||||
| Питфолл | Симптом | Как правильно |
|
||||
|---|---|---|
|
||||
| Пост отправлен от имени **бота** | `dropping posted event: bot not mentioned` — Балда молчит | слать токеном **админа**: `mmctl --local token generate admin …` |
|
||||
| Забыт `@balda` в тексте | то же `bot not mentioned` | всегда упоминать бота |
|
||||
| Правка `.env`, а не plist/конфига | «перезапустил, но старое значение» | смотреть, откуда реально читается |
|
||||
| `docker logs` сразу после `up` | логи пусты (буферизация) | подождать ~60-80 с (OnStart ~47 с) |
|
||||
| Ожидание, что `/reset` пересоздаст сессию при выключенном `session_memory` | «серверы в списке, а инструментов нет» | серверы в списке ≠ инструменты подключены |
|
||||
|
||||
---
|
||||
|
||||
@@ -361,8 +486,11 @@ docker exec mattermost mmctl --local token generate admin whale-check --json | j
|
||||
3. ✅ Образ обновлён до `0.2.0` на Kraken, `tools/list` отдаёт 3 инструмента из docker-сети.
|
||||
4. ✅ Балда перезапущена с валидным типом — **стартует без ошибок**, `Balda bot started`,
|
||||
`type: http` принят валидатором. Сквозной пост отработан, Балда **ответила**.
|
||||
5. 🔴 **Блокер:** Балда отвечает «MCP не вижу». Корень — снапшот каталога (**§10g**):
|
||||
в `state.db` снапшот содержит только `builtin`. Нужно решение Alex (вариант A или B, §10g).
|
||||
5. 🔴 **Блокер:** Балда отвечает «MCP не вижу». Корень найден (**§10h**): провайдер не
|
||||
ссылался на серверы (исправлено) **+** `session_memory.enabled` ломает `/reset` на
|
||||
Mattermost (`no session-memory classifier for "mattermost"`) → включатель MCP держать
|
||||
выключенным. Нужно решение Alex по разблокировке (вариант A или B, §10h).
|
||||
⛔ `state.db` НЕ чистить — каталог исправен (§10g).
|
||||
6. 🔴 Сквозная приёмка: `executor_run` из чата → задача на Mac → результат.
|
||||
7. 🔴 Логин CLI внутри `balda-executor` — делает **Alex вручную**.
|
||||
|
||||
|
||||
Reference in new issue
Block a user