diff --git a/personal/plans/balda-mm-phase4-agents.md b/personal/plans/balda-mm-phase4-agents.md index b010708c..d0b3f2b0 100755 --- a/personal/plans/balda-mm-phase4-agents.md +++ b/personal/plans/balda-mm-phase4-agents.md @@ -17,6 +17,8 @@ | 5 | Связка Балда → мост | ✅ конфиг `type: http`, образ 0.2.0, `tools/list` из docker-сети | | 6 | Obsidian MCP для Балды | ✅ контейнер `obsidian-mcp` на Kraken + `type: sse` в конфиге (транспорт проверен) | | 6a | Инструменты MCP в сессии Балды | ✅ **исправлено 2026-09-26** — Mattermost-классификатор добавлен в `ScopeResolver` (`app.go` + `preflight.go`), образ `balda-mm:0.1.11`, `session_memory.enabled: true`. См. §12 | +| 6b | Дубль MCP-ID → `duplicate tool`, Балда молчала | ✅ **исправлено 2026-09-26** — список ID только у провайдера, `balda.mcp_servers: []`; без пересборки образа. См. §13 | +| 6c | Structured-output память на `deepseek-chat` | ⚠️ open: `dead_letter / model_failure` — память не наполняется, ответы работают. См. §13 | | 7 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI | --- @@ -301,6 +303,10 @@ lsof -nP -iTCP:8790 -sTCP:LISTEN игнорирует (правильно), в логе `dropping posted event: bot not mentioned`: `docker exec mattermost mmctl --local token generate admin …`. И **не забывать `@balda`** в тексте поста. + 🔴 **Продолжение (§13):** после фикса классификатора всплыл второй дефект — дубль MCP-ID + в двух секциях → `duplicate tool` → ход падал, Балда молчала. Список ID оставлен **только + у провайдера**. И третий, неблокирующий: память на `deepseek-chat` не наполняется + (`dead_letter / model_failure`). 4. 🔴 Логин CLI (Alex вручную) — без него сквозная приёмка невозможна. 5. 🔴 **Завести Access Application** на `agents-bridge.qentra.top` — сейчас туннель отвечает 200 без авторизации (проверено). Защита — только `X-Bridge-Secret`. @@ -530,8 +536,65 @@ Balda bot started. Press Ctrl+C to stop. → перечисляет obsidian и executor-mcp как ИНСТРУМЕНТЫ ``` -⚠️ На момент записи **живой ответ Балды ещё не подтверждён** — проверка за Alex'ом. -Статус шага 6a: код+деплой ✅, приёмка ⏳. +--- + +## 13. 🔴 Второй дефект того же включения: `duplicate tool` (2026-09-26, та же сессия) + +**Симптом после деплоя 0.1.11:** `/reset` больше **не падает** (классификатор работает — звено 2 +исправлено), но Балда **молчит** на обычное сообщение. В логе на каждый пост: + +``` +error … "agent run: duplicate tool: \"balda.control.shutdown\"" +warn … command failed with retryable error (delivery_attempt 1..5) +warn … command deadlettered (terminal:unclassified) +``` + +**Причина:** ID MCP-серверов были перечислены **дважды** — в `balda.mcp_servers` **и** в +`runtime.providers.deepseek.mcp_servers`. Эффективный набор = **объединение**, поэтому +bundled-`balda` подключался второй раз, и его инструмент `balda.control.shutdown` +регистрировался повторно. + +**Где падает:** не в Балде, а в **`google.golang.org/adk`**: +```go +// $(go env GOMODCACHE)/google.golang.org/adk/v2@v2.4.0/tool/toolutils/toolutils.go:47-48 +if _, ok := req.Tools[name]; ok { + return fmt.Errorf("duplicate tool: %q", name) +} +``` +📌 `grep -rn "duplicate tool"` по репозиторию Balda **пуст** — искать надо в ADK. Дедупликация +в `session/manager.go:251-270` чистит **ID серверов**, но не повторную регистрацию **инструментов**. + +**Фикс — конфиг, без пересборки образа:** список ID держим **только у провайдера**. +```yaml +balda: + mcp_servers: [] # 🔴 пусто — иначе дубль +runtime: + providers: + deepseek: + mcp_servers: + - executor-mcp + - obsidian +``` +Бэкап: `config.yaml.bak-nodedup-`. Применено `docker compose up -d --force-recreate`. + +⚠️ **Третий (не блокирующий) дефект того же включения:** `session_memory.enabled: true` +активирует structured-output derivation памяти, и `deepseek-chat` не отдаёт нужную схему: +``` +warn … operation=dead_letter kind=boundary.v1 error_code=model_failure + error: output does not match schema: (root): output is required +``` +→ `session-memory capability retry limit exhausted`, 7 попыток. Ответы работают, +**память не наполняется**. Отдельная задача: либо модель со structured output, либо +политика derivation без structured-выхода. + +### Статус после §13 + +| Проверка | Результат | +|---|---| +| `SessionMCPBinder` в DI-графе | ✅ `provided` | +| старт без ошибок классификатора | ✅ `session memory … started` | +| `duplicate tool` после фикса конфига | ✅ в логах чисто (после recreate) | +| живой ответ Балды на `@balda …` | ⏳ за Alex'ом | --- diff --git a/personal/tech/balda-go-fx-pitfalls.md b/personal/tech/balda-go-fx-pitfalls.md index 4837de77..ed0a1738 100755 --- a/personal/tech/balda-go-fx-pitfalls.md +++ b/personal/tech/balda-go-fx-pitfalls.md @@ -182,6 +182,38 @@ func(broker *sessionmemorymcp.ContextBroker, enabled bool) baldaagent.SessionMCP effective = bundled(balda) + runtime.providers..mcp_servers + balda.mcp_servers ``` +🔴 **Не перечислять ID серверов ДВАЖДЫ** (`balda.mcp_servers` **и** +`runtime.providers..mcp_servers`) — эффективный набор это **объединение**, и каждый +сервер подключается дважды. Симптом — ход падает ещё до вызова модели: + +``` +error … "agent run: duplicate tool: \"balda.control.shutdown\"" +→ в чате тишина; в логе `turn task failed` + `command deadlettered` (5 попыток) +``` + +Источник ошибки — библиотека ADK, не Балда: +```go +// google.golang.org/adk/v2/tool/toolutils/toolutils.go:47-48 +if _, ok := req.Tools[name]; ok { + return fmt.Errorf("duplicate tool: %q", name) +} +``` +🔴 **`duplicate tool` в исходниках Balda не найти** (`grep` пустой) — это `google.golang.org/adk`. +Искать в `$(go env GOMODCACHE)/google.golang.org/adk/`, а не в репозитории. +📌 Правило: держать список ID в **одном** месте — у провайдера; `balda.mcp_servers: []`. +Слияние в `session/manager.go:251-270` дедуплицирует **ID**, но не защищает от +повторной регистрации **инструментов** уже подключённого сервера. + +⚠️ **`session_memory.enabled: true` тянет за собой structured-output память.** На +`deepseek-chat` worker памяти не может отдать нужную JSON-схему: +``` +warn … session-memory capability retry limit exhausted + operation=dead_letter kind=boundary.v1 error_code=model_failure + error: output does not match schema: (root): output is required +``` +7 попыток → dead letter. Ответы бота при этом работают, но **память не наполняется**. +Не блокер для MCP; отдельный дефект совместимости модели и схемы derivation. + --- ## 🔴 Сброс падающих тестов: сначала `git stash`, потом слово «регрессия»