Оптымізацыя агента Terraform за дапамою DSPy: створыце верыфікатора раней, чым оптымізатора
Агенты Terraform ствараюць схемы. Збудуйце дэтэрміністычны верыфікатор, задаце базовы стан агента, а пасля выкарыстаце DSPy і GEPA, каб оптымізаваць яго файл навыкаў, атрымліваючы адрадныя рэзультаты.
Крыніцы: Your Terraform Agent Is Probably Wrong Half the Time
Агент, які пішчотнаўае код інфраструктуры, верагатыча ў які момент стварыў такі модуль: ён выглядае абсалютна разумна, чытаецца як кожны модуль, які колі-небудзь затвердзіў рэвізор, але потым не праходзіць тэст terraform validate з прычыны, якую ніхто не зможа з’явіць вочыма.
Эта проблема таксама вімерваецца. IaC-Eval — гэта бенчмарк з 458 ручна падобраных сцэнарыяў Terraform, і, як паведамляецца, GPT-4 успевае праходзіць яго тэсты толькі на 19,36% pass@1, тады як для таго ж модэлю на строгіх бенчмарках Python показнік становіць 86,6%. Новэйша даследжэння генеравання IaC агентамі паказвае 14,0% для Qwen2.5-Coder 7B, 41,4% для GPT-4o і 62,4% для GPT-5.4.
Чаму код для інфраструктуры такі складны, у працоўнасці з якім стоіць больш трудоў, чым з Python? Правільная настройка павінна адпавядаць чатыром вакументам адразу: правільнай сынтаксі, супаданню з схемай версіі прадаўца, якая вікорыстоўваецца, ціслаея залежнасцях, якія залішаюцца стабільнымі, і правілам самай організацыі. Адсутнасць хоць аднаго з гэтых элементаў робіць усё некорэктным. Python, які ў 90% правільны, часта ўсё роўна працюе, тады як Terraform, які ў 90% правільны, не можа выконваць своі функцыі.
Раследаванне таксама паказала рэзультаты, якія павінны змусіць сумневацца ў стандартных способах рашэння проблем. Методы на кшталт кількісных падказак, аналізу логікі рашэння і багатоэтапнай взаімадзеі дапамаглі толькі часткова. Але прыблізна 79% працоўных недаспэчаў было вызвана адсутнасцю інформацыі ў падказках, пры чым спроможнасць мадэлі не была прычыной. Модэль была дастаткова спроможная. Яе проста не паведамілі про неабходную інформацыю.
Гэта проблема оптымізацыі. Таму план заключаецца у тым, ўзяць рабочага агента Terraform, створыць неабходную оцэнку, каб з’ясавіць, чы робіцца прыгода, а пасля запустыць цэлы працэс DSPy на адной з гэтых оцэнак: запуск програмы, ўзнікненне рэзультата, оптымізацыя. Усё гэта можна запусціць. Супутній праект складаецца з трохоў папак, по адной на кожны этап:
agent/ the deepagents Terraform agent, its tools, prompts, and skills
eval/ the verifier, 38 tasks with Rego policies, and the benchmark
optimizer/ the DSPy program, metric, and GEPA compile
Пачатковы агент
Зробіце эту частку короткай, адказ яна не ўскладнена, і ў яе ролі можа выступіць будзь-яны агент. Штось такое вышуквае рэальную схему, запісвае файлы, якія пераканальваюцца, і пераканальвае іх перад тым, як адпавядаць.
У agent/tools.py знаходзяцца тры інструменты. Агент чытае рэальную схему прадаўцы перад тым, як запісваць. Файлы, якія ён запісвае, фарматуюцца і пераканальваюцца за сінтаксам пад час стварэння. Потым ён пераканальвае всю конфігурацыю і вылечвае усё, на шта скаржыцца компілятор, і толькі пасля чаго адпавядае.
Ён пабудаваны на deepagents — фрэймворку LangChain для агентаў з інструментамі і навыкамі, хоць сам фрэймворк практычна не мае значэння. Галоўнае — гэтая структура:
from deepagents import create_deep_agent
agent = create_deep_agent(
model="openrouter:openai/gpt-5.6-luna",
tools=[provider_schema, write_terraform, validate_config],
system_prompt=SYSTEM_PROMPT,
skills=["./agent/skills"], # SKILL.md — the thing we'll optimize
)
Каталог навыкаў заслуговае на аблікаваныя, таму што ўсе іншае тут спрыяе яго оптымізацыі. Навык — гэта файл у фармаце Markdown з інструкцыямі, які агент завантажае разам з сваім сістэмным запрошэнням у фармаце, які называецца Agent Skills і які швыдка стае популярным: Claude, GitHub Copilot і deepagents усе чытаюць той самы фармат SKILL.md. Ён чыста раздзеляе разныя аспекты. Сістэмнае запрошэння застаецца простым механізмам (як вызываць інструменты, як фарматаваць выходны матэрыял), а навык містыць спецыялізаваныя знанні, такія як стандарты Terraform, стандартныя настройкі безпекі і особлівасці прадаўцоў. Паколькі той самы файл працуе ў калькі агентоў, навыкі таксама ёсць перэноснымі.
Большасць аналізаў DSPy спрамоўваюць увагу оптымізатора на галоўны запіт программы. Задача, якая тут пераглядаецца, ўжо іншая: чы можна оптымізаваць саму спэцыялізацыю? Рэальныя команды прыдзеляюць большую частку часу ручнай налаштовкі саме спэцыялізацыі, адколькі ў ёй знаходзіцца экспертыза, і яе без канца правяць пад час перагляду. У этым праекце яна напісана вручную, знаходзіцца ў git і праходзіць перагляд коду, як і будзь-яі іншы файл. А яе ніколі не мерылі, і гэта ёсць справжняя проблема; DSPy створаны, каб яе рашыць, адколькі кожная зміна ў гэтым файле зміняе працэс вырабоцтва без жадных тэстаў.
Як на самай працэ выглядае абыякавасць
Пачнім з рэальнага продукту, створанага даўна, перш чым былі зроблены якія-небудзь парадкі. Ён быў створаны пад час запісанага базовага тэста, калі агент не меў жадных інструментоў і пісаў HCL безпосерэдна у свой адпаведзень. У запытцы была просьба стварыць кантэйнер стану з вікорыстаннем шыфравання, кераванага кліентам:
encryption {
kms_key_name = var.encryption_key_name
}
lifecycle_rules {
...
Здаецца, ўсе гаразд, але запуск валідатора па ям паказвае іншую картыну:
$ tofu validate
Error: Missing required argument
on main.tf line 18, in resource "google_storage_bucket" "terraform_state":
The argument "default_kms_key_name" is required, but no definition was found.
Error: Unsupported argument
on main.tf line 19, in resource "google_storage_bucket" "terraform_state":
An argument named "kms_key_name" is not expected here.
Error: Unsupported block type
on main.tf line 22, in resource "google_storage_bucket" "terraform_state":
Blocks of type "lifecycle_rules" are not expected here. Did you mean
"lifecycle_rule"?
З аднаго рычага выйшла трохі канцэлактавання. Неабяжлівы аргумент — default_kms_key_name, тады калі kms_key_name належыць да іншых рычагоў Google, і самэ гэта стварае вражанне, што ён правільны. Назва блока таксама была пераканцэлавана ў ўтварэнне, якога не існуе.
Калі базовыя канцэлактавання былі класіфікаваны па тыпах, 80% канцэлактавання пад час праверкі было вызвана вымышленым схемам: аргументамі, блакамі чыста атрыбутамі, якія выглядаюць як справжнія HCL, але не існуюць у даным рычагу. Прадаўцу google прыналежыць або тысяча тыпаў рычагоў, і яны зменяюцца з кожным выласкам, таму модэль запамятавае версію API і з памяці стварае правдападобныя вымыслы.
Тры ўстрымкі назначаны безпасова на гэты дыягназ. Адзін за адзінныя яны дадалі 27,7 балоў (ранг прыйнятных рэзультатаў вырос з 41,7% да 69,4% для комплекта, опісанага ніжэй). Калекты падчас фарматавання зменіліся да нуля, таму што write_terraform запускае fmt, а не чакае, што модэль сама кераваць прыватным прасторам. Калекты схемы таксама зменіліся да нуля, таму што агент нарэшце мог убачыць тое, што бачыць кампайляр. Усё, што засталося, складалася практычна выключна з намеру — чы рэальна агент збудуе тое, што было запрасана, і самэ гэта і ёсць месца прызначэння DSPy.
Ёсць тая ж задача, выконаная агентам, які выкарыстоўвае устрымкі, які ён выкарыстоўвае сёння:
$ uv run python -m agent.run --task eval/tasks/backend-var-interpolation.json
task backend-var-interpolation (opentofu)
model openai/gpt-5.6-luna engine=deepagents
files main.tf, variables.tf
tools {'write_terraform': 2, 'validate_config': 1, 'validate_pass': 1}
elapsed 56602ms
Было два запісу і адзін пераканалення, і агент адказвае толькі тады, калі компілятор задоволены. Файл задання і мета opentofu практычна з набора для ацэнкі, куды і пераходзяць далей.
Ацэнкаер прыямо перад фрэймворкам
Што стосуецца ацэнкі, чы рэшты чаго-небудзь дапамагае, то трэба адказаць на пытанне „Чы ўсё правільна?“ з больш адказным падтверджэнням, чым проста вражэнне. Большасць сфер вікорыстоўвае суддзю на адной чыстай мове, якая прырównвае думкі аднаго модэлю з думкамі іншаго без жадных абсалютных стандартоў, а пасля самога суддзю таксама трэба пераканаць.
У Terraform няма такой проблемы, таму што самы інструментарый, які вядома працюе ў CI, являе сабою ацэнкаера:
tofu fmt -checkвыяўляе праблемы з фарматаваннем.tofu validateзнаходзіць сінтаксічныя, схематычныя і адносныя бяглы.tflintпазначае застарэлыя аргументы і некарэктныя тыпы машын.
tofu test з mock_provider пераканальвае сэмантыку без якіх-лібо крантэджаў воблака.conftest з OPA Rego пераканальвае намер, спытваючы, чы робат створыў тое, што было запрасана.Этыя пяць дэтэрміністычных фартакоў рэалізаваныя ў eval/verifier/run.py. Не ўчаствуе жадны суддзя, ацэнка не каштуе нічога па чынам API-званакоў, і жадны крок не можа стварыць рэсурс, за які можна было бы вырачыць плату, таму што ў праекце няма apply нідзе, спецыяльна.
Большасць людзей прыхіляецца да апошнага фартака, хоць самэ ўжо ён мае найбольшы важынек. validate паведамляе, што канфігурацыя скомпілявалася. Rego паведамляе, што бакс сапраўды мае актываўанае шыфраванне, што ўзгодна з запрасам і чыяму з’яўляецца правільнаю, а не проста валідной:
package main
import rego.v1
deny contains "bucket must use a customer-managed KMS key" if {
some name
bucket := input.resource.google_storage_bucket[name][_]
not bucket.encryption
}
Команда, якая ужо застосоўвае правілы праз OPA, верагатычна мае багато такіх правіл у Terraform Cloud або ў сваёй CI-паіплайн, і іх можна перадаць знову без змян. Гэта падход «shift-left», які застосоўваецца да агентаў. Terraform Cloud дзеясно адхіляе пакіянсты план пад час застосоўвання, але да тэх пэўнага часу код вялікі, яго вучыў рэвізор, і запуск ужо у чэрзі. Заўважэнне тых сабе правіла Rego, калі генеруецца код, дазволяе выявіць наявнасць правалянняя раней, чым будзе вітрачаны весь тыя зусіллі.
Тэстуйце таго, хто тэстуе. Гэты ўздзей карысны як ніколі. Правіло, якое схваляе все, ўжо гіршае за жаданне, таму што яно дае кожнай версіі агента той самы бал і тыха адзвяртаеся, што нічога не змянілася. Кожна правіла ў eval/tasks/policy/ таму прыносіцца з аднім прыемным фікстчэром і аднім не прыемным фікстчэром, а скрыпт пераглядае оба варыянты:
$ cd eval/tasks && ./check_policies.sh
...
✓ vpc-subnet-firewall good=0 bad=2
✅ 38/38 policies verified in both directions
Трыццаць восемь заданняў было напісана такім чынам. Яны абходзяць стандартныя настройкі безпекі, підключэнне рэсурсаў, шыроту схемаў прадаўцоў, нацэліванне на адпаведны дыялект, обмежэнні (чы не дадае агент таго, чаго ніхто не хацеў) і структуру файлаў. Кожна з заданняў супакоюе запіт з правіламі Rego, якія відбіваюць ўсі ўмесці запіту. Дыялекты атрымліваюць спецыяльныя правілы: кожна задання вказвае, чы нацэлівае яна на terraform чы на opentofu, адколькі гэтыя два інструменты развіліся (OpenTofu адказвае за шыфраванне стану і вікорыстоўвае функцію for_each прадаўцоў, а Terraform — функцыю Stacks); таму агент, які таямна нацэліваецца на неправільны інструмент, не праходзіць пераканання.
Схема пяці брам таксама паказвае межу зьвярэння, якую агент не можа перасягнуць, і лёгкая пераценіць значэння гэтага. У рэальных умовах агент можа, і, верагаць, трэба, апынуцца ў распаўе з інструментам OPA, які будзе перакантрацьяваць правілы прытымку да адпаведзення. Гэта той самы аргумент пра перашчыпанне процэсу на ранейшы этап, і ён дапамагае ствараць лепшы код.
Граніцы маюць іншую мэту. Ёсць тут тыя ж правілы, якія служаюць крэткам для параболення навыкаў, модэляў і рэзультатаў працы оптымізатора. Якбы агент могаў ітэратываўа працаваць з тымі самымі правіламі, якія оцэнююць эксперымент, усе варыянты бы прагрэсавалі ў напрамку успеху, і параболенне нічога бы не паказала. Гэта прынцып выдзельванага тэстовага набора, прыменены да інструментаў. Правіло: агент можа бачыць любыя перакананні, за выключэннем таго, якое ў даны момент оцэнюе эксперымент. Калі агент для практычнага выкарыстоўвання будзе працаваць з інструментам правіла, трэба таксама ацэніць гэту конфігурацыю, выкарыстоўваючы правілы, якія застаюцься без выкарыстоўвання, або пісаць колькасць неабходных ітэрацый для парадкі, а не чы гэта працуе.
Адпрыем DSPy
DSPy апісваецца як фрэймворк для працы з мадэлямі мов програмавання, а не для стварэння запытанняў да ўсіх іх. Яго дасягледба раздзеляе працэс працы на тры фазы: праця з кодам, ацэнка і оптымізацыя, і сама гэтая парада ёсць урокам: оптымізатора зміняюць апошнім, а усё, што ўсё раней, якраз і робіць оптымізатора надзеяным. Оптымізатор выкарыстоўвае роўна тры элементы: програму, метрыку і даны.
Усе функцыі DSPy знаходзяцца пад дырэктарыем optimizer/, а наладка залежыць ад uv:
uv add dspy
uv sync
Фаза 1: програмаванне
Падпіс — гэта тыпаваны контракт для адзінаго вызову LM, які складаецца з вхідных дадзеных, выхідных дадзеных і докстрынгу, які опісвае заведамо. Ён знаходзіцца ў optimizer/harness.py:
import dspy
class AuthorConfiguration(dspy.Signature):
"""Write a complete, valid Infrastructure-as-Code configuration."""
request: str = dspy.InputField(
desc="What the user wants built, in natural language."
)
target: str = dspy.InputField(
desc="Which dialect to target: 'terraform' or 'opentofu'. These have "
"diverged — code targeting the wrong one will fail validation."
)
config: str = dspy.OutputField(
desc="The complete configuration as fenced HCL blocks. Start each block "
"with a comment naming its file, e.g. '# main.tf'."
)
Самае гэтае і ёсць сэрца проблемы. Не існуе фразы „Вы — эксперт у Terraform“, няма прыкладаў рэшэнняў, а таксама няма просьбаў па правільнаму фармату. Назвы і апісанні поль несу значэнне, а аптымізатор пакірае формулюванні.
Кожны, хто сцярога глядзіць на гэта, можа знішчыць гэтае „чары“ адной лініяй:
dspy.inspect_history(n=1)
Ён выводзіць рэальны запрос, які адаптар стварыў на адвярненні. Рэальны выхід ад гэтага выглядае так:
System message:
Your input fields are:
1. `request` (str): What the user wants built, in natural language.
2. `target` (str): Which dialect to target: 'terraform' or 'opentofu'. ...
Your output fields are:
1. `config` (str): The complete configuration as fenced HCL blocks. ...
[[ ## request ## ]]
{request}
[[ ## target ## ]]
{target}
[[ ## config ## ]]
{config}
In adhering to this structure, your objective is:
Write a complete, valid Infrastructure-as-Code configuration ...
Нічаго не ўтаімае. Гэта структураваны шаблон, створаны на аднойчынку падпісу, і самэ гэта ў тым, што оптымізатор можа без апасцярожнасці яго перапісаць, а таксама чаму трэба спачатку паглядзець самэ тут, калі поведэнне ўскладненая.
Модуль прыменяе певную тэхніку да падпісу, і гэтыя два элементы зміняюцца незалежна. Аднойчынка падпісу можа выконвацца як адзін крок, спачатку з аналізу, або як кантролер ціклу інструмента:
generate = dspy.Predict(AuthorConfiguration) # one shot
generate = dspy.ChainOfThought(AuthorConfiguration) # reason first
generate = dspy.ReAct(AuthorConfiguration, tools=[...]) # run a tool loop
Этот агент выкарыстоўвае інструменты, таму ён адбіваецца за дапамою dspy.ReAct і атрымлеўчы роўна тыя функцыі інструментаў, якія выкарыстоўвае розмішчаны агент, імпортаваныя безпосерэдна з пакета агента. Код знаходзіцца ў optimizer/harness_deepagents.py:
from agent.tools import make_tools # the deployed agent's tools
class TerraformAuthoringAgent(dspy.Module):
def __init__(self, seed_instruction: str):
super().__init__()
# Seed the SIGNATURE before constructing ReAct, so DSPy appends its
# tool protocol to your instruction instead of replacing it.
seeded = AuthorConfiguration.with_instructions(seed_instruction)
lc_tools = make_tools(work_dir, binary="terraform", stats={})
self.react = dspy.ReAct(
seeded,
tools=[dspy.Tool(t.func, name=t.name, desc=t.description)
for t in lc_tools.values()],
max_iters=10,
)
def forward(self, request: str, target: str = "terraform"):
return self.react(request=request, target=target)
Найважнейшая рашынка па дызайне ў гэтым падходзе — оптымізаваць той самы прыграма, які выкананы. У аднам з першых эксперыментаў быў оптымізаваны генератор, які працаваў толькі аднакратна, а пасля рэзультат быў пераданы агенту, які выкарыстоўваў інструменты; гэта зробіла кожны чысловы рэзультат бессэнсавым, адколі інструкцыі былі налаштаваны на сітуацыі збою, якія рэальны агент больш не ствараў.
Інструкцыя-заснова — гэта існуючы файл SKILL.md, таму не існуе можлівасці новага пачатку, і справжнія пытанне — чы можна паспрабаваць падбаці рукапісны тэкст. DSPy не ведае, што гэты стрынг ёсць навыком, а не галоўным запітам, таму што бачыць толькі тэкст інструкцыі. Самэ гэта робіць эксперыменты з навыкамі працэспрэтнымі: рэзультаты зберагаюцца працоўна ў фармаце SKILL.md і працуюць для будзь-каго агента, які чытае навыкі.
Фаза 2: ацэнка
Тып дадзэнай у DSPy — dspy.Example, які ўсвядомляе, якія полья ўважаюцца вхіднымі. Завантажвач у optimizer/dataset.py безпасова ператварае гэты набор:
dataset = [
dspy.Example(
task_id=t["id"],
request=t["prompt"],
target=t["target"],
rego=t["rego"], # path to this task's policy
).with_inputs("request", "target")
for t in tasks
]
Поле, якія не пазначаны як вхідныя даны, перавозяцца як метаданы. Для пошуку адпаведнай політыкі выкарыстоўваецца rego, і модэль ніколі яго не бачыць.
Варта адзначыць, як выбіраюцься прыклады, калі ўжо говорыцься пра тое, што набор дадзеных вплывае на все, што можа выучыць оптымізатор. Яны не былі запрашаны з якога-небудзь стандарта. Яны імітуюць фармат рэальных запітоў, напрыклад, запіт на бакет у GCS, зашыфраваны ключам, кім керуе кліент, або запіт на тое, каб сервісны акаунт меў права толькі на доступ да аднаго бакета. Такі падход охопляе основныя рэсурсы, з якімі працюе команда платформы: зберохват, сеткавыя тычынкі, IAM, SQL і Pub/Sub, а таксама задачы, спрамованыя на распакаванне вялікіх схем, задачы контролю, каб агент не дадаў рэсурсаў, якіх ніхто не прасіў, і вышэўказаныя задачы на розныя дыялекты. Калькі з іх ўмышлена складныя, адтуды што набор, які праходзіць усе з першага дня, не можа паказаць прыбыль у своіх рэзультатах.
Трыяццац восемь прыкладоў здаецца малым, і так і ёсць. Мета — абслюцэннае расследаванне кожнага прыкладу, адколі кожны з іх мае сваю сябеэксклюзыўную палітыку, а таксама чыстыя падзелы. Здзейсніце падзелы перад оптымізацыяй і захавайце тэстовы набор каля святыні: набор для навчання — гэта тое, чым оптымізатор выучаецца, набор для параболізаціі — гэта спосаб, якім ён выбірае кандыдатаў, а тэст ацэнюецца толькі адной раз, у канцы. Большасць методаў праектавання запрошэнняў ігнаруе гэтую дысцыпліну, і самэ гэта ўскладнюе людзям зрозумець, чыі змяны дапамаглі, калі на самай працы гэта не было так. Гэта трапілася два разы ў гэтым праекце, і ў обох разах паводзкі на маленькай вялічыне вярыгацыйнага набора змініліся, калі набор расліўся.
Метрыка — гэта тое месца, дзе вярыгатор вводзіць даны, і гэта адна функцыя, якая выканае тры задачы (optimizer/metric.py):
def verify_metric(example, prediction, trace=None, pred_name=None, pred_trace=None):
"""Score with the SAME verifier that produces our benchmark numbers."""
result = verify(
config_dir=materialise(prediction.config),
policy_dir=example.rego,
target=example.target,
)
# Job 1 — bootstrapping (trace is set): a strict bool. Only outputs that
# FULLY pass may become worked examples.
if trace is not None:
return result.passed
# Job 2 — reflective optimization (pred_name is set): score AND feedback.
# GEPA reads the text to understand *why* a candidate failed.
if pred_name is not None:
return dspy.Prediction(score=result.score, feedback=format_failures(result))
# Job 3 — plain evaluation: a float.
return result.score
Зберагаеце яго лёгкім. Калі метрыка знова впрыскае якую-небудзь логіку ацэнкі, значэнне, якое оптымізуецца, і значэнне, якое публікуецца, адхіляюцца адзін ад другога, і тады нічога не вімерваецца.
Самэй тут выдзеляецца галузь зворотнага звязку. Оптымізаторы, якія аналізуюць, беруць тэкст, які пояснюе неудачу, і тут гэты тэкст ўсвободжаны і точны: справжніія памылкі кампайляра і справжніія адмовы па правілах, якія паказаны раней. Поле, якіе залежыць ад ацэнкі LLM, плаціць за кожную ацэнку і атрымле набліжна гorsыя падсказкі, а пасля таксама должна параболіць ацэнкуцеля.
Перш чым рабіць што-небудзь іншае, вімерце базовыя значэння, выкарыстоўваючы dspy.Evaluate:
evaluate = dspy.Evaluate(devset=valset, metric=verify_metric,
num_threads=8, display_table=True)
evaluate(program)
Рэзультаты тэста з 3 прыкладамі выглядаюць так, а ўсё прабаванне займае больш часу:
Average Metric: 0.59 / 3 (19.6%): 100%|██████████| 3/3 [01:16<00:00, 25.60s/it]
INFO dspy.evaluate.evaluate: Average Metric: 0.5882 / 3 (19.6%)
WARNING dspy.evaluate.evaluate: Skipping table display since `pandas` is not installed.
З тага фрагмента можна вывучыць два урокі. Кожны „прыклад“ — гэта цэлы эпізод агента, а не адзін вызов API, таму неабяжна правільна калькуляцыя бюджэту. Кроме тага, для функцыі display_table неабяжна бібліятэка pandas, таму яе трэба інсталаваць для стварэння табелей па кожны прыклад.
Якш чы гэты матэрыял даў хоць адну прычыну для практыкавання, то яе трэба зробіць такой: нічога не трэба оптымізаваць без базовага показніка. Лямоўка гэтага показніка ёдыная, што робіць рэзультаты оптымізатора значымымі.
Фаза 3: оптымізацыя
Разныя оптымізаторы налаштовуюць разныя аспекты:
BootstrapFewShotсінтэзуе працюючыя прыклады (дэманстрацыі).MIPROv2налаштовуе інструкцыі і дэманстрацыі за дапамою методу Байесаўскага пошуку.GEPAналаштовуе інструкцыі, аналізуючы следы неудач.
Настройка інструкцыяў ёсць правы спосаб для коду, які падлягае строгім правілам схемы, адколькі доследжэння агентных IaC паказала, што гэты падход працюе лепшая, чым вікорыстанне дэманстрацый, адносна таго, што дэмы не можаюць усунуць галюцынаціі схемы. Таму GEPA ёсць выборам, які візначаецца на основе файла optimizer/compile.py:
gepa = dspy.GEPA(
metric=verify_metric,
max_metric_calls=150,
reflection_lm=dspy.LM("openrouter/openai/gpt-5.6-luna", max_tokens=16000),
num_threads=8,
track_stats=True,
)
compiled = gepa.compile(student=program, trainset=trainset, valset=valset)
compiled.save("compiled_state.json", save_program=False)
Два модэлі выканаюць разныя задачы. Модэль для выканання задач пішае код на Terraform і ён той, які фактычна выкорыстоўваецца; модэль для аналізу выканае доследжэння неудач і стварае праекты інструкцый. Яна запускаецца прыблізна 40 разоў, тым час як модэль для выканання задач — адо 900 разоў, таму інвестыцыі ў сільнейшы модэль тут ёсць цяперашняя.
Калісці практычныя прыметкі:
- Установіце значэнне
num_threads. У аднам запуску яго забылі, і компіляцыя зайняла 69 хвілін; пазнейшыя замеры паказалі, што апарат быў неактыўны 94% часу, чакаючы на адна за іншайю запускаемыя API-запыты. - „150 метрычных запытоў“ означае 150 эпізодаў дзейнасці агента, што за дапамогою ціклу з інструментам даходзіць прыблізна да 900 запытоў LM.
save() пасля компіляцыі. Калі задаць save_program=False, рэзультат будзе звычным JSON: оптымізаваны тэкст інструкцый плюс будзь-якія дэманстрацыі. Той жа JSON ёсць спосабам выйсці з DSPy цэлкам, адколькі прыблізна дваццаць ліній (optimizer/to_skill_md.py) зноў ператварае яго ў файл SKILL.md, які вікорыстоўвае агент. У час выканання DSPy ніколі не імпортуецца. Оптымізатор — гэта проста крок падчас будовы, а тое, што ён стварае, падае ў pull request у вазе diff-а, які чалавек можа прачытаць і адхіліць.GEPA таксама знаходзіць рэальныя даны. У слабейшай модэлі агент продовжваў пакідаць свой цыкл з інструментамі: адна вызовка provider_schema, а потым ён зупініўся, не запісавшы нічога. GEPA ніколі не была прыглашана пра гэта. Яна прачытала следы абыяковасці і запісала гэта ў оптымізаваную інструкцыю: не завершаць работу толькі таму, што файл быў запісаны, адтуды што успешны запіс паўтарае толькі тое, што HCL можа быць распакаваны, а для выяўлення бяглых памылак у схеме падаўчыка і некорэктных апыланняў неабходна ўжыць validate_config. Гэта ўзважэнне на рэальныя следы выканання, якое дае кераванне, якое для сформулювання патрабавала бы гады на дэбагаванне.
Рабочы працэс продовжвае даваць рэзультаты пасля оптымізацыі
Осё ў той жа механізм ўключае ў сябе ўсё больш элементаў, якія перадаюць вялічыны, якія ужо існуюць.
Метрыка таксама можа выконвацца пад час аналізу. Функцыі BestOfN і Refine з бібліятэкі DSPy абгружаюць будзь-які модуль, выконваюць яго столькі разоў, сколькі задана цячка N, і вяртаюць першы рэзультат, якога оцэнка досягае заданага порога:
robust = dspy.Refine(module=program, N=3, reward_fn=verify_metric, threshold=1.0)
Шаблон павінен здавацца знайомым, адколькі цыкл «параболічна перапрацоўка» агента застаўляе тую ж ідэю з кампайлярам як функцыяю награды. Даныя атрыбутуюць яго як найэфектывейшы спосаб: у даследжэнні IaC ітэратыўная перапрацоўка з падтрымкай верыфікатора падняла рэзультаты Qwen 7B з 14,0% да 62,9%, што лепша за будзь-якія іншыя тэхнікі стварэння запытанняў. У будзь-якай форме неабходна запастыць модэль яе верыфікаторам пад час выканання.
Змены модэляў стаюць змянамі, якія можна выконаць за адну лінію коду, і цяпер іх можна вымерыць:
program.set_lm(dspy.LM("openrouter/qwen/qwen3-coder-30b-a3b-instruct"))
evaluate(program)
Гэта мела большое значэнне, чым чакалі. Змена на дырэжэнейшую версію ў серэдзіне праекта прывела да падышчы якосці з 83,3% да 37,5%, пры чым паўсотка запускаў прыпынілася на пачатку і не стварыла жадных файлаў. У ручным працэсе работы гэта не выяўляецца, а першыя сігналы прыходзяць ад кліента. Дванаццаць хвілін працы тэстовага набора выявілі гэта.
Чысты рэзультат
Для гэтага агента скомпільаваныя інструкцыі не паспелі перавыпэліць рукапісныя.
Порэванаўанне было адбытае дзевяць разоў для двух рэалізацый агента і двух модэляў, уключаючы тэстовы набор, дзе оба паспелі паставіць той самы рэзультат — 92,9%. Гэя цыфра належыць сільнейшаму модэлю, тады як ранейшыя цыфры 41,7%–69,4% стосуюцца дырэжэнейшага модэля ў тым жа наборэ. Два разы рэзультат здаваўся явным перамогам, яго фіксавалі, але пасля перагляду большага калекцыі рэшынкі змініліся.
Гэта самая ценная наўкія, якую можна атрымаць, і гэта не адначасова значыць, што DSPy не выканаў свою ролю. Адпаведны і правдападобны разлог можа поясніць, чаму гэта не прыменілася. Рэзультаты, публікуемыя GEPA, атрымліваюцца за дапамою модульных рабочых практык, трубопрацэсаў, у якіх кожны крок выкарыстоўвае іншы запіт, таму оптымізатор можа зваліць віну за неудачу на адзін з модуляў і яго парадкаваць. Агент жа выкарыстоўвае адзін і той самы запіт у кожны крок. Крытыка Бенджаміна Андэрсана стаўіць тэзу, што гэта пазбавляе сістэму той самой локальнасі, на якой базуецца GEPA.
Практычныя рэкамендацыі выглядаюць так:
- Для простага трубопрацэса публікуемыя рэзультаты, верагатна, будуць прыменямы.
- Для ціклу агента, спачатку запрацаваць свой сэт тэстаў, перш чым прыпускаць, што гэтыя рэзультаты будуць прыменямы.
- У обох случаях неабходна перадаць адзывы з верыфікатора безпосерэдна агенту; у гэтым случае ёсць найлепшыя доказы, якія прадстаўлены ў публікуемых робах і ў этых цыфрах.
І гэты тэст адбыцца, таму што сэт тэстаў існуе.
Які стан справ пасля гэтага
Мадэлі мовы адаптуюцься з трудам да інфраструктуры як коду. Рэзультат GPT-4 у розмерэ 19,36% у тэстах IaC-Eval — гэта не проблема, якая знікне за дапамою пасіянтных падзяроўкаў, а звычныя трыкі ледве дапамагаюць. Тым не менш, большасць проблем у практыцы вынікае з нехваткі інфармацыі, а не з нехваткі спроможнасцяў, таму існуе рэальны прырост, і ён крыўдзіцца ў інструкцыях і інструментах, а не ў вагах мадэлі.
Рабочы працэс, які падтрымваецца, — гэты, які выкладваецца ў сабеўласнай дакументацыі DSPy, у тым парадку, які там паказаны. Для программавання: падпісы для кантракта, модулі, якія відпавядаюць таму, што запускаецца, і inspect_history калі ёсць сумневы ў тым, што адправляецца. Для ацэнкі: прыклады з чысткімі вхіднымі данымі, простая метрыка, якая таксама служыць падказкай для оптымізатора, базовы рэзультат да першых змян і набор дадзеных, які пераглядаецца толькі раз. Для оптымізацыі — толькі тады, за дапамою той самой метрыкі, зберагаецца тэкст, які можна пераглянуць у запросе прыўязкі коду.
Хоча аптымайзер і не перамогаў ручна напісаную мэтодыку, вжыцце DSPy вымусіла задаць вялічыню „правільна“ у віглядзе коду, які можна запрацаваць. Гэты элемент продаваліся і даўжыну застаўся у вжыцці. Ён паказаў спад на 46 пунктав пасля замены модэлю, выявіў, што мэтадыка запускалася толькі 25% часоў (гэта было выправлена за дапамогою перапісанага рэчэння, пасля чаго ступень запуску вырасла да 100%), і два разы не дазволіў публікацыі рэзультата, вызванага шумам, як перамогі.
Нічога з гэтага строго не трэба DSPy, і верыфікатор тут не імпортуе яго. DSPy дапамагае там, дзе вымусвае наявнасць і павторны вжытак метрыкі, такім чынам адна вялічына керуе наборам тэстаў на спад, порэвананнямі модэляў, практыкамі пад час адгукнення і аптымайзерам. Команды, якія ужо маюць такую дысцыпліну, можаць не патрабаваць гэтага фрэймворку, а тыя, якія яе не маюць, могуць яго атрымаць. У будзь-якім случае, паставіце канец выдаванню змян у запрошэннях, якія нельгае змерыць.
Супаўзвязаныя матэрыялы
- Контроль паведання агента Bedrock: этапы запітаў, парсеры і працэсы інферэнцыі — Як пераканаць Bedrock на выкананне сваіх настройкаў прадзеяктування, оркестрацыі і паслядзеяктування за дапамогою Terraform, з вікорыстаннем парсера Lambda, контракта JSON і настройкаў інферэнцыі для кожнай этапы.
- Форма онтологіі протыяўлежна розмеру модэлю: што каштуе дызайн графа для AI-агента — Як тры разныя формы однага графа з інформацыяй пра страхаванне зменілі точнасць, колькасць вызоў інструментаў, колькасць токенаў і вартасць для малых і вялікіх модэляў у Azure Cosmos DB.