[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
@@ -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