From 20be9103af2780b2db26ebd6cf551c1b0fb263ca Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sun, 7 Jun 2026 12:12:26 +0400 Subject: [PATCH] =?UTF-8?q?feat(server):=20=D0=BF=D1=80=D0=B8=D1=91=D0=BC?= =?UTF-8?q?=20dynamic=5Fsamples,=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20DYNAMI?= =?UTF-8?q?C=5FPROMPT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/routes.py | 17 ++++++++++-- doc/dynamic-tests.md | 66 +++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/api/routes.py b/api/routes.py index 6c334b6..cd5a24b 100644 --- a/api/routes.py +++ b/api/routes.py @@ -92,7 +92,19 @@ def register(app): return jsonify({"error": "missing 'responses'"}), 400 responses = data["responses"] - logger.info(f"Upload: {len(responses)} responses") + + # Динамический тест: если есть — добавляем к обычным ответам + dynamic_samples = data.get("dynamic_samples") + has_dynamic = bool(dynamic_samples) + if has_dynamic: + # Прикрепляем все сэмплы к responses для парсинга + all_samples = [] + for sample in dynamic_samples: + all_samples.extend(sample) + logger.info(f"Upload: {len(responses)} обычных + {len(all_samples)} динамических ответов") + responses = responses + all_samples + else: + logger.info(f"Upload: {len(responses)} responses") # ── Идемпотентность: проверяем request_id ───── request_id = (data.get("request_id") or "").strip() @@ -132,8 +144,7 @@ def register(app): base_url=cfg["llm"].get("base_url", "https://api.aillm.ru/v1"), ) try: - is_dynamic = client_info.get("script_mode") == "dynamic" - prompt = DYNAMIC_PROMPT if is_dynamic else SYSTEM_PROMPT + prompt = DYNAMIC_PROMPT if has_dynamic else SYSTEM_PROMPT diagnosis = diagnoser.diagnose(prompt, _build_diagnosis_prompt(parsed, car_info)) llm_success = True except LLMError as e: diff --git a/doc/dynamic-tests.md b/doc/dynamic-tests.md index a83e2d0..9df56c2 100644 --- a/doc/dynamic-tests.md +++ b/doc/dynamic-tests.md @@ -6,48 +6,46 @@ ## Концепция Два теста: **на месте** и **в движении**. Разница — только в подсказке юзеру. -Физически оба делают одно: опрашивают 12 PID каждые 250мс, копят в памяти, отправляют на сервер одной пачкой. +Физически оба делают одно: опрашивают 12 PID каждые 250мс, копят в памяти. -## Flow +## Алгоритм работы (user flow) ``` -Юзер → кнопка «⏱ На месте» / «🚗 В движении» - → подсказка в окне вывода - → кнопка «▶ Старт» - → опрос 12 PID каждые 250мс, вывод: «запись... N отсчётов» - → кнопка «⏹ Стоп» - → отправка на сервер POST /api/v1/session/upload - → LLM-анализ → вывод в чат +1. Ошибки → scanDtc() — считывание DTC +2. Диагностика → обычный скрипт L0/L1/L2 на сервер → LLM +3. LLM сказал сделать тест на месте → + ⏱ На месте → СТАРТ → ... → СТОП → данные в памяти +4. Диагностика → обычный скрипт + динамические данные → LLM +5. LLM сказал сделать тест в движении → + 🚗 В движении → СТАРТ → ... → СТОП → данные в памяти +6. Диагностика → обычный скрипт + динамические данные → LLM (финал) ``` -## Скрипт (сервер) +## Android → Сервер -Новый режим `?mode=dynamic` в `api/scripts.py`: +При нажатии **Диагностика**: +1. Выполняется обычный скрипт (AT-команды, PID'ы) +2. Если есть `dynamicSamples` (данные теста) — прикрепляются к тому же запросу +3. POST `/api/v1/session/upload` с полем `dynamic_samples` +4. Сервер по наличию `dynamic_samples` понимает, что пришёл динамический тест +5. Выбирает промпт: `DYNAMIC_PROMPT` если только динамика, или `SYSTEM_PROMPT + DYNAMIC_PROMPT` если и то и то -```json -{ - "version": 1, - "title": "Динамический тест", - "mode": "dynamic", - "interval_ms": 250, - "steps": [ - {"id": "pid_04", "cmd": "0104", "desc": "Нагрузка"}, - {"id": "pid_05", "cmd": "0105", "desc": "ОЖ"}, - {"id": "pid_06", "cmd": "0106", "desc": "STFT"}, - {"id": "pid_07", "cmd": "0107", "desc": "LTFT"}, - {"id": "pid_0B", "cmd": "010B", "desc": "MAP"}, - {"id": "pid_0C", "cmd": "010C", "desc": "RPM"}, - {"id": "pid_0D", "cmd": "010D", "desc": "Скорость"}, - {"id": "pid_0E", "cmd": "010E", "desc": "Зажигание"}, - {"id": "pid_0F", "cmd": "010F", "desc": "IAT"}, - {"id": "pid_10", "cmd": "0110", "desc": "MAF"}, - {"id": "pid_11", "cmd": "0111", "desc": "Дроссель"}, - {"id": "pid_1F", "cmd": "011F", "desc": "Время работы"} - ] -} -``` +## Что надо сделать -## Хранение данных (Android) +### Уже сделано (написан код, не проверен) +- [x] Сервер: `build_dynamic_script()` — скрипт 12 PID 250мс +- [x] Сервер: `?mode=dynamic` в routes.py +- [x] Сервер: `DYNAMIC_PROMPT` в prompts.py +- [x] Android: кнопки «⏱ На месте» / «🚗 В движении» (layout) +- [x] Android: `DynamicCollector.kt` — сбор 12 PID 250мс +- [x] Android: `ElmChecker.kt` — добавлены методы для работы с DynamicCollector +- [x] Android: MainActivity — СТАРТ/СТОП, данные в памяти +- [x] Android: общий таймер `tv_timer` + +### Надо доделать +- [ ] Android: при старте Диагностики — прикрепить `dynamicSamples` к upload +- [ ] Сервер: принимать `dynamic_samples` в upload_session, выбирать промпт +- [ ] Отладка на реальной машине ### До отправки — в памяти ```kotlin