[2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/projects/balda/balda-kraken-mm-deploy.md personal/tech/balda-go-fx-pitfalls.md
This commit is contained in:
1 parent
0c7b1f6466
commit
aa5b66a442
3 files changed
+259
-49
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 в сессии Балды | 🔴 **корень: `session_memory` выключен; при включении на Mattermost падает `/reset`** (`no session-memory classifier for "mattermost"`). См. §9.3 |
|
||||
| 6a | Инструменты MCP в сессии Балды | ✅ **исправлено 2026-09-26** — Mattermost-классификатор добавлен в `ScopeResolver` (`app.go` + `preflight.go`), образ `balda-mm:0.1.11`, `session_memory.enabled: true`. См. §12 |
|
||||
| 7 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI |
|
||||
|
||||
---
|
||||
@@ -293,54 +293,10 @@ lsof -nP -iTCP:8790 -sTCP:LISTEN
|
||||
- obsidian
|
||||
```
|
||||
См. §11 ниже.
|
||||
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).
|
||||
|
||||
3. ✅ **MCP-инструменты не видны в сессии Балды — ИСПРАВЛЕНО (2026-09-26).** Полный разбор в §12.
|
||||
Кратко: причина была **не** в снапшоте каталога (он в порядке) и не только в провайдерском
|
||||
`mcp_servers`. Настоящий выключатель — `balda.session_memory.enabled`, а он на Mattermost
|
||||
падал из-за отсутствующего классификатора scope. Классификатор добавлен — см. §12.
|
||||
⚠️ Тест-пост должен идти **от админского токена** — посты от имени самого бота Балда
|
||||
игнорирует (правильно), в логе `dropping posted event: bot not mentioned`:
|
||||
`docker exec mattermost mmctl --local token generate admin …`.
|
||||
@@ -422,6 +378,163 @@ docker exec mattermost sh -c 'curl -sS -m 8 -H "Accept: text/event-stream" http:
|
||||
📌 У `mcp-proxy` **нет флага `--host`** — в отличие от Python-моста. Передавать только
|
||||
`--port` + `--`. Лишний `--host` роняет контейнер.
|
||||
|
||||
## 12. ✅ MCP-инструменты в сессии Балды — корень и фикс (2026-09-26)
|
||||
|
||||
**Симптом:** `/reset` перечисляет `MCP: balda, executor-mcp, obsidian`, но Балда отвечает
|
||||
«namespace `obsidian` не подключён к сессии» и **не вызывает инструменты**
|
||||
(`function_call_part_count: 0`).
|
||||
|
||||
### Три ложных следа (пройдены, чтобы не повторять)
|
||||
|
||||
| След | Почему ложный |
|
||||
|---|---|
|
||||
| Снапшот каталога `appruntime_catalog_snapshot` пуст | **Не пуст.** Самый свежий (`scope: effective`) содержит `builtin` + `configured-mcp: executor-mcp` + `configured-mcp: obsidian`, транспорты `streamable-http` / `sse` — всё верно. Я предлагал чистить `state.db` — **правка не нужна**, данные целы |
|
||||
| Провайдер `type: openai` не умеет MCP | Умеет. Ходит через agent runtime, ограничения по типу провайдера нет |
|
||||
| `balda.mcp_servers` достаточно | **Недостаточно, но и не корень.** Это лишь список ID для сессии |
|
||||
|
||||
### Настоящий корень — цепочка из двух звеньев
|
||||
|
||||
**Звено 1: серверы подключаются через секцию ПРОВАЙДЕРА.**
|
||||
Доки Balda: MCP-серверы описываются в `runtime.mcp_servers` и **подключаются провайдером**
|
||||
через `runtime.providers.<id>.mcp_servers`. Формула:
|
||||
```
|
||||
effective = bundled(balda) + runtime.providers.<provider>.mcp_servers + balda.mcp_servers
|
||||
```
|
||||
Без провайдерской секции `balda.mcp_servers` игнорируется.
|
||||
|
||||
```yaml
|
||||
runtime:
|
||||
providers:
|
||||
deepseek:
|
||||
type: openai
|
||||
openai: {…}
|
||||
mcp_servers: # 🔴 обязательная секция
|
||||
- executor-mcp
|
||||
- obsidian
|
||||
```
|
||||
|
||||
**Звено 2 (главное): 🔴 `balda.session_memory.enabled` — выключатель MCP-биндера сессии.**
|
||||
Биндер собирается в composition root:
|
||||
```go
|
||||
// internal/apps/balda/app.go:275-278
|
||||
fx.Annotate(
|
||||
func() bool { return cfg.Balda.SessionMemory.Enabled },
|
||||
fx.ResultTags(`name:"balda_session_memory_enabled"`),
|
||||
),
|
||||
// internal/apps/balda/sessionapp/module.go:191-196
|
||||
fx.Annotate(
|
||||
func(broker *sessionmemorymcp.ContextBroker, enabled bool) baldaagent.SessionMCPBinder {
|
||||
return SessionMCPBinderAdapter{Broker: broker, Enabled: enabled}
|
||||
},
|
||||
fx.ParamTags(``, `name:"balda_session_memory_enabled"`),
|
||||
),
|
||||
```
|
||||
⚠️ **`balda.memory` и `balda.session_memory` — РАЗНЫЕ секции.** Первая включает память,
|
||||
вторая — MCP-биндер сессии. В конфиге была только `memory`.
|
||||
|
||||
При включении `session_memory` сессия на Mattermost падала:
|
||||
```
|
||||
warn … "observe reset boundary: no session-memory classifier is registered for \"mattermost\""
|
||||
→ в чате: "Could not reset this session."
|
||||
```
|
||||
|
||||
### Фикс — классификатор Mattermost в `ScopeResolver`
|
||||
|
||||
**Ключевое:** классификатор для Mattermost **уже был написан** (в составе MM-коннектора,
|
||||
`channel/mattermost/locator_codec.go:141-156` — `dm`→personal, `channel`→group).
|
||||
Не хватало ровно **регистрации** в двух картах резолвера.
|
||||
|
||||
| Файл | Строки | Правка |
|
||||
|---|---|---|
|
||||
| `internal/apps/balda/app.go` | 28 | импорт → алиас `baldamattermost` |
|
||||
| `internal/apps/balda/app.go` | 284 | `baldamattermost.ChannelType: baldamattermost.ClassifyLocatorScope,` |
|
||||
| `internal/apps/balda/app.go` | 991 | `mattermost.ValidateConfig` → `baldamattermost.ValidateConfig` (следствие алиаса) |
|
||||
| `internal/apps/balda/preflight.go` | 13 | импорт алиаса |
|
||||
| `internal/apps/balda/preflight.go` | 169 | строка регистрации в карте |
|
||||
|
||||
🔴 **Карт резолвера ДВЕ** — `app.go` (рантайм) и `preflight.go` (валидация).
|
||||
Править **обе**: если поправить одну, preflight разойдётся с рантаймом.
|
||||
|
||||
```go
|
||||
// app.go:280-285 и preflight.go:165-170 — одинаковый список
|
||||
sessionmemoryapp.NewScopeResolver(map[string]sessionmemoryapp.ScopeClassifier{
|
||||
baldatelegram.ChannelType: baldatelegram.ClassifyLocatorScope,
|
||||
baldaslackagent.ChannelType: baldaslackagent.ClassifyLocatorScope,
|
||||
baldazulip.ChannelType: baldazulip.ClassifyLocatorScope,
|
||||
baldamattermost.ChannelType: baldamattermost.ClassifyLocatorScope,
|
||||
})
|
||||
```
|
||||
|
||||
📌 **Урок:** при добавлении нового транспорта в Балду классификатор scope надо
|
||||
**не только написать, но и зарегистрировать в двух картах**. Симптом отсутствия
|
||||
регистрации — `no session-memory classifier is registered for "<channel>"`,
|
||||
проявляется **только** при `session_memory.enabled: true`.
|
||||
|
||||
### Проверка (гейты)
|
||||
|
||||
```bash
|
||||
cd ~/Developer/balda
|
||||
go build ./... # ✅
|
||||
go tool go-arch-lint check --project-path . # ✅ OK
|
||||
gofmt -l internal/apps/balda/app.go internal/apps/balda/preflight.go # ✅ пусто
|
||||
go test -race ./internal/apps/balda/sessionmemoryapp/... \
|
||||
./internal/apps/balda/channel/mattermost/... # ✅ ok
|
||||
```
|
||||
|
||||
⚠️ **Полный `go test -race ./...` = 7 предсуществующих падений** (не регрессия от правок):
|
||||
`cmd/balda` (1), `internal/apps/balda` (2), `mcpruntime` (1), `session` (2), `sessionmemory`
|
||||
layout-тесты. Проверено `git stash` — **те же падения на чистом дереве**.
|
||||
📌 Метод: прежде чем объявлять падения регрессией, снять правки `git stash` и прогнать те же
|
||||
пакеты на базовой ревизии.
|
||||
|
||||
### Деплой
|
||||
|
||||
```bash
|
||||
# сборка (Mac, arm64 builder) — Dockerfile.release, НЕ Dockerfile
|
||||
docker buildx build --builder arm64builder --platform linux/arm64 \
|
||||
-f Dockerfile.release --provenance=false --load -t balda:mm-classifier .
|
||||
docker save balda:mm-classifier -o /tmp/balda-mm-classifier.tar # 20 МБ
|
||||
scp /tmp/balda-mm-classifier.tar kraken:/tmp/
|
||||
ssh kraken 'docker load -i /tmp/balda-mm-classifier.tar && docker tag balda:mm-classifier balda-mm:0.1.11'
|
||||
```
|
||||
|
||||
На Kraken: `docker-compose.yml` → `image: balda-mm:0.1.11`; `config/config.yaml` →
|
||||
`session_memory: enabled: true`; оба файла править **локально**, заливать `scp`
|
||||
(не sed/heredoc). Бэкапы: `config.yaml.bak-mm-classifier-<ts>`,
|
||||
`docker-compose.yml.bak-mm-classifier-<ts>`.
|
||||
|
||||
```bash
|
||||
ssh kraken 'cd /home/kraken/balda && docker compose up -d --force-recreate'
|
||||
```
|
||||
|
||||
✅ **Логи подтверждают фикс:**
|
||||
```
|
||||
SessionMCPBinder … provided
|
||||
session-memory runtime … started
|
||||
session memory … started
|
||||
session manager ready · balda_provider=deepseek
|
||||
bundled MCP listener started · 127.0.0.1:33703 · routes /mcp/balda
|
||||
Balda bot started. Press Ctrl+C to stop.
|
||||
```
|
||||
⚠️ Побочное: `failed to load bot identity for startup log: getMe response missing result` —
|
||||
**не дефект**, только косметика лога идентичности бота.
|
||||
|
||||
### Что проверяет Alex (живая приёмка)
|
||||
|
||||
```
|
||||
@balda /reset
|
||||
→ сессия сбрасывается БЕЗ «Could not reset this session»
|
||||
→ в стартовом сообщении MCP: balda, executor-mcp, obsidian
|
||||
|
||||
@balda какие mcp серверы тебе доступны?
|
||||
→ перечисляет obsidian и executor-mcp как ИНСТРУМЕНТЫ
|
||||
```
|
||||
|
||||
⚠️ На момент записи **живой ответ Балды ещё не подтверждён** — проверка за Alex'ом.
|
||||
Статус шага 6a: код+деплой ✅, приёмка ⏳.
|
||||
|
||||
---
|
||||
|
||||
## Связанные заметки
|
||||
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5)
|
||||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅)
|
||||
|
||||
Reference in new issue
Block a user