Spaces:
Sleeping
Sleeping
| # SEO AI Editor — исчерпывающая документация функционала, логики и алгоритмики | |
| Документ описывает приложение как инженерную спецификацию: что делает каждый модуль, какие данные принимает и возвращает, какие формулы использует, какие ограничения применяет и как воспроизвести поведение системы без чтения исходного кода. | |
| --- | |
| ## 1) Концепция приложения | |
| `SEO AI Editor` объединяет два аналитических контура и один контур улучшения текста: | |
| 1. **SEO-контур** (`POST /analyze`) | |
| - Word Count (total/significant) | |
| - N-gram анализ (1..4) | |
| - BM25-рекомендации (`add/remove/ok`) | |
| - BERT-семантика по ключам | |
| - Title-анализ (length/ngrams/coverage/BERT) | |
| 2. **Semantic Core** (`POST /api/v1/semantic/analyze`, `POST /api/v1/semantic/search`) | |
| - NLP-разбор и лемматизация | |
| - семантический граф (слова + фразы) | |
| - веса узлов и связей в шкале `1..100` | |
| - гипертекстовая разметка | |
| - реферат | |
| - смысловой поиск по словам и фразам | |
| - сравнение с конкурентами (включая таблицу мощных терминов) | |
| 3. **LLM Optimizer** (`POST /api/v1/optimizer/run` или в UI — SSE `run-stream`) | |
| - итеративная локальная оптимизация текста | |
| - многокритериальный скоринг с защитой от деградации | |
| - каскад уровней правок (от минимальных к более широким) | |
| - детализированный debug-лог по кандидатам | |
| --- | |
| ## 2) Архитектура и ответственность файлов | |
| - `app.py` — FastAPI оркестратор, endpoint-ы, связывание модулей. | |
| - `models.py` — Pydantic-модели входов/выходов. | |
| - `logic.py` — SEO-ядро: токены, n-grams, BM25, BERT, Title. | |
| - `nlp_processor.py` — NLP-предобработка для semantic-контура. | |
| - `semantic_graph.py` — построение графа и вычисление смысловых весов. | |
| - `highlighter.py` — разметка текста по semantic-весам. | |
| - `summarizer.py` — генерация реферата. | |
| - `search.py` — смысловой поиск в графе (фразы + слова). | |
| - `url_fetcher.py` — извлечение текста/title из URL с выбором user-agent. | |
| - `optimizer.py` — LLM-оптимизация с обратной связью от метрик. | |
| - `docs/TEXT_OPTIMIZER_PRINCIPLES.md` — живой регламент принципов оптимизатора (stage-пайплайн, допуски, guardrails). | |
| - `templates/index.html` — разметка UI. | |
| - `static/js/app.js` — вся клиентская логика (подключается как `/static/js/app.js`; без гигантского inline-скрипта — см. `docs/HF_SES_AND_UI.md`). | |
| --- | |
| ## 3) Поддерживаемые языки | |
| Поддерживаемые языки анализа: | |
| - `ru`, `en`, `de`, `es`, `it`, `pl`, `pt` | |
| Языки задаются кодом и сопоставляются с spaCy-моделями в `logic.py` (`MODEL_NAMES`). | |
| --- | |
| ## 4) Backend API и контракты | |
| ## 4.1 `POST /analyze` | |
| ### Назначение | |
| Комплексный SEO-анализ target-текста относительно конкурентов и ключевых фраз. | |
| ### Вход (`AnalysisRequest`) | |
| - `target_text: str` | |
| - `competitors: List[str]` | |
| - `keywords: List[str]` | |
| - `language: str` | |
| - `target_title: str` | |
| - `competitor_titles: List[str]` | |
| ### Выход (`AnalysisResponse`) | |
| - `ngram_stats` | |
| - `bm25_recommendations` | |
| - `bert_analysis` | |
| - `word_counts` | |
| - `title_analysis` | |
| ### Оркестрация в `app.py` | |
| 1. Word counts (`count_words`) для target и каждого competitor. | |
| 2. N-gram статистика (`calculate_ngram_stats`). | |
| 3. Нормализация ключей (`parse_keywords`) и BM25 (`calculate_bm25_recommendations`). | |
| 4. BERT-анализ (`perform_bert_analysis`). | |
| 5. Title-анализ (`analyze_title`) если `target_title` не пустой. | |
| --- | |
| ## 4.2 `POST /api/v1/semantic/analyze` | |
| ### Назначение | |
| Построение semantic-среза по target и конкурентам. | |
| ### Вход (`SemanticAnalyzeRequest`) | |
| - `text: str` | |
| - `competitors: List[str]` | |
| - `language: str` | |
| - `threshold: int` (порог подсветки) | |
| - `compression_ratio: float` (доля предложений в реферате) | |
| ### Выход (`SemanticAnalyzeResponse`) | |
| - `target`: | |
| - `graph` (`nodes`, `links`) | |
| - `markup_text` | |
| - `summary` | |
| - `top_keywords` | |
| - `word_weights` | |
| - `stats` | |
| - `competitors[]`: тот же формат | |
| - `comparison`: | |
| - `target_nodes`, `target_links` | |
| - `avg_comp_nodes`, `avg_comp_links` | |
| - `num_competitors` | |
| - `term_power_table` | |
| ### Логика таблицы `term_power_table` | |
| Для каждого термина из объединения target + competitors: | |
| - `target_weight` | |
| - `competitor_avg_weight` | |
| - `competitor_weights` (`K1..Kn`) | |
| - `comp_occurrence` (`X` в `X/Y`) | |
| - `comp_total` (`Y`) | |
| - `term_type` (`word` или `phrase`) | |
| --- | |
| ## 4.3 `POST /api/v1/semantic/search` | |
| ### Назначение | |
| Смысловой поиск по документу через граф. | |
| ### Вход (`SemanticSearchRequest`) | |
| - `query_text` | |
| - `text` | |
| - `language` | |
| - `top_n` | |
| ### Выход (`SemanticSearchResponse`) | |
| - `results[]`: `lemma`, `score (1..100)`, `type (word|phrase)` | |
| --- | |
| ## 4.4 URL Import API | |
| ### `GET /api/v1/url/user-agents` | |
| Возвращает список пресетов user-agent для выбора в UI. | |
| ### `POST /api/v1/url/fetch` | |
| Извлекает `title` и основной `text` страницы: | |
| - вход: `url`, `user_agent`, `timeout_seconds` | |
| - выход: `ok`, `status_code`, `title`, `text`, `error`, `final_url`, agent-метаданные. | |
| Обработка ошибок не ломает UI: endpoint возвращает `ok=false` и `error`. | |
| --- | |
| ## 4.5 `POST /api/v1/optimizer/run` | |
| ### Назначение | |
| Итеративная локальная дооптимизация target-текста через LLM. | |
| ### Вход (`OptimizerRequest`) | |
| - аналитические данные: `target_text`, `competitors`, `keywords`, `language`, `target_title`, `competitor_titles` | |
| - LLM: `api_key`, `api_base_url`, `model`, `temperature` | |
| - стратегия: `max_iterations`, `candidates_per_iteration`, `optimization_mode`, `phrase_strategy_mode`, `bert_stage_target` | |
| - `phrase_strategy_mode`: `auto | distributed_preferred | exact_preferred | ensemble` | |
| - `ensemble`: в пределах итерации циклически пробует несколько phrase-стратегий и ранжирует кандидаты общей utility-функцией. | |
| - `bert_stage_target`: пользовательский порог завершения этапа A (BERT), например `0.61` вместо `0.70`. | |
| ### Выход (`OptimizerResponse`) | |
| - `optimized_text` — итоговый body (target) | |
| - `optimized_title` — итоговая строка для поля **Title**; в ответе она берётся из снимка `title_analysis.target_title` (тот же текст, что учитывался в метрике Title BERT), с запасным вариантом из переменной оптимизатора. В `final_metrics` дополнительно есть `resolved_title` с тем же смыслом (удобно для UI/fallback). | |
| - `baseline_metrics`, `final_metrics` | |
| - `iterations[]` (подробный лог шагов) | |
| - `applied_changes` | |
| - `optimization_mode` | |
| - `phrase_strategy_mode` | |
| - `bert_stage_target` | |
| - `stopped_early`, `stop_reason` — при ручной остановке (частичный результат) | |
| - `error` (если есть) | |
| ### `POST /api/v1/optimizer/run-stream` (SSE) | |
| Тело как у `run`. Поток `text/event-stream`, события `job` (с `job_id`), `preparing`, `started`, `step_start`, `llm_call`, затем `complete` с полем `result` или `error`. | |
| ### `POST /api/v1/optimizer/cancel` | |
| Тело: `{"job_id": "..."}`. Только флаг отмены; клиент дочитывает SSE до `complete`. | |
| ### UI и HF/SES | |
| Клиентский код: **`static/js/app.js`** + `GET /static/js/app.js`. Прогресс оптимизатора — **локальная панель с текстовым логом** (и тонкая полоса), **без** `#loader`. Подробности про SES и «мёртвые кнопки»: `docs/HF_SES_AND_UI.md`. | |
| --- | |
| ## 5) Подробная алгоритмика по модулям | |
| ## 5.1 `logic.py` — SEO-ядро | |
| ### `load_model_if_missing(lang)` | |
| Ленивая загрузка spaCy-модели конкретного языка. Цель: не загружать все модели на старте (критично для HF ресурсов). | |
| ### `load_models()` | |
| Служебная массовая загрузка моделей (используется ограниченно; основной путь в проде — lazy). | |
| ### `get_doc(text, lang)` | |
| Единая точка получения spaCy `Doc` с предобработкой языка/модели. | |
| ### `is_valid_token(t)` | |
| Фильтр значимых токенов (исключает шумовые категории: punctuation/space/часть stop и др.). | |
| ### `get_lemmas_flat(text, lang)` | |
| Плоский список лемм значимых токенов. Базовый строительный блок для метрик. | |
| ### `generate_ngrams_safe(text, lang, n)` — Smart Window | |
| Ключевой принцип: | |
| - размер окна задается по **значимым** словам; | |
| - stop-слова внутри валидного окна могут сохраняться для естественных фраз; | |
| - символные границы (punct/num/sym) не дают сшивать ложные фразы. | |
| Это гарантирует более естественные n-grams и согласованность между разными подсистемами. | |
| ### `count_words(text, lang)` | |
| Возвращает: | |
| - `total` — количество словоформ | |
| - `significant` — количество значимых токенов после фильтра | |
| ### `calculate_ngram_stats(target_text, competitor_texts, lang)` | |
| Строит частотные словари 1..4-грамм, агрегирует: | |
| - частоты target | |
| - средние частоты competitors | |
| - сигналы дефицита/избытка | |
| - детализацию по каждому конкуренту (для интерфейсных таблиц) | |
| ### `parse_keywords(raw_phrases, lang)` | |
| Нормализует сырые ключи пользователя в: | |
| - фразовые ключи | |
| - униграммы | |
| с учетом текущего языка и лемматизации. | |
| ### `calculate_bm25_recommendations(...)` — Mirror Principle | |
| BM25 использует тот же подход токенизации/фразогенерации, что и n-gram ядро. | |
| Смысл: | |
| - сравнить релевантность target и среднего competitor-профиля по тем же термам; | |
| - выдать действие: | |
| - `add` — недобор терма, | |
| - `remove` — вероятный переспам, | |
| - `ok` — баланс. | |
| ### `get_bert_model()` | |
| Ленивая инициализация sentence-transformers модели. | |
| ### `perform_bert_analysis(target_text, competitor_texts, key_phrases, lang)` | |
| Для каждой ключевой фразы: | |
| - ищет наиболее близкие чанки текста; | |
| - считает similarity для target и competitors; | |
| - формирует детализацию (`my_max_score`, competitor-сравнение, статусы). | |
| ### `analyze_title(target_title, competitor_titles, raw_keywords, lang)` | |
| Оркестратор Title-пайплайна: | |
| - `_title_length` | |
| - `_title_ngrams` | |
| - `_title_keyword_coverage` | |
| - `_title_bert` | |
| #### `_title_length(...)` | |
| Сравнивает длину target title с конкурентным диапазоном/средним. | |
| #### `_title_ngrams(...)` | |
| N-gram сопоставление title-уровня. | |
| #### `_title_keyword_coverage(...)` | |
| Проверяет покрытие пользовательских ключей в target и competitor title. | |
| #### `_title_bert(...)` | |
| Оценивает semantic-близость title к ключевому набору. | |
| --- | |
| ## 5.2 `nlp_processor.py` | |
| ### `preprocess_text(text, lang)` | |
| Преобразует текст в структуру предложений: | |
| - `raw_text` | |
| - `tokens[]` с полями `text`, `whitespace`, `lemma`, `is_significant`, `is_punct`, `is_space` | |
| - `lemmas_clean` (очищенный список лемм) | |
| Критично: сохранение `whitespace` и исходных токенов позволяет восстановить текст UI-послойно без потери форматирования. | |
| --- | |
| ## 5.3 `semantic_graph.py` — математическое ядро | |
| ### `_normalize_to_1_100(values)` | |
| Нормализация произвольных весов в целочисленную шкалу `1..100`. | |
| ### `_extract_significant_lemmas(sent)` | |
| Достает значимые леммы из предложения. | |
| ### `_is_noise_sentence(text)` | |
| Отбрасывает шумовые фрагменты (короткие CTA, boilerplate-паттерны). | |
| ### `_canonicalize_term(term)` | |
| Rule-based каноникализация термов (снижение дублей и вариативности). | |
| ### `_extract_phrase_candidates(sentence_text, lang)` | |
| Извлечение кандидатных фраз через `generate_ngrams_safe` и фильтры. | |
| ### `_normalize_lemma_sequence(lemmas)` | |
| Нормализация последовательностей лемм для устранения артефактов. | |
| ### `build_semantic_graph(sentences_data, lang)` | |
| Базовые шаги: | |
| 1. Сформировать множество терминов (слова + фразы). | |
| 2. Подсчитать частоты терминов и совместные появления. | |
| 3. Построить направленный граф. | |
| 4. Рассчитать вес ребра: | |
| - `P(B|A) = cooc(A,B) / occ(A) * 100` | |
| - затем ограничение в `0..100`. | |
| 5. Рассчитать важность узлов: | |
| - PageRank как глобальная связность, | |
| - termness/coverage корректировки, | |
| - штрафы для слишком общих доменных токенов. | |
| 6. Вернуть граф и карту `word_weights`. | |
| ### `get_graph_data_for_frontend(graph, top_edges_per_node=8)` | |
| Сериализует `networkx` граф в плоский JSON: | |
| - `nodes[]` | |
| - `links[]` | |
| с ограничением числа ребер на узел для управляемого рендера. | |
| ### `get_top_keywords(node_weights, top_n=20)` | |
| Возвращает top-N терминов по весу. | |
| --- | |
| ## 5.4 `highlighter.py` | |
| ### `generate_markup_for_frontend(sentences_data, word_weights, threshold=50)` | |
| Маркирует важные блоки: | |
| - если вес леммы/фразы >= порога, блок становится `is_link=true`; | |
| - соседние значимые токены могут объединяться в один кликабельный сегмент; | |
| - возвращается структура, удобная для реактивного рендера в UI. | |
| --- | |
| ## 5.5 `summarizer.py` | |
| ### `generate_summary(sentences_data, word_weights, compression_ratio)` | |
| Скоринг предложения: | |
| - `score = sum(weight(unique_lemmas)) / sqrt(token_count)` | |
| Далее: | |
| 1. сортировка по score убыв. | |
| 2. выбор top по `compression_ratio` | |
| 3. восстановление хронологического порядка для читабельности. | |
| --- | |
| ## 5.6 `search.py` | |
| ### `_normalize_query_text(text)` | |
| Нормализует запрос для устойчивого поиска. | |
| ### `semantic_search(query_text, G, word_weights, language, top_n)` | |
| Алгоритм: | |
| 1. Нормализовать и лемматизировать запрос. | |
| 2. Приоритетно проверить фразы (tri/bi) из запроса. | |
| 3. Fallback на слова. | |
| 4. Для найденных точек входа добавить соседей по графу. | |
| 5. Собрать score из силы связи и веса узла. | |
| 6. Нормализовать score в `1..100`. | |
| 7. Вернуть top-N и тип (`phrase`/`word`). | |
| --- | |
| ## 5.7 `url_fetcher.py` | |
| ### `get_user_agent_presets()` | |
| Возвращает список пресетов (Googlebot, Bingbot, ChatGPT user-agent, GPTBot, Chrome Desktop и др.). | |
| ### `_normalize_whitespace(text)` | |
| Схлопывает лишние пробелы/переводы строк. | |
| ### `_normalize_url(url)` | |
| Приводит URL к валидному виду (схема, trimming). | |
| ### `_resolve_user_agent(user_agent_key)` | |
| По ключу выбирает фактическую строку user-agent. | |
| ### `_extract_main_text_and_title(html)` | |
| HTML extraction pipeline: | |
| - удалить `script/style/noscript/nav/footer/header/form/svg` и прочий boilerplate; | |
| - приоритетно извлекать `article/main`; | |
| - fallback на абзацы/списки; | |
| - final fallback на `body` текст; | |
| - вернуть очищенный `title` и основной `text`. | |
| ### `fetch_url_content(url, user_agent_key, timeout_seconds)` | |
| Выполняет HTTP-запрос и возвращает структурированный результат для UI/API. | |
| --- | |
| ## 5.8 `optimizer.py` — LLM-оптимизация текста | |
| ### Цель модуля | |
| Итеративно улучшать конкретные проблемные зоны из аналитики, избегая полной перегенерации текста и сохраняя стиль/повествование. | |
| ### Служебные функции подготовки | |
| - `_tokenize` — токенизация строки. | |
| - `_filter_stopwords` — удаление stop-слов. | |
| - `_split_sentences` — сегментация на предложения. | |
| - `_max_sentences_for_level` — лимиты длины кандидата по каскаду. | |
| - `_validate_candidate_text` — pre-check качества (пустота, дубль слова/сущности, подозрительные токен-склейки, превышение лимита предложений). | |
| ### Снимки аналитики | |
| - `_build_analysis_snapshot` — пересчет `/analyze` локально. | |
| - `_build_semantic_snapshot` — пересчет semantic среза локально. | |
| ### Скоринг и выбор цели | |
| - `_compute_metrics` — единый набор метрик состояния: | |
| - composite score | |
| - `bert_low_count` | |
| - `bert_phrase_scores` | |
| - `bm25_remove_count` | |
| - сигналы n-gram/semantic | |
| - `title_bert_score` | |
| - `semantic_gap_count`, `semantic_gap_sum`, `semantic_gap_terms` | |
| - `_choose_optimization_goal` — выбирает приоритетную проблему. | |
| - для BERT-целей используется порог `BERT_TARGET_THRESHOLD=0.7` (допустимый рабочий минимум может быть снижен до `0.65`), без конкурентного override; | |
| - `_choose_sentence_idx` — выбирает релевантный чанк для правки. | |
| ### Параметры Semantic Gap в оптимизаторе | |
| - `SEMANTIC_GAP_TOLERANCE_PCT = 0.15` — допускается отклонение до ~15% от target, без штрафа. | |
| - `SEMANTIC_GAP_MIN_ABS = 3.0` — игнорируются микрошумы с маленькой абсолютной разницей. | |
| - Gap считается значимым, если одновременно: | |
| - `competitor_avg_weight > target_weight * (1 + tolerance)` | |
| - `competitor_avg_weight - target_weight >= min_abs` | |
| Это убирает пере-жесткую подгонку к среднему конкуренту и снижает ложные колебания `semantic_gap_count`. | |
| ### Генерация кандидатов | |
| - `_llm_edit_chunk` — отправляет structured prompt в OpenAI-compatible API. | |
| - роль модели в prompt: **semantic-vector optimizer for SEO**, а не общий “copy editor”. | |
| - учитывает `cascade_level` и тип операции (`rewrite`/`insert`) | |
| - явно требует грамматически корректный и естественный текст | |
| - ограничивает число предложений по уровню | |
| - для BERT динамически выбирает стратегию по длине целевой фразы: | |
| - короткие цели: допустим один natural exact match; | |
| - длинные multi-word цели: приоритет у distributed semantic coverage (части фразы/леммы/близкие формулировки), без forced exact match. | |
| - exact phrase не должен повторяться: при неестественном звучании он запрещается в пользу распределённой формулировки. | |
| - для `rewrite` явно требует сохранить исходный смысл `sentence-by-sentence` и не менять субъект/ключевую сущность без необходимости. | |
| ### Применение правок | |
| - `_replace_span` — замена диапазона предложений. | |
| - `_insert_after` — вставка после диапазона. | |
| ### Принятие/отклонение кандидата | |
| - `_goal_improved`: | |
| - для BERT: улучшение score целевой фразы минимум на `BERT_GOAL_DELTA_MIN=0.005` **или** снижение `bert_low_count`; | |
| - для других целей: профильные метрики улучшения. | |
| - `_candidate_utility`: | |
| - многоцелевая функция полезности кандидата с динамическими весами; | |
| - учитывает одновременно `bert_phrase_delta`, `chunk_goal_delta`, `score_delta`; | |
| - добавляет мягкие штрафы за регрессии по BM25/BERT-low/N-gram/SemanticGap/Title; | |
| - в BERT-push режиме (когда фраза ниже порога) усиливает вес phrase-level прогресса. | |
| - `_is_candidate_valid`: | |
| - hard constraints (не ухудшать критичные метрики сверх допустимого); | |
| - режимы `conservative/balanced/aggressive` задают пороги регрессии; | |
| - решение учитывает и `goal_improved`, и общий `delta_score`. | |
| - `_is_stage_complete` для `bert`: | |
| - этап считается завершённым только когда **каждая** отслеживаемая ключевая фраза достигает `bert_stage_target` (проверка по `min(bert_phrase_scores)`); | |
| - достижение порога одной «сильной» фразой больше не завершает BERT-этап. | |
| - унифицированный цикл по целям: базовые параметры запроса `max_iterations` и `candidates_per_iteration` задают «якорь», но для **каждой** цели вычисляется эффективный бюджет (`_per_goal_budget`): число попыток и ширина пула кандидатов **масштабируются по дефициту** до таргета — для BERT по разрыву score до порога, для semantic по `semantic_gap`, для n-gram по отставанию/перегрузу относительно целевого счётчика, для BM25 по «лишним» вхождениям слова, для title по разрыву `title_bert_score`. После исчерпания лимита по текущей цели оптимизатор переходит к следующей цели той же стадии. | |
| - `_validate_candidate_text`: | |
| - отклоняет некачественные/спамные кандидаты (дубли слов/сущностей, подозрительные склейки токенов); | |
| - добавляет anti-stuffing фильтр для цели BERT (повторы exact phrase и чрезмерные повторы focus-термов). | |
| ### Главная функция `optimize_text` | |
| Итерационный цикл: | |
| 1. baseline metrics. | |
| - общий бюджет шагов оценивается как **сумма эффективных итераций по всем целям** (`_estimate_total_loop_budget`: для каждой цели — `_per_goal_budget`, затем сумма по стадиям с верхней отсечкой), то есть масштабируется и по числу целей, и по величине отставания от таргета. В SSE-событии `step_start` дополнительно передаются `goal_budget_iter` и `goal_budget_candidates` для текущей цели. | |
| 2. выбрать goal. | |
| 3. выбрать пул чанков и операцию каскада. | |
| - **Этап `title`:** если средняя BERT-близость Title к ключам (`title_bert_score`) ниже порога (`TITLE_TARGET_THRESHOLD` ≈ 0.65), цель — **только переписать текст из поля Title** (`target_title`), а не абзац основного текста. LLM получает текущий title, выдержку из body и ключевые слова; метрики пересчитываются с новым title. Пакетные правки по body с title не смешиваются. | |
| - **Проверка деплоя:** в debug кандидата для шага `title` в `llm_prompt_debug` должно быть `"operation": "title_rewrite"`, а `chunk_text` — короткая строка текущего Title. Если видите `"operation": "rewrite"` и длинный `chunk_text` из body — на сервере старая версия `optimizer.py` (или не пересобран образ). | |
| - на шаг выбирается несколько span-кандидатов (multi-chunk selection), а не один; | |
| - ранжирование учитывает `focus_terms/avoid_terms`, chunk-level relevance и шумовые эвристики (menu/CTA/header penalties); | |
| - для **n-gram** целей предложения ранжируются через **скользящие перекрывающиеся окна** из 2–4 предложений (шаг 1): каждому предложению присваивается лучший балл среди окон, оценка штрафует локальные повторы фразы и шумовые блоки; | |
| - для BERT-целей ранжирование не ограничивается участками с already-present вхождениями: дополнительно приоритизируются релевантные участки с недопредставленными core-термами, где их можно добавить естественно; | |
| - используется `attempt_cursor` по цели и `attempted_spans`, чтобы избежать циклов по одному и тому же участку. | |
| 4. сгенерировать `N` кандидатов для каждого выбранного span (`N` зависит от эффективного бюджета кандидатов для цели и каскада, см. `_per_goal_budget` и деление по span). | |
| 5. pre-validation (формат/качество/длины). | |
| 6. chunk-level оценка: | |
| - вычисляется `chunk_goal_delta` (релевантность чанка до/после к текущей цели); | |
| - кандидат помечается `local_chunk_improved`, если прирост выше порога цели. | |
| 7. document-level оценка: | |
| - полный пересчет метрик для всего текста; | |
| - проверка глобальных ограничений через `_is_candidate_valid`. | |
| 8. staged acceptance: | |
| - если кандидат улучшает цель/общий score и проходит глобальные ограничения — `applied`; | |
| - если локально улучшает чанк, но глобально не проходит — кандидат кладется в queue. | |
| - для BERT учитывается прямой документный `bert_phrase_delta` по целевой фразе: даже небольшой положительный рост считается полезным шагом при отсутствии регрессий по guardrails. | |
| - если нет `promotable` кандидата, но есть guardrail-valid кандидат с `local_chunk_improved`, применяется режим `applied_local_progress`: правка принимается локально и оптимизация переходит к следующему чанку (накопительная стратегия). | |
| - ранжирование и выбор best-кандидата дополнительно учитывают `candidate_utility`, чтобы BERT-оптимизация не вредила следующим этапам по другим метрикам. | |
| 9. batch-логика queue: | |
| - optimizer пробует совместно применить комбинации из 2..4 локально сильных не конфликтующих правок; | |
| - batch принимается только при прохождении глобальных ограничений и положительном совокупном локальном приросте. | |
| 10. при серии неудач эскалировать каскад (`L1 -> L2 -> L3 -> L4`), при успехе сбрасывать на `L1`. | |
| - `L1`: локальный rewrite (обычно 1 предложение), | |
| - `L2`: rewrite расширенного окна (2-3 предложения, несколько вариантов радиусов), | |
| - `L3`: insert bridge (вставка) с возможным fallback на rewrite, | |
| - `L4`: более широкий rewrite окна (до 5 предложений с вариативным охватом). | |
| 11. вести подробный лог по каждому кандидату. | |
| - в debug-таблице фиксируются и chunk-level сигналы (`local+`, `chunk Δ`, `rel before->after`) наряду с глобальными (`Δ score`, `valid`, `goal+`); | |
| - для каждого кандидата сохраняется `llm_prompt_debug` (операция, цель, фокус-термы, chunk и ближайший контекст), что позволяет анализировать фактический вход в LLM; | |
| - LLM возвращает поле `rationale` (1 строка) — краткое объяснение, почему правка должна повысить релевантность цели. | |
| - также сохраняется `metrics_delta` (вклад BM25/BERT/Semantic/N-gram/Title в общий сдвиг), включая `semantic_gap_sum` и изменение состава gap-термов (`semantic_gap_terms_added/removed`), чтобы видеть, за счет чего падает или растет `score`. | |
| --- | |
| ## 6) Frontend (`templates/index.html` + `static/js/app.js`) | |
| ## 6.1 Ввод данных и URL import | |
| - `loadUserAgentOptions` — загрузка пресетов UA. | |
| - `fetchUrlPayload` — запрос к URL API. | |
| - `fetchTargetFromUrl` — заполнение target text/title из URL. | |
| - `fetchCompetitorsFromUrls` — массовое заполнение competitors. | |
| Ручной ввод всегда остается рабочим fallback-сценарием. | |
| ## 6.2 Локальное сохранение проекта | |
| - `saveProject` — экспорт JSON. | |
| - `loadProject` — загрузка JSON. | |
| - `applyProjectData` — восстановление полей и результатов. | |
| - `clearProject` — новый проект/сброс. | |
| API-ключ оптимизатора в persist-состояние не сохраняется. | |
| ## 6.3 Запуск аналитики и отрисовка | |
| - `runAnalysis` | |
| - `runSemanticAnalysis` | |
| - `runSemanticSearch` | |
| - `renderResults` | |
| - `renderSemanticResults` | |
| - `renderTitleResults` | |
| - `showNgramTable` | |
| ## 6.4 Сводка и оптимизатор | |
| - `renderActionSummary` — агрегирует рекомендации BERT/BM25/N-grams/Title/Semantic в табличный формат. | |
| - `runLlmOptimization` — `POST /api/v1/optimizer/run-stream` (SSE); локальная панель **лога** + тонкий progress bar; **без** `#loader`. | |
| - `requestStopOptimizer` — `POST /api/v1/optimizer/cancel`; поток дочитывается до `complete` (частичный результат). | |
| - `optimizerLogAppend` / `applyOptimizerStreamEvent` — текстовый ход работы. | |
| - `renderOptimizerResults` — итог и debug-лог; баннер при `stopped_early`. | |
| - `applyOptimizedText` — перенос optimized текста в `target_text`. | |
| - `nv(v, d)` — nullish-fallback без операторов `??` (SES на HF). | |
| ## 6.5 Сортировка таблицы мощных терминов | |
| - `setSemanticTermSortBy` | |
| - `toggleSemanticTermSortDir` | |
| Поддерживаются сортировки по: | |
| - `Мой вес` | |
| - `Avg K` | |
| - `Freq (X/Y)` (с приоритетом большего `X` при одинаковом `Y`) | |
| --- | |
| ## 7) Данные и модели (`models.py`) | |
| Ключевые модели: | |
| - `AnalysisRequest`, `AnalysisResponse` | |
| - `SemanticAnalyzeRequest`, `SemanticAnalyzeResponse` | |
| - `SemanticSearchRequest`, `SemanticSearchResponse` | |
| - `UrlFetchRequest`, `UrlFetchResponse`, `UserAgentInfo`, `UserAgentsResponse` | |
| - `OptimizerRequest`, `OptimizerResponse` | |
| Роль моделей: | |
| - жестко фиксируют API-контракты; | |
| - упрощают валидацию; | |
| - создают стабильный интерфейс между frontend/backend. | |
| --- | |
| ## 8) Практические вычислительные принципы | |
| 1. **Согласованная нормализация** | |
| Одни и те же правила токенизации/лемматизации используются в нескольких модулях, чтобы избежать рассинхронизации метрик. | |
| 2. **Локальные правки вместо полной перегенерации** | |
| Оптимизатор меняет только локальные участки текста и проверяет эффект после каждой правки. | |
| 3. **Многокритериальная защита** | |
| Кандидат не принимается, если улучшение одной метрики достигается ценой неприемлемой деградации других. | |
| 4. **Объяснимость** | |
| Подробный лог итераций фиксирует baseline шага, кандидатов, причины отклонения и примененный вариант. | |
| --- | |
| ## 9) Рекомендации по воспроизведению приложения по документации | |
| Минимальный путь воспроизведения: | |
| 1. Поднять FastAPI-приложение с endpoint-ами из раздела 4. | |
| 2. Реализовать `logic.py` и `semantic_*` модули с описанными формулами и пайплайнами. | |
| 3. Сделать frontend с соответствующими сценариями (`runAnalysis`, `runSemanticAnalysis`, `runLlmOptimization`). | |
| 4. Добавить URL extractor и LLM optimizer как отдельные backend сервисы. | |
| 5. Проверить контракты ответов, чтобы UI-таблицы и вкладки заполнялись без адаптеров. | |
| --- | |
| ## 10) Эксплуатация и деплой (Hugging Face) | |
| - Рекомендуемый режим: lazy загрузка моделей. | |
| - При проблемах типа `Could not resolve host: huggingface.co` рассматривать как внешнюю инфраструктурную проблему DNS/egress. | |
| - Для диагностики сверять: | |
| - `repo_sha` и `runtime_sha` | |
| - `runtime_stage` | |
| - При зависании сборки использовать повторный trigger-build. | |
| --- | |
| ## 11) Smoke-check после любых изменений | |
| 1. `python -m py_compile app.py logic.py semantic_graph.py search.py optimizer.py url_fetcher.py` | |
| 2. Проверка endpoint-ов: | |
| - `/analyze` | |
| - `/api/v1/semantic/analyze` | |
| - `/api/v1/semantic/search` | |
| - `/api/v1/url/user-agents` | |
| - `/api/v1/url/fetch` | |
| - `/api/v1/optimizer/run` | |
| 3. Проверка UI: | |
| - табы рендерятся; | |
| - сортировки и таблицы работают; | |
| - URL import заполняет text/title; | |
| - save/load/new project работают; | |
| - оптимизатор пишет лог и применяет текст. | |