[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
@@ -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` = ждёт мьютекс/канал.
|
||||
Reference in new issue
Block a user