В LLM-инженерии постепенно меняется объект оптимизации.

Сначала подбирали промпты. Потом настраивали RAG. Параллельно тюнили модели под конкретные задачи и домены, подбирали грамматики, засовывали модель в цикл.
И вот появилось модное слово harness — по сути, сборная солянка из всего, что не LLM: тулы, MCP, память, агентные workflow, guard rails, record/replay-механики, механизмы компакции, маскирование, сабагенты, скиллы и много чего ещё. В попытках систематизировать весь этот зоопарк технологий был разработан интерактивный mindmap, который доступен для всех желающих.

Следующий логичный шаг — оптимизировать harness целиком: не только промпты или top-k в retriever, не только веса модели, а весь исполняемый runtime, в котором действует модель.
В литературе встречаются названия типа compound AI systems optimization или meta-harness optimization — оптимизация AI-систем, состоящих из нескольких взаимодействующих компонентов, а не из одного вызова модели.
Мы с командой не ограничились чтением статей, а разработали небольшой бенчмарк с записью работы СУБД и MCP-тулов в реальных нагрузочных кейсах и с последующим ускоренным Replay этой записи на диагностическом агенте с целью оптимизации его harness. Саму оптимизацию проводили через циклическое генетическое сэмплирование и выбор наилучшего варианта harness посредством парето-оптимизации.
Оказалось, что это работает. Да, на небольшом бенчмарке, но целевая метрика в двух независимых запусках с разной методологией оценки заметно выросла:
в первом запуске:
0.478 -> 0.597, то есть+24.9%к baseline;во втором запуске:
0.591 -> 0.695, то есть+17.6%к baseline.
Тут интересна даже не сама метрика, а то, какие изменения нашёл оптимизатор. Он не просто переписывал промпты. В удачных кандидатах он начал менять этапность workflow, доступные MCP-профили и процесс сбора доказательств как отдельный этап формирования AI-вердикта. Это уже похоже не на prompt-engineering, а на маленький AutoML для agent harness.
1. Минимальная формализация
Пусть есть модель:
где — веса модели.
Вокруг неё есть harness:
где — всё, что мы можем менять без дообучения модели:
Большинство этих параметров нечисловые (это важно в плане отсутствия возможности градиентной оптимизации параметров). Они могут представлять собой markdown, Python, YAML, JSON schemas, описания тулов, MCP-профили, порядок и состав этапов, разные агентные циклы, политики compaction/masking и так далее.
На шаге harness строит контекст:
где:
— исходная задача;
— история сообщений, вызовы тулов и разные observations;
— состояние среды (например, в случае анализа СУБД: телеметрия, snapshots, различные отчёты, query fingerprints);
— context builder.
Модель порождает действие:
Действие может быть финальным ответом или вызовом инструмента.
Среда возвращает новый observation:
И получается траектория:
В случае с обычным single-turn LLM-приложением нас часто интересует только финальный ответ. Для агентной системы этого, очевидно, мало. Тут важно оценивать ещё и путь:
вызвал ли агент нужные тулы;
не пропустил ли negative evidence;
не сделал ли неподтверждённых выводов;
не перепутал ли причинно-следственную связь;
нашёл ли root cause;
не ушёл ли в слишком долгий цикл решения и вызова тулов;
не сломал ли JSON.
Поэтому objective лучше писать как функцию от траектории:
Или чуть более наглядно в виде схемы:

И вот это уже более честная постановка задачи. Мы оптимизируем не то, насколько красиво агент ответил, а насколько хорошо он прошёл диагностический процесс
2. Prompt/RAG optimization как первый симптом
Prompt optimization был первым массовым проявлением этой идеи.
OPRO
OPRO предложил использовать LLM как optimizer: мы даём модели описание задачи, историю предыдущих кандидатов и их оценки, а модель предлагает новых кандидатов. После оценки лучшие попадают в следующий раунд.
Условно:
archive = [] for step in range(num_steps): prompt_candidates = llm.generate( task_description=task, previous_attempts=archive, ) for p in prompt_candidates: score = evaluate_prompt(p, dev_set) archive.append((p, score)) best_prompt = max(archive, key=lambda x: x.score)
То есть мы больше не пишем сами "please think step by step", "you are super expert" или иную ересь, за нас это теперь делает LLM. И тут уже работает неградиентная оптимизация по дискретному пространству текстов.
TextGrad
TextGrad предложил ещё более интересную аналогию: текстовый фидбек как «градиент» для compound AI system. LLM-critic генерирует natural-language feedback, который распространяется назад по computation graph и улучшает промпты, сниппеты и другие промежуточные компоненты.
Градиент здесь работает именно как метафора: LLM даёт семантически богатый фидбек, который потом учитывается на каждом этапе. Если критик говорит «ответ плохой, потому что агент не проверил negative evidence», это гораздо полезнее, чем просто score = 0.42.
GEPA
Из статей по оптимизации промптов GEPA (Genetic-Pareto reflective prompt optimizer) выделяется сочетанием нескольких идей:
Genetic evolution: кандидаты мутируют и рекомбинируются как в классических генетических алгоритмах.
Reflection: LLM анализирует траектории и формулирует, что именно пошло не так.
Pareto selection: сохраняются кандидаты, которые хороши по разным осям.
GEPA позиционируется как оптимизатор для compound AI систем. Авторы пишут, что он использует траектории, вызовы тулов и их результаты, отражает ошибки на естественном языке, предлагает изменения промптов и использует Pareto frontier.
Схематично:
archive = initialize_prompts() for generation in range(G): rollouts = evaluate(archive, tasks) reflections = [ llm_reflect(trace=trace, score=score) for trace, score in rollouts ] mutants = [ mutate_prompt(parent.prompt, reflection) for parent, reflection in select_parents(archive, reflections) ] evaluated = evaluate(mutants, tasks) archive = pareto_select( archive + evaluated, objectives=[ "task_score", "robustness", "-latency" ], )
Тут важно вот что, если optimizer видит только:
{"candidate": "c_009", "score": 0.695}
он почти ничего не понимает.
Если он видит:
{ "candidate": "c_009", "score": 0.695, "trace": [ {"stage": "recent_context", "tools": ["get_snapshot", "get_query_fingerprints"]}, {"stage": "query_regression", "tools": ["explain_query_fingerprint"]}, {"stage": "waits_replication", "tools": ["get_wait_event_breakdown"]}, {"judge_comment": "good OLTP recovery, missed backend_xmin evidence in sleep_blocking"} ] }
он уже может строить обоснованные гипотезы и направлять поиск в нужное русло, как антиградиент направляет поиск оптимума в классической гладкой оптимизации.
AutoRAG
RAG-оптимизация тоже прошла похожий путь.
В простом варианте мы выбираем embedding model, chunk size, top-k и reranker. AutoRAG формализует это как автоматический подбор комбинации RAG-модулей под конкретный dataset. AutoRAG можно воспринимать как ранний прототип meta-harness: он оптимизирует в основном retrieval pipeline, но общая идея та же: мы ищем лучшую конфигурацию системы вокруг модели на базе верифицируемой метрики.
3. Meta-harness: оптимизация агентной среды
Следующий шаг после оптимизации промптов и RAG — это оптимизация самого пайплайна работы агента: какие шаги он проходит, какие инструменты видит на каждом шаге, когда собирает дополнительные данные, когда передаёт задачу на следующую стадию и как валидирует финальный ответ.
AFlow формализует работу агента как граф: в узлах находятся вызовы модели, инструменты, проверки и промежуточные преобразования данных, а рёбра задают порядок выполнения. Дальше оптимизатор не просто переписывает промпты, а меняет сам граф: добавляет или убирает шаги, переставляет их местами, меняет связи между ними и проверяет, стало ли решение лучше. Для поиска используется MCTS — метод, который часто применяют там, где нужно исследовать большое пространство вариантов и постепенно смещаться в сторону более перспективных веток.
ADAS смотрит на задачу шире. Здесь агентная система задаётся кодом, а отдельный meta-agent пробует проектировать новые варианты таких систем: с другими промптами, другим порядком рассуждения, другим использованием инструментов и другим управляющим циклом. Это похоже на переход от ручного выбора «ReAct или planner?» к автоматическому поиску по пространству возможных агентных архитектур.
Meta-Harness делает следующий шаг: ищет лучший вариант, модифицируя исходный harness-код. В одноимённой работе harness определяется как код, который решает, что хранить, что доставать и что показывать модели; система использует agentic proposer, который имеет доступ к исходному коду агента, оценкам и траекториям предыдущих кандидатов через персистентное хранилище.
AgentSquare вводит модульный design-space: Planning, Reasoning, Tool Use, Memory; дальше идут эволюция и рекомбинация модулей.
Если обобщить, все эти работы, хоть и с разной степенью свободы, двигают нас в сторону оптимизации работы агента как исполняемой программы. Где-то меняется граф шагов, где-то — код harness, где-то — модульный состав агента, где-то — стратегия использования инструментов.
4. Эксперимент: meta-harness optimization для Virtual DBA
Мы с командой попробовали meta-harness-оптимизацию в одном из разрабатываемых агентов для диагностики проблем с СУБД. У агента уже уже есть MCP-инструменты для отслеживания live PostgreSQL activity, waits, locks, settings, query fingerprints, read-only SQL, PWR reports. AI healthcheck использует эти инструменты для диагностического вердикта по текущему состоянию системы.
Ключевая техническая деталь — record/replay. Во время записи интересующей нагрузки сохраняются PostgreSQL-телеметрия, контекст системы, текущие инциденты, настройки и результаты MCP-тулов. Результаты тулов хранятся как JSONL по tool name + args/kwargs: replay сначала пытается сделать exact match, а для некоторых записей умеет применять явные операции вроде сортировки и обрезки уже записанного набора строк. За счёт этого разные версии harness можно прогонять по одному и тому же трейсу нагрузки с произвольной скоростью, ограниченной временем самого AI healthcheck.
В meta-harness loop кандидаты подключались прямо в runtime. Таким образом, без перезапуска сервиса мы могли проверять предлагаемых кандидатов посредством создания агентов на базе заранее заданного абстрактного интерфейса. Затем для каждого кандидата мы прогоняли бенчмарк и записывали полный трейс с вердиктом оценщика.

Тут менялись не веса модели, а параметры среды вокруг неё: промпты, MCP tool descriptions, порядок и состав этапов. Такое пространство поиска уже способно покрывать множество оптимальных конфигураций для агента, но при этом не превращать оптимизатор в генератор произвольного кода, что потребовало бы больших вычислительных затрат.
В эксперименте было два replay-кейса:
Первый проверял, видит ли агент missing-index / query-plan recovery в OLTP/OLAP-сценарии;
Второй проверял, как агент реагирует на паразитную нагрузку в рамках TPC-C, отлеживал ли этапы pressure/recovery, какие waits/vacuum/xmin evidence способен предоставить.
В каждом кейсе производилось порядка 10 AI healthcheck.
Результат получился достаточно интересным для маленького бенчмарка:
Run | Judge | Candidates | Baseline | Best | Gain |
|---|---|---|---|---|---|
Первый | deterministic | 10 |
|
|
|
Второй | LLM judge | 10 |
|
|
|
Первый запуск был скорее smoke test: score вырос, но выигрыш почти целиком пришёл из одного кейса. Второй уже больше похож на рабочий optimization loop: лучший кандидат candidate_009 поднял score с 0.591 до 0.695, а почти такой же результат дал более простой candidate_005 — 0.694.
Самое полезное здесь не само число, а найденные оптимизации.
candidate_005 оставил всего два этапа: recent_context и query_regression. При этом recent_context получил набор тулов, ориентированный на анализ запросов, чтобы проверять recovery через query fingerprints и plan evidence. Это резко улучшило OLTP/OLAP-кейс: агент начал лучше видеть, какие изменения происходят на уровне запросов и что текущая картина связана с index/plan evidence, а не с абстрактным shared_buffers.
candidate_009 добавил к этой схеме лёгкий waits_replication stage. Он стал лучшим по overall score: 0.695, без failed runs, с лучшим успешным OLTP/OLAP: score 0.633. Но преимущество над candidate_005 составило всего 0.001, поэтому с production-точки зрения candidate_005 может быть более консервативным выбором: чуть проще pipeline, почти тот же score, меньше surface для latency/context regressions.
Был и показательный negative result. candidate_007 дал recent_context сразу инструментарий в доменах query + vacuum + waits. TPC sleep стал лучшим в запуске: 0.897, но OLTP/OLAP упал до 0.300. То есть широкий выбор тулов помог одному типу задач и сломал другой. Это хороший аргумент за Pareto-анализ вместо выбора по одному среднему score: такой кандидат плох как новый baseline, но может быть полезен как специалист для wait/vacuum-heavy-сценариев.
В итоге Pareto-картина выглядела примерно так:
candidate_009: лучший raw score, лучший OLTP/OLAP, чуть сложнее. candidate_005: почти тот же score, проще, хороший кандидат на baseline. candidate_007: лучший TPC sleep, плохой OLTP/OLAP, полезный кандидат для будущего conditional routing.
Из эксперимента получились довольно практичные выводы.
Во-первых, full prompt replacement оказался разрушительным. Он терял базовый контракт: политики для использования тулов, следование формату вывода и правила для конечного синтеза. Промпт лучше менять как patch поверх устойчивого шаблона, а не заменять целиком.
Во-вторых, stage composition влияла сильнее, чем очередная формулировка в промпте. Перестановка stages и изменение MCP profiles меняли пространство поиска у агента, а вместе с ним и путь поиска причины, по которому он шёл к вердикту.
В-третьих, «больше тулов» не всегда хорошо. Широкий выбор тулов увеличивает шанс найти нужный evidence, но также расширяет пространство ложных причин. Для OLTP/OLAP recovery выбор специфичных query tools оказался лучше, чем смесь query/vacuum/waits в одном этапе.
В-четвёртых, метрика должна быть multi-objective. Нужны не только mean_score, но и failed_run_count, per-case floor, timeout penalty, complexity penalty, tool-call count, latency и process/evidence score.
Если переносить это на более общий язык, meta-harness-оптимизация выглядит как маленький генетический поиск по конфигурациям agent runtime. Кандидат — это «геном» из prompt patches, stages, MCP profiles, tool descriptions и разных policy. Mutation меняет один из этих элементов. Judge возвращает score и качественный фидбек. Selection должен смотреть не только на среднее качество, но и на Pareto frontier: качество, устойчивость, стоимость, сложность, per-case деградации.
Даже на двух replay-кейсах видно, что такой outer loop способен находить неочевидные улучшения harness. Пока бенчмарк небольшой, остаётся риск overfitting, нужны ablations и больше сценариев. Но как демонстрация направления результат показательный. Мы оптимизировали не модель и не один промпт, а форму среды, в которой агент собирает доказательства, выбирает тулы и строит диагностический вывод.
