Spis treści:
-
-
- Trudne środowisko dla modeli
- Vibe-engineer test
- Jak używać LLM-ów z głową?
- Code review
- Praca z dokumentacją
- Analiza logów
- Bezpieczeństwo danych i dostępów
- Od copilota do agentów
Model językowy napisze playbook Ansible’a w kilka sekund. Nie potwierdzi jednak, że ta zmiana nie odetnie oddziału od SD-WAN i że w ogóle ma sens w tej topologii. Różnica między tymi dwoma zdaniami to cała inżynieria sieciowa.
Automatyzacja sieci od dawna nie jest już skryptem, który wysyła konfigurację na urządzenia. To proces. Zaczyna się od wymagań, przechodzi przez model danych, kod, testy i wdrożenie, a kończy się na weryfikacji stanu po zmianie. Polecenie „napisz skrypt, który doda VLAN na przełącznikach” obejmuje tylko jeden etap tego procesu. Podejście „zaprojektuj, zaimplementuj, zweryfikuj” obejmuje całość.
Różnicę widać już na poziomie samego zapytania. Porównajmy dwa prompty dotyczące tego samego zadania:
Prompt 1:
Napisz playbook Ansible dodający VLAN 250 na przełącznikach dostępowych.
Prompt 2:
Kontekst: Catalyst 9300, IOS-XE 17.9, kolekcja cisco.ios w wersji 8.x.
Inventory w YAML, grupa access_switches, 14 urządzeń.
Standard nazewnictwa VLAN: <LOKALIZACJA>_<FUNKCJA>, wielkie litery.
Zadanie: playbook dodający VLAN z pliku vars/vlans.
yml, tryb check domyślnie włączony, bez zapisu konfiguracji do startup-config.
Wypisz założenia, których nie da się potwierdzić na podstawie tego opisu.
Pierwszy prompt daje kod uśredniony z tysięcy przykładów z internetu, bo na nich uczone są popularne ogólnodostępne LLM-y. Drugi daje kod dopasowany do naszego środowiska i, co ważniejsze, listę rzeczy, których model nie wie. Ta lista jest zwykle cenniejsza niż sam playbook. LLM świetnie radzi sobie z generowaniem kodu, ale z oceną skutków bez kontekstu zupełnie nie, bo nie zna naszej sieci, polityk ani tego, co zmiana wywoła o trzeciej w nocy w oddziale w Katowicach. Stąd porównanie do pary pilotów samolotu. Drugi pilot czyta checklistę, liczy paliwo, podpowiada procedurę i wyłapuje błędy. Ale za maszynę odpowiada kapitan. W automatyzacji sieci kapitanem jest inżynier i nie ma od tego wyjątków.
Trudne środowisko dla modeli
W aplikacji webowej usterka zazwyczaj dotyczy jednej funkcji. W sieci błąd konfiguracji potrafi dotknąć całej infrastruktury naraz wraz z systemami, które od niej zależą. Zasięg pomyłki jest tu po prostu inny. Do tego dochodzi różnorodność – Cisco, Juniper, Arista, Palo Alto i Fortinet inaczej interpretują te same pojęcia i odmiennie reagują na te same polecenia. Nawet w portfolio jednego producenta zachowanie zależy od wersji systemu operacyjnego, modelu urządzenia i tego, czy pracujemy przez CLI, NETCONF, czy REST API. Model językowy uśrednia to, co widział w danych treningowych. A my potrzebujemy odpowiedzi dla konkretnej wersji IOS-XE na określonej platformie.
Dokumentacja do urządzeń sieciowych też bywa problemem. Nie zawsze jest kompletna, nie zawsze nadąża za oprogramowaniem. Zdarza się też, że opisuje zachowanie, które na naszym urządzeniu po prostu nie występuje. Jeżeli człowiek ma z tym kłopot, model tym bardziej. Najgroźniejsza jest jednak inna właściwość – kod wygenerowany przez LLM może być poprawny składniowo, a jednocześnie całkowicie błędny operacyjnie. Mimo że playbook zostanie wykonany, szablon wyrenderowany, a testy składni przejdą, konfiguracja i tak rozjedzie się z projektem sieci. Klasyczny przykład zna każdy, kto pracował z przełącznikami Cisco:
interface GigabitEthernet1/0/1
switchport trunk allowed vlan 250
Składnia tych poleceń jest poprawna. Każda z komend się wykona. Jednak w momencie uruchomienia drugiego polecenia na tym trunku zostaje wyłącznie VLAN 250, bo nie dodaje ono wpisu do listy, tylko ją nadpisuje. Wersja bezpieczna różni się jednym słowem: „add”:
interface GigabitEthernet1/0/1
switchport trunk allowed vlan add 250
Model językowy generuje obie formy, zależnie od tego, jak sformułujemy prośbę. Żaden linter tego błędu nie wyłapie, bo z punktu widzenia składni obie są prawidłowe. Zauważy to inżynier, który wie, jak ta komenda działa, albo test porównujący listę VLAN-ów na trunku przed zmianą i po niej. Model domyślnie nie ma też dostępu do naszego inventory, standardów nazewnictwa, planu adresacji, polityk bezpieczeństwa ani historii zmian. Pełnego kontekstu sieci nie da się zmieścić w jednym prompcie, nawet przy dużym oknie kontekstowym.
Wniosek jest prosty. Halucynacja modelu przy tworzeniu aplikacji to błąd, który wyłapią testy jednostkowe. Halucynacja w trakcie konfiguracji sieci to awaria o potencjalnie bardzo dużej skali.
Vibe-engineer test
Model językowy nie jest systemem zarządzania konfiguracją. Nie zna stanu urządzeń, nie utrzymuje desired state i nie wie, co zostało wdrożone wczoraj. Nie jest też źródłem dokumentacji producenta. Potrafi ją streścić, jeżeli mu ją damy, ale sam z siebie równie chętnie wymyśli nazwę parametru, która brzmi wiarygodnie, ale nie istnieje. Zapytany o moduł Ansible’a do konkretnej operacji na Nexusie potrafi zaproponować parametr, którego w danej wersji kolekcji nie ma, albo taki, który istniał w dwóch wcześniejszych release’ach i został usunięty. Bardzo łatwo możemy to sprawdzić, prosząc dowolny model o wygenerowanie nawet prostego playbooka dla tej platformy. Rzadko kiedy pierwsza wersja – choć została dla nas szybko wygenerowana – będzie poprawna nawet semantycznie. Co więcej, generowanie playbooków Ansible’a pokazuje jeszcze jedno ograniczenie – duża próbka danych treningowych w postaci prawidłowych playbooków dla starszych wersji Ansible’a sprawia, że modele nie radzą sobie z generowaniem poprawnych plików dla najnowszych wersji. Chyba że podamy od razu odnośniki do dokumentacji do przeanalizowania.
Jednym z moich osobistych testów oceny danego modelu językowego jest przeprowadzenie czegoś, co nazywam „dummy-engineer test” lub „vibe-engineer test”. Drugi termin odnosi się bezpośrednio do vibe codingu, którym zazwyczaj zajmują się osoby niemające pojęcia o programowaniu. Jest to proces tworzenia oprogramowania głównie poprzez opisywanie LLM-owi pożądanego efektu i iteracyjne poprawianie wyniku zamiast ręcznego pisania oraz kontrolowania każdej linii kodu.
Jest to test zbieżności połączony z testem halucynacji. Zadajemy modelowi zadanie napisania playbooka Ansible’a, podając mu podstawowe dane wejściowe opisujące to, co chcemy uzyskać. Następnie próbujemy wykonać playbook. Nie czytamy komunikatów błędów, natomiast za każdym razem wklejamy je do konsoli modelu, tak jak prawdopodobnie czyniłby to niedoświadczony inżynier. Czynność tę powtarzamy, aż nie uzyskamy poprawnie działającego playbooka, zakładając oczywiście, że model sobie z zadaniem poradzi. W tym procesie mierzy się m.in. liczbę iteracji potrzebnych do uzyskania pierwszego czystego wykonania. Ale sama ta liczba niewiele mówi – ciekawsze są inne rzeczy, przede wszystkim to, czy model zbiega, czy krąży. Zdrowy przebieg to malejąca liczba błędów. Patologia to cykl, w którym trzecia poprawka przywraca błąd z pierwszej iteracji. Wtedy widać, że LLM nie buduje modelu problemu, tylko reaguje na ostatni komunikat.
Kolejnym aspektem jest to, czy poprawka wynika z komunikatu o błędzie. Model, który nie potrafi naprawić modułu, chętnie przejdzie na ansible.builtin.raw albo ansible. builtin.shell z komendami CLI. Playbook zacznie działać, ale automatyzacja jako taka trochę przestanie istnieć. Kolejnym aspektem jest to, czy model degraduje warunki zadania. Jeżeli to robi, zacznie dorzucać takie elementy jak ignore_errors: true, failed_when: false, wyłączy walidację albo usunie tryb check. Błąd zniknie, ale problem zostaje. Kolejny aspekt to pytanie o dane, których nie ma. Po dwóch nieudanych próbach dobry LLM powinien poprosić o wersję kolekcji albo wynik komendy. Model podatny na halucynacje będzie zgadywał do samego końca. Na koniec pozostaje jeszcze kwestia logiki, a nie tylko semantyki. Przyjęte kryterium „aż zwróci poprawnie działający playbook” jest nieprecyzyjne. Playbook, który został wykonany bez błędów, nie musi być bowiem poprawnym playbookiem. Wróćmy do przykładu sprzed chwili, w którym switchport trunk allowed vlan 250 wykona się bezbłędnie, ale zdejmie trzy VLAN-y z trunka. Test zakończyłby się sukcesem, ale rzeczywiste wykorzystanie wywołałoby awarię. Dlatego subiektywna ocena musi należeć do inżyniera.
Czym zatem jest LLM? Partnerem do pracy z kodem, który jest bardzo szybki i pojętny, jeżeli chodzi o znajomość bibliotek i wzorców. Jest też dobrą pomocą przy interpretacji logów, pisaniu testów i analizie diffów konfiguracji, gdy trzeba wyłapać, co się zmieniło i dlaczego to może mieć znaczenie. AI proponuje rozwiązania, ale kod, testy i proces muszą być weryfikowane przez inżyniera.
Jak używać LLM-ów z głową?
Przyjrzyjmy się zatem kilku obszarom, w których LLM-y na pewno nam pomogą. Sfery te nie wymagają nakładów na trenowanie modeli i mogą być skutecznie wykorzystywane także przez mniej doświadczonych inżynierów prawie „out of the box”.
Najwięcej korzyści widać na samym początku projektu czy zadania – już na etapie projektowania, zanim powstanie pierwsza linijka kodu. LLM dobrze radzi sobie z rozbiciem wymagań na role i moduły. Podpowie strukturę repozytorium, zaproponuje model danych w YAML-u lub JSON-ie, pomoże zdefiniować kontrakt wejściowy funkcji. Szczególnie przydatne jest szukanie przypadków brzegowych. Zapytajmy, co się stanie, gdy urządzenie jest nieosiągalne, gdy w inventory brakuje pola, lub gdy VLAN już istnieje z inną nazwą. Dostaniemy listę scenariuszy, o części z nich pewnie sami byśmy nie pomyśleli. To oczywiście nie jest gotowy projekt nadający się od razu do realizacji. To materiał do dalszego przemyślenia już z udziałem człowieka, który wniesie swoją wiedzę i doświadczenie. Dobrze sprawdza się też porównanie kilku podejść implementacyjnych. Nornir czy Ansible, szablony Jinja2 czy budowanie konfiguracji w kodzie, konfiguracja przez CLI czy przez model YANG. LLM przedstawi argumenty za i przeciw, przyspieszając i ułatwiając nam podjęcie decyzji.
Wspomoże nas także w procesie planowania i szacowania czasochłonności projektów. Rozbicie całego projektu na poszczególne etapy pomoże nam wyliczyć konieczne zaangażowanie poszczególnych osób oraz czas niezbędny do wykonywania poszczególnych czynności. Może także opisać ryzyka, które nie przyszłyby nam do głowy, aby uwzględnić je w całości projektu.
Na etapie implementacji LLM-y pozwolą nam oszczędzić najwięcej czasu na powtarzalnych czynnościach. Szkielety kodu dla Nornira, Netmiko, NAPALM-a, pyATS-a czy Ansible’a powstają w kilka sekund. Podobnie funkcje walidujące dane wejściowe, parsery JSON, YAML i XML, szablony Jinja2 oraz obsługa REST API z paginacją i obsługą błędów. Dobrym przykładem jest walidacja danych wejściowych.
Poniżej prezentujemy efekt prośby – skierowanej do jednego z popularnych LLM-ów – o napisanie modelu Pydantic dla wpisu VLAN:
from pydantic import BaseModel, Field, field_validator
class Vlan(BaseModel):
vlan_id: int = Field(ge=2, le=4094)
name: str = Field(min_length=3, max_length=32)
svi: bool = False
@field_validator(„name”)
@classmethod
def check_naming(cls, value: str) -> str:
if value != value.upper():
raise ValueError(„nazwa VLAN musi być zapisana wielkimi literami”)
if „_” not in value:
raise ValueError(„nazwa VLAN musi mieć format LOKALIZACJA_FUNKCJA”)
return value
Trzydzieści sekund przetwarzania modelu zamiast 15 minut naszej pracy (o ile wiemy już, jak posługiwać się biblioteką Pydantic). Przyjęty zakres VLAN-ów od 2 do 4094 to decyzja inżynierska, nie generatywna, choć oczywiście LLM podpowie nam wartości. Jeżeli w naszej sieci VLAN-y od 3900 wzwyż są zarezerwowane dla infrastruktury, walidator musi to odzwierciedlać, a model o tym nie wie. Kod dostajemy niemalże od ręki, ale warunki biznesowe czy techniczne uzupełniamy już sami. Osobno warto wymienić refaktoryzację. Skrypt napisany trzy lata temu pod jedną platformę, który po drodze obrósł wyjątkami, to dobry materiał do pracy dla modelu. Poprosimy go o rozbicie na funkcje, wydzielenie konfiguracji, dodanie typowania. Efekt jego pracy trzeba przeczytać linijka po linijce i przetestować, ale punkt startowy do dalszej pracy dostaniemy od razu.
Skoro już jesteśmy przy programowaniu, kolejnym obszarem są testy, w których LLM daje bardzo dobry stosunek nakładu do efektu. Tego typu procedury pisze się niechętnie i zbyt rzadko. Model szybko wygeneruje dla nas testy jednostkowe i integracyjne, przypadki pozytywne i negatywne, testy idempotencji oraz schematów danych w Pydantic czy JSON Schema. Warto od razu prosić o podanie przykładów błędów jak choćby zły format adresu IP, VLAN ID poza zakresem, brak wymaganego pola, timeout połączenia. To właśnie te scenariusze ratują nas później na produkcji. Test dla pokazanego wcześniej modelu wygląda następująco:
import pytest
from pydantic import ValidationError
from vlans import Vlan
def test_poprawny_vlan():
vlan = Vlan(vlan_id=250, name=”KRK_VOICE”)
assert vlan.svi is False
@pytest.mark.parametrize(„vlan_id”, [0, 1, 4095, 5000, -1])
def test_vlan_id_poza_zakresem(vlan_id):
with pytest.raises(ValidationError):
Vlan(vlan_id=vlan_id, name=”KRK_VOICE”)
@pytest.mark.parametrize(„name”, [„krk_voice”, „VOICE”, „”, „A_B”])
def test_niepoprawna_nazwa(name):
with pytest.raises(ValidationError):
Vlan(vlan_id=250, name=name)
Zwróćmy uwagę na wartości brzegowe w parametryzacji, takie jak 0, 1, 4095. To dokładnie te miejsca, w których walidacja się psuje, a człowiek, pisząc testy ręcznie, zwykle testuje poprawną wartość i jedną błędną, bo przecież komu by przyszło do głowy podać ujemną wartość identyfikatora VLAN-u, prawda? Tutaj model jest po prostu dokładniejszy niż my. Czy wygenerowany zestaw wartości jest optymalny? To już decyzja inżyniera, który bazuje na swojej wiedzy i doświadczeniu. Dla mnie byłby to zestaw [-1, 0, 1, 4095, 4096, 5000]. Mimo że poprawkę w kodzie nanosiłbym ręcznie, sam szkielet kodu testu powstał bardzo szybko.
Code review
LLM nieźle wyłapuje błędy logiczne, powtórzenia, brakującą obsługę wyjątków i niebezpieczne założenia w kodzie. Zapyta, co się stanie przy pustej liście, gdzie jest obsługa wyjątku przy połączeniu, dlaczego zakres urządzeń nie jest ograniczony. Zastosowań jest jednak więcej.
W automatyzacji sieci najbardziej opłaca się kierować model na dwie rzeczy. Pierwsza to wspomniana przed chwilą idempotencja. Zadanie, które przy drugim uruchomieniu daje inny rezultat niż przy pierwszym, jest częstym błędem w kodzie sieciowym i wyjątkowo trudnym do zauważenia podczas zwykłego czytania kodu przez człowieka. Model porówna stan wejściowy z operacją i wskaże miejsca, w których coś się dopisuje, zamiast ustawiać. Druga to zakres wykonania skryptu. Pytanie „na ilu urządzeniach ten kod wykona się bez –limit” potrafi zatrzymać zmianę na etapie pull requestu, zanim ktokolwiek zdąży ją zatwierdzić. Poza tym LLM dobrze radzi sobie ze sprawdzaniem zgodności z konwencjami zespołu, o ile wcześniej podamy mu je w odpowiednim kontekście. Nazewnictwo, układ katalogów, wymagany format inventory, sposób opisywania zmiennych. Jest to praca nudna i powtarzalna, więc oddajemy ją bez żalu. Podobnie z wyszukiwaniem sekretów w repozytorium, choć tutaj traktowałbym model wyłącznie jako dodatek do właściwych skanerów, nie jako ich zamiennik.
LLM przydaje się też do wychwytywania niezgodności wykonywanych czynności z platformą sprzętową, na której chcemy je wykonać. Parser napisany pod output z IOS-XE nie wykona się poprawnie na NX-OS. Wyrażenie regularne dopasowane do formatu w konkretnej wersji oprogramowania nie musi poprawnie zadziałać w innej. Pomoże też wyłapać takie błędy jak choćby sztywno wpisana w skrypt nazwa interfejsu. Model wskaże te miejsca szybciej niż recenzent. Na koniec dwa zastosowania okołoprocesowe. Pytanie o procedurę wycofania zmiany, zadane wprost, daje zaskakująco konkretne odpowiedzi i często ujawnia, że rollbacku po prostu nie ma. Przy dużym pull requeście model przygotuje streszczenie zmiany i wskaże pliki, które trzeba przeczytać najuważniej. Recenzent dostaje wtedy punkt startowy zamiast 300 linii diffa bez kontekstu. I wszystko to możemy zaprogramować jako element całego pipeline’u CI/CD.
Tu jednak zaczyna się problem, który dwa lata temu w tej skali nie występował. Modele generują kod znacznie szybciej, niż ktokolwiek jest w stanie go przeczytać ze zrozumieniem. Jedna sesja pracy potrafi wyprodukować kilkaset lub kilka tysięcy linii Pythona, komplet szablonów Jinja2, testy i playbook. Całość zajmuje kilkanaście minut, podczas gdy rzetelna recenzja tego samego materiału to kilka godzin uważnej lektury, która męczy bardziej niż pisanie kodu. Różnica jest ogromna i będzie się powiększać. Konsekwencje widać już teraz w wielu zespołach. Recenzja polega na przewijaniu diffa i sprawdzaniu, czy pipeline świeci na zielono. Zamiast rozumieć logikę, zakładamy, że skoro kod powstał maszynowo i testy przechodzą, to pewnie jest w porządku. W efekcie do repozytorium trafia kod, którego nikt nie przeczytał w całości, a którego wykonanie zmienia konfigurację urządzeń. W sieciach boli to bardziej niż w typowym projekcie aplikacyjnym. Kod automatyzacji przez większość czasu nikogo nie obchodzi, dopóki działa. Ale w dniu awarii staje się jedynym wiarygodnym opisem tego, co faktycznie zostało wdrożone i w jakiej kolejności. Jeżeli nikt w zespole go nie rozumie, diagnostyka zaczyna się od czytania cudzego kodu pod presją czasu, przy dzwoniącym telefonie.
Rozwiązania idealnego obecnie nie ma, ale kilka rzeczy realnie pomaga. Najprostsza to twardy limit rozmiaru zmiany. Pull requesty, których nie da się przeczytać w pół godziny, dzielimy na mniejsze części. Druga to przesunięcie ciężaru uwagi z kodu na testy i asercje. Testów jest mniej, są prostsze i to one definiują, co uznajemy za poprawny wynik. Jeżeli w danym tygodniu mamy czas przeczytać uważnie jeden plik, niech to będzie ten z testami, a nie implementacją. Trzecią pomocną kwestią jest maksymalne obciążenie bramek automatycznych, czyli mechanizmów kontrolnych, które działają bez udziału człowieka i blokują zmianę, gdy coś jest nie tak. Ruff, ansible-lint, skanowanie sekretów, weryfikacja konfiguracji w Batfishu, obowiązkowy dry run z diffem załączonym do pull requesta to tylko niektóre przykłady. Wszystko, co maszyna utworzy bez naszego udziału, powinno być sprawdzane przez maszynę, żeby uwaga recenzenta skupiła się na tym, czego żadne narzędzie nie wyłapie. Czyli na zgodności z projektem sieci, na zakresie działania i planie wycofania.
I rzecz, którą należy uznać za granicę nienegocjowalną – model może recenzować kod, bo jest w tym szybki i tani. Nie może jednak nigdy zatwierdzać zmian. Podpis pod pull requestem nadal musi być ludzki.
Praca z dokumentacją
Dokumentacja API kontrolera SD-WAN albo systemu IPAM potrafi mieć kilkaset stron i nikt tego nie czyta w całości. Szukamy jednego interesującego nas w danym momencie endpointu, sprawdzamy, jakie pola są wymagane, budujemy zapytanie i wracamy do pracy. Model językowy skraca tę pętlę z kwadransa do minuty, pod warunkiem że dostanie właściwy materiał. Najlepiej sprawdza się w kilku konkretnych zadaniach, takich jak choćby streszczenie sekcji dokumentacji do postaci, którą da się przeczytać przy kawie. Albo wyciągnięcie listy pól obowiązkowych i opcjonalnych dla konkretnego wywołania oraz zbudowanie przykładowego zapytania wraz z obsługą uwierzytelnienia i paginacji. Albo porównanie tego samego ustawienia w dwóch reprezentacjach, na przykład w klasycznym CLI i w modelu YANG, co podczas migracji na NETCONF oszczędza sporo czasu. Przydaje się też do wyłapywania różnic między wydaniami, jeżeli podamy release notes z dwóch wersji i poprosimy o wskazanie zmian dotyczących akurat tej funkcji, nad którą pracujemy.
Pamiętajmy jednak, że dokumentacja musi trafić do modelu jako kontekst. Może być wklejona bezpośrednio w okno rozmowy, załadowana przez RAG-a albo udostępniona za pośrednictwem serwera MCP. Bez tego nie dostajemy streszczenia, tylko rekonstrukcję z pamięci modelu, czyli przypuszczenie, jak dokumentacja prawdopodobnie wygląda. Różnica jest fundamentalna, a na pierwszy rzut oka niewidoczna, bo obie odpowiedzi brzmią równie pewnie. W praktyce możemy przekazać dokumentację wraz z wersją, której dotyczy, i poprosić o odpowiedź ograniczoną do tego materiału:
Poniżej fragment dokumentacji API vManage 20.12.
Odpowiadaj wyłącznie na podstawie tego tekstu.
Jeżeli informacji tam nie ma, napisz „brak w dokumentacji”.
Przy każdym polu podaj nagłówek sekcji, z której pochodzi.
<tu wklejona dokumentacja>
Pytanie: jakie pola są wymagane przy tworzeniu device template dla C8000V i które z nich są niezmienne po utworzeniu?
Prośba o wskazanie sekcji źródłowej jest tu najważniejsza. Jeżeli model nie potrafi powiedzieć, skąd zna odpowiedź, prawdopodobnie wziął ją ze swojej pamięci. To najprostszy sposób, by odróżnić streszczenie od halucynacji. Osobna sprawa to wersjonowanie. Dokumentacja producenta żyje własnym życiem i strona, która wczoraj opisywała zachowanie w wydaniu 17.9, dziś może omawiać je w 17.15. Jeżeli budujemy bazę wiedzy dla RAG-a, warto trzymać w niej wersję jako metadane i filtrować po niej zapytania. W innym wypadku model będzie mieszał w odpowiedziach informacje o różnych wydaniach.
Niedocenianym i często mało eksploatowanym obszarem jest również własna dokumentacja zespołu. Standardy nazewnictwa, plan adresacji, polityki, opis inventory, historia decyzji projektowych. Gdy te materiały trafiają do modelu wraz z pytaniem, jakość odpowiedzi rośnie bardziej niż po zmianie modelu na większy. Przy okazji wychodzi na jaw, ile z tej wiedzy nigdzie nie jest zapisane i siedzi wyłącznie w głowach dwóch osób w zespole. Wiele korporacyjnych rozwiązań, takich jak Microsoft Copilot, pozwala na bezpieczne udostępnianie modelom wewnętrznej dokumentacji znajdującej się w zasobach firmowych.
Analiza logów
Praca z logami polega w dużej mierze na czytaniu tekstu i szukaniu w nim wzorca. To czynność, którą modele wykonują bardzo dobrze. Typowe zastosowania LLM-ów to grupowanie powtarzających się błędów w pipeline CI/CD, tłumaczenie tracebacku z biblioteki, której nie znamy, oraz korelacja zdarzeń z kilku urządzeń w jednym oknie czasowym. Do tego dochodzi wyjaśnianie rozbieżności między stanem zamierzonym a rzeczywistym, gdy playbook zgłasza zmianę przy każdym uruchomieniu, choć niczego nie modyfikuje. Model potrafi też przetłumaczyć komunikat producenta na zwykły język, co przy kodach błędów wielu platform bywa realną pomocą. Analizując logi, LLM uporządkuje je w łańcuch przyczynowy i wskaże zależności między nimi. Zaproponuje też kierunki dalszego sprawdzenia czy potencjalne przyczyny powstania awarii.
Nie możemy jednak zapomnieć o ograniczeniach – wszystko, co dostajemy, to hipotezy. Model nie ma dostępu do urządzenia, ale analizuje jedynie tekst, który mu podaliśmy. Zazwyczaj nie wie, czy ten interfejs to łącze do operatora, czy port do serwera. Oczywiście, w zaawansowanych rozwiązaniach możemy skorelować analizę logów z zaprogramowanymi, na przykład w pyATS, testami. Osobno warto ustawić sobie pracę z logami z CI/CD, bo tam materiał jest powtarzalny. Jeżeli pipeline zwraca błąd co drugie uruchomienie na tym samym etapie, model porówna kilka kolejnych przebiegów i wskaże, co je różni. Przy błędach niedeterministycznych, czyli takich, które pojawiają się losowo, to realna pomoc i oszczędność czasu. Człowiek porównujący ręcznie cztery logi po tysiąc linii najpewniej przeoczy różnicę w timeoucie, podczas gdy model ją zauważy.
Bezpieczeństwo danych i dostępów
Używając publicznie dostępnych modeli, nie zapominajmy o anonimizacji. W logach znajdują się adresacja, nazwy hostów, czasem nazwy klientów w opisach interfejsów, a podczas debugowania uwierzytelniania potrafią zawierać znacznie więcej. Do zewnętrznego modelu nie wysyłamy haseł, tokenów ani kluczy prywatnych. Nie wrzucamy pełnych konfiguracji zawierających dane wrażliwe, nie udostępniamy też danych klientów. To wydaje się oczywiste do momentu, w którym ktoś w pośpiechu nie wklei całego show running-config z routera produkcyjnego, żeby szybciej znaleźć błąd.
Praktyki, które warto wdrożyć, są znane z innych obszarów. Sekrety trzymamy w Vault albo w mechanizmach sekretów naszego CI. Adresację, nazwy klientów i hostów redagujemy przed wklejeniem. Konta automatyzacji mają minimalne uprawnienia, wystarczające do wykonywania zadania i nic ponadto. Wywołania API są audytowane. Przy danych wrażliwych wybieramy środowisko firmowe z podpisaną umową zamiast publicznego czatu lub uruchamiamy model na własnych urządzeniach.
Redakcję można zautomatyzować. Krótki filtr przed wklejeniem konfiguracji wystarczy na początek:
sed -E \
-e 's/(password|secret|key) .*/\1 <REDACTED>/I’ \
-e 's/snmp-server community [^ ]+/snmp-server \ community <REDACTED>/I’ \
-e 's/([0-9]{1,3}\.){3}[0-9]{1,3}/10.0.0.1/g’ \
running-config.txt > running-config.safe.txt
To nie jest narzędzie klasy enterprise i nie zastąpi przeglądu pliku przez człowieka. Zmniejsza jednak ryzyko najczęstszego scenariusza, w którym ktoś w pośpiechu kopiuje konfigurację razem z community SNMP i hasłem do TACACS-a. Na rynku dostępny jest szereg produktów zapewniających anonimizację danych (z możliwością ich późniejszej deanonimizacji). Odpowiednie skrypty możemy również rozwijać samodzielnie, dostosowując je do swoich potrzeb, środowiska i wykorzystywanych urządzeń. Warto też ustalić na poziomie zespołu konkretne zasady dotyczące tego, co wolno wysłać do modelu, a czego nie. Takie podejście zadziała lepiej niż założenie, że każdy sam się domyśli.
Od copilota do agentów
Kierunek rozwoju automatyzacji połączonej z wykorzystaniem AI jest widoczny i warto go rozumieć. Zaczęliśmy od samego modelu, który generował tekst w oknie czatu. Potem doszły narzędzia, czyli możliwość wywoływania funkcji. Następnie dostęp do dokumentacji przez RAG i serwery MCP. Dziś model potrafi już czytać repozytorium, uruchamiać testy, a przy odpowiedniej integracji odpytywać API urządzeń. Każdy taki krok zwiększa autonomię. Podnosi zarazem wagę tego, co ją ogranicza – uprawnienia kont, przez które agent działa, bramki zatwierdzania przy operacjach zmieniających stan, audyt wszystkich wywołań czy możliwość szybkiego rollbacku.
W praktyce granicę wyznacza konto, na którym agent pracuje. Poziom uprawnień na urządzeniu jest tu skuteczniejszy niż jakakolwiek instrukcja w prompcie. Agent z odpowiednio ograniczonym kontem może odpytywać o stan, ale nie będzie w stanie niczego zmienić, nawet jeżeli w jego kontekście znajdzie się instrukcja, która go do tego namówi. To ważne również z innego powodu. Model czytający logi, tickety czy opisy urządzeń przetwarza dane z zewnątrz, a te mogą zawierać treści sterujące jego zachowaniem. Jest to znany i udokumentowany atak typu indirect prompt injection. Taki atak nie jest trudny do przeprowadzenia. W wielu sieciach administratorzy logują wszystkie komendy wykonywane przez użytkowników na konsolach urządzeń sieciowych. Wystarczy zatem, że atakujący lub sabotażysta, mający dostęp do konsoli, zamiast prawidłowego polecenia wpisze złośliwy prompt, który zostanie następnie przetworzony przez model na podstawie logów. Uprawnienia są jedyną warstwą, która działa niezależnie od tego, co model przeczyta. Agent z dostępem tylko do odczytu, który zbiera stan sieci i przygotowuje raport, to duża wartość przy niewielkim ryzyku. Agent z uprawnieniami do zapisu na urządzeniach produkcyjnych to zupełnie inna kategoria decyzji.
Autor
Piotr Wojciechowski
Autor jest specjalistą w dziedzinach routing & switching, data center oraz service providers. Promuje automatyzację w środowiskach sieciowych i udziela się jako deweloper w projektach open source. Jest twórcą modułów do Docker Swarm w Ansible’u i jednym z opiekunów modułów w community.docker. Pracuje jako niezależny konsultant IT, posiada certyfikat CCIE.