docs: CHANGELOG.md — полное описание проекта для нового агента

This commit is contained in:
“Naeel”
2026-06-03 06:36:03 +03:00
parent 829487ad67
commit f215cff21a
+255
View File
@@ -0,0 +1,255 @@
# elmAI — Changelog / Полное описание проекта
> Файл для нового агента: прочитай — и ты в курсе всего.
> Актуально: v0.36.0-dev, 31 мая 2026
---
## 1. Идентификация
| Поле | Значение |
|------|----------|
| Название | **elmAI** (ранее Elmer, elmAI rebrand в конце мая) |
| Суть | OBD2-диагностика: Android → ELM327 → LLM (анализ ошибок) |
| Репозиторий сервера | `gitea.services.ngcloud.ru/Nail/elmer` |
| Репозиторий Android | `github.com/Repinoid/elmer-android` (отдельный!) |
| Сервер (prod) | `obdai.ru` (5.172.178.213) |
| Язык сервера | Python 3 + Flask + gunicorn |
| Язык клиента | Kotlin, minSdk 24 |
| LLM-провайдер | `api.aillm.ru` (OpenAI-совместимый) |
| База данных | SQLite (WAL mode) |
---
## 2. Архитектура (master — продакшен, актуально)
```
📱 Android → ELM327 (Bluetooth SPP)
▼ ScriptRunnerService (фоновая служба)
│ выполняет скрипт: OBD-команды → ждёт ответы → пишет лог
│ HTTPS POST /api/v1/session/upload
🌐 obdai.ru (nginx :443 → gunicorn :8000)
├── web/app.py — точка входа Flask
├── api/routes.py — 5 эндпоинтов
├── api/db.py — SQLite (sessions, cars, dtc, params)
├── api/parser.py — парсинг батча ELM-ответов (VIN, DTC, PID)
├── api/scripts.py — сборка диагностических скриптов
├── api/config.py — загрузка config.yaml (c lru_cache)
├── brain/client.py — Diagnoser: HTTP к api.aillm.ru
├── brain/prompts.py — SYSTEM_PROMPT (10 правил, табличный формат)
└── obd/protocol.py — AndrOBD-стейт-машина (1:1 копия ElmProt.java)
```
### Модули подробно
#### `api/` — REST + БД + парсинг
**5 эндпоинтов:**
| Эндпоинт | Метод | Что делает | Время |
|----------|-------|-----------|-------|
| `/api/v1/ping` | GET | `{"ok": true}` — проверка сервера | ~5ms |
| `/api/v1/ping-llm` | GET | Проверка LLM (кэш 60с, глобальная переменная) | ~2s |
| `/api/v1/script?mode=full` | GET | Выдача скрипта диагностики (JSON со steps) | ~50ms |
| `/api/v1/session/upload` | POST | Приём батча + LLM-анализ, идемпотентность | ~30-120s |
| `/api/v1/chat` | POST | Свободный вопрос к LLM (с историей) | ~5-15s |
**Ключевые особенности:**
- `upload_session()`: принимает `responses` (массив `{cmd, raw, decoded}`) + `request_id` (UUID для идемпотентности). Если `request_id` уже есть в БД — возвращает кэшированный ответ (200).
- LLM fallback: если нет API key или LLM ошибка — возвращает `format_no_llm()` (сырые данные без анализа).
- `_build_diagnosis_prompt()`: собирает промпт из VIN, DTC stored/pending, параметров, raw_log. Требует глубокого разбора.
- `/chat`: передаёт историю как массив `messages[{role, content}]`, срез последних 10.
- `config.load()``@lru_cache(maxsize=1)`, сбрасывать рестартом процесса.
**База данных (SQLite, WAL):**
- `sessions` — сводная таблица (клиент, ELM, авто, LLM, request_id, response_json для кэша)
- `cars` — VIN → id (уникальные)
- `diagnostic_tokens`, `llm_messages`, `ecu_parameters`, `dtc_codes` — детальные таблицы (не используются в upload, только legacy)
- `PRAGMA journal_mode=WAL`, `busy_timeout=30000`, `check_same_thread=False`
**Парсер (`parser.py`):**
- `parse_batch(responses)``{vin, dtc_stored[], dtc_pending[], parameters[{name,value}], raw_log[]}`
- Парсит из `decoded` поля, fallback из сырого HEX (490201..., 43..., 47...)
- DTC из HEX: декодирует P/B/C/U коды из байтов после 43/47
- PID: всё что с `:` в decoded, кроме VIN/DTC/ELM/Protocol
**Скрипты (`scripts.py`):**
- `build_full_script()`: 7 PID (ОЖ, RPM, скорость, дроссель, нагрузка, STFT, LTFT)
- `build_default_script()`: 1 PID (0105 — температура ОЖ) для отладки
#### `brain/` — LLM
- `Diagnoser(api_key, model="gpt-oss-120b", base_url, timeout=180)`
- `diagnose(system, user_prompt, history=None)` → str
- Ошибки: `LLMError` с безопасным для клиента сообщением (без деталей)
- Различает: Timeout, HTTP 429 («слишком много запросов»), HTTP 5xx, HTTP 4xx
- `SYSTEM_PROMPT`: 10 правил, формат ответа — таблицы, степени уверенности в %, план действий по приоритету
- Модели: `gpt-oss-120b` (основная), `qwen3-6-27b-fp8` (быстрая, но с CoT leak bug)
#### `obd/protocol.py` — ELM327 стейт-машина (AndrOBD)
**Состояния:** `UNDEFINED → INITIALIZING → READY → BUSY → READY`, ошибка → `ERROR`/`DISCONNECTED`
**Классификация ответов (Rsp.identify):**
| Ответ | Тип | Реакция |
|-------|-----|---------|
| `>` | PROMPT | Конец ответа |
| `OK` | OK | Уменьшить таймаут |
| `SEARCHING...` | SEARCHING | Нормально при ините |
| `NODATA` | NODATA | Увеличить таймаут, ATST |
| `UNABLE/BUS BUSY/CAN ERROR` | BUS ERROR | DISCONNECTED → ATPC → ATSP0 |
| `ERROR/DATA ERROR/BUFFER FULL` | ERROR | ATWS (warm start) |
| Всё остальное | DATA | Успех, уменьшить таймаут |
**Ключевые особенности (1:1 с AndrOBD):**
- Побайтовое чтение с поллингом 1ms (НЕ readLine!)
- `>` (0x3E) — не спецсигнал, а разделитель строк как CR/LF
- Адаптивный таймаут: 50-2000ms, шаг 20ms, ATST = timeout/4
- `_write()`: `reset_input_buffer()` перед записью — чистит хвосты
- Инициализация: ATSP0 → ATAT1 → ATST → ATS0 → ATL0 → ATE0 (без ATZ)
- BUS ERROR recovery: ATPC → ATSP0
- `_read()`: требует `>` перед возвратом, иначе TimeoutError
#### `web/app.py` — точка входа
- `sys.path.insert(0, корень_проекта)` — чтобы импортировать `api/`, `brain/`, `obd/`
- `config = load()` — глобально
- `register_api(app)` — подключает эндпоинты
- `/elmer.apk``send_from_directory("static", "app-debug.apk")`
---
## 3. Версии и история
### Система версионирования
- **Сервер:** `web/templates/index.html` (два места: подзаголовок и подпись APK)
- **Android:** `android/app/build.gradle.kts``versionName`
- **Документация:** заголовки `.md` файлов
- **Менять одновременно во всех местах**
### История версий (сервер)
#### v0.36.0-dev (31 мая 2026)
- Ребрендинг Elmer → elmAI (лого, сайт)
- Opus review: серверные фиксы (WAL, идемпотентность, таймауты LLM, кэш ping-llm)
- `api/config.py``@lru_cache`, `api/db.py` — контекстный менеджер + request_id
- `brain/client.py` — обработка ошибок (различает 429, 5xx, Timeout), DEFAULT_MODEL
- `obd/protocol.py``reset_input_buffer()`, не затирать ERROR
- CI/CD в документацию (никаких GitHub Actions — всё вручную)
- Правило №1 в `.instructions.md`: вопрос → только ответ, никаких действий
#### v0.35.0-dev (29-30 мая 2026)
- Рефакторинг архитектуры: `elmer/``api/` + `brain/` + `obd/`
- Удалён мёртвый код (`elmer/diagnose.py`, `elmer/elm.py`, `elmer/prompts.py`, `web/raw_endpoint.py`)
- `doc/architecture.md` — полное описание структуры
- `obd/protocol.py` — вынесен из elm_proto, доработан
#### v0.13.0-dev (28 мая 2026) — AndrOBD стейт-машина
- Стейт-машина 1:1 с AndrOBD (ElmProt.java)
- 5 багов исправлено (команды подряд без пауз, ATST не читал ответ, частичный read, таймаут 200ms, BUS ERROR recovery)
- AdaptiveTiming (500ms start, 50-2000ms range, ATST)
- OkHttp timeout 30→120с
- `tools/mock_elm327_v2.py` — мок с реалистичными задержками
- `tools/test_androbd.py` — тест стейт-машины (2/3 зелёные)
#### v0.11.0-prod (25-27 мая 2026) — fat-client архитектура
- Fat-client: телефон сам гоняет протокол, сервер только батч-анализ
- Скрипт диагностики: GET /api/v1/script
- Загрузка батча: POST /api/v1/session/upload
- Эндпоинт /chat
- Исследование 19 ELM/BT проектов → AndrOBD = золотой стандарт
- `doc/elm-reference.md` — 1100+ строк паттернов
- Домен obdai.ru, решение НЕ деплоить до стабильного ELM↔Android
#### v0.x — ранние версии (fat-client ветка)
- `TestService.kt` — зелёная кнопка: самостоятельный прогон протокола (100% работает)
- `ElmForwardService.kt` — транспорт BT/TCP ↔ HTTP (проблемы: deadlock, паузы)
- Побайтовое чтение в mock (исправлен мусор `\r` vs `\r\n`)
- Фиксированный debug.keystore (пароль `android`, alias `androiddebugkey`)
- `AndroidManifest.xml`: `usesCleartextTraffic="true"`
---
## 4. Android-клиент (отдельный репо)
### Структура
```
app/src/main/java/ru/elmer/client/
├── elm/
│ └── ElmProtocol.kt — ELM327 стейт-машина (AndrOBD)
├── obd/
│ └── ObdDecoder.kt — декодер PID/DTC/VIN
├── server/
│ └── ServerClient.kt — HTTP к серверу (retry 3x, OkHttp)
├── script/
│ ├── ScriptEngine.kt — движок скриптов
│ └── ScriptRunnerService.kt — фоновая диагностика
├── db/
│ └── SessionDb.kt — локальная SQLite история
└── ui/
└── MainActivity.kt — UI + кнопки
```
### Что НЕ ДОРАБОТАНО (по opus-fix-plan, этап 3-4):
- [ ] `request_id` на клиенте (UUID до цикла ретраев, в JSON + заголовок Idempotency-Key)
- [ ] `X-Api-Key` через `BuildConfig.API_KEY` из `local.properties`
- [ ] `ElmProtocol.sendCommand()` — не затирать ERROR, дренаж буфера
- [ ] `SessionDb.onUpgrade()` — ALTER TABLE вместо DROP TABLE
- [ ] `MainActivity` — троттлинг `/ping-llm` (не чаще 60с), убрать дублирующий receiver
- [ ] `ScriptRunnerService` — null intent → stopSelf, try/finally для progress.stop()
- [ ] Exponential backoff в ретраях
---
## 5. Деплой
```bash
# Сервер
ssh obdai.ru "cd /opt/elmer && git pull origin master && sudo systemctl restart elmer"
# APK (локально)
cd android && ./gradlew assembleDebug
scp app/build/outputs/apk/debug/app-debug.apk obdai.ru:/opt/elmer/web/static/app-debug.apk
```
**Сервер:** gunicorn `-w 4 -b 127.0.0.1:8000 web.app:app`, nginx :443 → :8000, SSL certbot.
**Ветка:** `master` — продакшен.
**НИКАКИХ** GitHub Actions, CI/CD сервисов, Docker, Kubernetes.
---
## 6. Правила работы (критически важно!)
1. **Вопрос в любой форме → только ответить. НИЧЕГО НЕ ПРЕДПРИНИМАТЬ.** Только прямые императивы («сделай», «исправь», «напиши») — команда к действию.
2. **Не выдумывать инфраструктуру.** Никаких GitHub Actions, Docker, K8s, CI/CD.
3. **Читать документацию перед действиями.** `doc/architecture.md` — канонический источник.
4. **Не редактировать отчёты Опуса** (`doc/opus-review*.md` — только для чтения).
5. **После правок:** коммит → пуш → (если сервер) деплой через SSH.
6. **Версию менять в трёх местах:** `index.html`, `build.gradle.kts`, доки.
7. **Не редактировать файлы Android-репо** (elmer-android) — это отдельный репо.
---
## 7. Известные архитектурные решения
- **Почему не Docker:** владелец принципиально против. Всё вручную через git + systemd.
- **Почему fat-client (скрипт + батч):** в движении связи с сервером нет. Телефон сам гоняет протокол, потом заливает данные.
- **Почему AndrOBD (не своё):** AndrOBD — 10 лет продакшена, 1993⭐, вылизанный протокол. Копировать 1:1, не изобретать.
- **Почему не PostgreSQL:** SQLite достаточно для одного сервера. Миграция будет когда-нибудь потом.
- **Почему obdai.ru не в продакшене:** пока не отлажен ELM↔Android на 100%. Телефон + ноутбук в одной WiFi — быстрее и надёжнее.
---
## 8. Ветки
| Ветка | Описание |
|-------|----------|
| `master` | Продакшен (актуальная: v0.36.0-dev) |
| `opus-fixes` | Правки по отчётам Опуса (31 мая; влита в master) |
| `arch-refactor` | Рефакторинг elmer/ → api/ brain/ obd/ |
| `fat-client` | Старая fat-client архитектура (устарела) |
| `androbd-proto` | Прототип AndrOBD стейт-машины (устарела) |
| `elm-layer-v2` | Старый ELM-слой (устарела) |