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

246 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Балда / Валера — эксплуатация & 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|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`:
```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 стэша:**
```bash
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`
## Сборка
### Полный цикл сборки
```bash
# 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)
Если снова встанет вопрос «что у нас есть, чего нет в апстриме» — этот порядок даёт полную картину
без догадок:
```bash
# 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`)