Перейти к содержанию

Smoke Checklist

Ручная проверка в живом QGIS — то, что не покрыть тестами: настоящие вызовы PyQGIS к слоям и отрисовка Qt. Всё остальное закрыто набором tests/.

Техническая проверка после правок

python3 -m unittest discover -s tests -t .

Набор проходит без QGIS. Если он красный — в живой QGIS идти рано.

Read-тулы без агента

Открыть проект со слоями, затем Плагины → Консоль Python:

from ai_agent.qgis_tools.registry import execute_tool
execute_tool("get_project_info", {})
execute_tool("list_layers", {})
execute_tool("describe_layer", {"layer_name": "ИМЯ_СЛОЯ"})
execute_tool("get_field_values", {"layer_name": "ИМЯ_СЛОЯ", "field_name": "ПОЛЕ"})
execute_tool("sample_features", {"layer_name": "ИМЯ_СЛОЯ", "limit": 3})
execute_tool("query_layer", {"layer_name": "Дороги", "aggregate": "count"})
execute_tool("query_layer", {"layer_name": "Дороги", "aggregate": "count", "filter": "highway = 'motorway'"})
execute_tool("query_layer", {"layer_name": "Города", "order_by": "population DESC", "limit": 5, "fields": ["name", "population"]})
execute_tool("query_layer", {"layer_name": "Реки", "order_by": "$length DESC", "limit": 1})
execute_tool("query_layer", {"layer_name": "Озера и пруды", "aggregate": "sum", "expression": "$area"})
execute_tool("query_layer", {"layer_name": "Дороги", "aggregate": "mean", "expression": "$length", "group_by": "highway"})
execute_tool("describe_style", {"layer_name": "ИМЯ_СЛОЯ"})
execute_tool("get_qgis_info", {})
execute_tool("get_canvas_extent", {})
execute_tool("search_processing", {"query": "buffer"})
execute_tool("describe_processing", {"algorithm_id": "native:buffer"})

Ожидания:

  • get_project_info отдаёт дерево слоёв с группами и видимостью, единицы, темы
  • describe_layer для слоя в EPSG:4326 отдаёт crs_is_geographic: True, suggested_metric_crs, а также provider, is_valid и style_summary
  • get_field_values для числового поля отдаёт min/max, для текстового — список уникальных значений с пометкой об обрезке
  • sample_features отдаёт реальные записи, длинные значения обрезаны
  • describe_style показывает тип рендерера и классы с цветами
  • describe_processing отдаёт enum парами {"value": 0, "label": "Round"}
  • несуществующее имя слоя или поля даёт ошибку со списком доступных
  • query_layer без aggregate отдаёт объекты, с aggregate — одно число
  • group_by даёт список групп со значением и числом объектов в каждой
  • $length и $area считаются в единицах CRS слоя: на слое в градусах число получится бессмысленным, агент обязан это сказать
  • ошибка в выражении даёт текст парсера, а не падение

Ветки классов в describe_style

Категории, градации и правила не проверить на проекте с одиночными символами. Снипет подставляет категоризацию, проверяет и возвращает рендерер обратно — в проекте ничего не остаётся:

from qgis.core import QgsProject, QgsCategorizedSymbolRenderer, QgsRendererCategory, QgsSymbol
from ai_agent.qgis_tools.registry import execute_tool

layer = QgsProject.instance().mapLayersByName("ИМЯ_СЛОЯ")[0]
saved = layer.renderer().clone()
field = layer.fields().names()[1]
values = list(layer.uniqueValues(layer.fields().indexOf(field)))[:4]
categories = [
    QgsRendererCategory(v, QgsSymbol.defaultSymbol(layer.geometryType()), str(v))
    for v in values
]
layer.setRenderer(QgsCategorizedSymbolRenderer(field, categories))
print(execute_tool("describe_style", {"layer_name": "ИМЯ_СЛОЯ"}))
print(execute_tool("describe_layer", {"layer_name": "ИМЯ_СЛОЯ"})["style_summary"])
layer.setRenderer(saved)

Ожидания: class_attribute, список classes со значением, подписью и символом на каждый класс, style_summary вида «категории по полю «…», классов: 4».

Секреты в источнике

Если есть слой PostGIS — проверить, что пароль не утекает:

execute_tool("describe_layer", {"layer_name": "ИМЯ_СЛОЯ_POSTGIS"})["source"]

В строке должно быть password=‹скрыто›, а не сам пароль.

Агентный цикл

  • Чтение без подтверждения. «что у меня в проекте?» → агент вызывает list_layers, отвечает по факту, кнопки подтверждения не появляются.
  • Данные из проекта. «какие поля в слое X?» → describe_layer, в ответе реальные поля.
  • Формулировка до подтверждения. Агент пишет «предлагаю», а не «я создал».
  • Отмена. Нажать «Отмена» → в проекте ничего не изменилось.
  • Ошибка тула не рвёт прогон. Запрос с несуществующим слоем → агент видит ошибку и исправляется сам.
  • Лимит ходов. Заведомо неподъёмная задача → остановка на MAX_ITERATIONS с внятным сообщением, QGIS не виснет.

Скиллы

  • Вопрос про слои не требует load_skillinspect предзагружен.
  • Запрос про обработку → в логе видно Загружен скилл: processing.
  • Вопрос про цвета и оформление → Загружен скилл: style.
  • Скилл загружается один раз за прогон.

Сбор контекста

  • «расскажи про мой проект» → get_project_info, в ответе группы слоёв, что включено, единицы измерения
  • «какие значения в поле X» → get_field_values, реальные значения
  • «почему города красные» → load_skill(style)describe_style, ответ называет поле классификации и цвет, а не просто «красные»
  • битый слой (переименовать исходный файл) → describe_layer отдаёт is_valid: false, агент предупреждает, а не строит планы поверх

Обработка и проверка CRS

Главный сценарий, ради которого сделана валидация до очереди:

  1. «построй буфер 500 м вокруг городов» на слое в EPSG:4326
  2. первый run_processing отклоняется — шаг помечен ✕ в чате
  3. агент не останавливается: читает ошибку и строит план из двух шагов — native:reprojectlayernative:buffer на его результате
  4. после подтверждения в проекте появляются оба слоя, буфер геометрически верный

Дополнительно:

  • «найди алгоритм буфера» → search_processing возвращает native:buffer
  • enum передан подписью вместо индекса → тул сам приводит к индексу

Транспорт

  • Эндпоинт с function calling — работает нативный путь.
  • Эндпоинт без него — первый запрос падает на tools, адаптер уходит в JSON-протокол, флаг supports_tools записывается, следующие запросы сразу идут по JSON-пути.
  • Смена URL в настройках не ломает выбор: флаг хранится по хешу URL.
from ai_agent.core.settings import get_api_url, get_supports_tools
get_supports_tools(get_api_url())

Потоки и выгрузка

  • UI не подвисает во время многошагового прогона.
  • Во время применения батча кнопка «Отправить» заблокирована, в чате видно прогресс по шагам.
  • Выгрузка плагина при активном запросе не роняет QGIS.

Диалоги

Кнопка «Диалоги» — в шапке панели, слева от «Очистить».

  1. Диалог переживает перезапуск. Спросить «какие у меня слои?», дождаться ответа, закрыть QGIS, открыть заново, открыть плагин → «Диалоги» → в списке есть строка с текстом вопроса. Выбрать её: переписка вернулась целиком.
  2. Модель помнит контекст восстановленного диалога. После пункта 1 спросить «а сколько их?» — ответ должен опираться на прошлый вопрос, а не переспрашивать.
  3. Новый диалог не тянет старое. «Диалоги» → «Новый диалог»: лента пуста. Задать вопрос, вернуться к прошлому диалогу — оба на месте и не перемешаны.
  4. Заголовок — первая реплика. Длинный вопрос обрезается многоточием, не ломает ширину меню.
  5. Привязка к проекту. Открыть другой проект QGIS → «Диалоги»: диалогов из прошлого проекта в списке нет. Вернуть прежний проект — список вернулся.
  6. Пустое не копится. Нажать «Новый диалог» пять раз подряд, ничего не спрашивая → в списке не появилось пяти пустых строк.
  7. Переключение во время работы запрещено. Дать долгую задачу и, пока агент работает, нажать «Новый диалог» → служебное сообщение «Дождитесь окончания текущей задачи», лента не очистилась.
  8. Переключение с неприменённым планом запрещено. Дождаться карточки плана и, не нажимая «Применить», выбрать другой диалог → сообщение про запланированные изменения, карточка на месте, кнопка «Применить» всё ещё работает.
  9. «Очистить диалог» не удаляет сохранённое. Очистить ленту, открыть «Диалоги» → диалог по-прежнему в списке и открывается с сообщениями.

Остановка прогона

  1. Кнопка меняется. Задать любой вопрос: в момент работы кнопка отправки становится красным квадратом, подсказка — «Работаю… нажмите ■, чтобы остановить».
  2. Остановка срабатывает сразу. Дать долгую задачу и нажать ■ — панель разблокируется мгновенно, не дожидаясь ответа модели. Появляется сообщение «Прогон остановлен».
  3. Поздний ответ не всплывает. После остановки подождать минуту: ответ модели на прерванный запрос не должен появиться в ленте задним числом.
  4. После остановки можно работать дальше. Задать новый вопрос — он проходит обычным порядком.
  5. Остановка отменяет план. Довести до карточки плана, нажать ■ (если прогон ещё идёт) или проверить, что после остановки «Применить» ничего не применяет.
  6. Enter во время работы поправляет, а не ставит в очередь. Начать долгую задачу, ввести «вообще-то сделай синим, не красным», нажать Enter → реплика появляется в ленте с пометкой, что передана агенту, и агент учитывает её на следующем шаге, не начиная задачу заново.

Изменение оформления

Требует живого проекта со слоями. Все пять тулов — пишущие, значит каждый раз должна появляться карточка плана с кнопкой «Применить».

  1. Один цвет. «Сделай реки синими» → карточка плана, после «Применить» слой перекрашен и легенда обновилась без перезапуска QGIS.
  2. Отмена ничего не меняет. Повторить пункт 1 и нажать «Отмена» — цвет слоя прежний.
  3. Категории. «Раскрась дороги по типу» → категорий столько же, сколько уникальных значений поля; в легенде подписи значений.
  4. Палитра по умолчанию. Тот же запрос без указания палитры не должен падать с ошибкой про палитры.
  5. Несуществующая палитра. «Раскрась по типу палитрой ЧегоТоТам» — агент получает список доступных и повторяет вызов сам, не спрашивая пользователя.
  6. Градации. «Раскрась по населению, 7 классов, jenks» → семь классов, границы не одинаковые.
  7. Текстовое поле в градациях. «Градации по названию» — агент должен переключиться на категории, а не сдаться.
  8. Подписи. «Подпиши города названиями» → подписи появились. «Убери подписи» → исчезли, оформление символов при этом не изменилось.
  9. Прозрачность. «Сделай подложку на 50% прозрачной» → слой полупрозрачный, рендерер не сброшен.
  10. Растр. Прозрачность работает и на растровом слое.
  11. Цвет словами. «Сделай леса тёмно-зелёными» — агент подбирает hex сам, ошибки про нераспознанный цвет быть не должно.
  12. Чтение перед записью. «Почему города красные? Сделай их синими» — в ленте сначала describe_style, потом set_symbol.

Подписи: обводка текста

  1. Обводка включается. «Сделай у подписей городов белую обводку, чтобы читались» → в плане один шаг, в нём видно «обводка текста white». После применения подписи с ореолом, размер и цвет текста не сброшены.
  2. Обводка снимается. «Убери обводку у подписей» → ореол исчез, подписи остались.
  3. Шаги в плане различимы. Запрос, где меняется и цвет, и обводка, не должен давать два одинаковых по тексту шага.
  4. Дубли схлопываются. Если агент поставил один и тот же вызов дважды, в карточке плана он должен быть один раз.
  5. Агент не обещает словами. Запрос про оформление должен сразу давать карточку плана, а не фразу «предлагаю изменить…» без единого шага.

Подписи: свойства одним вызовом

  1. Шрифт. «Сделай подписи городов шрифтом Arial, жирным» → в плане один шаг, в нём видно font и bold. После применения шрифт сменился.
  2. Сдвиг. «Сдвинь подписи городов на 3 мм вверх» → подписи сместились; вверх это offset_y отрицательный, проверить направление глазами.
  3. Размещение. «Поставь подписи вокруг значков, а не поверх» → placement сменился на around.
  4. Каталог свойств. «Какие настройки подписей есть?» → агент зовёт describe_style_options и перечисляет реальные свойства, а не выдуманные.
  5. Незнакомое свойство. Попросить что-то на грани, например «сделай подписи вразрядку» — агент либо находит свойство, либо честно говорит, что такого нет, а не обещает прозой.
  6. Всё одним шагом. «Подпиши названиями, жирным, 12 пунктов, с белой обводкой» → в карточке ровно один шаг, а не четыре.

Символ: свойства одним вызовом

  1. Пунктир. «Сделай дороги серыми пунктирными» → в плане один шаг со stroke_style=dash, после применения линии пунктирные.
  2. Форма значка. «Города квадратными значками» → значки квадратные.
  3. Только контур. «Полигоны без заливки, только контур» → fill_style=none, контур виден.
  4. Неприменимое свойство. «Сделай дороги квадратными» → агент не должен молча отчитаться об успехе: в результате будет skipped с пояснением, и агент сообщит пользователю, что форма к линиям неприменима.
  5. Каталог символа. «Какие настройки символа есть?» → describe_style_options с kind=symbol, перечислены реальные свойства.

Проект и слои

  1. Загрузка слоя. «Загрузи /путь/к/файлу.geojson» → карточка плана, после применения слой в дереве и на карте.
  2. Несуществующий файл. «Загрузи /нет/такого.shp» → внятная ошибка про путь, ничего не поставлено в очередь.
  3. Повтор имени. Загрузить тот же файл второй раз → агент получает отказ и сам предлагает другое имя, а не пытается снова.
  4. Группа. «Добавь слой в группу Фон» → группа создалась, слой внутри.
  5. Видимость. «Спрячь слой дорог» → галочка снята, карта перерисована.
  6. Переименование. «Переименуй слой в «Дороги города»» → имя сменилось и в дереве, и в легенде.
  7. Порядок. «Подними реки наверх» → слой стал первым в своей группе и рисуется поверх остальных.
  8. Возврат в корень. Переместить слой в группу, затем «вынеси из группы» → слой в корне дерева.
  9. Зум. «Покажи слой городов» → карта подогналась под охват, подтверждения не спрашивалось.
  10. CRS проекта. «Переведи проект в EPSG:3857» → сменилась проекция карты, CRS самих слоёв прежняя.
  11. Сохранение. «Сохрани проект» на уже сохранённом проекте → записался туда же, заголовок окна без звёздочки.
  12. Новый проект. «Сохрани проект» на несохранённом → агент не выдумывает путь, а просит его.
  13. Удаление слоя. «Убери слой X» → слой исчез из проекта, файл на диске остался.
  14. Сквозной сценарий. «Загрузи файл, раскрась по типу, подпиши названиями и сохрани проект» → один прогон, все шаги в одной карточке плана.

Данные из OpenStreetMap

Требует интернета. Overpass — публичный сервис, отказ «занят» это не баг.

  1. Кафе по имени места. «Скачай кафе в Твери» → карточка плана, после применения слой точек с кафе, объектов больше нуля.
  2. Текущий вид. Приблизиться к небольшому району, «загрузи здания в текущем виде» → агент передаёт bbox=canvas, а не выдумывает координаты.
  3. Геометрия. «Скачай дороги» → слой линий; агент не должен добавлять заодно слой точек, если его не просили.
  4. Слишком широкий охват. Отдалиться до континента и повторить пункт 44 → отказ до отправки запроса, с объяснением про пять градусов.
  5. Русское значение тега. «Скачай amenity=кафе» → пустой результат, агент должен объяснить про английские теги, а не повторять запрос.
  6. Несуществующее место. «Скачай кафе в Нетакогогорода» → пусто, внятное сообщение.
  7. Занятый Overpass. Если сервис вернул отказ — агент сообщает и предлагает повторить, а не долбит запрос заново.
  8. Сквозной сценарий. «Скачай парки в Твери, раскрась зелёным, подпиши названиями и сохрани проект» → один прогон, все шаги в одной карточке.
  9. Пустые подслои не приезжают. «Скачай кафе в Твери» с geometry=all → в проект добавлен только слой точек; пустые линии и полигоны отброшены.
  10. Несколько тегов разом. «Скачай кафе, бары и рестораны в Твери одним слоем» → один вызов с selectors и регуляркой, один слой точек.
  11. Исключение. «Загрузи дороги кроме грунтовых» → selectors с !~, в результате нет highway=track.
  12. Разные ключи вместе. «Магазины и кафе» → несколько селекторов в одном вызове, а не два отдельных вызова.
  13. Селектор без условий. Попросить «скачай вообще всё отсюда» → отказ с объяснением, что нужен хотя бы один тег.

Штатные инструменты QGIS

Проверяют, что агент решает типовые задачи без блужданий по поиску.

  1. Буфер находится с первого раза. «Построй буфер 500 м вокруг школ» → в ленте native:buffer, а не native:taperedbuffer.
  2. Площадь — не алгоритм. «Посчитай площадь каждого полигона» → агент зовёт native:fieldcalculator с $area, а не native:serviceareafrompoint.
  3. Длина. «Добавь поле с длиной линий в километрах» → $length / 1000.
  4. Площадь на географической CRS. То же на слое в EPSG:4326 → агент сначала перепроецирует, а не выдаёт квадратные градусы.
  5. NDVI. «Посчитай NDVI по этому снимку» → native:rastercalc с выражением по каналам, extent и cell size взяты из describe_layer растра.
  6. Обрезка растра. «Обрежь растр по границе района» → gdal:cliprasterbymasklayer.
  7. Зональная статистика. «Средняя высота по каждому району» → native:zonalstatisticsfb.
  8. Пространственное соединение. «Добавь к точкам название района, в котором они лежат» → native:joinattributesbylocation.
  9. Растворение по полю. «Объедини районы в области по полю region» → native:dissolve с полем.
  10. Цепочка. «Обрежь дороги по границе города, посчитай длину и сохрани» → все шаги в одной карточке плана.
  11. Запрос не на английском в поиске. Агент не должен искать «растровый калькулятор» — поиск по русскому даёт ноль, он обязан переводить сам.

Локальная модель

  1. Подключение без ключа. Запустить Ollama, в настройках указать http://localhost:11434/v1, модель — любую установленную, ключ оставить пустым → «Проверить подключение» отвечает.
  2. Удалённый адрес без ключа. Стереть ключ, вернуть адрес OpenAI → внятное сообщение про ключ, а не про сеть.
  3. Прогон на локальной модели. «Какие у меня слои?» отрабатывает; если модель мелкая и путается в вызовах — это её предел, а не поломка плагина.
  4. OpenRouter. Адрес https://openrouter.ai/api/v1, ключ OpenRouter, модель вида anthropic/claude-sonnet-4 → «Проверить подключение» отвечает, прогон с вызовом тулов проходит.
  5. Anthropic напрямую. Адрес https://api.anthropic.com/v1, ключ Anthropic, формат auto → плагин сам уходит на /messages, вызовы тулов работают.
  6. Диалект вручную. Поставить формат openai на адресе Anthropic → запрос уходит на /chat/completions и сервис отвечает ошибкой; это проверка, что ручной выбор действительно перебивает определение.

Окно настроек

  1. URL помещается. Открыть настройки: длинный адрес шлюза виден целиком, окно не приходится растягивать.
  2. Пресет подставляет адрес. Выбрать «Anthropic» → адрес и формат API заполнились сами, поле модели показывает подсказку.
  3. Пресет определяется обратно. Вписать вручную адрес OpenRouter → в списке провайдеров сам выбрался OpenRouter.
  4. Локальный провайдер. Выбрать «Ollama» → подсказка у ключа меняется на «Не требуется».
  5. Проверка не открывает окон. «Проверить подключение» → результат строкой внизу диалога: зелёной при успехе, красной при ошибке. Нажать несколько раз подряд — окна поверх окна не появляются.
  6. Сохранение. «Сохранить» → строка «Настройки сохранены», диалог остаётся открытым.
  7. Тёмная и светлая тема. Переключить тему QGIS и открыть настройки — карточки и подсказки читаются в обеих.
  8. У полей видна рамка. В настройках у «Базовый URL», «Модель», «API-ключ» и у выпадающих списков есть контур; при клике в поле контур становится акцентным.
  9. Глубина видна. В настройках карточки светлее фона окна, а поля ввода темнее карточек — три различимых уровня, а не одно тёмное пятно.
  10. Иконки шапки. Три значка — часы, корзина, шестерёнка — одного цвета и веса, не разноцветные. На retina не мылят и не обрезаются. У шестерёнки различимы зубцы, а не сплошной кружок.
  11. Иконки следуют теме. Переключить светлую и тёмную тему QGIS и открыть панель заново: значки читаются в обеих.

Язык интерфейса и ответов

  1. Русский QGIS. Настройки QGIS → «Общие» → язык интерфейса «Русский», перезапустить QGIS, открыть панель агента: заголовок кнопки отправки, подсказка в поле ввода, «Настройки», «Диалоги», «Очистить диалог» — по-русски. Это проверяет, что переводчик встаёт до импорта модулей: именно эти строки вычисляются на импорте.
  2. Окно настроек по-русски. Открыть настройки: «Подключение», «Провайдер», «Базовый адрес», «Ключ API», «Проверить подключение», «Сохранить» — переведены, ничего не осталось английским.
  3. Карточка плана по-русски. Попросить перекрасить слой → в карточке «Изменит проект — N шагов» с правильной формой: 1 шаг, 2 шага, 5 шагов. Кнопки — «Применить» и «Отменить», после применения — «Применено».
  4. Лента шагов по-русски. Во время прогона строки вида «Смотрю слои проекта.», «Оформляю «Дороги»: color=blue.» — не английские.
  5. Английский QGIS. Переключить язык QGIS на English, перезапустить: весь интерфейс плагина по-английски, без вкраплений русского.
  6. Язык ответа модели следует за пользователем. В английском QGIS написать агенту по-русски → он отвечает по-русски. В русском QGIS написать по-английски → отвечает по-английски. Интерфейс при этом не меняется.
  7. Незнакомый язык QGIS. Поставить, например, немецкий: интерфейс плагина английский (перевода нет), плагин не падает, агент отвечает по-английски, пока пользователь не напишет иначе.

Зрение и самопроверка

  1. render_map возвращает картинку. В консоли Python: execute_tool("render_map", {}) → словарь с width, height, extent и непустым image_base64; после декодирования — валидный PNG текущего вида.
  2. Агент смотрит на карту. Спросить «как выглядит карта?» на модели со зрением → в ленте render_map, а в ответе — настоящие цвета с экрана, не догадки.
  3. Слепой эндпоинт деградирует мягко. Тот же вопрос на модели без зрения → первый запрос повторяется без картинки, прогон завершается текстовой пометкой, а не ошибкой; следующие прогоны сразу идут без изображений.
  4. Проверка идёт после «Применить». «Сделай реки синими» → Применить → в ленте «Проверяю применённые изменения…», затем describe_style или render_map, затем короткий вердикт, что реки теперь синие.
  5. Проверка чинит падение. Уронить один шаг (например, переименовать слой между постановкой и применением) → проверочный прогон видит падение и ставит исправленный вызов; появляется новая карточка плана.
  6. Переключатель работает. Снять галочку «Проверять результат после применения изменений» в настройках → после «Применить» проверочный прогон не стартует.
  7. Проверка не зацикливается. Применить план, поставленный САМИМ проверочным прогоном → изменения легли, новая проверка сама не начинается.

Выделение, подложки, базы, правка данных

  1. Агент видит выделение. Выделить несколько объектов руками, спросить «что я выделил?» → get_selection называет слой, количество и настоящие атрибуты.
  2. Счёт по выделенному. «Суммарная площадь выделенных полигонов» → query_layer с selected_only=true; число сходится с ручной проверкой.
  3. Пустое выделение называется прямо. Снять выделение и повторить → агент говорит, что ничего не выделено, а не считает по всему слою.
  4. Подложка ложится вниз. «Добавь подложку OpenStreetMap» → после «Применить» тайловый слой под всеми слоями, карта показывает тайлы.
  5. Свой URL без {z} отклоняется с внятным сообщением до постановки в план.
  6. Подключения к базе только читаются. С сохранённым подключением PostGIS «загрузи дороги из базы» проходит путь list_db_connections → list_db_tables → add_db_layer и никогда не спрашивает пароль в чате.
  7. Правка спрашивает дважды. «Поставь type=park выделенным объектам» → карточка плана, затем «Применить» → дополнительный предупреждающий диалог ровно с необратимыми шагами. Отказ оставляет данные нетронутыми.
  8. Правка действительно ложится. Согласие в диалоге меняет значения; проверочный прогон перечитывает их и подтверждает количество.
  9. delete_features не удаляет всё молча. «Удали все объекты» работает только с фильтром-словом all; пустой фильтр отклоняется с объяснением.

Печатные макеты

  1. Лист одним прогоном. «Собери альбомный А4 с картой, заголовком, легендой и масштабной линейкой» → одна карточка плана; после «Применить» лист в менеджере макетов, всё внутри страницы.
  2. Агент смотрит на лист. Проверочный прогон зовёт render_layout; если легенда накрыла карту — появляется план исправления с configure_layout_item.
  3. Легенда раньше карты отклоняется. Попросить легенду в пустом макете → внятная ошибка «сначала добавьте карту», ещё при постановке.
  4. Элементы за страницей отклоняются при постановке, с размером страницы в сообщении.
  5. Экспорт. «Экспортируй макет в /tmp/map.pdf» → файл существует и открывается; путь в несуществующую папку отклоняется до очереди.
  6. Элементы адресуемы. describe_layout показывает map-1/title; «опусти заголовок чуть ниже» меняет ровно этот элемент.

Полное покрытие: python, растры, поля, сервисы, откат

  1. run_python сначала показывает код. «Поставь режим наложения, которого нет в тулах» → карточка плана, затем «Применить» → предупреждающий диалог содержит сам сниппет, читаемый. Отказ ничего не выполняет.
  2. Сломанный сниппет отклоняется при постановке с синтаксической ошибкой и номером строки, ещё до вопроса пользователю.
  3. Вечный цикл не вешает QGIS. Поставить while True: pass → бюджет строк останавливает его, агент получает объяснение.
  4. Агент предпочитает штатные тулы. «Сделай реки синими» НЕ должно идти через run_python — это покрыто тулами стиля.
  5. Псевдоцвет растра. «Раскрась DEM палитрой Viridis» → палитра покрывает настоящие min/max канала, в легенде видны классы.
  6. Отмывка. «Сделай отмывку по DEM» → появляется теневой рельеф, азимут по умолчанию 315.
  7. Скрытие no-data. Растр с заполнителем -9999 → «скрой значения -9999» делает их прозрачными, цвета становятся осмысленными.
  8. Виртуальное поле. «Добавь виртуальное поле с площадью в гектарах» → колонка появилась в таблице атрибутов, файл на диске не тронут.
  9. Реальное поле, переименование, удаление. Добавить текстовое поле, переименовать, удалить → удаление спрашивает подтверждение необратимого шага; источник только для чтения сообщает об откате, а не падает молча.
  10. WMS и WFS. Добавить публичный WMS-слой и WFS-слой → тайлы рисуются, объекты WFS запрашиваются через query_layer.
  11. Откат. Применить изменение стиля, затем «откати» → проект возвращается к прежнему состоянию; агент говорит, что правки данных это не покрывает.
  12. Закладки и темы. «Запомни этот вид как центр», «сохрани этот вид как печать» → оба появляются в list_views и в панелях QGIS.

Автономность: этапные прогоны, план, бюджет

  1. Целая задача за один запрос. «Скачай кафе <города> из OSM, раскрась, подпиши, собери лист А4 и экспортируй в PDF» → агент составляет план, ставит в очередь скачивание, просит применить, а затем продолжает тот же прогон: читает новый слой, оформляет, применяет снова, собирает макет, смотрит на него, экспортирует. Один запрос, несколько нажатий «Применить», без повторных объяснений.
  2. План виден. Во время такого прогона в ленте идут строки вида «План 2/5: …», продвигающиеся по мере выполнения шагов.
  3. Отказ на этапе завершает чисто. Нажать «Отменить» на этапной карточке → прогон заканчивается сообщением, что изменения отклонены; наполовину применённого не остаётся.
  4. apply_now не злоупотребляется. Односложный запрос («сделай реки синими») по-прежнему должен давать одну карточку в конце, а не паузу на этапе.
  5. Счётчик токенов работает. В шапке растёт число токенов во время прогона и обнуляется при новом запросе.
  6. Бюджет останавливает прогон. Поставить в настройках маленький бюджет (скажем, 5000), запустить длинную задачу → прогон вежливо останавливается с объяснением, а не сжигает ключ.
  7. Длинный прогон не рвёт контекст. Прогон на 20+ ходов продолжает работать; старые результаты тулов приходят свёрнутыми, а не валят запрос ошибкой о длине контекста.
  8. Проверка итеративна. Сделать так, чтобы изменение стиля легло неверно → проверочный прогон ставит исправление, и если оно тоже неверно, идёт второй круг; после трёх кругов останавливается, а не крутится вечно.

Онбординг и ответ картой

  1. Пустая панель приглашает. Открыть плагин на свежем профиле без ключа → в ленте карточка с объяснением про ключ и кнопкой в настройки. Никакой загадочной ошибки при первой отправке.
  2. После сохранения ключа карточка меняется на три кликабельных примера запросов; нажатие отправляет запрос как набранный вручную.
  3. Выделение как ответ. «Покажи мне автомагистрали» → объекты выделены, карта подлетела к ним, они мигнули; в ответе назван счёт.
  4. Пустое выделение честно. «Покажи автомагистрали» на слое, где их нет → агент говорит, что ничего не совпало, а не рапортует об успехе.
  5. Экспорт слоя. «Сохрани слой дорог в /tmp/roads.gpkg» → файл существует и открывается в QGIS; неверное расширение отклоняется до постановки в план.
  6. Экспорт выделенного. Выделить несколько объектов, «экспортируй выделенное в GeoJSON» → в файле только эти объекты.

Поправка на ходу и память проекта

  1. Поправка доходит во время прогона. Запустить «покрась дороги красным» и во время работы написать «вообще-то синим» → реплика появляется с пометкой о передаче, агент переключается на синий, не начиная заново.
  2. Та же фраза в покое — обычный запрос, а не поправка: начинается новый прогон.
  3. Запоминание. «Запомни, что POP2020 — это перепись 2020 года» → в карточке плана видно; после «Применить» list_notes это возвращает.
  4. Память переживает перезапуск и привязана к проекту. Перезапустить QGIS, спросить «что ты помнишь?» → заметка на месте. Открыть другой проект → её нет.
  5. Заметки доходят до модели. С этой заметкой спросить «что в POP2020?» → ответ опирается на запомненное, не переспрашивая вас.
  6. Забывание. «Забудь заметку про POP2020» → её нет в list_notes; попытка забыть несохранённое отклоняется с подсказкой.

Стриминг

  1. Ответ растёт. Спросить что-то с длинным ответом — «расскажи, что в этом проекте и что бы ты исправил» → текст появляется по словам, а не целиком после паузы.
  2. Markdown в итоге корректный. Попросить ответ со списком и жирным → по ходу разметка может быть недособранной, но готовое сообщение отрисовано как markdown, без торчащих звёздочек.
  3. Вызов тула снимает черновик. Спросить так, чтобы агент сначала полез читать проект → сказанное до тула исчезает, когда появляется строка тула; ничего не показывается дважды.
  4. Сохранённая переписка совпадает с экраном. После потокового ответа переключиться на другой диалог и обратно → реплика на месте, в одном экземпляре, ровно та, которую читали.
  5. Эндпоинт без SSE работает. Указать сервер, который отказывает в стриминге → ответ приходит целиком, без ошибки, а следующий запрос сразу идёт обычным путём.
  6. Остановка во время потока. Нажать ■ пока текст растёт → прогон останавливается, черновик перестаёт расти, QGIS не подвисает.

Думающие модели

  1. Локальная думающая модель не выносит монолог наружу. Указать плагин на Ollama с дистиллятом DeepSeek-R1 или QwQ, спросить что угодно → ответ остаётся ответом, а рассуждение лежит в своём блоке, не в тексте реплики.
  2. Блок сворачивается сам. Пока модель рассуждает, блок раскрыт и растёт; как только пошёл ответ — сворачивается в строку с затраченным временем, а по клику раскрывается снова.
  3. DeepSeek подаёт признаки жизни. На эндпоинте DeepSeek с рассуждающей моделью блок наполняется во время паузы, а не панель стоит неподвижно.
  4. Рассуждение не уходит обратно. После ответа с рассуждением задать следующий вопрос → счётчик токенов на следующем шаге не тянет прошлый монолог, и модель на него не ссылается.
  5. Многошаговая задача с думающей моделью доходит до конца. Попросить то, что требует тулов → у каждого шага может быть свой блок, вызовы тулов по-прежнему идут по порядку.
  6. Расширенное рассуждение Anthropic выключено по умолчанию. С бюджетом 0 Claude отвечает ровно как раньше.
  7. Расширенное рассуждение Anthropic включено. Поставить бюджет 4096, попросить то, что требует тулов → появляется блок рассуждения, тулы отрабатывают, прогон заканчивается без ошибки API про блоки размышления.
  8. Модель без размышления не ломается от настройки. С заданным бюджетом указать модель без extended thinking → ответ всё равно приходит, отказ запоминается, дальнейшие запросы идут сразу.

Стриминг на Anthropic

  1. Claude тоже стримит. На эндпоинте Anthropic попросить длинный ответ → текст растёт по словам, ровно как на openai-совместимом.
  2. Рассуждение течёт вместе с ним. Поставить бюджет 4096 и спросить что-то сложное → блок рассуждения наполняется во время паузы, а не появляется целиком в конце, и показывает затраченное время.
  3. Тулы работают через поток. Попросить то, что требует тулов → аргументы приходят по кускам, но вызов уходит с правильными значениями, а в карточке плана перечислено то, что нужно.
  4. Отказ в размышлении не путается с отказом в стриминге. С заданным бюджетом указать модель без extended thinking → ответ приходит потоком; выключается только размышление, не стриминг.

Вопросы пользователю

  1. Настоящая неоднозначность становится вопросом, а не догадкой. При двух похоже названных слоях попросить «раскрась дороги» → агент спрашивает, какой из них, панель пишет, что ждёт, и следующее сообщение продолжает тот же прогон.
  2. Ответ продолжает, а не перезапускает. После ответа агент использует его сразу — без чтения проекта заново, план задачи переживает паузу.
  3. Никаких вопросов-разрешений. Обычные запросы по-прежнему кончаются карточкой плана, а не «можно продолжать?».

Интернет

  1. Место превращается в bbox через одобренный сервис. В настройках выберите Photon или укажите разрешённый тестовый Nominatim-сервис, затем попросите «Скачай кафе в Дивноморском» → в плане виден хост геокодера; после отдельного подтверждения агент передаёт полученный bbox в download_osm.
  2. Вопрос не о проекте уходит в веб. «Какой EPSG-код у UTM-зоны Казани?» → в плане названы DuckDuckGo/Wikipedia; после подтверждения search_web отрабатывает, ответ называет ссылку-источник.
  3. Вставленная ссылка читается. Вставить URL документации и спросить, о чём она → в плане виден публичный HTTPS-хост; после подтверждения fetch_url возвращает читаемый текст, ответ цитирует страницу, а не галлюцинацию.
  4. Закрытая сеть деградирует громко. В сети, где поисковик недоступен, search_web падает на Википедию, и ответ это говорит — без тихой имитации поиска по всему вебу.
  5. Публичный геокодер OSMF остаётся заблокирован. Попытка сохранить https://nominatim.openstreetmap.org как свой Nominatim отклоняется до сетевого запроса со ссылкой на правила использования OSMF.

Новые возможности и журнал аудита

  1. Аннотации проходят полный цикл. Попросить текстовую заметку и маркер → оба ждут в плане, появляются после Применить, видны в list_annotations и удаляются по id.
  2. 3D-вид открывается в заявленных границах. Попросить именованный 3D-вид → план говорит, что инструмент только открывает окно; после Применить вид появляется, а сборка QGIS без нужного API возвращает явную подсказку.
  3. Журнал прогона видим и точен. После применения плана чат сообщает путь к Markdown-файлу в каталоге ai_agent_runs активного профиля QGIS. Где поддерживается, проверить права каталога/файла 0700/0600, затем открыть файл и проверить сокращённые тексты запроса и агента, имена инструментов, ошибки неудачных вызовов, число применённых шагов и итог; отдельной выгрузки успешных результатов быть не должно.
  4. Gemini использует общий диалект. Выбрать пресет Google Gemini, указать действующие ключ и модель, нажать Проверить подключение → запрос идёт на документированный OpenAI-совместимый адрес и возвращает ответ проверки.

Скиллы и слэш-команды

  1. Попап по / показывает скиллы и вставляет выбранный. Набрать / в композере → над полем список всех встроенных скиллов с однострочниками; /o сужает до совпадений по префиксу; ↓ и Tab вставляют /osm с курсором после пробела; Esc закрывает, не трогая текст.
  2. Скилл из слэш-команды загружен до первого хода. Отправить /osm кафе в Казани → в ленте нет шага «Загружаю знания: osm», первое действие уже download_osm, а пузырь пользователя сохраняет исходный текст /osm ….
  3. Локальный скилл проходит весь путь. Настройки → Скиллы → Создать пример → папка открывается по кнопке, пример в списке; изменить description → он появляется в / с пометкой локальный, и агент может сделать load_skill; добавить неизвестный инструмент → страница показывает проблему, а скилл загружается только с известными.
  4. Неизвестная команда отклоняется, а не отправляется. /nope сделай → одно системное сообщение со списком доступных команд; до модели ничего не доходит.