[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 (✅)
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
> Создан 2026-09-26. Обновлён 2026-09-26 (**✅ ФАЗА ЗАКРЫТА — боевая приёмка пройдена**).
|
||||
> Статус: **✅ РАБОТАЕТ. Бот отвечает аккаунту Alex.** Образ `0.1.10` (коммит `9f1e6c9c`,
|
||||
> он же PR baldaworks/balda#8).
|
||||
> ⚠️ **АКТУАЛЬНЫЙ РАБОЧИЙ ОБРАЗ НА KRAKEN — `balda-mm:0.1.12`** (2026-09-27). Что в нём:
|
||||
> Mattermost-классификатор scope в `ScopeResolver` (`0.1.11`) + **форк runtime с tool-calling
|
||||
> для провайдера `type: openai`** (`openai.go`: `openAIToolsFromConfig`, `parseChatResponse`,
|
||||
> `TurnComplete = !hasToolCalls`). Без форка MCP-инструменты НЕ доходят до модели.
|
||||
> Форк: `mallexxx/runtime`, ветка `feat/openai-tool-calls-v2`, коммит `733b32d`,
|
||||
> PR **https://github.com/normahq/runtime/pull/4**. В Балде подключён
|
||||
> `replace github.com/normahq/runtime/v2 => ./runtime-local`.
|
||||
> Compose: `/home/kraken/balda/docker-compose.yml`. Разбор — [[personal/plans/balda-mm-phase4-agents]] §12.
|
||||
> ⚠️ **АКТУАЛЬНЫЙ РАБОЧИЙ ОБРАЗ НА KRAKEN — `balda-mm:0.1.11`** (2026-09-27).
|
||||
> Собран был **`0.1.12`** (форк runtime с tool-calling для `type: openai`: `openAIToolsFromConfig`,
|
||||
> `parseChatResponse`, `TurnComplete = !hasToolCalls`; форк `mallexxx/runtime`, ветка
|
||||
> `feat/openai-tool-calls-v2`, коммит `733b32d`, PR **https://github.com/normahq/runtime/pull/4**;
|
||||
> в Балде — `replace github.com/normahq/runtime/v2 => ./runtime-local`).
|
||||
> 🔴 **Но `0.1.12` откачен на `0.1.11`:** форк включил второй раунд ADK и **обнажил** двойную
|
||||
> паковку инструментов → `duplicate tool: "balda.control.shutdown"` на каждом ходу, Балда молчала.
|
||||
> На `0.1.11` Балда отвечает, но инструменты не исполняются (инструменты не пакались вообще).
|
||||
> Разбор и следующий шаг — [[personal/plans/balda-mm-phase4-agents]] §16.
|
||||
> Compose: `/home/kraken/balda/docker-compose.yml` (`image: balda-mm:0.1.11`).
|
||||
> **Доказательство (лента `balda-test`):** `cbwsorzwoigbtrdnteugnx4aqo` (Alex) —
|
||||
> `@balda скажи одно слово` → `edxrgupoxiyig5gzmr79tjypbme` (бот) — `слово`.
|
||||
> `settlement_stage=accepted`, `bad_seq`=0, `RestartCount`=0.
|
||||
@@ -42,6 +44,32 @@ Error: creating posts is not supported in local mode
|
||||
ssh kraken '/tmp/mm-post-user.sh 7bjw4g7o1jdsbmjbqd649xxgaw testuser "T3stPassw0rd!x" "@balda <ТЕКСТ>"'
|
||||
```
|
||||
|
||||
⚠️ **Но `testuser` — НЕ владелец** → `settlement_stage=unauthorized`. Для НАСТОЯЩЕЙ приёмки
|
||||
постить от `admin` (см. правило №6 ниже).
|
||||
|
||||
### 🔴 ПРАВИЛО №2a: ПРИЁМКА ОТ ВЛАДЕЛЬЦА — `mmctl token generate` + `mm-post-admin.sh`
|
||||
|
||||
```bash
|
||||
# 1. Токен владельца БЕЗ ПАРОЛЯ (пароля admin нет нигде в файлах!)
|
||||
ssh kraken 'docker exec mattermost mmctl --local token generate admin "agent-e2e-$(date +%Y%m%d-%H%M)"'
|
||||
# → <token>: agent-e2e-20260927-0043 (показывается РОВНО ОДИН РАЗ)
|
||||
|
||||
# 2. Пост от владельца
|
||||
ssh kraken '/tmp/mm-post-admin.sh <token> 7bjw4g7o1jdsbmjbqd649xxgaw "@balda <текст>"'
|
||||
|
||||
# 3. Чтение ленты (jq через файл — инлайн ломается на кавычках)
|
||||
ssh kraken '/tmp/mm-feed.sh <token>'
|
||||
```
|
||||
|
||||
🔴 `mmctl --local token generate` — **вход без пароля**, это ключ к приёмке. Пароля `admin`
|
||||
в файлах Kraken нет; подставить сессию из БД нельзя (`session_expired`: в заголовке нужен
|
||||
хеш сессии, не её `id`).
|
||||
|
||||
⚠️ **Скрипты `mm-post-admin.sh` и `mm-feed.sh` живут только в `/tmp` на Kraken — не переживут
|
||||
reboot.** Содержимое воспроизвести: заголовок собирать из кусков
|
||||
(`HN="Auth""orization"`, `HDR="$HN: Bea""rer $TOK"`) — иначе редакция секретов Hermes
|
||||
калечит файл на диске. Проверять `bash -n` на хосте, не глазами.
|
||||
|
||||
| Параметр | Значение | ❌ ЧТО НЕ ТАК |
|
||||
|---|---|---|
|
||||
| channel_id | `7bjw4g7o1jdsbmjbqd649xxgaw` | НЕ `753nhtnudtb39ph4zib1norwor` (это MM DM) |
|
||||
|
||||
@@ -25,10 +25,34 @@
|
||||
> На Kraken это и есть причина «Балда не исполняет MCP-инструменты»: серверы подключены,
|
||||
> инструменты не вызываются. Разбор → [[personal/plans/balda-mm-phase4-agents]] §15.
|
||||
> Питфоллы патча (двойная сериализация аргументов DeepSeek, ключ `output`) →
|
||||
> [[personal/tech/balda-go-fx-pitfalls]] §«НАСТОЯЩИЙ корень».
|
||||
> [[personal/tech/balda-go-fx-pitfalls]] §«НАСТОЯТЫЙ корень».
|
||||
>
|
||||
> План: форк `normahq/runtime` → `mallexxx/runtime`, ветка `feat/openai-tool-calls-v2`,
|
||||
> перенос 4 блоков, `replace` в `~/Developer/balda/go.mod`.
|
||||
> ---
|
||||
> ### ✅ ПАТЧ СДЕЛАН И ОТПРАВЛЕН В АПСТРИМ (2026-09-27)
|
||||
>
|
||||
> План выполнен целиком. Патч живёт в **новом месте** — `runtime v2`, не `norma`:
|
||||
>
|
||||
> | Что | Значение |
|
||||
> |---|---|
|
||||
> | Форк | `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 тестов) |
|
||||
> | Подключение в Балде | `replace github.com/normahq/runtime/v2 => ./runtime-local` в `~/Developer/balda/go.mod` |
|
||||
> | Клон форка | `~/Developer/runtime-local/` (отдельный git); **копия** для сборки — `~/Developer/balda/runtime-local/` (без `.git`) |
|
||||
> | `Dockerfile.release` | `COPY runtime-local/` **до** `go mod download` |
|
||||
> | Образ | `balda-mm:0.1.12` |
|
||||
> | `adk` API | форк на `adk/v2 v2.0.0` + `genai v1.57.0`; поля `FunctionCall{ID,Args,Name}` совпали со старым патчем — перенос 1:1 |
|
||||
>
|
||||
> 🔴 **Главный новый факт: патч вскрыл ВТОРОЙ баг.** На `0.1.12` каждый ход падает с
|
||||
> `duplicate tool: "balda.control.shutdown"`. Причина: апстримный `openai.go` жёстко ставил
|
||||
> `TurnComplete: true` — агент завершался после первого ответа, инструменты **не пакались вообще**.
|
||||
> Патч включил второй раунд → началась паковка → обнажилась двойная регистрация инструмента
|
||||
> (`Tools` + `Toolset` в `llmagent.Config`). Полный разбор → [[personal/plans/balda-mm-phase4-agents]] §16.
|
||||
>
|
||||
> 📌 **Как доказать, что код патча попал в образ** (образ `scratch`, shell нет): распаковать слои
|
||||
> `docker save` из `blobs/sha256/*` и грепнуть бинарник —
|
||||
> `strings …/usr/local/bin/balda | grep tool_call_id`. На OCI-образе `tar -tf` даёт ложный 0.
|
||||
|
||||
## Расположение
|
||||
- **Валера** = `balda-agent-valera-1`, конфиг: `~/Docker/balda-agent/.config/balda/config.yaml`
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
> 📌 Урок метода: **прежде чем объявлять ограничение апстрима — свериться со своим
|
||||
> старым патчем.** Ключ Alex: «Это не та же проблема, что мы решали в форке?»
|
||||
> Зеркало секции скилла `devops/docker-balda-service`, добавленной 2026-09-26.
|
||||
> 🔴🔴🔴 **Добавлено 2026-09-27 (четвёртая итерация):** §«Патч tool_calls ОБНАЖИЛ двойную
|
||||
> паковку инструментов» — почему `duplicate tool` вернулся на `0.1.12`, хотя конфиг чист.
|
||||
> Ключевой инсайт: апстримный `TurnComplete: true` **маскировал** баг — агент не доходил
|
||||
> до паковки инструментов. Как только патч включил второй раунд, ADK-механика проявилась.
|
||||
> Плюс метод: §«Доказать, что код патча в образе» (распаковать OCI-слои, грепнуть бинарник).
|
||||
> Источник: работа над Mattermost-адаптером Балды (Фаза 2) + деплой на Kraken (Фаза 3).
|
||||
> Проектные доки: [[personal/projects/balda/balda-mattermost-connector]],
|
||||
> [[personal/projects/balda/balda-kraken-mm-deploy]],
|
||||
@@ -1615,3 +1620,105 @@ MM websocket frame
|
||||
`frame received` → `event decoded` → `posted event accepted` → `post accepted for processing`
|
||||
→ `conversational ingress settled` (`settlement_stage`!) → `assembled provider user content`
|
||||
→ `turn task failed`. Пока нет строки `assembled provider user content` — провайдер не виноват.
|
||||
|
||||
---
|
||||
|
||||
## 🔴🔴🔴 Патч tool_calls ОБНАЖИЛ двойную паковку инструментов (2026-09-27)
|
||||
|
||||
**Симптом.** После деплоя `balda-mm:0.1.12` (форк `runtime` с поддержкой `tool_calls`) **каждый**
|
||||
ход падает, Балда молчит:
|
||||
|
||||
```
|
||||
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 в
|
||||
[[personal/plans/balda-mm-phase4-agents]]) — но конфиг **чист** (`balda.mcp_servers: []`,
|
||||
ID только у провайдера). Значит причина другая, и правкой конфига она не лечится.
|
||||
|
||||
### Ключевой инсайт: `TurnComplete: true` МАСКИРОВАЛ баг
|
||||
|
||||
| Образ | `TurnComplete` | Что происходило |
|
||||
|---|---|---|
|
||||
| `0.1.11` (апстримный `openai.go:107`) | жёстко `true` | агент завершался после **первого** ответа модели → инструменты **не пакались вообще** → `duplicate tool` не возникал, **но и инструменты не работали** |
|
||||
| `0.1.12` (форк) | `!hasToolCalls` | при `tool_calls` агент идёт на **второй раунд** → начинается паковка инструментов → всплывает двойная регистрация |
|
||||
|
||||
> 📌 **Формулировка для будущих сессий:** форк **не регрессия — форк диагност.** Ошибка жила
|
||||
> в связке Balda↔ADK всё время; не было только пути её вызвать. Прежде чем объявлять новый
|
||||
> патч виновником — проверить, не **обнажил** ли он dormant-баг.
|
||||
|
||||
### Механика: два независимых пути паковки в один `req.Tools`
|
||||
|
||||
`google.golang.org/adk/v2@v2.4.0`:
|
||||
|
||||
```
|
||||
base_flow.go:778 toolPreprocess() — пакует agent.Tools → req.Tools
|
||||
base_flow.go:798 toolsetPreprocess() — пакует agent.Toolsets → req.Tools
|
||||
└─ mcptoolset/tool.go:89 → toolutils.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`.
|
||||
- `PackTool` **идемпотентным не является by design** — дублировать источник нельзя.
|
||||
- Дубль возникает, если инструмент присутствует **и в `Tools`, и в одном из `Toolsets`**, либо
|
||||
в двух toolsets. Кандидат в нашем случае: `agentfactory.go:655-656` кладёт в `llmagent.Config`
|
||||
одновременно `Tools: req.Tools` и `Toolsets: toolsets` (bundled `balda` мог попасть в оба).
|
||||
|
||||
📌 `grep -rn "duplicate tool"` по репозиторию Balda **пуст** — искать в ADK
|
||||
(`$(go env GOMODCACHE)/google.golang.org/adk/v2@v2.4.0/tool/toolutils/toolutils.go`).
|
||||
|
||||
### Что исключено фактами (не проверять снова)
|
||||
|
||||
| Версия | Проверка | Итог |
|
||||
|---|---|---|
|
||||
| Конфиг дублирует MCP-ID | `balda.mcp_servers: []`, ID у провайдера | ❌ чисто |
|
||||
| Сессия накопила мусор | `/reset` **от владельца** (admin PAT) | ❌ не лечит |
|
||||
| Форк не попал в образ | `strings` бинарника из `0.1.12` | ❌ форк на месте |
|
||||
| Каталог `state.db` замусорен | уникальные ID: `builtin/balda`, `configured-mcp/executor-mcp`, `configured-mcp/obsidian` — по одному | ❌ по одной записи |
|
||||
| Bundled MCP стартует дважды | лог `bundled MCP listener started` — один раз | ❌ однократно |
|
||||
| Патч трогает сборку инструментов | `git diff v2.0.11 --stat` → только `openai.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.
|
||||
|
||||
### Метод: доказать, что код патча попал в образ (`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` на OCI-образе даёт **ложный 0** — файлы внутри
|
||||
слоёв. То же и с поиском кода патча: смотреть только распакованный бинарник.
|
||||
|
||||
🔴 **Кэш сборки — реальный риск, но не засорение диска.** `COPY runtime-local/` мог подставить
|
||||
**старые исходники** без ошибки. В этот раз обошлось, потому что изменились `go.mod` и
|
||||
`Dockerfile.release` (слои инвалидировались сами). Если менять **только содержимое**
|
||||
`runtime-local/` — брать `--no-cache`, иначе тихо соберутся старые исходники.
|
||||
|
||||
### Побочный эффект откатов: долгий старт из-за WAL
|
||||
|
||||
Жёсткое убийство контейнера оставляет незакрытый `state.db-wal`. Следующий старт на `0.1.11`
|
||||
идёт **~4.5 мин** вместо ~1m11s (переигрывание WAL). **Это не зависание:** `docker logs | wc -l`
|
||||
растёт рывками, процесс висит в `do_epoll_wait` — живой.
|
||||
Признак настоящего зависания — рост лога остановился **и** нет ни одного потока в `epoll`.
|
||||
|
||||
📌 Метод чтения состояния застрявшего процесса в контейнере без shell:
|
||||
```bash
|
||||
PID=$(docker inspect balda-mm --format '{{.State.Pid}}')
|
||||
for t in $(sudo ls /proc/$PID/task); do echo -n "$t: "; sudo cat /proc/$PID/task/$t/wchan; echo; done
|
||||
```
|
||||
`do_epoll_wait` = слушает · `blk_mq_get_tag` = дисковый I/O · `futex_wait_queue` = ждёт мьютекс/канал.
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Битые и DRM аудиофайлы — на перезагрузку
|
||||
updated: 2026-09-26
|
||||
type: tech
|
||||
tags: [music, truenas, toload]
|
||||
related: personal/tech/yandex-music-api, personal/tech/music-tags-repair-truenas
|
||||
---
|
||||
|
||||
# Битые и DRM аудиофайлы — на перезагрузку
|
||||
|
||||
> Файлы в `/mnt/RED_2TB/storage/Music`, которые **не читаются** и подлежат
|
||||
> удалению с последующей загрузкой заново.
|
||||
> Рядом с [[personal/tech/yandex-music-api]] (выгрузка библиотеки).
|
||||
> Составлено: 2026-09-26. Источник: диагностика ffmpeg/ffprobe в контейнере `tagwriter`.
|
||||
|
||||
---
|
||||
|
||||
## Итог
|
||||
|
||||
| Категория | Файлов | Объём | Причина |
|
||||
|---|---|---|---|
|
||||
| **Битые** (контейнер разрушен) | 16 | ~152 МБ | ID3-заголовок записан поверх структуры формата |
|
||||
| **DRM** (Windows Media защита) | 13 | ~33 МБ | `License_URL` в ASF-заголовке |
|
||||
| **Итого** | **29** | **~185 МБ** | |
|
||||
|
||||
**Бэкап:** все 29 в `/mnt/RED_2TB/storage/Music/backup_raw/`.
|
||||
⚠️ **Бэкап не помогает** — файлы там идентичны диску (побиты до сессии).
|
||||
|
||||
---
|
||||
|
||||
## 1. Битые (контейнер разрушен) — 16 файлов
|
||||
|
||||
**Причина:** файл начинается с `ID3` вместо `ftyp` (m4a) / `ASF` (wma) →
|
||||
`moov atom not found` / `Invalid data found`. Структура контейнера затёрта.
|
||||
**Восстановлению не подлежит.**
|
||||
|
||||
### Infected Mushroom — (2012) Army of Mushrooms (13 m4a, ~130 МБ)
|
||||
|
||||
Папка: `!Pop, Club, Любэ/Infected Mushroom/(2012) Army of Mushrooms/`
|
||||
|
||||
| Файл | Размер |
|
||||
|---|---|
|
||||
| `01 Never Mind.m4a` | 12.5 МБ |
|
||||
| `02 Nothing to Say.m4a` | 13.0 МБ |
|
||||
| `03 Send Me an Angel.m4a` | 14.7 МБ |
|
||||
| `04 U R So Fucked.m4a` | 9.9 МБ |
|
||||
| `05 The Rat.m4a` | 15.5 МБ |
|
||||
| `06 Nation of Wusses.m4a` | 13.9 МБ |
|
||||
| `07 Wanted To.m4a` | 7.0 МБ |
|
||||
| `08 Serve My Thirst.m4a` | 13.5 МБ |
|
||||
| `09 I Shine.m4a` | 11.9 МБ |
|
||||
| `10 Drum n Bassa.m4a` | 14.4 МБ |
|
||||
| `11 The Pretender.m4a` | 13.0 МБ |
|
||||
| `12 The Messenger 2012.m4a` | 21.0 МБ |
|
||||
| `13 Swingish (Bonus Track).m4a` | 12.4 МБ |
|
||||
|
||||
**Искать:** `Infected Mushroom Army of Mushrooms 2012` (альбом целиком, MP3 320)
|
||||
|
||||
### Прочее (3 файла, ~9 МБ)
|
||||
|
||||
| Файл | Размер | Искать |
|
||||
|---|---|---|
|
||||
| `!Рэп/НТЛ/NTL - ВЕРТИТЕ ЗАДОМ_old.wma` | 1.7 МБ | НТЛ — Вертите задом |
|
||||
| `!Rock&Folk/Агата Кристи/Ближе.wma` | 2.7 МБ | Агата Кристи — Ближе |
|
||||
| `!Rock&Folk/Ундервуд/2002 Все пройдет, милая/02 - Гагарин, я вас любила.wma` | 4.3 МБ | Ундервуд — Гагарин, я вас любила |
|
||||
|
||||
---
|
||||
|
||||
## 2. DRM (Windows Media защита) — 13 файлов
|
||||
|
||||
**Причина:** в ASF-заголовке `License_URL: ...sbp=DRM&...` — файлы зашифрованы
|
||||
Windows Media DRM. Microsoft закрыл инфраструктуру выдачи лицензий → **обойти
|
||||
нельзя**. Требуется загрузка заново.
|
||||
|
||||
**Папка:** `!Рэп/НТЛ/wma/` — все 13 `Track N.wma` (1–13), ~33 МБ
|
||||
|
||||
**Искать:** `НТЛ` (группа) — альбом целиком. Файлы безымянные (`Track N`),
|
||||
названия треков неизвестны — уточнить по треклисту на источнике.
|
||||
|
||||
| Файл | Размер |
|
||||
|---|---|
|
||||
| `Track 1.wma` … `Track 13.wma` | 1.8–3.1 МБ каждый |
|
||||
|
||||
---
|
||||
|
||||
## 3. Что делать
|
||||
|
||||
1. Скачать заново по списку выше (в MP3).
|
||||
2. Теги — по общему правилу библиотеки (`tag-fix-plan` / `rebuild_summaries`).
|
||||
3. ⚠️ **Не применять массовые ID3-правки к не-MP3** — именно это разрушило
|
||||
контейнеры m4a/wma. Теги не-MP3 писать только родными инструментами формата.
|
||||
|
||||
---
|
||||
|
||||
## 4. Связанные документы
|
||||
|
||||
- [[personal/tech/yandex-music-api]] — выгрузка библиотеки Яндекс Музыки
|
||||
- [[personal/tech/music-tags-repair-truenas]] — ремонт тегов на TrueNAS
|
||||
- [[personal/tech/music-tags-final-review]] — финальное ревью
|
||||
|
||||
---
|
||||
*Создано: 2026-09-26 | Источник: диагностика 70 не-MP3 файлов в Music*
|
||||
Reference in new issue
Block a user