Перейти к содержимому
Оригинал
Juejin · Популярное об ИИ за неделю· 冬奇Lab·· 10 часов назадИзбранное редакциейОценка ИИ71

Автоматизированное тестирование на основе LLM (10): сравнение механизмов эвристической оценки Healenium и семантического самовосстановления с помощью LLM

Краткий обзор ИИ

Автор декомпилировал байт-код tree-comparing-0.4.14.jar из Maven Central и выяснил, что заявленное в Healenium «самовосстановление на основе машинного обучения» на деле представляет собой жёстко заданную формулу взвешенной оценки с весами TAG=100, ID=50, CLASS=40, VALUE=30, а сходство вычисляется через расстояние редактирования Левенштейна — ни обучения, ни инференса здесь нет.

Почему это важно

Сравнивая байт-код с исходным кодом, автор разбирает, чем отличаются два подхода — эвристическая оценка Healenium и семантическое самовосстановление на основе LLM — и где проходит граница возможностей каждого из них.

Полный текст · Перевод ИИ

Какую задачу решает самовосстанавливающаяся локация и какое утверждение нужно сначала разоблачить

Самая хрупкая часть автоматизированного тестирования Web UI — не логика утверждений, а локация элементов — id, class, иерархия DOM может сломаться при любом редизайне, и скрипт выдаёт ошибку не из-за бизнес-логики, а из-за «селектор этой кнопки не найден». **Самовосстанавливающаяся локация (Self-Healing Locators)** решает именно эту проблему: при неудачной локации система пытается автоматически найти новый способ локации «того же самого элемента», вместо того чтобы тест просто покраснел и ждал, пока человек починит селектор.

Самый старый и самый звёздный открытый проект в этой области — Healenium (healenium/healenium-web, 201 звёзд, последний коммит 2026-09-07, всё ещё активно поддерживается). В его README есть фраза: «leverages machine learning to allow Selenium tests to self-heal». Первое, что делает эта статья, — читает исходный код, чтобы проверить эту фразу. Вывод: в маркетинговом смысле это не ложь, но в техническом смысле это вводит в заблуждение. Основной механизм самовосстановления Healenium — не машинное обучение, а набор эвристических формул с взвешенным скорингом, где веса — жёстко закодированные константы с плавающей точкой; обучающих данных, вывода модели и других базовых элементов машинного обучения в этих формулах нет вообще.

Сначала обозначим три вопроса, на которые отвечает эта статья: как именно считается этот «якобы машинно-обучаемый» эвристический скоринг Healenium, и где его потолок? Какую конкретную форму имеет AI-возможность, добавленная в 2026 году, и почему в коде она заперта за платным доступом? Для сравнения: какой архитектурой пользуется действительно основанный на LLM открытый проект семантического самовосстановления — MarketSquare/robotframework-selfhealing-agents — чтобы реализовать «перелокацию по тому, что элемент делает, а не по тому, где он находится»?


Формула скоринга Healenium: схожесть дерева + расстояние Левенштейна, без единой капли машинного обучения

Процесс самовосстановления Healenium делится на два слоя: SelfHealingEngine (healenium-web основной репозиторий) во время выполнения теста перехватывает неудачную локацию, разбирает DOM текущей страницы в дерево Node (через JsoupHTMLParser), а затем передаёт его отдельно публикуемому tree-comparing (Maven-координаты com.epam.healenium:tree-comparing, версия зафиксирована в pom.xml healenium-web как 0.4.14) для скоринга совпадения деревьев.

Ядро логики сопоставления находится в PathFinder.findScoresToNodes(): сначала с помощью LCSPathDistance (наибольшая общая подпоследовательность) сравниваются «исторический путь отказавшего элемента» и «пути всех листовых узлов текущей страницы», находятся структурно наиболее похожие пути-кандидаты, а затем для каждого узла на пути-кандидате вызывается скоринг HeuristicNodeDistance.distance(). Эта функция скоринга проверена прямой декомпиляцией байт-кода (tree-comparing-0.4.14.jar на Maven Central, числа с плавающей точкой в пуле констант полностью совпадают с исходником), и формула выглядит так:

private static final double POINTS_FOR_TAG = 100.0D;
private static final double POINTS_FOR_ID = 50.0D;
private static final double POINTS_FOR_CLASS = 40.0D;
private static final double POINTS_FOR_VALUE = 30.0D;
private static final double POINTS_FOR_OTHER_ATTRIBUTE = 30.0D;
// maximumScore = 350.0

double score = LCSDistance / curPathHeight * 100.0;   // 路径结构相似度部分
if (tag相同) score += 100.0;
if (id存在) score += 50.0 * levenshteinScore(id1, id2, 0.3);
score += 30.0 * levenshteinScore(innerText1, innerText2, 0.3);
score += classNamesIntersect比例 * 40.0;
score += otherAttributes逐项Levenshtein均值 * 30.0;
return score / maximumScore;   // 归一化到 [0, 1]

Внутри calculateLevenshteinScore() используется LevenshteinDistance из Apache Commons Text, который преобразует расстояние редактирования двух строк в значение схожести [0,1]; пороговый параметр (0.3 или 0.75) определяет, при каком расстоянии редактирования строки сразу считаются непохожими. Во всей этой формуле нет ни капли «обучения» — веса (100, 50, 40, 30) это жёстко прописанные константы, а не параметры, подогнанные по данным; алгоритм схожести — это расстояние редактирования строк, а не векторные представления слов или семантические эмбеддинги. Если id элемента меняется с login-btn на signin-btn, расстояние Левенштейна мало, скор высокий, элемент признаётся «тем же самым»; но если он меняется на submit-auth с полностью тем же смыслом, но совершенно другой строкой, расстояние редактирования максимально, скор резко падает, хотя человеку очевидно, что это «та же самая кнопка входа». Именно в этом потолок эвристического скоринга: он измеряет поверхностное сходство строк и структуры, а не семантическое сходство функции элемента — поэтому упомянутое в плане «LLM-семантическое самовосстановление: перелокация по тому, что элемент делает, а не по тому, где он находится» — это реальный технический пробел, а не потребность, выдуманная маркетингом.

Итоговый скор фильтруется по порогу в HealingService с помощью score-cap из конфигурации (по умолчанию в application.conf — .6, в тестовой конфигурации — .55); узлы-кандидаты выше порога сортируются по убыванию скора и по очереди преобразуются в CSS-селектор или XPath, а с помощью driver.findElements() проверяется, можно ли однозначно локализовать один элемент — первый успешно проверенный и есть результат самовосстановления. Это полностью детерминированный движок правил: быстрый, объяснимый, не требующий никаких внешних вызовов API, но и границы его ясны: как только изменение выходит за пределы сравнимых измерений «тег, id, class, текст, атрибут» — например, весь компонент сменил реализацию, но семантика осталась прежней — эта формула уже ничего не может определить.


AI-интерфейс, добавленный в 2026 году: путь, напрямую закрытый платным доступом

В RestClient.java есть отдельный метод, не относящийся к вышеописанной логике скоринга:

public String getXpathSelector(Node node, String sessionId) {
    HttpRequest request = new HttpRequest(HttpMethod.POST, "/selectors/xpath");
    ...
    HttpResponse response = aiServiceExecute(request);
    if (HTTP_NOT_FOUND == response.getStatus()) {
        throw new RuntimeException("[Get Xpath Selector] Compatibility error. You must have a paid hlm-ai service.");
    }
    ...
}

Этот метод отправляет POST с информацией об узле DOM в отдельный aiServiceUrl (параметр конфигурации — hlm.ai.url, по умолчанию в application.conf указывает на http://localhost:6565), полностью развязанный с движком скоринга — HealingService.createXPathFromElement() вызывается только тогда, когда useXPath(engine) истинно (то есть когда selector-type настроен как xpath), в качестве альтернативного пути помимо построения CSS-селектора. Этот код сам по себе подтверждает две вещи: во-первых, команда Healenium сама признаёт, что чистого скоринга по правилам недостаточно, и специально открыла канал «пусть AI-сервис генерирует XPath»; во-вторых, этот канал явно закрыт платным доступом — при получении 404 сразу выбрасывается исключение с прямолинейным текстом без всяких прикрас: «You must have a paid hlm-ai service». Сам сервис hlm-ai — это закрытый коммерческий компонент, не входящий в открытый репозиторий, и какая там используется модель и как реализовано семантическое понимание, извне проверить совершенно невозможно.

Эта деталь ценнее самого суждения вроде «правдива ли AI-реклама какого-то звёздного инструмента»: она показывает, что даже самый старый и долго поддерживаемый открытый проект в этой области сам считает, что чистого скоринга по правилам уже недостаточно и нужно вводить семантическое понимание — но эту часть решили сделать закрытой и коммерческой, а не развивать сообща в открытом виде. Это не тот же случай, что паттерн, который мы увидим дальше в статье 11 (когда «умная» реклама звёздных проектов часто упаковывает механизмы без LLM): Healenium не занимается ложной рекламой, он честно признаёт ограничения, а затем делает заплатку платной функцией.


Настоящее LLM-самовосстановление: архитектура двух агентов «оркестрация — локация» в MarketSquare/robotframework-selfhealing-agents

MarketSquare/robotframework-selfhealing-agents (27 звёзд, Apache-2, создан 2025-4, последний коммит 2026-2, MarketSquare — официальная организация в экосистеме Robot Framework) — редкий в этой области открытый проект, в исходном коде которого можно напрямую подтвердить вызов настоящего LLM API. Это плагин-Listener для Robot Framework, обрабатывающий неудачную локацию с помощью двухуровневой архитектуры агентов:

Первый уровень: OrchestratorAgent, построенный на PydanticAI, чья задача — определить, «является ли эта неудача проблемой локатора». Сама логика определения тоже делегируется вниз по цепочке вызовов LLM к locator_agent.is_failed_locator_error() — например, в сценарии Selenium проверяется, соответствует ли текст ошибки шаблону вроде "with locator" ... "not found" — и если это не неудачная локация (например, провал утверждения или сетевой таймаут), сразу возвращается NoHealingNeededResponse, не тратя токены на запуск процесса исправления. Только после признания неудачи проблемой локатора запрос маршрутизируется в pydantic-ai Agent, где с помощью output_type=[self._get_healed_locators] «исправление локатора» регистрируется как вызываемый инструмент, и запускается настоящий цикл LLM tool-call.

Второй уровень: BaseLocatorAgent (и его подклассы SeleniumLocatorAgent/BrowserLocatorAgent) — это часть, которая действительно выполняет семантическую локацию, поддерживающая два режима работы, переключаемых параметром конфигурации use_llm_for_locator_generation (по умолчанию True):

  • Режим чистой генерации LLM: дерево DOM на момент отказа, сообщение об ошибке, строка отказавшего локатора и список уже испробованных, но всё ещё неудачных локаторов вместе отправляются в промпт, и модель напрямую выдаёт 3 новых кандидатов локаторов. Системный промпт написан прямолинейно: «Using the elements in the DOM at failure time, suggest 3 new locators... Make sure you do not suggest a locator that is on that list.» — это чисто семантическое рассуждение языковой модели, без какого-либо вмешательства скоринга по правилам.
  • Режим генерации DOM-инструментом + выбора LLM: сначала детерминированный код (_dom_utility.get_locator_proposals()) на основе структуры DOM перечисляет кандидатов локаторов, а затем отдельный selection_agent выбирает из списка кандидатов «наиболее подходящий». Этот путь — комбинация «детерминированная генерация + суждение LLM», которая по сравнению с режимом чистой генерации LLM снижает риск галлюцинации недействительного селектора, но и пространство для семантического понимания меньше — LLM может выбирать только из уже перечисленных кандидатов и не может выдать ответ за пределами множества кандидатов.

Выбор между этими двумя режимами — это, по сути, конкретный ответ на вопрос из плана о том, как балансировать между успешностью самовосстановления и вероятностью ложных срабатываний: режим чистой генерации через LLM даёт более широкий охват (модель может предложить способы локализации, которые пропустил детерминированный перебор), но чаще выдаёт галлюцинации — синтаксически корректные, но не находящие ни одного элемента; режим генерации через DOM-инструменты консервативнее и надёжнее, но его потолок упирается в то, что вообще способен найти детерминированный перебор. Проект решил вынести этот переключатель пользователю, а не выбирать за него, — и уже этим признаёт, что у обоих путей есть своя цена и ни один не даёт одновременно и высокого охвата, и низкой вероятности ложных срабатываний.

В слое проверки ответов есть ещё одна инженерная деталь, которую стоит зафиксировать: generation_agent.output_validator после получения вывода модели прогоняет его через дополнительный слой детерминированной фильтрации — _sort_locators() с помощью is_locator_unique() выдвигает вперёд кандидаты, которые однозначно указывают на один элемент, а _filter_clickable_locators() для случаев сбоя по ключевым словам вроде click/tap/select специально отбирает реально кликабельные элементы. Если в итоге ни один кандидат не проходит проверку, код выбрасывает ModelRetry и запускает встроенный в pydantic-ai механизм повторных попыток, а не возвращает потенциально ошибочный локатор. Это не то же самое, что подход «что модель сказала, то и правда» — результат семантического суждения LLM принимается только после проверки детерминированным кодом. Так «семантическое понимание» и «структурная корректность» разделяются на две независимые задачи, вместо того чтобы ждать, что один вызов модели сделает обе правильно.

Слой конфигурации (SelfhealingAgents/utils/cfg.py) подтверждает, что это действительно мультипровайдерная архитектура: orchestrator_agent_provider/locator_agent_provider поддерживают openai/azure, модель по умолчанию — gpt-4o-mini, а отдельные request_limit (по умолчанию 5) и total_tokens_limit (по умолчанию 6000) задают жёсткие ограничения по стоимости — эти ограничения и есть прямой ответ на опасение, что самовосстановление через LLM выйдет из-под контроля и сожжёт токены: у каждого запроса на самовосстановление есть чёткий лимит расхода токенов, при превышении процесс обрывается, а не повторяется бесконечно.


Где упирается баланс между успешностью самовосстановления и вероятностью ложных срабатываний

Если поставить механизмы двух проектов рядом, на вопрос из плана о том, когда самовосстановление маскирует настоящий баг UI-регрессии, находится конкретный ответ: и эвристическая оценка, и семантическое суждение LLM — сам механизм самовосстановления не умеет отличить безобидную правку деталей реализации от настоящей функциональной UI-регрессии. Он проверяет, есть ли на новой странице что-то похожее на старый элемент, а не соответствует ли изменение ожиданиям продукта. Формула оценки Healenium может выдать одинаково высокий балл и за чистое переименование id, и за случай, когда кнопку случайно удалили, а потом добавили новую — визуально похожую, но функционально другую; семантическое суждение LLM при появлении на странице кнопки с похожим текстом, но совершенно другой логикой тоже может обмануться поверхностным сходством.

Из этого следует, что разумная граница самовосстановления — не «поднять точность суждения почти до 100%», а относиться к результату самовосстановления как к сигналу, который нужно зафиксировать и проверить вручную, а не как к полностью надёжному автоисправлению, сразу делающему тест зелёным. Система отчётов robotframework-selfhealing-agents (reports/report_generator.py и целый ряд report_types) специально формирует healed_files_report/diff_files_report — уже сам этот дизайн признаёт, что «успешное самовосстановление» не равно «проблемы нет», а каждое восстановление записывается как изменение, которое нужно просмотреть, чтобы понять, не прячется ли за исправлением настоящая UI-регрессия. Это тот же ход, что и параметр backlight-healing в Healenium (по умолчанию true, в отчётах тестов подсвечиваются элементы, найденные самовосстановлением) — два проекта с совершенно разными техническими подходами сделали один и тот же инженерный выбор: результат самовосстановления должен быть прослеживаемым и проверяемым, и это говорит о границах консенсуса в этой области больше, чем любая отдельная формула оценки или дизайн промпта.


Итог

  1. Заявление в README Healenium о том, что проект «leverages machine learning», — маркетинговое преувеличение: основной механизм самовосстановления (HeuristicNodeDistance.distance()) — это формула взвешенной оценки с жёстко закодированными весами (TAG=100, ID=50, CLASS=40, VALUE=30), а для сходства используется расстояние Левенштейна, что никак не связано с парадигмой обучения и инференса в машинном обучении; подлинность исходников подтверждена декомпиляцией пула констант байт-кода tree-comparing-0.4.14.jar из Maven Central.
  2. Добавленный в Healenium в 2026 году RestClient.getXpathSelector() — это отдельный канал генерации XPath через ИИ: в коде при ответе 404 сразу выбрасывается исключение «You must have a paid hlm-ai service». Даже самый старый открытый проект в этой области признаёт, что чисто правиловая оценка не тянет семантическое понимание, но решает сделать эту часть возможностей закрытым платным сервисом, а не развивать её сообща.
  3. MarketSquare/robotframework-selfhealing-agents — это действительно открытая реализация самовосстановления на основе LLM с двухуровневой архитектурой: ИИ-агент-оркестратор (решает, нужно ли самовосстановление) плюс ИИ-агент локализации (генерирует или выбирает новый локатор). Поддерживаются два режима — «чистая генерация через LLM» и «перебор DOM + выбор через LLM»; они по-разному балансируют между охватом и вероятностью ложных срабатываний, и проект отдаёт этот выбор пользователю, а не задаёт ответ по умолчанию.
  4. Вывод LLM здесь не принимается на веру — слой output_validator проверяет детерминированным кодом уникальность и кликабельность предложенных локаторов, а при провале проверки запускает повторную попытку ModelRetry, а не возвращает потенциально ошибочный результат: семантическое суждение и проверка структурной корректности разведены на две независимые задачи.
  5. И эвристическая оценка, и семантическое суждение LLM — сам механизм самовосстановления не отличает безобидную правку реализации от настоящей функциональной регрессии. Два проекта с совершенно разными техническими подходами — один через подсветку backlight-healing, другой через структурированные отчёты healed/diff — выбрали один и тот же способ реакции: считать результат самовосстановления сигналом, который нужно зафиксировать и проверить вручную, а не полностью надёжным автоисправлением.

Заглядывайте на PrimeSkills — тщательно отобранный маркетплейс ИИ-агентов и навыков, где всё проверено на реальных корпоративных рабочих процессах. Никаких пустых обещаний — только то, что действительно работает.

Больше полезных материалов и интересных продуктов — на моей личной странице

Источник: Juejin · Популярное об ИИ за неделю · juejin.cn