Files
obsidian-vault/personal/projects/balda/balda-valera.md
T

15 KiB
Raw Blame History

Балда / Валера — эксплуатация & MCP debug

⚠️ УСТАРЕЛО (2026-09-25) — читать с оговоркой

Этот док описывает старый форк-стек: свой патч norma-local (hostedagent/openai.go), go.mod replace на локальную норму, DeepSeek через OpenAI-совместимый рантайм.

Апстрим ушёл далеко вперёд. Актуально: normahq/balda → baldaworks/balda, архитектура переписана (handlers/* → handlersfx/*, Zulip → channel/zulip/*), провайдеры теперь ACP (claude_code_acp, codex_acp, ...) вместо hostedagent/openai.

Планируется поднять чистую Балду без наших коммитов → см. personal/projects/balda/balda-mm-delegation-plan.

Что здесь всё ещё верно: грабли эксплуатации (NATS, owner, DOCKER_OPTS, stream_only_with_session), бранчи/стэши, диагностика молчания. Что мертво: раздел про openai.go/norma-local replace как «текущее».

Расположение

  • Валера = balda-agent-valera-1, конфиг: ~/Docker/balda-agent/.config/balda/config.yaml
  • Клавдий = claudio-agent-claudio-1, конфиг: ~/Docker/claudio-agent/.config/balda/config.yaml
  • Общий образ: собирается из ~/Docker/claudio-agent/Dockerfile.claudio, контекст /Users/admin/Developer
  • Norma-local (форк с MCP tools): ~/Developer/norma-local/
    • go.mod replace: github.com/normahq/norma => ./norma-local (в ~/Developer/balda/go.mod)
    • Модифицирован pkg/runtime/hostedagent/openai.go + pkg/runtime/agentfactory/agentfactory.go
  • Dockerfile: ~/Docker/claudio-agent/Dockerfile.claudio
    • Копирует norma-local/ в /src/norma-local/
    • go mod edit -replace github.com/normahq/norma=./norma-local перед go mod download

Архитектура

  • Валера = DeepSeek (provider: deepseek, модель deepseek-chat)
  • Клавдий = Claude через прокси (provider: claude, claude-sonnet-4-6)
  • Валера использует Balda runtime — OpenAIModel из openai.go вызывается (hostedagent провайдер).
  • agentfactory.go используется обоими — там лог версии на старте.

Версионный лог (norma-tools)

При старте Валеры в логах:

norma-tools version=v0.0.10 build=whale-YYYYMMDD-N

Где:

  • version — номер версии нормы (из openAIVersion)
  • build — тег сборки (из buildTag)

Перед каждым билдом апать buildTag в openai.go:

const openAIVersion = "v0.0.10"
const buildTag      = "whale-YYYYMMDD-N"   // ← менять!

Файлы где апать:

  • ~/Developer/norma-local/pkg/runtime/hostedagent/openai.go — buildTag константа

Статус: DeepSeek MCP tools — РАБОТАЕТ

Что сделано (openai.go)

Добавлена полная поддержка OpenAI tool_calls:

  • openAIToolDefinition, openAIFunction, openAIToolCall — структуры
  • Tools []openAIToolDefinition в openAIChatRequest
  • openAIToolsFromConfig() — конвертация genai.Tool[] → OpenAI definitions
  • parseChatResponse() — парсинг tool_calls из ответа DeepSeek (ID сохраняется)
  • contentToOpenAI() — конвертация FunctionCall/FunctionResponse в историю
  • generate() — TurnComplete=false при tool_calls (ADK делает второй раунд)

Исправленные проблемы

1. DeepSeek возвращает аргументы с двойной сериализацией ✓ 22.06.2026

Симптом: read_note/write_note падали — Obsidian MCP возвращал "Cannot read properties of undefined (reading 'replace')".

Корень: DeepSeek возвращает function.arguments как JSON-строку (экранированную), а не как JSON-объект.

Фикс в parseChatResponse: сперва пробуем распарсить Arguments как строку (json.Unmarshal(&argsStr)), потом эту строку как объект (json.Unmarshal([]byte(argsStr), &args)).

2. Obsidian MCP: get_vault_stats работает, read/write/delete нет ✓ 22.06.2026

Корень: двойная сериализация аргументов (см. проблему 1). После её фикса всё работает.

3. Key output не проверялся в contentToOpenAI ✓ 22.06.2026

Obsidian MCP возвращает response как {"output":"..."} — добавлена проверка на ключ output.

Текущий билд

  • buildTag: whale-20260622-7
  • Все фиксы закоммичены в master (норма-локаль)
  • OPENAI-DEBUG стэш дропнут — логи в рабочей копии (не коммитятся)
  • Replace на норму работает через go.mod + Dockerfile
  • norma-tools лог подтверждён: time=2026-06-22T13:00:37.170Z level=INFO msg=norma-tools version=v0.0.10 build=whale-20260622-7

Промежуточные статусы — ПОЧИНЕНО (23.06.2026)

Статус: работает.

  • RunSessionTurnPayload (zulip_handler.go) — публикует промежуточный текст и ⚙️ function call статусы для !ev.TurnComplete ивентов через sendPlain.
  • balda.go (Telegram) — те же промежуточные публикации.
  • handleAutoClaimMention / handleMessage / enqueueTurn — передают messageID для точного Zulip threading.
  • Коммит: 05159d1 (main), запушен.

Стэши debug-логов (23.06.2026)

Дебаг логи не коммитятся, хранятся в стэшах.

balda (~/Developer/balda):

  • stash@{0}: debug: whale-20260623 — TASK-ACTOR logging in swarm_task_actor, event count & response_len debug in zulip_handler
    • internal/apps/balda/actors/swarm_task_actor.go — TASK-ACTOR: dispatching/dispatch OK/FAILED
    • internal/apps/balda/handlers/zulip_handler.go — eventCount, response_len, running session turn log
  • stash@{1}: WIP on feat/zulip-events-polling

Восстановление balda стэша:

cd ~/Developer/balda && git stash pop stash@{0}

norma-local (~/Developer/norma-local):

  • OPENAI-DEBUG стэш дропнут — 9x fmt.Fprintf(os.Stderr, "OPENAI-DEBUG:...") в рабочей копии openai.go

Бранчи (23.06.2026)

norma-local:

  • feat/hostedagent-mcp-tools — коммит 56a6e1e (DeepSeek tool_calls фикс), чистая бранча от origin/master
  • master — коммит 56a6e1e (тот же, DeepSeek фиксы)

balda:

  • main — 05159d1 (intermediate status фикс запушен)
  • backup/our-main-before-upstream — старый main
  • feat/zulip-transport-intermediate — от коммита bc5fd03

Сборка

Полный цикл сборки

# 1. Инкремент buildTag в openai.go (строка buildTag)
# ~/Developer/norma-local/pkg/runtime/hostedagent/openai.go
# buildTag = "whale-YYYYMMDD-N"

# 2. Применить OPENAI-DEBUG стэш (если нужно дебажить)
cd ~/Developer/norma-local && git stash pop stash@{0}

# 3. Сборка
docker build --no-cache \
  -f ~/Docker/claudio-agent/Dockerfile.claudio \
  -t balda-agent-valera:latest \
  /Users/admin/Developer

# 4. Деплой
cd ~/Docker/balda-agent && docker-compose up -d --force-recreate

# 5. Проверка версии в логах
docker logs balda-agent-valera-1 2>&1 | grep "norma-tools"

Особенности сборки

  • Dockerfile копирует norma-local/ в /src/norma-local/
  • go.mod replace: github.com/normahq/norma => ./norma-local
  • go mod edit -replace применяется до go mod download
  • --no-cache обязателен при изменении norma-local
  • Образ надо таргетировать под нужное имя: balda-agent-valera для Валеры, claudio-agent для Клавдия
  • После сборки docker-compose up -d --force-recreate чтобы подхватить новый образ

Диагностика молчания / проблем

NORMA-TOOLS не появляется в логах

Причина: агент использует не hostedagent провайдер. openai.go задействован только при provider=deepseek через ADK.

MCP ошибка: failed to list MCP tools: failed to connect: Not Found

Фикс: в config.yaml url должен заканчиваться на /sse: url: http://obsidian-mcp:3101/sse

В логах command running → command handled за секунду, без received provider event

Причина: стухший embedded NATS. Фикс: docker-compose restart или up -d.

Удалён / пустой state.db

Фикс: rm state.db и up -d (balda создаст заново).

Нет owner

Фикс: прописать allowed_owners в config.yaml.

stream_only_with_session

Фикс: написать @Валера <текст> чтобы создать сессию.

Как инвентаризировать форк перед сверкой с апстримом (проверено 2026-09-25)

Если снова встанет вопрос «что у нас есть, чего нет в апстриме» — этот порядок даёт полную картину без догадок:

# 1. Подтянуть метаданные апстрима (не менять рабочую копию!)
cd ~/Developer/balda
git fetch --all --tags --prune

# 2. Насколько мы отстали / опередили
git rev-list --left-right --count HEAD...upstream/main     # ожидаем "N  M"

# 3. Наши уникальные коммиты по КАЖДОЙ ветке (главное)
for ref in origin/main backup/our-main-before-upstream feat/zulip-transport feat/zulip-events-polling; do
  echo "--- $ref ---"; git log --oneline upstream/main.."$ref"
done

# 4. Размер расхождения по файлам
git diff --stat $(git merge-base upstream/main origin/main)..upstream/main | tail -5

# 5. Что апстрим сделал с файлами, которые мы правили
git log --oneline --all -- pkg/runtime/hostedagent/openai.go

Результат для нашего форка (2026-09-25):

Репо Отставание Наши уникальные коммиты
~/Developer/balda 900 файлов 05159d15 (промежуточные статусы Zulip, handlers/balda.go), bc5fd03c (то же + Telegram + messageID), feat/zulip-events-polling (Events API long-poll)
~/Developer/norma-local 26 коммитов 56a6e1e=91c2c0a (DeepSeek tool_calls в hostedagent/openai.go)

Важно про 5ad65955 в balda — это НЕ фича, а replace normahq/norma => ./norma-local в go.mod. При переезде на чистый апстрим его нужно удалить, иначе сборка не пройдёт (файлов norma-local там нет).

Что апстрим сделал с нашими файлами:

  • norma v2 (f8a7336) удалила целиком pkg/runtime/hostedagent/ и pkg/runtime/agentfactory/. Наш DeepSeek-фикс не переносится патчем. Перед переносом проверить, есть ли поддержка tool_calls для OpenAI-совместимых провайдеров в normahq/runtime/v2 v2.0.11 — если да, фикс не нужен вовсе.
  • В balda наши handlers/* заменены на handlersfx/*; Zulip переехал в channel/zulip/*. Наш фикс промежуточных статусов надо переписывать под handlersfx/zulip_ingress.go.

Питфоллы при сверке:

  • origin у norma-local — это normahq/norma (реальный апстрим), а у balda origin — наш mallexxx/balda, апстрим живёт в upstream. Не путать: «тянуть до апстрима» для balda ≠ git pull origin main.
  • Стэши теряются (не переживают git gc/клоны) — выносить в ветку до любых операций сброса.

Известные грабли

OPENAI_BASE_URL — обязательная env var

Norma не читает base_url из config.yaml для provider: deepseek. Читает env.

DOCKER_OPTS — пустая строка убивает старт

Должен быть валидный JSON: DOCKER_OPTS={"max_tokens": 2048}

docker compose up -d не работает

На этом хосте docker compose (без дефиса) не принимает -d. Использовать docker-compose up -d.

Docker кеш COPY norma-local/

Слой копирования кешируется. --no-cache обязателен при изменении norma-local.

go.mod replace

replace github.com/normahq/norma => ./norma-local — путь относительно /src в контейнере, куда копируется norma-local/.

zulip-router owner-форвард падал с 400 (23.06.2026)

Симптом: Валера не отвечал на сообщения в закреплённых тредах без @mention. Router лог: forward failed (owner) error="post ... status 400 body=bad request". Валера лог: invalid zulip webhook payload error="unsupported message.type \"\"".

Корень: В zulip-router/config.go структура Message не имела поля Type. Zulip Events API присылает message.type ("stream"/"private"), но роутер его не парсил — в вебхук приходил пустой "". Balda-agent валидирует message.type.

Фикс: Добавлен Type string \json:"type,omitempty"`вMessage struct. Коммит 803ae68(локальный, без remote). docker-compose up -d --build. Фикс: messageID int в сигнатуру handleAutoClaimMention, передаётся payload.Message.ID из processMessage. Файл:~/Developer/balda/internal/apps/balda/handlers/zulip_handler.go(в коммите05159d1`)