185 lines
10 KiB
Markdown
185 lines
10 KiB
Markdown
# Балда / Валера — эксплуатация & MCP debug
|
||
|
||
## Расположение
|
||
- **Валера** = `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
|
||
Фикс: написать `@Валера <текст>` чтобы создать сессию.
|
||
|
||
## Известные грабли
|
||
|
||
### 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`) |