[2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/plans/tagwriter-container.md personal/projects/balda/balda-valera.md personal/tech/balda-go-fx-pitfalls.md personal/tech/music-tags-repair-truenas.md
This commit is contained in:
1 parent
593b867467
commit
84fee3eea2
5 files changed
+507
-16
No files matched your search
@@ -19,6 +19,8 @@
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
||||
---
|
||||
@@ -594,7 +596,176 @@ warn … operation=dead_letter kind=boundary.v1 error_code=model_failure
|
||||
| `SessionMCPBinder` в DI-графе | ✅ `provided` |
|
||||
| старт без ошибок классификатора | ✅ `session memory … started` |
|
||||
| `duplicate tool` после фикса конфига | ✅ в логах чисто (после recreate) |
|
||||
| живой ответ Балды на `@balda …` | ⏳ за Alex'ом |
|
||||
| живой ответ Балды на `@balda …` | ✅ **подтверждён 2026-09-26** — см. §14 |
|
||||
|
||||
---
|
||||
|
||||
## 14. ✅ Живая приёмка без Alex: чтение ленты канала (2026-09-26)
|
||||
|
||||
**Проблема метода:** Alex справедливо отказался быть «руками» для проверки
|
||||
(«ты ведь даже не поверил ожил ли балда вообще»). Нужен способ проверить ответ бота
|
||||
**самому**, не прося пользователя писать в чат.
|
||||
|
||||
### Что НЕ работает
|
||||
|
||||
| Путь | Ошибка | Почему |
|
||||
|---|---|---|
|
||||
| `mmctl post create qentra:balda-test --message '…'` | `creating posts is not supported in local mode` | `--local` общается через unix-сокет: чтение разрешено, **запись от имени человека — нет** |
|
||||
| `mmctl post create --channel <id>` | `unknown flag: --channel` | формат цели — `team:channel` позиционным аргументом: `mmctl post create qentra:balda-test` |
|
||||
| `docker exec mattermost curl -X POST /api/v4/users/login` | `Syntax error: Unterminated quoted string` | вложенные кавычки JSON ломаются в ssh+sh; нужен **файл-скрипт**, не инлайн |
|
||||
|
||||
### Что работает — `post list` по ленте
|
||||
|
||||
```bash
|
||||
# имена: team → channel
|
||||
docker exec mattermost mmctl --local team list # → qentra
|
||||
docker exec mattermost mmctl --local channel list qentra # → balda-test, off-topic, town-square
|
||||
|
||||
# чтение ленты (это и есть доказательство «бот отвечает»)
|
||||
docker exec mattermost mmctl --local post list qentra:balda-test 2>&1 | tail -25
|
||||
```
|
||||
|
||||
Фактический вывод после фикса (§12 + §13) — **Балда отвечает**:
|
||||
|
||||
```
|
||||
[admin] @balda /reset
|
||||
[balda] 🚀 Session Started • Name: balda • ID: mm-c-7aafea13 •
|
||||
Model: deepseek-chat • Reasoning: none • Type: openai •
|
||||
MCP: balda, executor-mcp, obsidian
|
||||
[admin] @balda какие mcp видишь
|
||||
[balda] MCP-доступ в этой сессии: только balda.memory.
|
||||
Недоступно: balda.workspace (git-тулинг) и субагентские MCP-инструменты
|
||||
```
|
||||
|
||||
📌 **Метод приёмки без GUI и без участия заказчика:** `mmctl --local post list <team>:<channel>`.
|
||||
Пост в треде и пост в канале видны одинаково; автор виден по `user_id`.
|
||||
⚠️ Отправить пост **от имени человека** через `--local` нельзя — для генерации
|
||||
человеческого поста нужен токен админа через REST (не `--local`).
|
||||
|
||||
### Серверы исправны — проверено из docker-сети
|
||||
|
||||
```bash
|
||||
# executor-mcp: initialize → сессия → tools/list
|
||||
docker exec mattermost curl -s -X POST "http://balda-executor-mcp:8890/mcp" \
|
||||
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"1.0"}}}'
|
||||
# → 200, mcp-session-id, capabilities:{tools:{}}
|
||||
```
|
||||
|
||||
`tools/list` отдаёт **3 инструмента** с валидной схемой:
|
||||
`executor_run`, `executor_status`, `executor_health`.
|
||||
|
||||
```bash
|
||||
# obsidian-mcp живой
|
||||
docker exec mattermost curl -s -m 5 -i "http://obsidian-mcp:3101/sse"
|
||||
# → 200, Content-Type: text/event-stream, event: endpoint / data: /messages?sessionId=…
|
||||
```
|
||||
|
||||
### 🔴 Остаток: серверы перечислены, инструменты не исполняются
|
||||
|
||||
Сессия показывает `MCP: balda, executor-mcp, obsidian`, серверы исправны, но Балда
|
||||
на вопрос отвечает «доступно только `balda.memory`» — **и то же про свой `balda.workspace`**.
|
||||
То есть **ни один** MCP-инструмент не работает, включая встроенный.
|
||||
|
||||
**Причина — тип провайдера.** `internal/apps/balda/agent/builder.go`:
|
||||
|
||||
```go
|
||||
// :518 — bundled `balda` добавляется КОДОМ, не конфигом
|
||||
MCPServers: mergeMCPServerIDsWithBase([]string{"balda"}, agentCfg.MCPServers, nil),
|
||||
|
||||
// :479-500 — GetAgentMetadata разбирает ДВА класса провайдеров
|
||||
switch strings.TrimSpace(agentCfg.Type) {
|
||||
case agentconfig.AgentTypeCodexACP: acp = agentCfg.CodexACP
|
||||
case agentconfig.AgentTypeClaudeCodeACP: acp = agentCfg.ClaudeCodeACP
|
||||
case agentconfig.AgentTypeGeminiACP: acp = agentCfg.GeminiACP
|
||||
// … всего 10 ACP-типов
|
||||
}
|
||||
// :508 — openai попадает сюда: только модель, без ACP-конфига
|
||||
case agentCfg.OpenAI != nil:
|
||||
model = strings.TrimSpace(agentCfg.OpenAI.Model)
|
||||
```
|
||||
|
||||
`type: openai` (наш `deepseek`) получает **список ID** серверов — поэтому `MCP:` в стартовом
|
||||
сообщении их перечисляет — но **не получает ACP-рантайм**, который исполняет инструменты.
|
||||
Инструменты требуют провайдера **ACP-типа**: `claude_code_acp`, `codex_acp`, `gemini_acp`,
|
||||
`opencode_acp`, `copilot_acp`, `grok_acp`, `registry_acp`, `agy_acp`, `antigravity_acp`, `generic_acp`.
|
||||
|
||||
⚠️ **Осторожно с формулировкой:** ранее в §12 «провайдер `type: openai` не умеет MCP» было
|
||||
записано как **ложный след** — и это верно в части *подключения* (ID попадают в сессию через
|
||||
секцию провайдера, ограничения по типу на этом шаге нет). Но **исполнение** инструментов
|
||||
действительно требует ACP-провайдера. Оба утверждения верны на своих слоях:
|
||||
подключение ≠ исполнение.
|
||||
|
||||
📌 **Проверка лимитов (не они):** `mcpruntime/reconciler.go:97` — defaults
|
||||
`MaxServers: 32, MaxTools: 256, MaxToolMetadataBytes: 256<<10`. Три сервера и три
|
||||
инструмента в них укладываются; гипотеза «лимит отбрасывает серверы» **опровергнута**.
|
||||
|
||||
**Открытое решение (за Alex'ом):** перевести `balda.provider` на ACP-тип
|
||||
(`claude_code_acp` / `codex_acp` / `gemini_acp`) — тогда MCP-инструменты заработают.
|
||||
|
||||
---
|
||||
|
||||
## 15. 🔴 Корень найден: в `runtime v2` вырезан tool_calls (2026-09-26)
|
||||
|
||||
**Поворот в диагностике.** Alex спросил: «Это не та же проблема, которую мы решали в
|
||||
mallexxx-форке старой балды?» — **да, ровно та же.**
|
||||
|
||||
### Что было в старом форке (закрыто Alex'ом 2026-06-22)
|
||||
|
||||
`~/Developer/norma-local` → `pkg/runtime/hostedagent/openai.go`, коммит `91c2c0a`
|
||||
(+286 строк кода, +312 строк тестов). Патч делал 4 вещи:
|
||||
|
||||
| Блок | Что добавлял |
|
||||
|---|---|
|
||||
| Структуры | `openAIToolDefinition`, `openAIFunction`, `openAIToolCall` |
|
||||
| Запрос | `Tools []openAIToolDefinition` + `openAIToolsFromConfig()` (genai.Tool[] → OpenAI), фильтр имён `^[a-zA-Z0-9_-]+$` |
|
||||
| Ответ | `parseChatResponse()` — сборка `FunctionCall` из `tool_calls`, **разбор двойной сериализации аргументов DeepSeek** |
|
||||
| История | `contentToOpenAI()` — конвертация `FunctionCall`/`FunctionResponse`, ключ `output` (специфика Obsidian MCP) |
|
||||
| Цикл | `TurnComplete = !hasToolCalls` — ADK делает второй раунд для исполнения tool |
|
||||
|
||||
Плюс фикс двойной сериализации: DeepSeek отдаёт `function.arguments` как **JSON-строку**,
|
||||
поэтому сначала `json.Unmarshal` в `string`, затем эту строку — в объект.
|
||||
|
||||
### Что в `runtime v2.0.11` сейчас — вырезано НОЛЬ из четырёх
|
||||
|
||||
`~/go/pkg/mod/github.com/normahq/runtime/v2@v2.0.11/hostedagent/openai.go` — **246 строк**:
|
||||
|
||||
| Что | Статус в v2 |
|
||||
|---|---|
|
||||
| `Tools` в запросе | ❌ `openAIToolsFromConfig` отсутствует целиком |
|
||||
| `tool_calls` в ответе | ❌ `parseChatResponse` знает только `Content` |
|
||||
| История tool-результатов | ❌ `openAIMessagesFromRequest` режет всё, кроме текста (:204-208) |
|
||||
| `TurnComplete` | ❌ жёстко `true` (:107) |
|
||||
|
||||
🔴 **Отдельный регресс v2:** `parseChatResponse` (:180-182) падает, если `Content` пуст:
|
||||
```go
|
||||
if len(parsed.Choices) == 0 || strings.TrimSpace(parsed.Choices[0].Message.Content) == "" {
|
||||
return nil, fmt.Errorf("openai response missing content")
|
||||
}
|
||||
```
|
||||
Ответ **только** с `tool_calls` (без текста) — валидный для OpenAI API, но v2 считает его ошибкой.
|
||||
Это надо чинить **первым**.
|
||||
|
||||
📌 В `norma-local` был импорт `adk` v1; в `runtime v2` — `adk/v2`. `genai.Tool` /
|
||||
`FunctionDeclaration` могут отличаться по полям — сверять при переносе.
|
||||
|
||||
### Принятый план (Alex: «1»)
|
||||
|
||||
1. Форк `normahq/runtime` → `mallexxx/runtime`, ветка `feat/openai-tool-calls-v2`.
|
||||
2. Перенести 4 блока в `hostedagent/openai.go` по образцу `norma-local`.
|
||||
3. Применить в Балде: `replace github.com/normahq/runtime/v2 => ./runtime-local` в
|
||||
`~/Developer/balda/go.mod` (по образцу старого `norma-local`).
|
||||
4. Собрать образ на Mac → залить на Kraken.
|
||||
5. E2E: `mmctl --local post list qentra:balda-test` → ждём вызов `executor_run` / `session_memory.search`.
|
||||
6. Обновить `personal/projects/balda/balda-valera.md` (патч живёт снова) + этот док.
|
||||
|
||||
**Альтернатива (отклонена Alex'ом):** перевод `balda.provider` на ACP-тип
|
||||
(`claude_code_acp` / `codex_acp`) — инструменты работают из коробки, но меняет модель
|
||||
(DeepSeek оплачен и настроен; у Alex есть готовый референс патча).
|
||||
|
||||
**Мета-вывод:** апстрим переписал `hostedagent/openai.go` под `runtime v2` и **потерял**
|
||||
фикс tool_calls. Наш старый патч не переносится автоматически — файл существует, но
|
||||
содержимое урезано до chat-completions без tools.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user