From 6dfff145ecc69fae61ff5c37e35c034423949765 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Wed, 17 Jun 2026 11:35:20 +0600 Subject: [PATCH] =?UTF-8?q?[2026-06-17]=20update=20balda=20docs=20?= =?UTF-8?q?=E2=80=94=20context=20fix,=20MCP=20/sse,=20single-image=20build?= =?UTF-8?q?=20flow,=20current=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personal/projects/balda/balda-valera.md | 172 +++++++-------------- personal/projects/balda/claudio-klaudiy.md | 27 +++- 2 files changed, 82 insertions(+), 117 deletions(-) diff --git a/personal/projects/balda/balda-valera.md b/personal/projects/balda/balda-valera.md index 578d39a3..d265b068 100644 --- a/personal/projects/balda/balda-valera.md +++ b/personal/projects/balda/balda-valera.md @@ -1,63 +1,88 @@ # Балда / Валера — эксплуатация ## Расположение -- Repo: `~/Developer/balda/` +- Repo: `~/Developer/balda/` (ветка `main`) +- Code: upstream `normahq/balda` + `feat/zulip-transport` влит, `normahq/norma` v0.0.10 - **Рабочий compose**: `~/Docker/balda-agent/docker-compose.yaml` → контейнер `balda-agent-valera-1` - Config (persistent): `~/Docker/balda-agent/.config/balda/config.yaml` - Env: `~/Docker/balda-agent/.env` -- Дублирующий compose `~/Developer/balda/compose.valera.yaml` — НЕ использовать (сеть `balda_default` без DNS) +- Owner: `allowed_owners` в config.yaml (статически, `/start owner=` не нужен) ## Запуск / рестарт ```bash cd ~/Docker/balda-agent -docker-compose restart +docker-compose restart # без пересборки, только конфиг/env не менялись +docker-compose up -d # пересоздать контейнер, перечитать .env # Rebuild после изменений в коде: -cd ~/Docker/claudio-agent && docker-compose build && docker-compose up -d +cd ~/Docker/claudio-agent && docker-compose build +docker tag claudio-agent-claudio:latest balda-agent-valera:latest +cd ~/Docker/claudio-agent && docker-compose up -d cd ~/Docker/balda-agent && docker-compose up -d ``` -> Валера и Клавдий — **один образ** (`balda-agent-valera`), собирается из одного Dockerfile (`Dockerfile.claudio`). Разница только в конфиге (`config.yaml`) и `.env`, которые монтируются volumes. -> -> **Сборка образа**: `cd ~/Docker/claudio-agent && docker-compose build && docker-compose up -d` -> **Перезапуск Валеры**: `cd ~/Docker/balda-agent && docker-compose up -d` -> -> Сборка идёт из `~/Docker/claudio-agent/` — там есть build секция с Dockerfile. +> Валера и Клавдий — **один образ** (`balda-agent-valera`), собирается из одного Dockerfile (`Dockerfile.claudio`). Разница только в конфиге (`config.yaml`) и `.env`, которые монтируются volumes. Сборка идёт из `~/Docker/claudio-agent/` — там есть build секция. ## Архитектура получения сообщений Валера — **outgoing webhook bot** (bot_type=3). Zulip отправляет webhook только для @mention и DM. -Для получения всех сообщений в теме — **Events API polling** (отключён, после деплоя zulip-router будет удалён из кода): -- `events_polling.enabled: false` в config.yaml (текущее состояние) +- `events_polling.enabled: false` в config.yaml - Сообщения приходят только через **outgoing webhook** (webhook_token в .env) - После @mention создаётся сессия; последующие сообщения без @mention обрабатываются -**После перезапуска**: нужно один раз написать `@Валера <текст>` чтобы создать сессию в теме. +**После перезапуска**: написать `@Валера <текст>` чтобы создать сессию. + +## MCP Obsidian — конфиг +Balda через SSE подключается к obsidian-mcp. **URL обязательно с `/sse`**: + +```yaml +runtime: + mcp_servers: + obsidian: + type: sse + url: http://obsidian-mcp:3101/sse # <-- без /sse → 404 Not Found + name: obsidian +``` + +## Контекст (история сообщений) +**Работает** (на upstream `normahq/balda` с поддержкой hosted LLM agent sessions). ADK inject'ит историю корректно для OpenAI-совместимых провайдеров. + +Раньше не работало — исправлено в upstream `32e33a8 fix: support hosted MCP toolsets` / `d774c78 fix: use hosted llmagent sessions`. ## Диагностика молчания -1. Проверить логи: `docker logs balda-agent-valera-1 --tail 50` -2. Проверить что бот запущен и webhook слушает: в логах `zulip webhook server starting addr=0.0.0.0:8091` -3. Стухший NATS-таск (симптом: логов нет после @mention) - Фикс: `docker-compose restart` (NATS embedded, состояние в памяти) -4. Проверить webhook со стороны: отправить POST с curl: - ```bash - curl -X POST http://localhost:8091/zulip/webhook \ - -H "Content-Type: application/json" \ - -d '{"type":"test"}' - ``` -## Патчи в коде (общие с Клавдием) +### 1. MCP ошибка: `failed to list MCP tools: failed to connect: Not Found` +**Причина**: в config.yaml url без `/sse` на конце. +**Фикс**: `url: http://obsidian-mcp:3101/sse` → `docker-compose restart`. -В `zulip_handler.go` добавлены: -1. `botName` — извлекается из bot_email (часть до @). Используется чтобы отличить свой @mention от чужого. -2. `extractOtherMention()` — проверяет текст на @**Name**, где Name не равен botName и не @**all**/@**everyone**. Если найден чужой @mention — сообщение игнорируется. -3. Детальное логирование в `handleAutoClaimMention` — видны все шаги от входа до ошибки. +### 2. В логах `command running → command handled` за секунду, без `received provider event` +**Причина**: стухший embedded NATS. Swarm внутри процесса не доставляет команду до session/task actor'ов. +**Фикс**: `docker-compose restart` (если не помогло → `up -d`). + +### 3. Удалён / пустой state.db +После удаления balda не может зарегистрировать swarm акторы. +**Фикс**: `rm state.db` и `up -d` (balda создаст заново). + +### 4. Нет owner +`handleMessage` выходит при `getOwnerID() == 0`. +**Фикс**: прописать `allowed_owners` в config.yaml (не нужен `/start owner=`). + +### 5. stream_only_with_session +Сообщение в теме без сессии молча дропается. +**Фикс**: написать `@Валера <текст>` чтобы создать сессию. + +### 6. Проверить webhook со стороны +```bash +curl -X POST http://localhost:8091/zulip/webhook \ + -H "Content-Type: application/json" \ + -d '{"type":"test"}' +``` ## Ключевые грабли -### 1. OPENAI_BASE_URL — обязательная env var -Norma не читает `base_url` из config.yaml для `provider: deepseek`. Она читает env `OPENAI_BASE_URL`. +### OPENAI_BASE_URL — обязательная env var +Norma не читает `base_url` из config.yaml для `provider: deepseek`. Читает env `OPENAI_BASE_URL`. Без неё запросы уходят на `api.openai.com` (401). **В `.env` обязательно:** @@ -65,89 +90,12 @@ Norma не читает `base_url` из config.yaml для `provider: deepseek`. OPENAI_BASE_URL=https://api.deepseek.com/v1 ``` -**ВАЖНО:** `docker-compose restart` не перечитывает `.env`. Нужно `docker-compose up -d` (пересоздание контейнера). - -### 2. DOCKER_OPTS — пустая строка убивает старт -Если `DOCKER_OPTS=""` (или пустая строка в `.env`), norma падает на парсинге float: -``` -strconv.ParseFloat: parsing "" -``` - -**Должен быть валидный JSON:** +### DOCKER_OPTS — пустая строка убивает старт +Должен быть валидный JSON: ``` DOCKER_OPTS={"max_tokens": 2048, "temperature": 0.7} ``` -### 3. composerestart vs compose up -d -- `docker-compose restart` — не перечитывает `.env`, не пересоздаёт контейнер -- `docker-compose up -d` — пересоздаёт контейнер с обновлённым `.env` - -При изменении `.env` всегда использовать `up -d`. - -## Контекст (история сообщений) - -**Не работает** для OpenAI-совместимых провайдеров (DeepSeek, ChatGPT и т.д.). - -Причина: ADK Runner (`google.golang.org/adk`) inject'ит историю в мульти-тур только для Gemini API. Для OpenAI-compatible провайдеров (через `base_url` переопределение) ADK не inject'ит предыдущие сообщения в вызов модели — каждый turn получает только одно сообщение. - -`balda.sessions.persistent: true` — влияет на хранение balda-сессии между рестартами, но не на inject истории в API вызов. - -### Возможные решения -1. **Inject'ить историю в RunSessionTurnPayload** — перед вызовом `r.Run()` загрузить предыдущие сообщения из ADK session store и передать как мульти-контент. Патч в `zulip_handler.go` и `balda.go`. -2. **Передать историю через global_instruction** — обогащать промпт предыдущими сообщениями из balda session хранилища. -3. **Использовать Gemini API** (нативный ADK inject истории). - -## Owner token - -При старте генерируется owner token. Выводится в лог: -``` -balda owner authentication required auth_command="/start owner=" -``` - -Для авторизации через Telegram (или Zulip с /start): -``` -/start owner= -``` - -При старте с пустым `state.db` (после `rm`) — owner не зарегистрирован. Бот принимает сообщения но не отвечает до `/start`. - -`state.db` путь: контейнер `/workspace/.config/balda/state.db`, на хосте `~/Docker/balda-agent/.config/balda/state.db`. - -## Диагностика: Валера не отвечает в Zulip - -### Симптом: в логах `command running` → `command handled` за секунду, без `received provider event` - -Причины (проверять по порядку): - -1. **Стухший embedded NATS** — swarm внутри процесса не может доставить команду до session/task actor'ов. - Фикс: `docker-compose restart` (если не помогло → `up -d`) - -2. **Удалён / пустой state.db** — после удаления balda не может зарегистрировать swarm акторы. - Фикс: `rm state.db` и `up -d` (balda создаст заново). Owner токен сбросится — нужен `/start owner=...`. - -3. **Нет owner** — `handleMessage` выходит при `getOwnerID() == 0`. - Фикс: `/start owner=` в Telegram или Zulip. - -4. **stream_only_with_session** — сообщение в теме без сессии молча дропается. - Фикс: написать `@Валера <текст>` чтобы создать сессию. - -5. **DeepSeek не отвечает (context loss)** — см. раздел «Контекст». - -## Конфигурация провайдера -В `config.yaml` обязательно: -```yaml -balda: - provider: deepseek - sessions: - persistent: true - persistence: sqlite - zulip: - events_polling: - enabled: false - stream_only_with_session: true -runtime: - providers: - deepseek: - openai: - base_url: https://api.deepseek.com/v1 -``` +### composerestart vs compose up -d +- `restart` — не перечитывает `.env` +- `up -d` — пересоздаёт контейнер с обновлённым `.env` diff --git a/personal/projects/balda/claudio-klaudiy.md b/personal/projects/balda/claudio-klaudiy.md index e1933333..d7847f51 100644 --- a/personal/projects/balda/claudio-klaudiy.md +++ b/personal/projects/balda/claudio-klaudiy.md @@ -5,33 +5,50 @@ | Item | Value | |------|-------| | Repo | `~/Developer/balda/` (один код с Валерой) | +| Ветка | `main` | | Compose | `~/Docker/claudio-agent/docker-compose.yaml` → контейнер `claudio-agent-claudio-1` | | Config | `~/Docker/claudio-agent/.config/balda/config.yaml` | | Env | `~/Docker/claudio-agent/.env` | | Dockerfile | `~/Docker/claudio-agent/Dockerfile.claudio` | +| Образ | `claudio-agent-claudio` (тагается как `balda-agent-valera` для Валеры) | ## Запуск / рестарт ```bash cd ~/Docker/claudio-agent docker-compose restart # без пересборки -docker-compose build && docker-compose up -d # после изменений в коде +docker-compose up -d # пересоздать контейнер + +# Rebuild после изменений в коде: +cd ~/Docker/claudio-agent && docker-compose build +docker tag claudio-agent-claudio:latest balda-agent-valera:latest +cd ~/Docker/claudio-agent && docker-compose up -d +cd ~/Docker/balda-agent && docker-compose up -d ``` +> Валера и Клавдий — **один код и один образ** (`claudio-agent-claudio` → `balda-agent-valera`). Разница только в конфиге, `.env` и порте webhook (8091 vs 8092). Сборка всегда из `~/Docker/claudio-agent/`. + ## Архитектура получения сообщений Клавдий — **outgoing webhook bot** (bot_type=3). Zulip отправляет webhook только для @mention и DM. -Для получения всех сообщений в теме — **Events API polling** (отключён, после деплоя zulip-router будет удалён из кода): -- `events_polling.enabled: false` в config.yaml (текущее состояние) -- Сообщения приходят только через **outgoing webhook** - ## Отличие от Валеры - **Провайдер**: `claude` (claude-sonnet-4-6) через Claude Proxy (контейнер, порт 3457) - **Порт webhook**: 8092 (у Валеры 8091) - **База**: отдельная SQLite (NATS embedded) +## MCP Obsidian — конфиг +Аналогично Валере — URL обязательно с `/sse`: +```yaml +runtime: + mcp_servers: + obsidian: + type: sse + url: http://obsidian-mcp:3101/sse + name: obsidian +``` + ## Проблемы и решения ### Валера отвечал вместо Клавдия на @mention