# 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`) - итеративная локальная оптимизация текста - многокритериальный скоринг с защитой от деградации - каскад уровней правок (от минимальных к более широким) - детализированный 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-оптимизация с обратной связью от метрик. - `templates/index.html` — frontend (UI + клиентская логика JS). --- ## 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` ### Выход (`OptimizerResponse`) - `optimized_text` - `baseline_metrics`, `final_metrics` - `iterations[]` (подробный лог шагов) - `applied_changes` - `optimization_mode` - `error` (если есть) --- ## 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. - учитывает `cascade_level` и тип операции (`rewrite`/`insert`) - явно требует грамматически корректный и естественный текст - ограничивает число предложений по уровню ### Применение правок - `_replace_span` — замена диапазона предложений. - `_insert_after` — вставка после диапазона. ### Принятие/отклонение кандидата - `_goal_improved`: - для BERT: улучшение score целевой фразы минимум на `BERT_GOAL_DELTA_MIN=0.005` **или** снижение `bert_low_count`; - для других целей: профильные метрики улучшения. - `_is_candidate_valid`: - hard constraints (не ухудшать критичные метрики сверх допустимого); - режимы `conservative/balanced/aggressive` задают пороги регрессии; - решение учитывает и `goal_improved`, и общий `delta_score`. ### Главная функция `optimize_text` Итерационный цикл: 1. baseline metrics. 2. выбрать goal. 3. выбрать пул чанков и операцию каскада. - на шаг выбирается несколько span-кандидатов (multi-chunk selection), а не один; - ранжирование учитывает `focus_terms/avoid_terms`, chunk-level relevance и шумовые эвристики (menu/CTA/header penalties); - используется `attempt_cursor` по цели и `attempted_spans`, чтобы избежать циклов по одному и тому же участку. 4. сгенерировать `N` кандидатов для каждого выбранного 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`: правка принимается локально и оптимизация переходит к следующему чанку (накопительная стратегия). 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. - также сохраняется `metrics_delta` (вклад BM25/BERT/Semantic/N-gram/Title в общий сдвиг), включая `semantic_gap_sum` и изменение состава gap-термов (`semantic_gap_terms_added/removed`), чтобы видеть, за счет чего падает или растет `score`. --- ## 6) Frontend (`templates/index.html`) — сценарии и функции ## 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` — запуск оптимизации. - `renderOptimizerResults` — итог и debug-лог по шагам/кандидатам. - `applyOptimizedText` — перенос optimized текста в `target_text`. ## 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 работают; - оптимизатор пишет лог и применяет текст.