- obd/probe.py: трехуровневый каскад (L0/L1/L2) - obd/commands.py: каталог всех AT-команд с метаданными - obd/classifier.py: классификация ответов + определение уровня - obd/connection.py: транспортный слой (SerialTransport) - obd/protocol.py: init() только база, без ATAT1/ATST - api/db.py: таблица device_profiles по BT MAC - api/scripts.py: три уровня скриптов (l0/l1/l2) - api/routes.py: /elm/probe, /elm/profile/<mac>, /script?level= - web/templates/index.html: v0.48.0 - CHANGELOG.md, doc/architecture.md, resume.txt: версии
16 KiB
16 KiB
elmAI — Changelog / Полное описание проекта
Файл для нового агента: прочитай — и ты в курсе всего. Актуально: v0.48.0, 7 июня 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.48.0 (7 июня 2026)
- Пробинг ELM327: трехуровневый каскад (L0/L1/L2)
- Рефакторинг
obd/: разделение на независимые сервисыcommands.py— каталог всех AT-командclassifier.py— классификация ответов + определение уровняconnection.py— транспортный слой (SerialTransport)
- Fix:
init()больше не шлёт ATAT1/ATST (висли на клонах v1.5) - Скрипты: три уровня (
build_script_l0/l1/l2) - БД: таблица
device_profilesпо BT MAC - API:
POST /api/v1/elm/probe,GET /api/v1/elm/profile/<mac> api/routes.py—/script?level=0|1|2
v0.36.0-dev (31 мая 2026)
- Ребрендинг Elmer → elmAI (лого, сайт)
- Opus review: серверные фиксы (WAL, идемпотентность, таймауты LLM, кэш ping-llm)
api/config.py—@lru_cache,api/db.py— контекстный менеджер + request_idbrain/client.py— обработка ошибок (различает 429, 5xx, Timeout), DEFAULT_MODELobd/protocol.py—reset_input_buffer(), не затирать ERROR- CI/CD: GitHub Actions для Android APK (сборка + деплой на сервер)
- Правило №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 (исправлен мусор
\rvs\r\n) - Фиксированный debug.keystore (пароль
android, aliasandroiddebugkey) 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.propertiesElmProtocol.sendCommand()— не затирать ERROR, дренаж буфераSessionDb.onUpgrade()— ALTER TABLE вместо DROP TABLEMainActivity— троттлинг/ping-llm(не чаще 60с), убрать дублирующий receiverScriptRunnerService— null intent → stopSelf, try/finally для progress.stop()- Exponential backoff в ретраях
5. Деплой
# Сервер
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 — продакшен.
Никаких Docker, Kubernetes. Сервер на голом железе. Android APK: GitHub Actions → сборка → авто-деплой на сервер.
6. Правила работы (критически важно!)
- Вопрос в любой форме → только ответить. НИЧЕГО НЕ ПРЕДПРИНИМАТЬ. Только прямые императивы («сделай», «исправь», «напиши») — команда к действию.
- Не выдумывать инфраструктуру. Никаких Docker, K8s. GitHub Actions можно (Android).
- Читать документацию перед действиями.
doc/architecture.md— канонический источник. - Не редактировать отчёты Опуса (
doc/opus-review*.md— только для чтения). - После правок: коммит → пуш → (если сервер) деплой через SSH.
- Версию менять в трёх местах:
index.html,build.gradle.kts, доки. - Не редактировать файлы 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-слой (устарела) |