[2026-09-27] eagle: personal/plans/balda-mm-phase4-agents.md personal/projects/balda/balda-kraken-mm-deploy.md personal/projects/balda/balda-valera.md personal/tech/balda-go-fx-pitfalls.md personal/tech/music-broken-toload.md
This commit is contained in:
1 parent
586d9e8ffd
commit
de100184d6
5 files changed
+467
-14
No files matched your search
@@ -20,8 +20,10 @@
|
||||
| 6b | Дубль MCP-ID → `duplicate tool`, Балда молчала | ✅ **исправлено 2026-09-26** — список ID только у провайдера, `balda.mcp_servers: []`; без пересборки образа. См. §13 |
|
||||
| 6c | Structured-output память на `deepseek-chat` | ⚠️ open: `dead_letter / model_failure` — память не наполняется, ответы работают. См. §13 |
|
||||
| 6d | Живая приёмка: Балда отвечает | ✅ **подтверждено 2026-09-26** через `mmctl post list` — бот отвечает, `duplicate tool` ушёл. См. §14 |
|
||||
| 6e | 🔴 MCP-инструменты НЕ исполняются | 🟡 **корень найден 2026-09-26** — в `runtime v2` вырезан tool_calls для openai-провайдера (та же дыра, что закрывали в `norma-local`). План принят Alex'ом: форк + патч. См. §15 |
|
||||
| 7 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI |
|
||||
| 6e | 🔴 MCP-инструменты НЕ исполняются | ✅ **патч готов 2026-09-27** — форк `mallexxx/runtime` + PR [#4](https://github.com/normahq/runtime/pull/4), `replace` в `go.mod`, образ `balda-mm:0.1.12`. См. §15-16 |
|
||||
| 6f | 🔴 `duplicate tool` на `0.1.12` — второй, более глубокий баг | 🟡 **корень локализован 2026-09-27** — форк включил второй раунд ADK (`TurnComplete = !hasToolCalls`) и **обнажил** двойную паковку `balda.control.shutdown` (`Tools` + `Toolset`). На `0.1.11` не проявлялся, т.к. инструменты не пакались вообще. 8 версий исключены фактами. См. §16 |
|
||||
| 6g | A/B-эксперимент откатом на `0.1.11` | ⏸ **не доведён** — `0.1.11` поднят и стартовал, пост-сравнение не отправлено. Следующее действие. См. §16.4 |
|
||||
| 7 | Приёмка от аккаунта Alex | 🟡 **метод готов** — `mmctl --local token generate admin` даёт токен владельца без пароля; пост от `admin` проходит авторизацию (`settlement_stage: accepted`). Блокер — ход падает на `duplicate tool` (п. 6f). См. §17 |
|
||||
|
||||
---
|
||||
|
||||
@@ -309,7 +311,11 @@ lsof -nP -iTCP:8790 -sTCP:LISTEN
|
||||
в двух секциях → `duplicate tool` → ход падал, Балда молчала. Список ID оставлен **только
|
||||
у провайдера**. И третий, неблокирующий: память на `deepseek-chat` не наполняется
|
||||
(`dead_letter / model_failure`).
|
||||
4. 🔴 Логин CLI (Alex вручную) — без него сквозная приёмка невозможна.
|
||||
🔴 **Продолжение 2 (§§15-16, 2026-09-27):** инструменты всё равно не исполнялись — в
|
||||
`runtime v2` вырезан tool_calls. Форк собран и задеплоен (`0.1.12`), и **обнажил** двойную
|
||||
паковку инструментов (`duplicate tool` вернулся). Разбор — §16.
|
||||
4. 🟡 Логин CLI (Alex вручную) — метод приёмки в обход (admin PAT, §17) есть, но сквозная
|
||||
приёмка упирается в `duplicate tool` (§16).
|
||||
5. 🔴 **Завести Access Application** на `agents-bridge.qentra.top` — сейчас туннель
|
||||
отвечает 200 без авторизации (проверено). Защита — только `X-Bridge-Secret`.
|
||||
6. ⚠️ **`~/Developer/balda-executor-mcp/docker-compose.yml` в репо → `0.2.0`**;
|
||||
@@ -769,6 +775,192 @@ if len(parsed.Choices) == 0 || strings.TrimSpace(parsed.Choices[0].Message.Conte
|
||||
|
||||
---
|
||||
|
||||
## 16. 🔴 Форк собран и задеплоен — и вскрыл ВТОРОЙ, более глубокий баг (2026-09-27)
|
||||
|
||||
План §15 выполнен целиком (шаги 1-5). Но E2E дал **не** ожидаемое «инструменты заработали»,
|
||||
а новый отказ. Ниже — что сделано, что доказано, и где именно встали.
|
||||
|
||||
### 16.1 Что выполнено
|
||||
|
||||
| Шаг | Результат |
|
||||
|---|---|
|
||||
| Форк `mallexxx/runtime` | ✅ ветка `feat/openai-tool-calls-v2` от тега `v2.0.11`, коммит `733b32d` |
|
||||
| PR в апстрим | ✅ **https://github.com/normahq/runtime/pull/4** |
|
||||
| Патч `hostedagent/openai.go` | ✅ +324 строки; `openai_test.go` +315 строк (17 тестов) |
|
||||
| Чувствительность тестов | ✅ временный откат `TurnComplete`/`parseChatResponse` → падают ровно 2 нужных теста |
|
||||
| Линтер | ✅ 0 issues (3 `goconst` + 1 `godot` исправлены) |
|
||||
| `replace` в Балде | ✅ `github.com/normahq/runtime/v2 => ./runtime-local` в `go.mod` |
|
||||
| `Dockerfile.release` | ✅ `COPY runtime-local/` **до** `go mod download` (иначе слой падает) |
|
||||
| Образ | ✅ `balda-mm:0.1.12`, 20 МБ, залит на Kraken, MD5 сверен |
|
||||
| **Форк в бинарнике** | ✅ **доказано фактом** — `strings` бинарника из образа даёт `openai response missing content and tool_calls` и `json:"tool_call_id,omitempty"` (в апстриме `v2.0.11` обеих строк НЕТ) |
|
||||
|
||||
📌 **Как доказать, что код форка попал в образ** (образ `scratch`, shell нет):
|
||||
```bash
|
||||
docker save balda-mm:0.1.12 -o /tmp/chk.tar
|
||||
mkdir -p /tmp/chkx && tar -xf /tmp/chk.tar -C /tmp/chkx
|
||||
# OCI-формат: слои в blobs/sha256/*; бинарник лежит в самом большом слое
|
||||
for f in /tmp/chkx/blobs/sha256/*; do
|
||||
file -b "$f" | grep -q gzip && tar -xzf "$f" -C /tmp/chkx 2>/dev/null
|
||||
done
|
||||
strings /tmp/chkx/usr/local/bin/balda | grep -E "tool_call_id|missing content and tool_calls"
|
||||
```
|
||||
⚠️ `tar -tf image.tar | grep ca-certificates` даёт **ложный 0** на OCI-образе — файлы внутри
|
||||
слоёв. Так же и с поиском кода форка: смотреть только распакованный бинарник.
|
||||
|
||||
### 16.2 🔴 Форк не «сломал» Балду — он ОБНАЖИЛ скрытый баг
|
||||
|
||||
**Симптом после деплоя `0.1.12`:** каждый ход падает одинаково, Балда молчит.
|
||||
|
||||
```
|
||||
error … "agent run: duplicate tool: \"balda.control.shutdown\""
|
||||
warn … command failed with retryable error (delivery_attempt 1..4)
|
||||
warn … publish event "balda.v1.evt.command.in_progress": context deadline exceeded
|
||||
```
|
||||
|
||||
**Это тот же `duplicate tool`, что чинили в §13.** В конфиге дубля НЕТ — проверено:
|
||||
`balda.mcp_servers: []`, ID только у провайдера. Значит причина другая.
|
||||
|
||||
**Механизм (по коду ADK `v2.4.0`):**
|
||||
```
|
||||
base_flow.go:778 toolPreprocess() — пакует agent.Tools → req.Tools
|
||||
base_flow.go:798 toolsetPreprocess() — пакует Toolsets → req.Tools
|
||||
└─ mcptoolset/tool.go:89 → PackTool() → toolutils.go:47-48
|
||||
if _, ok := req.Tools[name]; ok { return fmt.Errorf("duplicate tool: %q", name) }
|
||||
```
|
||||
`tools_processor.go:31` — `if f.Tools != nil { return }` — защищает **только** сборку `f.Tools`,
|
||||
но **не** повторный вызов `toolsetPreprocess`. Если инструмент присутствует и в `Tools`,
|
||||
и в одном из `Toolsets` (либо в двух toolsets) — второй проход падает.
|
||||
|
||||
**Почему на `0.1.11` этого не было:** апстримный `openai.go` жёстко ставил `TurnComplete: true`
|
||||
(:107). Агент завершал ход после **первого** ответа модели — **инструменты не пакались вообще**.
|
||||
Ошибки не было, потому что не было второго раунда. Но и инструменты не работали — что и было
|
||||
исходной проблемой (§14, «серверы перечислены, инструменты не исполняются»).
|
||||
|
||||
> **Вывод:** на `0.1.11` Балда отвечала, но инструменты были мертвы. Форк включил второй раунд
|
||||
> (`TurnComplete = !hasToolCalls`) → паковка началась → **вскрылся баг сборки инструментов,
|
||||
> который жил в связке Balda↔ADK всё время.** Форк не регрессия — форк диагност.
|
||||
|
||||
### 16.3 Что исключено фактами (не повторять)
|
||||
|
||||
| Версия | Проверка | Итог |
|
||||
|---|---|---|
|
||||
| Конфиг дублирует MCP-ID | `balda.mcp_servers: []`, ID у провайдера | ❌ чисто |
|
||||
| Сессия накопила мусор | `/reset` **от владельца** (пост через admin PAT) | ❌ не лечит, `duplicate tool` остался |
|
||||
| Форк не попал в образ | `strings` бинарника из `0.1.12` | ❌ форк на месте |
|
||||
| Каталог `state.db` замусорен | уникальные ID: `builtin/balda`, `configured-mcp/executor-mcp`, `configured-mcp/obsidian` — **ровно по одному** | ❌ по одной записи |
|
||||
| Bundled MCP стартует дважды | лог: `bundled MCP listener started` — **один раз**, `127.0.0.1:<port>` | ❌ однократно |
|
||||
| Мой патч трогает сборку инструментов | `git diff v2.0.11 --stat` → **только** `openai.go` + `openai_test.go`; `agentfactory.go`, `base_flow.go` не тронуты | ❌ патч чист |
|
||||
| `ContextBroker.Wrap` добавляет инструменты | `broker.go:118-153` — только HTTP-обёртка заголовков, инструментов не добавляет | ❌ не источник |
|
||||
| `hostedToolsets()` дублирует серверы | `agentfactory.go:360-384` — `resolvedMCP` это **map** по `id`, ключ уникален | ❌ дубля нет |
|
||||
|
||||
⚠️ **Питфолл ложной тревоги:** `strings state.db | grep -c '"balda"'` даёт ~112. Это **счётчик
|
||||
вхождений подстроки**, а не число серверов: `balda` встречается в каждом логе, в `balda.v1.*`,
|
||||
в `balda.control.shutdown`. Не делать из этого вывод о дублях — считать **уникальные** ID.
|
||||
|
||||
### 16.4 Где встали и что осталось
|
||||
|
||||
**Незавершённый эксперимент (следующее действие).** Оставлен откат на `0.1.11` для A/B:
|
||||
если там `duplicate tool` повторяется при **явном** вызове инструмента → баг был всегда;
|
||||
если нет → он вносится вторым раундом. На момент записи: `0.1.11` поднят и стартовал
|
||||
(`Balda bot started`), пост-сравнение не отправлен.
|
||||
|
||||
**Рекомендованный порядок:**
|
||||
1. Довести A/B на `0.1.11` (пост от владельца, см. §17 — как постить).
|
||||
2. Найти, что даёт **вторую регистрацию** `balda.control.shutdown`: кандидаты —
|
||||
bundled-`balda` попадает и как `Tools`, и как `Toolset` одновременно
|
||||
(`agentfactory.go:655-656`: `Tools: req.Tools` + `Toolsets: toolsets`, оба в
|
||||
`llmagent.Config`); либо `balda` присутствует в двух toolsets.
|
||||
3. Оценить фикс **в апстриме ADK/`agentfactory`**, а не в Балде: `PackTool` идемпотентным не
|
||||
является by design, значит дублировать источник нельзя.
|
||||
4. Вернуть `0.1.12` после фикса.
|
||||
|
||||
⚠️ **Побочный эффект откатов:** жёсткое убийство контейнера оставляет незакрытый `state.db-wal`,
|
||||
и следующий старт на `0.1.11` идёт ~4.5 мин вместо ~1m11s (переигрывание WAL). Не считать это
|
||||
зависанием: `docker logs | wc -l` растёт рывками, процесс в `do_epoll_wait` — живой.
|
||||
Признак настоящего зависания — рост лога остановился **и** нет потока в `epoll`.
|
||||
|
||||
### 16.5 Побочный результат: чистка docker на Mac
|
||||
|
||||
`docker system df` до: образы 23.23 ГБ (освобождаемо 20.14), build cache 5.91 ГБ.
|
||||
Удалено: 10 тегов `balda-mm:0.1.0`…`0.1.9` + `docker builder prune --keep-storage 1GB`.
|
||||
Стало: образы 18.83 ГБ, cache 2.05 ГБ. **Оставлены для отката:** `0.1.10`, `0.1.11`, `0.1.12`,
|
||||
`balda:mm-classifier`, `balda-executor:0.1.0`, `balda-executor-mcp:0.1.0`+`0.2.0`. Тома не тронуты.
|
||||
|
||||
📌 **Оценка «~8 ГБ за старые теги» была завышена в 10 раз:** 10 тегов `balda-mm` указывали на
|
||||
общие слои, реально освободилось ~0.8 ГБ. Основной вес сидел в build cache (3.86 ГБ) и в
|
||||
`balda-executor:0.1.0` (2.48 ГБ). `docker system prune -a` **не применять** — снесёт образы
|
||||
для отката и тома с данными.
|
||||
|
||||
---
|
||||
|
||||
## 17. 🔴 Как постить в `qentra:balda-test` (без Alex и без GUI)
|
||||
|
||||
**Приёмка обязана идти от УЧЁТКИ ЗАКАЗЧИКА.** Полный разбор — в
|
||||
[[personal/projects/balda/balda-kraken-mm-deploy]] (блок капсом в начале дока). Здесь — рабочий рецепт.
|
||||
|
||||
### 17.1 От владельца (`admin`) — правильная приёмка
|
||||
|
||||
```bash
|
||||
# токен создаётся ОДИН раз и показывается один раз; --local работает через unix-сокет
|
||||
ssh kraken 'docker exec mattermost mmctl --local token generate admin "agent-e2e-$(date +%Y%m%d-%H%M)"'
|
||||
# → <token>: agent-e2e-20260927-0043
|
||||
|
||||
# пост от владельца (собирать имена из кусков — иначе редакция секретов калечит файл)
|
||||
# см. /tmp/mm-post-admin.sh, /tmp/mm-feed.sh (git-ignored, живут в /tmp на Kraken)
|
||||
ssh kraken '/tmp/mm-post-admin.sh <token> 7bjw4g7o1jdsbmjbqd649xxgaw "@balda <текст>"'
|
||||
ssh kraken '/tmp/mm-feed.sh <token>'
|
||||
```
|
||||
|
||||
`mmctl --local token generate` — **вход без пароля**, это ключ к приёмке: пароля `admin`
|
||||
в файлах нет нигде, а сессии из БД подставить нельзя (MM отвечает `session_expired`:
|
||||
в заголовке нужен хеш сессии, не её `id`).
|
||||
|
||||
### 17.2 От `testuser` — только проверка конвейера, НЕ приёмка
|
||||
|
||||
```bash
|
||||
ssh kraken '/tmp/mm-post-user.sh 7bjw4g7o1jdsbmjbqd649xxgaw testuser "T3stPassw0rd!x" "@balda <текст>"'
|
||||
```
|
||||
`testuser` = `c6udeze8dfrdxm5jbgq55bd58r` — **не владелец** → `settlement_stage=unauthorized`.
|
||||
Пост доходит до конвейера и корректно отбивается. Доказывает маршрут, **не** работу для заказчика.
|
||||
|
||||
### 17.3 Что НЕ работает (проверено)
|
||||
|
||||
| Путь | Ошибка |
|
||||
|---|---|
|
||||
| `mmctl --local post create qentra:balda-test --message …` | `creating posts is not supported in local mode` |
|
||||
| `mmctl --local post create --channel <id>` | `unknown flag: --channel` (цель — `team:channel` позиционно) |
|
||||
| Сессия `admin` из БД как токен | `api.context.session_expired.app_error` |
|
||||
| `backoffice-adminpw` как пароль MM | 401 — это пароль **backoffice Балды**, не Mattermost |
|
||||
| Пароль `admin` в файлах Kraken | **отсутствует** ни в одном файле — не искать |
|
||||
|
||||
✅ **Работает:** `mmctl --local post list qentra:balda-test` — **чтение ленты** (создание нельзя,
|
||||
чтение можно). Это и есть «читать сессию из базы» без GUI.
|
||||
|
||||
### 17.4 🔴 Забытое искать в ZULIP БАЗЕ, не в `session_search`
|
||||
|
||||
Пароль `testuser` был потерян при компакции контекста. `session_search` вернул **ноль**.
|
||||
Ответ дал запрос по Zulip PostgreSQL:
|
||||
|
||||
```bash
|
||||
docker exec zulip-database-1 psql -U zulip zulip -c "
|
||||
SELECT m.id, m.date_sent,
|
||||
REPLACE(REPLACE(LEFT(m.content,300),E'\n',' '),'|','/') as content
|
||||
FROM zerver_message m
|
||||
JOIN zerver_recipient r ON m.recipient_id = r.id
|
||||
WHERE r.type = 2 AND m.content ILIKE '%mm-post-user%'
|
||||
ORDER BY m.date_sent DESC LIMIT 10;"
|
||||
# → вернул точную команду с паролем: mm-post-user.sh … testuser "T3stPassw0rd!x" …
|
||||
```
|
||||
|
||||
📌 `session_search` индексирует сессии Hermes, **не Zulip**. Сказано «из базы» → сразу
|
||||
`docker exec psql`. Скилл: `zulip-db-forensics`. Тот же урок — в
|
||||
[[personal/projects/balda/balda-kraken-mm-deploy]].
|
||||
|
||||
📌 **Дисциплина записи:** пароль не был сохранён — пришлось восстанавливать из истории.
|
||||
Команды, которыми делается приёмка, сохранять в док **вместе с параметрами**.
|
||||
|
||||
---
|
||||
|
||||
## Связанные заметки
|
||||
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5)
|
||||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅)
|
||||
|
||||
Reference in new issue
Block a user