AP 2.0: Учим ИИ думать, прежде чем патчить. ai.. ai. ap.. ai. ap. python.. ai. ap. python. yaml.. ai. ap. python. yaml. ИИ.. ai. ap. python. yaml. ИИ. искусственный интеллект.. ai. ap. python. yaml. ИИ. искусственный интеллект. патч.. ai. ap. python. yaml. ИИ. искусственный интеллект. патч. Программирование.

Не так давно я рассказывал вам о рождении формата .ap (AI-friendly Patch) — моей попытке избавить мир от боли ручного копипаста при работе с AI-ассистентами. Идея была проста: вместо генерации блоков кода, который нужно переносить в исходники руками, ИИ генерирует семантический патч в специальном, удобном именно для ИИ формате, который применяется автоматически. Судя по числу добавлений статьи в закладки, идея многим пришлась по душе!

Но теория — это одно, а суровая практика — совсем другое. За время активного использования ap в реальных задачах (в том числе при работе над far2l) вскрылись узкие места и накопились идеи, как сделать формат ещё надёжнее, удобнее и, что самое главное, — ещё более «понятным» для нейросетей. Сегодня я хочу рассказать вам о результате этой работы — большом обновлении ap 2.0

Это не просто косметические правки, а серьезный шаг вперёд, основанный на главном инсайте: лучшие результаты ИИ показывает тогда, когда мы позволяем ему сначала спланировать свои действия на человеческом языке, и только потом — реализовать их в виде кода. Поехали!

Что нового в 2.0: от инструкций к осмысленным планам

Ключевых изменений несколько, и каждое из них решает конкретную проблему, выявленную в «боевых» условиях.

1. Принцип «Сначала План» (The “Plan-First” Principle)

Это, пожалуй, главное идеологическое изменение. В версии 1.0 ИИ должен был сразу формировать машиночитаемую структуру. Это похоже на то, как если бы вы попросили младшего разработчика внести правку, и он бы молча сразу начал писать код, не объяснив, что и почему он собирается делать.

В 2.0 мы вводим «Принцип „Сначала План“». Теперь ИИ обязан сначала описать свои намерения в виде комментария в начале .ap файла. Этот комментарий имеет чёткую структуру: Summary (что и зачем делаем) и Plan (пошаговый план, как именно).

Помните пример из прошлой статьи? Вот как он выглядел в v1.0:


# afix.ap (v1.0)
version: "1.0"
changes:
  - file_path: "greeter.py"
    modifications:
      - action: REPLACE
        target:
          snippet: 'print("Hello, world!")'
        content: 'print("Hello, AI-powered world!")'

А вот как выглядит тот же патч в v2.0, с учётом нового принципа:


# afix.ap (v2.0)
# Summary: Update the greeting message in greeter.py.
#
# Plan:
#   1. In `greeter.py`, replace the "Hello, world!" string with
#      "Hello, AI-powered world!".
#
version: "2.0"
changes:
  - file_path: "greeter.py"
    modifications:
      - action: REPLACE
        snippet: |
          print("Hello, world!")
        content: |
          print("Hello, AI-powered world!")

Чем это хорошо:

  • Для человека: Патч становится самодокументируемым. Вы сразу видите намерение ИИ, как в хорошем коммит-сообщении. Это в разы упрощает ревью.

  • Для ИИ: Мы разделяем две когнитивно сложные задачи — планирование и кодирование. Сначала ИИ строит логическую цепочку действий, а затем, опираясь на собственный план, генерирует код. Это снижает вероятность ошибок и повышает качество итогового патча.

2. Диапазоны: Прощайте, гигантские snippet’ы

Одна из главных болей v1.0 — замена больших, многострочных блоков кода. ИИ приходилось копировать весь исходный блок в snippet, что было неудобно и хрупко: ошибка всего в одном пробеле внутри блока ломала поиск.

Размышляя над тем, как справиться с этой ситуацией, я вспомнил, как модели генерируют инструкции, когда просишь их «объясни, как внести изменения, как джуну»: они пишут «замени блок с такого-то места и до такого-то». То есть задают не весь блок для замены, а уникальные фрагменты в его начале и конце. Но ведь то же самое можно делать и автоматически!

Встречайте, диапазонные модификации. Теперь для действий REPLACE и DELETE вместо одного snippet можно указать start_snippet и end_snippet. Патчер найдёт блок, начинающийся с одного фрагмента и заканчивающийся другим, и применит действие ко всему, что между ними (включая сами фрагменты).

Представьте, что нам нужно заменить сложный блок логики в функции:

def complex_calculation(data):
    # ... какая-то подготовка ...

    # Stage 2: Core logic (this whole block will be replaced)
    # It has multiple lines and comments.
    intermediate_result = 0
    for val in processed_data:
        intermediate_result += val * 1.1
    result = intermediate_result / len(processed_data)

    # Stage 3: Post-processing
    return f"Final result: {result}"

С помощью диапазона патч становится лаконичным и супернадёжным:

version: "2.0"
changes:
  - file_path: "22_range_replace.py"
    modifications:
      - action: REPLACE
        start_snippet: |
          # Stage 2: Core logic (this whole block will be replaced)
        end_snippet: |
          result = intermediate_result / len(processed_data)
        content: |
          # New, simplified implementation
          result = sum(processed_data)

ИИ больше не нужно пытаться без ошибок воспроизвести 5-10 (а в реальных условиях это скорее 50-100) строк кода. Достаточно найти надёжное начало и надёжный конец — это на порядок проще и устойчивее к несущественным ошибкам генерации.

3. Упрощение и унификация формата

Опираясь на практику, я внес в формат ещё несколько улучшений, которые делают его чище и надёжнее:

  • Плоская структура: Теперь нет избыточной вложенности, добавляемой объектом target. snippetanchor и другие поля находятся на одном уровне с action. Это упрощает и генерацию, и парсинг, и работу с ap патчами вручную.

  • YAML-блоки (|) обязательны: Чтобы раз и навсегда покончить с проблемами экранирования спецсимволов и забытых кавычек вокруг строки, теперь все поля с кодом (snippet, snippet_start, snippet_end, anchor и contentобязаны использовать YAML-синтаксис литеральных блоков. Даже для одной строки. Это обеспечивает единообразие и стопроцентную надёжность, и, опять-таки, сильно повышает читаемость патча человеком и удобство ручной работы с ним. Патчер не сработал? Не беда, перенос изменений вручную теперь ещё проще.

  • Уточнение поиска: В спецификации явно прописано, что поиск snippet‘а после anchor‘а начинается со строки, следующей за концом якоря. Это более интуитивно и предотвращает случайные совпадения внутри самого якоря.

Полный пример в действии

Давайте посмотрим, как все нововведения работают вместе на примере из обновлённой спецификации. Допустим, у нас есть такой файл:


# src/calculator.py
import math

def add(a, b):
    # Deprecated: use sum() for lists
    return a + b

def get_pi():
    return 3.14

А вот патч v2.0, который добавляет импорт, рефакторит функцию add и удаляет get_pi:


# Summary: Refactor the calculator module to enhance the `add` function
# and remove deprecated code.
#
# Plan:
#   1. Import the `List` type for type hinting.
#   2. Update the `add` function to also handle summing a list of numbers.
#   3. Remove the unused `get_pi` function.
#
version: "2.0"
changes:
  - file_path: "src/calculator.py"
    modifications:
      - action: INSERT_AFTER
        snippet: |
          import math
        content: |
          from typing import List

      - action: REPLACE
        anchor: |
          def add(a, b):
        snippet: |
          return a + b
        content: |
          # New implementation supports summing a list
          if isinstance(a, List):
              return sum(a)
          return a + b

      - action: DELETE
        snippet: |
          def get_pi():
              return 3.14
        include_leading_blank_lines: 1

Чисто, читаемо и надёжно. Именно таким и должен быть инструмент для автоматизации.

Самотестирование

Все изменения и в спецификацию, и в патчер, и в тесты вносились с помощью .ap файлов, обеспечивая дополнительный стресс-тест формата прямо в процессе работы над ним. Собственно, диапазонная модификация как раз и появилась как ответ на несколько сбоев, связанных с необходимостью заменять длинные блоки текста в спеке. Подобные проблемы случались и раньше, но я решал их нажатием кнопки «сгенерировать снова». С новым форматом нажимать эту кнопку приходится гораздо реже :)

Заключение

Переход на версию 2.0 — это не просто обновление синтаксиса. Это смена парадигмы. Мы смещаем фокус с «ИИ как генератора токенов» на «ИИ как партнёра по разработке», который сначала формулирует план, а потом его выполняет. Такой подход оказался гораздо эффективнее и для машин, и для людей.

Все изменения — от «плана» до диапазонов — нацелены на одно: повысить надёжность и предсказуемость автоматического применения патчей. Меньше хрупкости, меньше ошибок, больше сэкономленного времени и нервов.

Весь проект, включая обновлённую спецификацию, патчер и полный набор тестов, как всегда, доступен на GitHub. Я обновил там все примеры и документацию до версии 2.0 и добавил новые тесты.

Буду рад вашим отзывам, идеям и, конечно же, пулл-реквестам. Давайте вместе сделаем взаимодействие с нашими кремниевыми помощниками ещё удобнее!

Предыдущая статья цикла

Автор: unxed

Источник

Rambler's Top100