Перенос LLM-сервисов BloodGPT с Python на TS (Mastra-агенты или ai-sdk напрямую — см. mastra). Главная сложность портажа — parity-гэпы не видны на компиляции и часто не видны даже на бенчмарк-числах: TS проходит тесты, но генерирует subtly другой output, потому что где-то потерялся production-промпт, порядок полей в схеме, словарь или кейс. Эта страница — что мы из этого вынесли; конкретные кейсы — в session-дайджестах ниже.
Валидация портажа — только через реальный LLM-вызов
Process rule: каждый портированный шаг валидируется реальным LLM-вызовом с output-comparison, а не «compile-time проходит / теоретически работает / LLM может быть buggy — забьём».
Ильдар: «нужна ПОЛНАЯ ПРОВЕРКА. С LLM. НИКАКИХ ОБСУЖДЕНИЙ, ЧТО МЫ ЧТО-ТО НЕ БУДЕМ ПРОВЕРЯТЬ» (session
dd17b3fd)
Обоснование — в портажах LOINC (833ec924 / 82806132) и V2 param analysis (4791d030) parity-гэпы находились только при реальных LLM-вызовах с side-by-side comparison; compile-time валидация их не ловит.
Production reference output — обязателен, бенчмарк-чисел мало
Parity на бенчмарк-числах недостаточен. Нужно:
- production reference output на том же patient’е (например,
full_report_output.jsonот Никиты — для V2 param analysis); - side-by-side text comparison сгенерированного контента;
- трейсить промпты и version-pin’ить их.
В session 18b47185 после parity на бенчмарке всплыла divergence в промптах — продакшен уехал на новую версию промпта, которую TS-порт не скопировал («Погоди, у Никиты новый, у Никиты другой промпт, мы его не скопировали»). Без production-reference V2 проходит tests, но выдаёт subtly другой output.
Side-by-side VERBOSE comparison Python ↔ TS
Для портажа до архитектурного parity (а не только функционального) — обязателен side-by-side trace comparison: гонять один и тот же кейс в обоих пайплайнах и сравнивать score-traces. Отдельные баги через бенчмарк-числа не видны.
В LOINC-портаже (833ec924 + 82806132) эта стратегия нашла 4 разных issue, которые без VERBOSE-сравнения все выглядели бы как «LLM judgment difference»:
getGeneralSystems("bld")возвращал[]в TS (vs корректный array в Python) → +0.3 score bonus не применялся;- Zod stripping
generalSystemsна tool boundary (см. mastra § Gotchas); - compound keyword expansion отсутствовал;
- кириллические единицы — на самом деле уже портированы (false alarm, verified).
Воспроизводить порядок полей в схеме точно
Порядок полей во входной/выходной схеме ("sex":"male" перед "age":35 и т.п.) влияет на iteration count и качество — модель «ловит» порядок при reasoning, по сути это chain-of-thought-сигнал. По нашей гипотезе это general LLM property (не Mastra-specific) — измерением строго не доказано, но при портаже воспроизводить порядок полей точно (не только содержание) дёшево и того стоит. Развёрнуто — structured-output-field-order-cot; всплыло в 4791d030.
snake_case ↔ camelCase — конвертация на границе LLM ↔ TS
LLM генерит output по схеме, которая для Python-parity остаётся snake_case (parameter_range_type, loinc_code, reference_range); TS-код следует JS-конвенции — camelCase. Смешивание на consumer-side даёт silent field-mismatch (один потребитель видит оба варианта, трактует как разные поля).
Решение — boundary conversion: на границе LLM-output → TS-consumer единая утилита (snakeToCamelDeep / inverse) конвертирует snake_case → camelCase; LLM-схема остаётся snake_case (Python parity), TS-downstream видит только camelCase.
Ильдар: «давай зафиксируем, да что на границех у нас происходит конвертация кейса» · «для других агентов тоже будет актуально… эта разница между кейсами» (session
dd17b3fd)
Применимо ко всем портированным агентам. Открытое: где живёт утилита (packages/analysis-core vs packages/utils) и testing-конвенция.
Promise.all — TS может обогнать Python
Где LLM-вызовы независимы — в TS их гоняют конкурентно через Promise.all, что часто быстрее последовательного Python. V2 param analysis: 312s (TS, personalizer ∥ generator) vs 608s (Python, sequential) — вдвое (session 7cc1d514). Это инвертирует расхожее «Python быстрее, TS — overhead абстракций»: архитектурный паттерн важнее языка, и при портаже синхронного Python стоит искать места под Promise.all (независимые LLM-вызовы / tools).
Связано
- mastra — TS-execution-engine для портированных агентов; Mastra-specific gotchas (Zod stripping, скрытые 4 слоя default-инъекций, Studio, cascade-нет) — там
- agent-vs-workflow — decompose vs agentic loop при портаже structured-LLM задач
- structured-output-field-order-cot — приём «порядок полей схемы = chain-of-thought»
- loinc-unification-direction — design-philosophy сохраняется в TS-порте
- loinc-harmonization-pipeline — конкретный портаж (LOINC: 3-step workflow + tools)
- biomarker-analysis-pipeline — V2 / V2.5 param analysis портаж
Источники
Сноски
-
Сессия
ildar/833ec924, 2026-03-24 — ` (LOINC port). ↩ -
Сессия
ildar/82806132, 2026-04-05 — ` (LOINC closeout BG-1140 — Zod stripping. ↩ -
Сессия
ildar/4791d030, 2026-04-10 — ` (V2 param analysis BG-1191 — hidden layers. ↩ -
Сессия
ildar/18b47185, 2026-04-15 — ` (V2 расширение — production-reference divergence). ↩ -
Сессия
ildar/dd17b3fd, 2026-04-22 — ` (V2. ↩ -
Сессия
ildar/7cc1d514, 2026-04-23 — ` (V2. ↩