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

162 lines
8.4 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.
# Балда / Валера — эксплуатация
## Расположение
- 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`
- Owner: `allowed_owners` в config.yaml (статически, `/start owner=` не нужен)
## Запуск / рестарт
```bash
cd ~/Docker/balda-agent
docker-compose restart # без пересборки, только конфиг/env не менялись
docker-compose up -d # пересоздать контейнер, перечитать .env
# 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
```
> Валера и Клавдий — **один образ** (`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_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`.
## Проблема: MCP не работают у Валеры (provider: type=openai)
### Коренная причина
hostedagent (`pkg/runtime/hostedagent/`) не поддерживает MCP инструменты.
`openAIConstructor` в `agentfactory.go` получает `resolvedMCP map[string]agentconfig.MCPServerConfig`,
но не передаёт их в `hostedagent.Config` — у Config нет поля для MCP.
`openAIConstructor` вызывает `newHostedAgent(hostedagent.Config{...})` без MCPServers.
Фикс: в `hostedagent.Config` добавить `MCPServers map[string]agentconfig.MCPServerConfig`,
а в `openAIConstructor` передавать `toRuntimeMCPServers(resolvedMCP)`.
Важно: hostedagent использует OpenAI-compatible API (не ACP). MCP инструменты нужно
интегрировать через OpenAI tool_calls — модель шлёт tool_call, код выполняет MCP вызов.
## Проблема: MCP не работают у Валеры — попытка фикса
### Что было сделано (2026-06-19)
#### 1. go.mod
Добавлен `replace github.com/normahq/norma => ../norma-local` — для локальной разработки.
#### 2. hostedagent — добавлена поддержка MCP
- `pkg/runtime/hostedagent/agent.go`:
- В Config добавлено `MCPServers map[string]acpagent.MCPServerConfig`
- Сохранено в `runtimeAgent`
- В `run()`: после создания модели вызывается `model.SetTools(openAIToolsFromMCPServers(r.MCPServers))`
- `pkg/runtime/hostedagent/openai.go`:
- В `openAIChatRequest` добавлено `Tools []openAIToolDefinition`
- В `OpenAIModel` добавлены `tools []openAIToolDefinition + `SetTools()`
- Добавлен `tool_calls` в парсинг ответа (`tool_calls` → content string)
#### 3. agentfactory — передача MCP
- В `openAIConstructor` добавлено `MCPServers: toRuntimeMCPServers(resolvedMCP)` в вызов `newHostedAgent()`
#### 4. Dockerfile
В `Dockerfile.claudio` добавлен `COPY norma-local/ ./norma-local/` — чтобы `replace` работал в контейнере.
#### 5. Сборка
Образ собран и запущен как `balda-agent-valera:latest`.
#### 6. Диагностика
- `mcpServerIDs` приходят корректно: `[balda obsidian fast-rlm]`
- Добавлен stderr-лог в `agentfactory.go` в `Build()`:
```go
fmt.Fprintf(os.Stderr, "BALDA-DEBUG: mcpServerIDs=%v resolvedMCP=%v\n", mcpServerIDs, resolvedMCP)
```
- Для просмотра логов нужен `docker logs balda-agent-valera-1 2>&1 | grep BALDA-DEBUG`
### Почему не заработало
Точная причина не установлена — нужно увидеть `resolvedMCP` в логах контейнера. Возможные варианты:
- `resolveMCPServers` возвращает пустой map
- `toRuntimeMCPServers` неправильно конвертирует
- `SetTools` не влияет на уже созданные сообщения в сессии
### Что осталось
1. Прочитать stderr из контейнера: `docker logs balda-agent-valera-1 2>&1 | grep BALDA-DEBUG`
2. Если `resolvedMCP` пустой — исправлять цепочку resolveMCPServers
3. Если не пустой — тестировать response с tool_calls в DeepSeek ответе
## Диагностика молчания
### 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`.
### 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"}'
```
## Ключевые грабли
### OPENAI_BASE_URL — обязательная env var
Norma не читает `base_url` из config.yaml для `provider: deepseek`. Читает env `OPENAI_BASE_URL`.
Без неё запросы уходят на `api.openai.com` (401).
**В `.env` обязательно:**
```
OPENAI_BASE_URL=https://api.deepseek.com/v1
```
### DOCKER_OPTS — пустая строка убивает старт
Должен быть валидный JSON:
```
DOCKER_OPTS={"max_tokens": 2048, "temperature": 0.7}
```
### composerestart vs compose up -d
- `restart` — не перечитывает `.env`
- `up -d` — пересоздаёт контейнер с обновлённым `.env`