Контракт задачи для AI-агента
AI-агент в разработке получает задачу, читает доступные файлы и предлагает либо выполняет изменения через предоставленные инструменты. Полезность такого помощника зависит не только от модели. Если результат задан расплывчато, даже аккуратно написанный код может решить другую проблему. В первом уроке определим контракт для небольшой Markdown-библиотеки: выбирать рекомендуемые материалы, сохраняя адреса и редакционные признаки страниц.
Серия переносима между инструментами. Мы не привязываем процесс к конкретному аккаунту, не отправляем исходники в модель и не показываем выдуманную беседу с агентом. В архиве находятся авторские запросы, документы и состояния Python-проекта. Они нужны, чтобы рассмотреть устройство задачи предметно. Нужны основы функций, словарей, JSON и файлов; внешних зависимостей в примере нет.
Результат, который можно увидеть
Представьте исходное пожелание: «Сделайте главную страницу удобнее». В нём не определены источник материалов, количество ссылок, порядок и отношение к служебным страницам. Один исполнитель добавит поиск, другой переставит карточки, третий перепишет шаблон. Каждый сможет обосновать решение удобством. Поэтому начнём с меньшего результата: функция возвращает не более двух публичных страниц с редакционным признаком featured, упорядоченных по устойчивому идентификатору.
В этой формулировке есть вход и наблюдаемый выход. Входом служит массив записей из manifest.json. Выход содержит идентификатор, название и прежний URL каждой выбранной страницы. При отсутствии подходящих записей получается пустой список. Это существенно: пустое состояние не даёт исполнителю основания показывать частные заметки или случайные статьи вместо рекомендованных. Поведение при отсутствии данных является частью задачи, а не украшением интерфейса.
Наш корпус содержит документы urls, markdown, sitemap и notes. Первые три публичные; notes — вымышленная редакционная заметка. У двух публичных документов выставлен featured: true. Поле url хранится отдельно от имени файла. Такая форма связана с опытом ProfessorWeb: современный исходник не обязан повторять старую структуру каталогов, но публичный адрес должен оставаться устойчивым. Весь пример самостоятельный и не читает рабочие файлы настоящего сайта.
Исключения из задачи
Контракт включает не только добавляемое поведение. В TASK.md записано, что можно менять catalog.py, а манифест и тексты служат входом. Следовательно, исполнитель не должен исправлять результат отбором удобных исходных данных. Если частная заметка мешает примеру, менять её публичность недопустимо: нужно исправить функцию. Если старый URL выглядит неудобным, переименование адреса также не является выполнением нашего пожелания.
Запись «не менять сайт на хостинге» ограничивает внешний эффект. Она не означает, что локальное изменение бесполезно. Наоборот, задача становится обозримой: мы можем прочитать небольшой кандидат, сопоставить его с входом и отдельно решить вопрос дальнейшей публикации. Качество кода и разрешение на размещение — разные основания. Ни одно из них нельзя вывести только из уверенного сообщения «готово» в конце ответа агента.
Лимит задаётся явно: целое число от одного до пяти, значение по умолчанию — два. Почему нужны границы, если главная показывает две ссылки? Функция может использоваться другим шаблоном или вызываться с ошибочным аргументом. Без договора появится неожиданное поведение при нуле, отрицательном числе, строке или логическом значении. Мы не обязаны заранее реализовать все варианты интерфейса, но должны определить, какие входы считаются допустимыми для текущего результата.
Запрос как рабочий документ
Хороший запрос содержит задачу, исходные файлы, область записи и критерий результата. Отдельная просьба «перед изменением прочитайте контракт» связывает действие с материалом, который действительно существует в проекте. Это надёжнее, чем длинное описание предполагаемого устройства библиотеки по памяти. Когда фактические файлы расходятся с пожеланием, исполнитель должен назвать расхождение, а не молча выбрать более удобную версию задачи.
В архивном TASK.md нет рекламных характеристик агента и обещания идеального результата. Он содержит проверяемые условия. Например, фразу «сохрани SEO» заменяет точное правило: значения url в записях не меняются. Это правило ещё не доказывает сохранение всех поисковых показателей настоящего сайта, зато относится к операции, которую мы действительно рассматриваем. Широкая цель разбивается на конкретные свойства, каждое из которых имеет свой способ наблюдения.
На первом шаге catalog.py только читает корпус и выводит публичные записи. Рекомендуемый отбор ещё не добавлен; он появится после подготовки контекста и реестра адресов. Такое исходное состояние важно сохранить: иначе будет трудно понять, что именно является новым изменением. Не подменяйте начальный проект финальным кодом, если хотите рассмотреть весь маршрут. Сравнение двух законченных состояний полезнее обсуждения неопределённого «старого кода».
Ожидаемое чтение manifest.json даёт четыре записи, а публичный отбор — три. Это описание будущего самостоятельного запуска, не полученный протокол выполнения. В самой статье и исходниках статус обозначен not_run. Если вы позже выполните пример, результаты можно записать отдельно с командой, версией среды и исходным состоянием. Тогда станет ясно, к каким данным относится наблюдение, и его можно будет повторить.
Требование без скрытого выбора
Рассмотрим дополнительное условие: на главной должно быть ровно две ссылки даже тогда, когда рекомендована только одна страница. Оно отличается от текущего договора «не более двух». Чтобы выполнить его, понадобится запасной набор и правило его выбора. Если такое условие обнаружилось только после появления кандидата, сначала уточняют задачу, а не обвиняют функцию в нарушении неизвестного правила. Контракт помогает заметить момент, когда меняется предмет работы.
Для нашей исходной задачи единственная рекомендованная публичная запись должна остаться единственной. Это объясняет, почему лимит является верхней границей, а не обещанием заполнения. Можно попросить агента перечислить такие неоднозначности до правки, но не разрешать ему самостоятельно выбирать редакционную политику. При обсуждении полезно назвать конкретный вход: одна подходящая запись и две неподходящие. Тогда отличие между верхним пределом и обязательным количеством видно без сложного интерфейса.
Запишите ответ на этот вопрос в TASK, если правило меняется. Оно должно быть доступно следующему исполнителю вместе с данными. Устное решение без обновления рабочего документа быстро потеряется при передаче. При этом изменение контракта не требует автоматически менять URL, структуру корпуса или способ хранения: новый результат остаётся отдельной операцией выбора.
Прежде чем перейти к карте проекта, сформулируйте другой небольшой результат на тех же данных: например, показать только название и адрес документа sitemap. Укажите поведение для неизвестного идентификатора и запрет изменения публичности. Если формулировка требует сразу менять поиск, шаблон, хранение и размещение, она всё ещё слишком широка для одного обозримого изменения. Первый результат серии — понятный рабочий контракт, на который будут опираться следующие шаги.
Полный файл TASK.md из состояния lesson-01. Полное самостоятельное состояние находится в архиве серии; фрагмент не заменяет остальные определения.
# Учебная задача PW-FEATURED-1
Результат: выбрать не более двух публичных рекомендуемых страниц, сохранив каждый URL.
Вход: manifest.json и corpus/*.md. Источник permalink — только поле url.
Менять: catalog.py. Не менять: корпус, manifest.json, правила публикации и внешний сайт.
Граница: лимит — целое число от 1 до 5; boolean не является лимитом.
Порядок: id по возрастанию. Нет рекомендованных страниц — пустой список.
Проект учебный; сообщения агента и реальные запуски отсутствуют.
Статус примера: not_run. При подготовке не выполнялись учебные программы, тесты, вызовы AI или деплой. Описанные эффекты ожидаемые.
Оглавление серии · Полные исходники
Первичные документы: Источник 1.