Автономный исследователь на языковых моделях

В отличие от помощника оператора, ожидающего подтверждения на каждую запись в оборудование, система, рассматриваемая в настоящей главе, работает без человека в контуре: она самостоятельно выбирает задачу, самостоятельно ставит эксперимент и самостоятельно пишет статью по полученным результатам.

Назначение системы

Автономный исследователь состоит из большой языковой модели и обвязки, выполняющей полный цикл научной работы. Он находит задачу, разрабатывает методику, ставит эксперимент, анализирует полученные числа и оформляет статью. Специализация узкая, ускорительная: коррекция орбиты пучка, управление магнитными элементами, оптимизация параметров, анализ данных измерений.

Основой служит открытая система AI Scientist, описанная в работе Lu et al. (2024) [43] и адаптированная здесь к ускорителям. Адаптация свелась к одному новому шаблону, размещённому рядом с готовыми шаблонами про языковые модели, диффузию и гроккинг. Шаблон, добавленный в форк, содержит постановку задачи, рабочий код, затравочные идеи и каркас статьи. Ничего специфически ускорительного в самой обвязке нет: вся физика сосредоточена в шаблоне, подключаемом по имени каталога.

Устройство автономного исследователя

Схема делит работу на три стадии. На первой модель формулирует идею, проверяет её на новизну поиском по литературе и помещает в архив с оценками. На второй идея превращается в код: правки в experiment.py вносит aider, редактор, управляемый моделью, а обвязка запускает переписанный код и возвращает модели напечатанные числа. Третья стадия собирает статью из каркаса LaTeX, после чего отдельный проход рецензирует написанное. Пунктирная стрелка, замыкающая схему, возвращает накопленные идеи в начало, так что следующая идея формулируется с учётом уже проверенных.

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

Постановка задачи

Всё, что системе известно о предметной области, взято из файла prompt.json. Роль и цель, поставленные перед моделью, заданы в нём первыми двумя полями.

System: You are an AI scientist specializing in accelerator physics and
beam dynamics with expertise in machine learning applications for particle
accelerator control systems. Your knowledge spans MAD-X simulations, beam
position monitoring (BPM) systems, and both traditional (SVD-based) and
AI-driven orbit correction methods.

Task: Minimize orbit deviations in the accelerator by optimizing corrector
magnet settings using a combination of numerical optimization and machine
learning techniques.

В том же файле перечислены требования, ограничивающие решение, и среда, в которой рассчитывается орбита.

"technical_requirements": [
    "Process beam position data from 16 BPMs (x/y coordinates)",
    "Account for hidden misalignment parameters affecting magnetic elements",
    "Maintain magnet currents within ±7A operational limits",
    "Achieve orbit residuals below 1e-4 m at all BPMs"
],
"simulation_environment": {
    "tool": "MAD-X",
    "configurations": [
        "vepp5_full.seq lattice",
        "0.4 GeV electron beam energy",
        "Error definitions: ealign with tgauss-distributed displacements"
    ]
}

Это задача, разобранная в главе про коррекцию равновесной орбиты: 16 датчиков положения, дающих по две координаты, предел корректора \(\pm 7\) А, модель накопителя ВЭПП-5. Разница заключается в том, что ставится она не человеку, а программе.

Смещения магнитных элементов, названные в требованиях «hidden», разыгрываются внутри модели и решателю не сообщаются, поэтому судить о них можно только по орбите, измеряемой в мониторах. Порог в \(10^{-4}\) м записан там же, и по нему система оценивает как свои прогоны, так и базу, рассчитанную заранее.

Сам шаблон представляет собой обычный каталог, размещённый среди прочих.

templates/closed_orbit_correction/
├── prompt.json       постановка задачи и системная роль
├── seed_ideas.json   пять затравочных идей
├── experiment.py     расчёт орбиты и коррекция по SVD
├── plot.py           графики по собранным результатам
├── vepp5_full.seq    оптика ВЭПП-5, читаемая MAD-X
└── latex/            каркас статьи

Прогон запускается командой python launch_scientist.py --experiment closed_orbit_correction, после чего участие человека не требуется до самого конца. Каталог результатов, названный по метке времени и имени идеи, создаётся в начале работы.

Точка отсчёта

Вместе с постановкой модели выдаётся работающий код, служащий одновременно образцом и базой сравнения. Ускоритель инициализируется через cpymad, пучок электронов задан с энергией 0.43 ГэВ, в постановке округлённой до 0,4 ГэВ, а ошибки расстановки квадруполей разыгрываются директивой ealign с гауссовым распределением, усечённым на двух с половиной сигмах. Орбита снимается в шестнадцати мониторах, отобранных по классу monitor. Коррекция выполнена классически, через псевдообратную матрицу отклика с отсечением малых сингулярных чисел.

def correct_orbit(self):
    elem_val_limit = 7
    svd_cutoff = 1e-3
    target_orbit = np.zeros(2 * self.num_bpms)

    matrix = self.calculcate_resp_mat()
    inv_mat = np.linalg.pinv(matrix, rcond=svd_cutoff)

    madx = self.start_madx()
    x, y = self._get_orbit(madx)
    current_orbit = np.concatenate((x, y))

    tmp_elem_val = -inv_mat.dot(current_orbit - target_orbit)
    tmp_elem_val = np.clip(tmp_elem_val, -elem_val_limit, elem_val_limit)

    elems_deltas = dict(zip(self.globals.keys(), tmp_elem_val))
    self.change_elements(elems_deltas)
    x, y = self._get_orbit(madx)

    self.stop_madx(madx)
    return x, y, elems_deltas

Матрица отклика здесь не берётся из модели, а измеряется: каждый корректор смещается на шаг вверх и вниз, а разность откликов, снятых в мониторах, делится на этот шаг. Так же поступают и на реальной машине. Ограничение np.clip по \(\pm 7\) А расположено внутри алгоритма, до подачи токов, а не в проверке, добавленной после него. Опечатка в имени calculcate_resp_mat оставлена без изменений, поскольку на неё опирается код, написанный системой позже.

Этот файл модель видит целиком, вместе с постановкой. Идеи, предложенные далее, сформулированы в терминах уже написанных методов: «доработать correct_orbit», «добавить в calculcate_resp_mat выбор подмножества корректоров».

Идеи, придуманные системой

Пять затравочных идей содержатся в seed_ideas.json, и модель дополняет их новыми, читая постановку и код. В запрос включается весь архив, накопленный к этому моменту, чтобы новая идея не повторяла предыдущие. Каждая идея описана коротким планом эксперимента и снабжена тремя оценками от одного до десяти: интересность, выполнимость и новизна. Оценки выставляет сама модель, следуя указанию, включённому в запрос: быть осторожной и реалистичной.

Сформулированное проходит через три раунда правки, в которых модель перечитывает и улучшает собственный ответ, а когда улучшать нечего, ответ помечается словами I am done. Затем идея проходит проверку на новизну: модель формулирует поисковые запросы, получает по десять статей с аннотациями из Semantic Scholar или OpenAlex и выносит вердикт строкой Decision made: novel. На решение отведено десять раундов, однако обычно достаточно двух-трёх. Идеи, признанные несамостоятельными, до эксперимента не допускаются.

Ниже приведены идеи, предложенные системой по коррекции орбиты.

ИдеяInt./Feas./Nov.
Hybrid SVD-Neural Network Correction Framework. Каскад: грубая коррекция по SVD, затем нейросеть, обученная на остатках. Сравнить чистые NN и SVD с гибридом. До 100 000 особей и до 10 итераций9 / 7 / 8
Genetic Algorithm for Optimizing Orbit Correction. Генетический алгоритм с отбором, скрещиванием и мутацией для настройки корректоров. Сравнить норму \(L_2\) и время счёта с SVD и NN9 / 8 / 9
Predictive Control Using LSTM-Based Forecasting. Предсказание положения пучка по истории BPM и упреждающая подстройка корректоров до появления отклонения9 / 5 / 9

В работу была принята первая из них.

Эксперимент

Идею принимает цикл, отмеряющий системе лимит попыток. Прогонов по умолчанию не более пяти, неудачных заходов подряд не более четырёх, и работа, не завершённая в отведённых рамках, прерывается.

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

MAX_ITERS, MAX_RUNS = 4, 10

def perform_experiments(idea, folder_name, coder, baseline_results) -> bool:
    current_iter, run = 0, 1
    next_prompt = coder_prompt.format(
        title=idea["Title"], idea=idea["Experiment"],
        max_runs=MAX_RUNS, baseline_results=baseline_results,
    )
    while run < MAX_RUNS + 1:
        if current_iter >= MAX_ITERS:
            break
        coder_out = coder.run(next_prompt)
        if "ALL_COMPLETED" in coder_out:
            break
        return_code, next_prompt = run_experiment(folder_name, run)
        if return_code == 0:
            run += 1
            current_iter = 0
        current_iter += 1

Каждый прогон запускается как python experiment.py --out_dir=run_i, и модель получает обратно либо рассчитанные средние из final_info.json, либо последние полторы тысячи знаков stderr. Завершившийся с ошибкой прогон не прерывает работу, а возвращается модели текстом ошибки, по которому она исправляет код. Каталог неудачного захода при этом удаляется, чтобы незавершённые результаты не попали в статью. Результаты, признанные пригодными, дописываются в notes.txt вместе с описанием прогона, и оттуда их впоследствии берёт стадия написания статьи. Код, исполняемый на этом шаге, написан языковой моделью, поэтому авторы системы рекомендуют изолировать прогон в контейнере, и Dockerfile для этого включён в репозиторий.

По окончании расчёта той же модели поручается переписать plot.py и перечислить в нём прогоны, заслуживающие графика. Заголовки, подписи осей и легенда выбираются на этом шаге, а подробное описание каждого графика заносится в те же заметки.

Код гибридной коррекции система написала самостоятельно. Сеть в нём обучается не отображению «орбита — токи» целиком, а поправке к решению, найденному через SVD, и эта поправка накапливается по итерациям.

def hybrid_correct_orbit(self, model=None, iterations=0, svd=True):
    elem_val_limit = self.corr_limit
    x_new, y_new, elems_deltas = self.correct_orbit()   # грубая коррекция
    R = np.concatenate((x_new, y_new))
    if model is None:
        return x_new, y_new, elems_deltas

    dI_nn_total = np.zeros(len(self.globals)) + list(elems_deltas.values())
    for it in range(iterations):
        dI_nn = model.predict(R.reshape(1, -1))[0]      # поправка по остатку
        dI_nn_total += dI_nn
        dI_nn_total = np.clip(dI_nn_total, -elem_val_limit, elem_val_limit)

        self.change_elements(dict(zip(self.globals.keys(), dI_nn_total)))
        madx = self.start_madx()
        x_new, y_new = self._get_orbit(madx)
        self.stop_madx(madx)
        R = np.concatenate((x_new, y_new))

    return x_new, y_new, elems_deltas

Обучающая выборка набиралась отдельным прогоном: 100 000 случайных расстроек, для каждой из которых снят остаток после SVD и рассчитана идеальная добавка по номинальной матрице отклика, измеренной без расстроек. Сеть выбрана простая, MLPRegressor с двумя скрытыми слоями 64 и 32, а обученная модель сохранена в nn_model.joblib и далее только читается. Прогоны с первого по десятый отличаются одним числом — количеством итераций сети, извлечённым из имени каталога.

Коррекция орбиты гибридом SVD и нейросети: начальная и остаточная невязка по числу итераций

На рисунке приведены одиннадцать пар столбцов, по паре на прогон, шкала логарифмическая. Синий столбец — начальная невязка \(\sum (x_i^2 + y_i^2)\) в м², оранжевый — остаточная, а планки погрешностей, построенные по стандартной ошибке среднего, показывают разброс. В каждом прогоне сто независимых расстроек, разыгранных заново, поэтому начальная невязка остаётся примерно одинаковой от прогона к прогону, и оранжевые столбцы сравнимы между собой. Нулевая итерация, рассчитанная без сети, соответствует чистому SVD: \(1.9 \cdot 10^{-4}\) м² до коррекции и \(4.8 \cdot 10^{-5}\) после. Три итерации сети дают \(5.9 \cdot 10^{-6}\), что является лучшей точкой на графике. К десятой итерации остаток возвращается к \(3.5 \cdot 10^{-5}\).

Статья и рецензия

Написанием статьи занимается тот же aider, но с другим набором файлов, открытых для правки: код эксперимента, заметки и latex/template.tex. Ссылки, вставляемые в текст, система находит самостоятельно тем же поиском по литературе. Готовый PDF собирается pdflatex, а chktex проверяет разметку. Ошибки сборки, найденные этими программами, возвращаются модели тем же способом, что и ошибки расчёта.

Последним включается рецензент. Статья, преобразованная обратно в простой текст, подаётся модели с формой отзыва в стиле NeurIPS, в которой необходимо выставить оценки за оригинальность, качество, ясность и значимость, дать общую оценку от одного до десяти и вынести решение Accept или Reject. Собирается пять отзывов, которые сводятся мета-рецензентом; температура установлена равной 0.1. Статья про гибридную коррекцию, оценённая собственным рецензентом на 3 из 10, была им же отклонена.

Ограничения и перспективы

Ниже перечислены ограничения, признанные за системой.

  • Отсутствие работы с изображениями. Модель читает только числа и текст, а построенные графики не анализирует.
  • Неверная реализация идей. Написанный код нередко не соответствует тому, что задумано в описании эксперимента.
  • Ошибки в численном анализе. Величины, сравниваемые между собой, модель путает, а выводы по таблицам даются ей плохо.
  • Ограниченная физическая интерпретация. Система работает с формой задачи, а не с её содержанием.

Направления, названные для дальнейшей работы: мультимодальные модели, способные читать графики; специализированные модули под предметную область; интеграция с реальными установками; развитие самих языковых моделей, лежащих в основе.

Полезные ссылки