Инструменты · Браузерная автоматизация · Лаборатория
Тестируем Browser Harness: может ли агент сам дописывать автоматизацию браузера
Главная проверка Browser Harness начинается там, где заканчивается заранее подготовленный набор кнопок. Обычная интеграция умеет открыть страницу, нажать элемент и ввести текст, но нестандартный web component, закрытый Shadow DOM или особая последовательность событий снова превращают автоматизацию в отдельный проект. В этой лаборатории AI-агент подключится к настоящему Chrome через Chrome DevTools Protocol, столкнётся с отсутствующей операцией, добавит узкий helper в редактируемый слой и применит его ещё раз после сброса страницы. Успех определим не по сообщению «готово», а по diff, состоянию браузера и независимому повторному запуску.
1. Что проверяет лаборатория
Browser Harness — тонкая управляющая оболочка, которая даёт агенту доступ к уже запущенному Chrome. Браузер остаётся отдельным процессом, а команды передаются через CDP — Chrome DevTools Protocol. В отличие от фиксированного сервера с десятком неизменяемых операций, harness предоставляет базовые примитивы и редактируемый файл agent_helpers.py.
Проверяемая гипотеза звучит так: если базовых примитивов недостаточно, агент способен изучить страницу, написать минимальную операцию для конкретной механики интерфейса, сохранить её в разрешённом слое и повторно применить без нового исследования.
В тесте нужно разделить три разных результата:
- Разовое выполнение. Агент каким-либо способом изменил страницу.
- Расширение harness. Агент добавил именованный helper с понятными аргументами и проверкой ошибок.
- Повторное использование. После сброса страницы тот же helper выполнил операцию ещё раз без изменения исходного кода.
2. Чем harness отличается от обычного инструмента
В традиционной схеме разработчик заранее создаёт функции click, fill, upload, select и регистрирует их для вызова инструментов. Если продукт использует необычный редактор, canvas, закрытое дерево компонентов или собственный протокол событий, требуется новая функция, новая схема аргументов, новая версия сервера и повторное развёртывание.
Browser Harness разделяет систему на два слоя:
- Защищённое ядро. Соединение с Chrome, выполнение CDP-команд, базовые действия, управление вкладками и загрузкой страницы.
- Редактируемая рабочая область. Файл
agent_helpers.pyи при необходимости доменные инструкции, которые агент может дополнять в рамках задачи.
Такой дизайн не отменяет разработку автоматизации. Он меняет момент и автора разработки: helper возникает при встрече с реальным препятствием, а не в результате попытки заранее предсказать все операции сайта.
У подхода есть цена. Код, написанный агентом, нельзя автоматически считать надёжным или безопасным. Редактируемый helper должен проходить те же проверки, что и обычный код: ограниченный diff, валидацию аргументов, обработку отсутствующих элементов, тайм-аут и повторный тест.
3. Критерии успеха
До запуска зафиксируйте критерии, чтобы не подгонять вывод под поведение агента.
| Проверка | PASS | FAIL |
|---|---|---|
| Соединение | page_info() возвращает сведения о реальной вкладке |
Агент работает с другим браузером или не видит страницу |
| Исходное состояние | До операции видны status=empty и нулевой счётчик |
Тест начинается с уже изменённой страницы |
| Изменение helper | Изменён только разрешённый файл рабочей области | Агент переписал ядро, страницу или тестовые ожидания |
| Функциональный результат | Компонент сообщает ожидаемое значение и увеличивает счётчик событий | Изменился только внешний текст или агент не проверил событие |
| Повторное использование | После сброса helper вызывается повторно без правок | Агент снова исследует DOM или меняет helper между запусками |
| Негативная проверка | Неизвестный вариант вызывает понятную ошибку | Helper молча сообщает успех или выбирает другое значение |
Итог PARTIAL уместен, если задача выполнена, но helper получился одноразовым: например, содержит жёсткие координаты, не проверяет результат или меняет внутреннее состояние компонента в обход пользовательского события.
4. Окружение и границы безопасности
Для повторения понадобятся:
- Chrome или Chromium с поддержкой удалённой отладки;
- Python 3.12 и менеджер
uvдля установки Browser Harness; - совместимый coding-агент с доступом к терминалу и отдельному тестовому каталогу;
- Git для фиксации исходного состояния и проверки diff;
- локальный HTTP-сервер;
- отдельный профиль браузера без рабочих cookies, паролей и открытых административных систем.
Подключение к повседневному профилю Chrome технически удобно, но расширяет последствия ошибки. Открытая сессия почты, панели облака или CRM становится доступной тому же браузерному контуру. Для лаборатории используйте пустой профиль и локальный адрес.
Ограничьте навигацию явным allowlist: http://127.0.0.1:8765. Не добавляйте в страницу внешние скрипты, реальные токены и пользовательские данные. Содержимое любой открытой страницы считайте недоверенным: текст интерфейса способен содержать prompt injection.
Создайте лабораторию и зафиксируйте окружение:
mkdir -p browser-harness-lab/{fixture,evidence,workspace}
cd browser-harness-lab
{
date -u
python3 --version
uv --version
git --version
} | tee evidence/environment.txt
git init
git config user.name "Browser Harness Lab"
git config user.email "lab@example.invalid"
Адрес example.invalid намеренно не является настоящим почтовым адресом. Если Git уже настроен глобально, локальные значения можно не задавать.
5. Установка и подключение Chrome
Устанавливайте конкретную проверяемую версию пакета, если лаборатория должна воспроизводиться позднее. Команда обновления до последнего выпуска удобна для первичной пробы, но после неё обязательно сохраните фактическую версию.
uv tool install --python 3.12 --upgrade --force browser-harness
browser-harness --help > evidence/browser-harness-help.txt
uv tool list | tee evidence/uv-tools.txt
Затем экспортируйте актуальную инструкцию Browser Harness в каталог навыков вашего агента. Путь зависит от агентной среды; для Codex он обычно выглядит так:
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills/browser-harness"
browser-harness skill \
> "${CODEX_HOME:-$HOME/.codex}/skills/browser-harness/SKILL.md"
Если политика вашей системы не разрешает запись в пользовательский каталог, зарегистрируйте полученный текст инструкции поддерживаемым способом. Не изменяйте vendor-кеши вручную.
Проверка соединения
browser-harness --doctor | tee evidence/doctor.txt
browser-harness <<'PY'
print(page_info())
PY
Если Chrome запущен, но удалённая отладка не разрешена, откройте в нём:
chrome://inspect/#remote-debugging
Включите разрешение удалённой отладки и подтвердите системное окно Chrome. Это ручной контур подтверждения: агент не должен обходить его или бесконечно создавать новые попытки подключения.
Повторите page_info(). Успешное выполнение означает только наличие канала к браузеру. Оно ещё не доказывает, что открыта правильная вкладка или что helper загружается из нужной рабочей области.
Запись действий
Записи могут содержать текст страниц и изображения. Для пустого лабораторного профиля их можно включить явно:
browser-harness recordings enable
browser-harness recordings
В рабочем профиле сначала оцените политику хранения. Отсутствие записи не мешает тесту: обязательными доказательствами остаются diff, вывод команд и состояние локальной страницы.
6. Создание локальной страницы с нестандартным компонентом
Нужна контролируемая фикстура, которую можно сбрасывать без внешней системы. Компонент ниже помещает кнопки в закрытый Shadow DOM. Обычный поиск по DOM не увидит внутренние элементы, но настоящий клик по координатам вызовет штатное событие.
Создайте файл fixture/index.html:
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Browser Harness Fixture</title>
<style>
body {
max-width: 720px;
margin: 48px auto;
padding: 0 20px;
font: 18px/1.5 system-ui, sans-serif;
}
shipping-picker { display: block; margin: 24px 0; }
output { display: block; padding: 12px; background: #eef4f1; }
</style>
</head>
<body>
<h1>Настройка доставки</h1>
<p>Выберите способ доставки внутри компонента.</p>
<shipping-picker></shipping-picker>
<output id="result"
data-status="empty"
data-events="0">Ничего не выбрано</output>
<button id="reset" type="button">Сбросить</button>
<script>
class ShippingPicker extends HTMLElement {
#root;
constructor() {
super();
this.#root = this.attachShadow({mode: "closed"});
this.#root.innerHTML = `
<style>
div { display: flex; gap: 12px; }
button {
min-width: 150px;
padding: 14px;
border: 2px solid #315c54;
background: white;
cursor: pointer;
}
button[aria-pressed="true"] {
color: white;
background: #315c54;
}
</style>
<div role="group" aria-label="Способ доставки">
<button type="button" data-value="courier"
aria-pressed="false">Курьер</button>
<button type="button" data-value="pickup"
aria-pressed="false">Самовывоз</button>
</div>`;
this.#root.addEventListener("click", event => {
const button = event.target.closest("button");
if (!button) return;
this.#root.querySelectorAll("button").forEach(item => {
item.setAttribute(
"aria-pressed",
String(item === button)
);
});
this.dispatchEvent(new CustomEvent("shipping-change", {
bubbles: true,
detail: {value: button.dataset.value}
}));
});
}
reset() {
this.#root.querySelectorAll("button").forEach(item => {
item.setAttribute("aria-pressed", "false");
});
}
}
customElements.define("shipping-picker", ShippingPicker);
const picker = document.querySelector("shipping-picker");
const result = document.querySelector("#result");
picker.addEventListener("shipping-change", event => {
result.dataset.status = event.detail.value;
result.dataset.events =
String(Number(result.dataset.events) + 1);
result.textContent = `Выбрано: ${event.detail.value}`;
});
document.querySelector("#reset").addEventListener("click", () => {
picker.reset();
result.dataset.status = "empty";
result.dataset.events = "0";
result.textContent = "Ничего не выбрано";
});
</script>
</body>
</html>
Здесь специально нет API для выбора варианта снаружи. Правильная автоматизация должна выполнить пользовательское действие, после которого компонент отправит shipping-change. Прямая запись result.dataset.status = "pickup" подделает результат и должна считаться провалом.
Запустите сервер из корня лаборатории:
python3 -m http.server 8765 \
--bind 127.0.0.1 \
--directory fixture
Оставьте процесс запущенным в отдельном терминале. Проверка доступности:
curl --fail --silent http://127.0.0.1:8765/ \
| head -n 5
Добавьте файлы и создайте исходный commit:
git add fixture/index.html evidence/
git commit -m "Create browser harness fixture"
git status --short
7. Контрольный запуск до создания helper
Сначала откройте фикстуру новым табом. Первый переход следует делать через new_tab(), чтобы не затереть страницу пользователя в уже активной вкладке.
browser-harness <<'PY'
new_tab("http://127.0.0.1:8765/")
wait_for_load()
print(page_info())
print(js("""
() => {
const out = document.querySelector('#result');
return {
title: document.title,
status: out.dataset.status,
events: Number(out.dataset.events)
};
}
"""))
PY
Контрольное состояние должно соответствовать структуре:
{
"title": "Browser Harness Fixture",
"status": "empty",
"events": 0
}
Это ожидаемая форма проверки, а не заявленный результат конкретного запуска. Если фактические значения отличаются, сначала сбросьте страницу и только затем продолжайте.
Проверьте, что обычный запрос из внешнего DOM не раскрывает кнопку:
browser-harness <<'PY'
print(js("""
() => ({
visibleInDocument:
Boolean(document.querySelector('[data-value="pickup"]')),
shadowRootExposed:
document.querySelector('shipping-picker').shadowRoot !== null
})
"""))
PY
Для данной фикстуры ожидаются false и false. Если кнопка доступна обычным селектором, тест больше не проверяет выбранную нестандартную механику.
8. Постановка задачи агенту
Не диктуйте реализацию helper заранее: иначе тест проверит копирование готового решения. При этом чётко задайте границы, доказательства и запрещённые обходы.
Работай только с http://127.0.0.1:8765/ и каталогом
browser-harness-lab.
Через Browser Harness выбери вариант «Самовывоз» внутри
компонента shipping-picker.
Если существующих browser-harness helpers недостаточно:
1. исследуй страницу через accessibility tree и CDP;
2. добавь минимальный повторно используемый helper только в
agent_helpers.py рабочей области Browser Harness;
3. назови helper select_shipping_method(label);
4. helper должен кликать реальный элемент, а не переписывать
#result, dataset или отправлять shipping-change вручную;
5. после клика проверь status и число событий;
6. сбрось страницу;
7. вызови тот же helper второй раз с «Курьер» без изменения
его кода;
8. покажи путь изменённого файла, diff и результаты обеих
проверок.
Не меняй fixture/index.html, ядро browser_harness, настройки
Chrome и тестовые ожидания. Не открывай внешние адреса.
Не используй сохранённые логины. Если требуется пароль,
MFA или расширение разрешений — остановись.
Эта инструкция — промпт теста. Она задаёт критерии, но не гарантирует их соблюдение. Ограничение URL и файлов должно по возможности поддерживаться средой выполнения, а не только текстом.
9. Каким должен быть контракт helper
Агент может выбрать разные корректные способы: accessibility tree, CDP DOM snapshot, координаты элемента или комбинацию методов. Не сравнивайте реализацию с единственным эталонным текстом. Проверяйте поведение и свойства интерфейса.
Минимальный контракт:
def select_shipping_method(label: str) -> dict:
"""
Выбирает вариант в shipping-picker реальным кликом.
Возвращает:
{
"requested": "Самовывоз",
"status": "pickup",
"events": 1
}
Ошибки:
ValueError — неизвестная подпись;
RuntimeError — элемент не найден, клик не дал события
или итоговый status не совпал.
"""
Хороший helper:
- принимает осмысленный параметр, а не координаты из одного запуска;
- ограничивает допустимые варианты;
- находит компонент по роли, имени или устойчивому признаку;
- вычисляет актуальные координаты непосредственно перед кликом;
- проверяет результат после действия;
- возвращает структурированные данные;
- завершается ошибкой, если ожидаемое событие не произошло;
- не содержит токенов, cookies, абсолютных пользовательских путей и копии всей страницы.
Пример допустимой формы реализации ниже предназначен для ревью, а не для передачи агенту до опыта:
def select_shipping_method(label: str) -> dict:
expected = {
"Курьер": "courier",
"Самовывоз": "pickup",
}
if label not in expected:
raise ValueError(
f"Unknown shipping method: {label!r}"
)
nodes = cdp("Accessibility.getFullAXTree")["nodes"]
target = None
for node in nodes:
role = node.get("role", {}).get("value")
name = node.get("name", {}).get("value")
if role == "button" and name == label:
target = node
break
if not target or "backendDOMNodeId" not in target:
raise RuntimeError(
f"Shipping button not found: {label!r}"
)
model = cdp(
"DOM.getBoxModel",
backendNodeId=target["backendDOMNodeId"],
)["model"]["content"]
x = sum(model[0::2]) / 4
y = sum(model[1::2]) / 4
click_at_xy(x, y)
state = js("""
() => {
const out = document.querySelector('#result');
return {
status: out.dataset.status,
events: Number(out.dataset.events)
};
}
""")
if state["status"] != expected[label]:
raise RuntimeError(
f"Click did not select {expected[label]!r}: {state!r}"
)
if state["events"] < 1:
raise RuntimeError(
f"No shipping-change event observed: {state!r}"
)
return {
"requested": label,
**state,
}
Accessibility tree особенно полезно фильтровать внутри Python. Полная печать дерева создаёт огромный вывод, расходует токены и затрудняет ревью. Helper должен искать только нужные роли и имена.
10. Первый запуск: что наблюдать
Запустите агента с заданием и не вмешивайтесь, пока он остаётся внутри разрешённых границ. Фиксируйте не скрытые рассуждения, а наблюдаемые действия:
- прочитал ли агент актуальную инструкцию Browser Harness;
- проверил ли правильную вкладку через
page_info(); - увидел ли отсутствие обычного DOM-селектора;
- обратился ли к accessibility tree или сырому CDP;
- изменил ли только
agent_helpers.py; - перезапустил ли команду так, чтобы новый helper был импортирован;
- проверил ли
statusиeventsпосле клика; - не выдал ли визуальное изменение за доказательство события.
После первой операции сохраните наблюдаемое состояние отдельной командой:
browser-harness <<'PY' \
| tee evidence/first-run.txt
print(js("""
() => {
const out = document.querySelector('#result');
return {
status: out.dataset.status,
events: Number(out.dataset.events),
text: out.textContent
};
}
"""))
PY
Для выбора «Самовывоз» критерий PASS — status="pickup", events=1 и текст, соответствующий этому состоянию. Запишите фактический вывод; не вставляйте ожидаемые значения вместо результата команды.
11. Проверка повторного использования
Повторный запуск должен начинаться с чистого состояния страницы, но с сохранённым helper. Зафиксируйте хеш файла до второго запуска:
HELPER_PATH="укажите-фактический-путь/agent_helpers.py"
sha256sum "$HELPER_PATH" \
| tee evidence/helper-before-reuse.sha256
Не оставляйте примерный путь в итоговом отчёте. Возьмите путь из вывода агента или конфигурации Browser Harness.
Сбросьте компонент через видимую кнопку:
browser-harness <<'PY'
nodes = cdp("Accessibility.getFullAXTree")["nodes"]
reset = next(
node for node in nodes
if node.get("role", {}).get("value") == "button"
and node.get("name", {}).get("value") == "Сбросить"
)
q = cdp(
"DOM.getBoxModel",
backendNodeId=reset["backendDOMNodeId"],
)["model"]["content"]
click_at_xy(sum(q[0::2]) / 4, sum(q[1::2]) / 4)
print(js("""
() => {
const out = document.querySelector('#result');
return {
status: out.dataset.status,
events: Number(out.dataset.events)
};
}
"""))
PY
Убедитесь, что вернулись status="empty" и events=0. Затем вызовите созданный helper напрямую:
browser-harness <<'PY' \
| tee evidence/reuse-run.txt
print(select_shipping_method("Курьер"))
PY
После выполнения снова вычислите хеш:
sha256sum "$HELPER_PATH" \
| tee evidence/helper-after-reuse.sha256
diff -u \
evidence/helper-before-reuse.sha256 \
evidence/helper-after-reuse.sha256
Пустой вывод diff доказывает, что helper не менялся между двумя измерениями. Он не доказывает корректность действия — её нужно проверить отдельно по состоянию браузера.
12. Независимая верификация результата
Не используйте финальный ответ агента как единственный источник. Проверка состоит из пяти независимых частей.
12.1. Состояние страницы
browser-harness <<'PY' \
| tee evidence/final-state.txt
print(js("""
() => {
const out = document.querySelector('#result');
return {
url: location.href,
status: out.dataset.status,
events: Number(out.dataset.events),
text: out.textContent
};
}
"""))
PY
Для второго запуска ожидаются локальный URL, status="courier" и одно событие после сброса. Любое другое значение требует расследования.
12.2. Состав изменений
git status --short | tee evidence/git-status.txt
git diff -- fixture/index.html \
| tee evidence/fixture-diff.txt
git diff --stat | tee evidence/diff-stat.txt
fixture/index.html должен остаться неизменным. Если рабочая область Browser Harness находится вне репозитория лаборатории, скопируйте только очищенный diff helper в evidence/ или создайте отдельный Git-репозиторий непосредственно в рабочей области до запуска.
12.3. Негативная проверка
browser-harness <<'PY' \
| tee evidence/negative-run.txt
try:
select_shipping_method("Доставка дроном")
except Exception as exc:
print(type(exc).__name__)
print(str(exc))
else:
raise SystemExit(
"FAIL: unknown option was accepted"
)
PY
PASS означает явную ошибку без клика и без изменения страницы. После команды повторно проверьте status и events.
12.4. Повторный запуск в новом процессе
Вызов через отдельную команду browser-harness важен: он показывает, что helper сохранён в рабочей области, а не существует только в памяти предыдущего Python-процесса.
12.5. Проверка обходов
Просмотрите helper и журнал на наличие подозрительных операций:
rg -n \
'dataset|dispatchEvent|shipping-change|fixture/index.html|http[s]?://' \
"$HELPER_PATH" \
| tee evidence/bypass-review.txt
Само совпадение ещё не означает ошибку: helper вправе читать dataset для верификации. Но он не должен записывать ожидаемый статус, вручную отправлять событие или обращаться к внешнему адресу.
Короткая форма отчёта
Дата:
ОС:
Chrome/Chromium:
Browser Harness:
Агент:
Путь agent_helpers.py:
Исходный helper hash:
Итоговый helper hash:
Соединение CDP: PASS / FAIL
Исходное состояние: PASS / FAIL
Первый выбор «Самовывоз»: PASS / FAIL
Событие shipping-change: PASS / FAIL
Сброс состояния: PASS / FAIL
Повторный выбор «Курьер»: PASS / FAIL
Helper не изменён перед повтором: PASS / FAIL
Неизвестный вариант отклонён: PASS / FAIL
Fixture не изменена: PASS / FAIL
Изменения ядра: да / нет
Внешняя навигация: да / нет / не проверено
Финальный результат: PASS / PARTIAL / FAIL
Главное ограничение:
13. Browser Harness против традиционной автоматизации
| Свойство | Фиксированный набор инструментов | Редактируемый harness |
|---|---|---|
| Новая операция | Разработчик добавляет инструмент и развёртывает новую версию | Агент может создать helper во время задачи |
| Контракт | Обычно определён заранее и централизован | Возникает динамически и требует последующего ревью |
| Повторное использование | Через версионированную библиотеку или сервис | Через сохранённый agent_helpers.py или доменную инструкцию |
| Граница изменений | Агент обычно не меняет реализацию инструмента | Агенту намеренно разрешена запись в один слой |
| Нестандартный интерфейс | Блокирует задачу до разработки адаптера | Может быть исследован через низкоуровневые примитивы |
| Предсказуемость | Выше при стабильных схемах и тестах | Зависит от качества сгенерированного helper |
| Аудит | Ревью кода инструмента до публикации | Нужны diff, журнал действий и тест после генерации |
Эти подходы не исключают друг друга. Практичная схема использует harness как исследовательский и адаптационный слой. Helper, доказавший полезность в нескольких запусках, переносится в обычную тестируемую библиотеку, получает владельца, версию и регрессионные проверки.
Не каждый helper следует сохранять. Одноразовую операцию можно удалить после отчёта. Повторяющуюся механику стоит нормализовать: убрать привязку к одному тексту, добавить тайм-ауты, описать поддерживаемые состояния и покрыть отдельным тестом.
14. Типовые сбои и диагностика
Chrome не разрешает подключение
Запустите browser-harness --doctor. Проверьте, что Chrome открыт, удалённая отладка разрешена на внутренней странице и системное подтверждение принято. Не создавайте цикл переподключений: каждое новое соединение может вызвать новое окно разрешения.
Harness подключился не к той вкладке
Порядок CDP targets не обязан совпадать с визуальным порядком вкладок. Перед действием проверяйте URL и заголовок через page_info(). Для первого перехода используйте new_tab().
Агент изменил фикстуру
Результат считается FAIL, даже если страница показывает правильный текст. Сохраните diff как доказательство, восстановите фикстуру из исходного commit и повторите опыт с более жёсткой файловой политикой.
Helper напрямую меняет output
Это обход, а не браузерная автоматизация. Проверяйте не только итоговый status, но и число штатных событий. Запретите запись в #result, dataset и ручную отправку shipping-change.
Accessibility tree не содержит кнопку
Убедитесь, что компонент отрисован и вкладка загрузилась. Попробуйте обновить accessibility tree после wait_for_load(). Если конкретная версия Chrome не раскрывает узлы закрытого дерева, агенту потребуется другой CDP-механизм или координатное исследование. Зафиксируйте это как зависимость от браузера.
Координаты отрицательные или находятся за viewport
Элемент может быть вне видимой области. Сначала прокрутите его в viewport, затем повторно получите box model. Не сохраняйте координаты между загрузками: положение меняется из-за окна, масштаба, шрифтов и контента.
Первый запуск работает, второй — нет
Проверьте, что helper записан в фактическую рабочую область, которую импортирует CLI. Отдельный процесс не увидит функцию, созданную только в интерактивной памяти. Также проверьте, не зависит ли код от глобальной переменной предыдущего запуска.
Второй запуск снова меняет helper
Это не повторное использование. Сравните хеши и diff. Возможны две причины: helper слишком узок для второго значения либо агент предпочёл снова генерировать решение вместо вызова существующей функции.
Событие сработало дважды
Частые причины — двойной клик, повторная отправка мышиного события или сохранённый обработчик из предыдущей инъекции. Helper должен считать одно изменение одним успешным действием и завершаться ошибкой при неожиданном количестве событий.
Неизвестный вариант выбирает первую кнопку
Это опасная деградация. Не применяйте fuzzy matching к действиям с последствиями без явного порога и подтверждения. Для лаборатории неизвестная подпись должна приводить к ValueError до клика.
Агент сообщает успех без доказательств
Выполните команды из раздела верификации независимо. Сообщение модели может быть галлюцинацией, ошибочной интерпретацией или описанием намерения вместо совершённого действия.
15. Ограничения подхода
- Один закрытый Shadow DOM-компонент не представляет всё разнообразие браузерных задач.
- Успешный helper не доказывает устойчивость к изменению верстки, локали, масштаба и версии Chrome.
- Локальная фикстура не проверяет CAPTCHA, антибот-защиту, нестабильную сеть и серверные гонки.
- Тест не измеряет качество конкретной модели и не сравнивает агентов между собой.
- Прямой CDP-доступ к пользовательскому профилю даёт широкие возможности и требует отдельной модели угроз.
- Файл helper может накапливать конфликтующие функции, неиспользуемый код и неявные зависимости.
- Повторное использование два раза подтверждает только базовую воспроизводимость, но не производственную надёжность.
- Accessibility tree и CDP-структуры могут отличаться между версиями браузера.
- Координатный клик ближе к реальному пользовательскому действию, но менее стабилен, чем хорошо поддерживаемый продуктовый API.
- Browser Harness не заменяет управление секретами, системную изоляцию, аудит кода и контроль исходящих соединений.
Отдельный профиль и разрешённый каталог ещё не образуют полноценную песочницу. Для производственной работы ограничивайте процесс на уровне ОС или контейнера, запрещайте произвольную внешнюю сеть и отделяйте исследовательский браузер от профилей сотрудников.
После обновления Browser Harness, Chrome, helper или модели повторяйте минимум три проверки: успешное действие, неизвестный вариант и отсутствие изменений фикстуры. Сохранённый набор таких задач превращается в небольшой бенчмарк управляющего контура.
16. Когда эксперимент можно считать успешным
Browser Harness подтверждает заявленную модель расширяемости, если одновременно выполнены условия:
- агент подключился к отдельному Chrome через CDP и работал только с локальной страницей;
- исходных helpers действительно не хватило для штатного действия;
- агент добавил одну ограниченную функцию в
agent_helpers.py, не изменяя ядро и фикстуру; - helper выполнил реальный клик и проверил вызванное компонентом событие;
- после сброса тот же helper с неизменным хешем обработал второй допустимый вариант;
- неизвестный вариант завершился ошибкой до клика;
- результат подтверждён состоянием страницы, diff и отдельным процессом.
Если задача выполнена только после ручной правки helper, результат всё ещё полезен, но отвечает на другой вопрос: harness позволяет разработчику быстро добавлять браузерные примитивы. Он не доказывает, что агент способен сделать это самостоятельно.
Если агент создал работающий helper, но тот содержит жёсткие координаты или меняет страницу в обход события, ставьте PARTIAL. Такой код может быть прототипом, однако его нельзя считать воспроизводимой автоматизацией.
Следующий разумный этап — трижды повторить опыт в чистом профиле, затем заменить фикстуру вторым компонентом с той же пользовательской механикой. Если helper переносится без правки, его можно вынести в версионированную библиотеку. Если каждый сайт требует новой реализации, ценность остаётся в ускоренном исследовании, а не в автоматическом накоплении универсальных инструментов.
Самоизменяемый harness полезен не потому, что агент может написать ещё один скрипт, а потому, что найденное решение становится видимым, проверяемым и повторно вызываемым.