Spis treści:
-
- Architektura RAG
- Przygotowanie środowiska
- Budujemy wewnętrzną bazę wiedzy
- Lokalny pipeline w Pythonie
- Analiza i refaktoryzacja kodu Terraforma
- Interaktywna praca z RAG-iem
- Lokalnie i bezpiecznie
W ostatnich latach zespoły DevOps i SRE coraz częściej korzystają z modeli językowych, ale szybko się okazuje, że „gołe” LLM-y nie radzą sobie z realiami środowisk enterprise. I tutaj wchodzi RAG – podejście, w którym bierzemy model językowy i dokładamy do niego wiedzę z naszych prywatnych źródeł. Dzięki temu LLM nie tylko mówi, jak ogólnie funkcjonują VPC czy IAM, ale też potrafi wyjaśnić, jak działają u nas.
Tradycyjne modele bez dostępu do wewnętrznych modułów Terraforma, firmowej dokumentacji czy standardów operacyjnych nie są w stanie udzielić sensownej odpowiedzi na pytania dotyczące naszej infrastruktury. Nie znają naming conventions, polityk bezpieczeństwa ani tego, dlaczego w 2019 r. zdecydowaliśmy się na ten konkretny moduł zamiast innego. Podejście Retrieval-Augmented Generation (RAG) pozwala uniknąć tego typu niewiedzy i właśnie dlatego jest warte rozpatrzenia.
W tym artykule przejdziemy krok po kroku przez proces budowania wewnętrznego asystenta Terraform opartego na Ollamie i Code Llamie. Otwieramy terminal, instalujemy środowisko, klikamy w kilka komend, piszemy prosty pipeline w Pythonie i ładujemy do niego nasze moduły, zmienne, README oraz wewnętrzną dokumentację. Tak uzbrojony model zacznie odpowiadać na pytania, podpowiadać zmiany w kodzie, a nawet analizować błędy w konfiguracji. Wszystko oparte na realnych danych z naszej organizacji.
Dzięki RAG-owi przestajemy pytać: „jak to się robi w chmurze?”, a zaczynamy: „jak my to zrobiliśmy i czy da się to zrobić lepiej?”. To narzędzie, które realnie odciąża zespoły, skraca onboarding i pozwala szybciej przeglądać zmiany infrastruktury, bez ryzyka wynoszenia wiedzy poza firmę. W kolejnych sekcjach omówimy cały proces wdrożenia – utworzymy środowisko, indeks wektorowy, zintegrujemy go z modelem i zbudujemy pierwszą wersję asystenta, który rozumie naszą infrastrukturę tak dobrze jak doświadczony inżynier DevOps.
Architektura RAG
Zanim odpalimy pierwsze komendy, warto wiedzieć, z czego dokładnie składa się działający pipeline RAG i jak te elementy współpracują. Dzięki temu, kiedy później otwieramy terminal i piszemy kod w Pythonie, rozumiemy, co jest celem każdego kroku.
RAG to metoda, w której bierzemy model językowy i „dokarmiamy” go kontekstem pobranym z własnych danych. Model nie zgaduje, ale korzysta z dokumentów, które mu dostarczamy. W praktyce wygląda to tak:
- Zbieramy dane – otwieramy katalogi z modułami Terraforma, dokumentacją, ADR-ami, wiki, runbookami.
- Przekształcamy je w embeddingi – piszemy kod w Pythonie, który zamienia treść plików na wektory liczbowe reprezentujące znaczenie tekstu.
- Odkładamy embeddingi do indeksu – najczęściej korzystamy z FAISS (github.com/facebookresearch/faiss) albo ChromaDB (github.com/chroma-core/chroma). Indeks pozwala szybko znaleźć dokumenty najbardziej podobne do zapytania.
- Zadajemy pytanie modelowi – wpisujemy w prompt: „Dlaczego module x używa resource y?” albo „Podpowiedz poprawkę, żeby pozbyć się błędu Error: Invalid for_each argument”.
- Pipeline pobiera właściwe fragmenty dokumentów – indeks zwraca najbardziej pasujące kawałki kodu Terraforma i dokumentacji.
- Model generuje odpowiedź na podstawie tych danych – Code Llama nie musi znać całej naszej infrastruktury. Bierze fragmenty, które mu podsunęliśmy.
To właśnie w tym miejscu zaczyna się magia: model odpowiada w kontekście naszej organizacji.
Ollama
Ollama pełni rolę lokalnego runtime’u dla LLM-ów – działa szybko, bez konieczności stałego połączenia z internetem i pozwala odpalać modele na GPU lub CPU. Otwieramy terminal, piszemy: ollama run codellama. I to wszystko – mamy działający lokalnie LLM gotowy do działania. Możemy do niego wysyłać zapytania z Pythona równie łatwo jak do OpenAI. Z punktu widzenia architektury naszego rozwiązania Ollama generuje odpowiedź, indeks wektorowy podpowiada kontekst, a kod w języku Python skleja wszystko w całość. Takie rozdzielenie sprawia, że możemy wymienić model, dodać nowe źródła dokumentacji albo zmienić sposób chunkowania (dzielenia) tekstu, bez przepisywania reszty.
Code Llama
W artykule wykorzystujemy Code Llama, bo świetnie radzi sobie z kodem (w tym Terraformem). Możemy wybrać wersję zoptymalizowaną do mniejszych zasobów lub hostować model całkowicie lokalnie czy w naszym centrum danych. W praktyce, jeśli bierzemy duży projekt Terraforma, który żyje od sześciu lat, to Code Llama potrafi wyciągnąć z kontekstu to, czego GPT czy Claude nie zrobią bez kilku iteracji. Aby uporządkować całość, poniżej najprostsza mentalna mapa pipeline’u:
[Terraform+dokumentacja] → [Parser/chunker] → [Embedding model] → [Indeks wektorowy] → (zapytanie) → [Retriever → top K] → [Ollama+CodeLlama] → Odpowiedź
Każdy element można wymieniać i rozbudowywać. Największa zaleta: indeks jest niezależny od modelu, a model od indeksu, dzięki czemu możemy eksperymentować z innymi modelami dostępnymi publicznie w serwisie HuggingFace (huggingface.co/models). Najlepiej wybrać te modele, które są przygotowane (wstępnie trenowane) pod nasze potrzeby – w tym wypadku interesują nas modele przygotowane do generowania kodu. Wśród wyzwań, które rozwiązuje ta architektura, można wymienić:
- halucynacje modelu – kontekst sprawia, że model nie generuje zmyślonych odpowiedzi;
- duże repozytoria Terraforma – indeks zwraca tylko istotne fragmenty, nie cały katalog;
- rozproszone dokumentacje – RAG scala wszystkie materiały firmowe w jedną płynną bazę wiedzy;
- standaryzację – model odpowiada na bazie naszych własnych polityk i ADR-ów.
Przygotowanie środowiska
Zanim zaczniemy pisać pipeline w Pythonie, musimy przygotować środowisko do pracy z Ollamą i modelem Code Llama. Zakładamy, że pracujesz na Linuksie lub macOS-ie. Jeśli korzystasz z Windowsa, zdecydowanie polecamy uruchomić wszystko w WSL2, bo instalacja jest prostsza, a samo środowisko stabilniejsze pod narzędzia DevOpsowe.
Instalacja
Ollama to serce naszego rozwiązania – to on uruchamia modele i wystawia lokalne API, z którym komunikujemy się w późniejszych krokach. Otwieramy terminal i wpisujemy (w zależności od systemu):
Po instalacji uruchamiamy usługę: ollama serve. Włączy się serwis, który będzie nasłuchiwał na porcie 11434 – jego logi zapiszą się w terminalu. Aby go wyłączyć, wystarczy, że wciśniemy CTRL+c.
Pobranie Code Llamy
Teraz w osobnym terminalu (Ollama musi być uruchomiona) pobieramy model, na którym będziemy pracować, za pomocą polecenia: ollama pull codellama. W zależności od zasobów można wybrać wersję 7B lub 13B. Do naszych potrzeb 7B w zupełności wystarczy, ale jeśli mamy dużo VRAM-u, możemy śmiało się zdecydować na większy wariant. Po chwili model będzie gotowy do użycia. Sprawdzamy, czy działa: ollama run codellama. Jeśli zobaczymy prompt, w którym możemy wpisać zapytanie – jesteśmy gotowi.
Wymagania sprzętowe i optymalizacje
Nie musimy mieć najnowszego GPU, ale jeśli pracujemy z dużymi repozytoriami, warto pamiętać o kilku kwestiach:
- model zadziała również na samym CPU, ale może być wolniejszy;
- na GPU (nawet 6–8 GB VRAM) Code Llama radzi sobie świetnie;
- dla macOS-a modele działają bardzo szybko na procesorach Apple Silicon.
Jeśli planujemy z czasem uruchamiać większe modele lub wielu użytkowników, warto włączyć offloading na CPU, a także ustawić num_ctx na większą wartość, a samą Ollamę uruchomić jako systemd service (Linux), aby działał stale.
Przygotowanie środowiska Pythona
Otwieramy katalog projektu i konfigurujemy wirtualne środowisko do wykonywania skryptów Python (virtualenv):
python3 -m venv venv
source venv/bin/activate
Instalujemy biblioteki, z których będziemy korzystać do RAG-a:
pip install chromadb sentence-transformers langchain-community pyyaml
pip install ollama
Alternatywnie możemy użyć FAISS-a od Facebooka, ale na nasze potrzeby wystarczy ChromaDB.
Struktura projektu
Warto już teraz zorganizować projekt, żeby później nie szukać plików po całym repozytorium:
rag-terraform-assistant/
├── data/
│ ├── terraform/ # kod terraform, który indeksujemy
│ └── docs/ # dokumentacja markdown, ADR, runbooki
├── embeddings/
│ └── chroma/ # indeksy wektorowe – generowane
# za pomocą skryptu
├── src/
│ ├── ingest.py # parsowanie i tworzenie embeddingów
│ ├── query.py # pipeline: retrieval + ollama
│ └── utils.py
└── venv/
W kolejnych sekcjach będziemy krok po kroku wypełniać te pliki.
Pierwszy test połączenia z API Ollama
Na koniec zróbmy szybki sanity-check, żeby się upewnić, że połączenie Python–Ollama działa. Tworzymy prosty skrypt:
import ollama
response = ollama.chat(model=’codellama’, messages=[
{’role’: 'user’, 'content’: 'Hello, just testing connection.’}
])
print(response[’message’][’content’])
Umieszczamy skrypt w pliku src/connection_test.py i uruchamiamy tak jak poniżej:
python src/connection_test.py
It’s great to hear that you’re testing the connection! Is there anything in
particular you would like to talk about or ask? I’m here to help with any
questions you may have.
Jeśli skrypt zwróci błąd, warto poczekać jeszcze chwilę, aby model mógł się uruchomić – może to potrwać kilka minut na wolniejszych systemach. Jeśli środowisko działa poprawnie, to od modelu powinniśmy dostać odpowiedź podobną do powyższej. Teraz możemy przejść do kolejnego etapu – budowania bazy wiedzy z kodu Terraform i dokumentacji.
Budujemy wewnętrzną bazę wiedzy
W tym kroku otwieramy katalog z naszym kodem Terraform i dokumentacją, a następnie zamieniamy je na embeddingi, czyli wektorowe reprezentacje tekstu, które później wykorzystamy do wyszukiwania kontekstu. To fundament całego RAG-a – im lepiej przygotujemy dane, tym trafniejsze odpowiedzi dostaniemy od modelu.
Przygotowanie źródeł danych
W katalogu data/ umieszczamy wszystko, co chcemy udostępnić modelowi:
- pliki .tf oraz .tfvars;
- moduły Terraform (modules/);
- dokumentację Markdown (.md);
- ADR-y;
- runbooki i SOP-y;
- wyeksportowane wiki.
Reguła jest prosta – indeksujemy tylko to, co może być bezpiecznie dostępne dla każdej osoby korzystającej z asystenta. Jeżeli jakiś dokument ma klauzulę „tajne” – nie ładujemy go do bazy.
Struktura katalogów
W projekcie DevOps/SRE często spotykamy się z dokumentacją nie tylko kodu, ale też decyzji i procedur operacyjnych. Dwa ważne elementy, które warto wprowadzić do struktury katalogów, to ADR-y i SOP-y.
ADR (Architecture Decision Record) to dokument, w którym zapisujemy istotne decyzje architektoniczne. Otwieramy plik, piszemy, co postanowiliśmy, jakie mieliśmy opcje i dlaczego wybraliśmy konkretną drogę. Dzięki temu zespół może w przyszłości zrozumieć powody, dla których coś zostało zrobione w określony sposób, nawet jeśli pierwotny autor decyzji już nie pracuje przy projekcie. W praktyce ADR-y pomagają w utrzymaniu spójności architektury, zwłaszcza gdy projekt rozwija się dynamicznie.
SOP (Standard Operating Procedure) to z kolei szczegółowa instrukcja operacyjna. Pisząc SOP-a, dokładnie opisujemy kroki, które należy wykonać przy standardowych operacjach, np. wdrażaniu modułu Terraform, przywracaniu backupu czy aktualizacji klastrów. Dzięki temu nowe osoby w zespole lub inżynierowie w trybie on-call mogą szybko wykonać zadanie bez ryzyka popełnienia błędu.
W naszym projekcie katalog data/adr będzie przechowywał ADR-y, a data/runbooks (lub data/sop) zawierał SOP-y i runbooki, które dokumentują procedury operacyjne. W połączeniu z modelem RAG embeddingi tych dokumentów pozwolą LLM-owi odpowiadać na pytania typu: „Jak poprawnie wdrożyć nowy moduł VPC?” albo „Dlaczego w tym projekcie użyliśmy VPC z trzema publicznymi subnetami?” – zawsze używając zapisanych procedur i decyzji.
Chunkowanie dokumentów
Modele działają najlepiej, gdy dostają tekst podzielony na mniejsze bloki (chunki). Dzielimy więc każdy plik na fragmenty o rozmiarze ok. 500 znaków z niewielką nakładką, żeby model nie tracił kontekstu między sekcjami. Przykładowa funkcja chunkująca w src/ingest.py może wyglądać następująco:
def chunk_text(text, chunk_size=500, overlap=100):
chunks = []
start = 0
while start < len(text):
end = min(start + chunk_size, len(text))
chunks.append(text[start:end])
start += chunk_size – overlap
return chunks
Dla kodu Terraforma chunkowanie działa znakomicie – każda sekcja (resource, module, variable) zachowuje swoją spójność, dzięki czemu nie wymaga dalszej obróbki.
Wczytywanie dokumentów
W ingest.py piszemy funkcję, która zbiera wszystkie dokumenty:
from pathlib import Path
def load_files(path, extensions={’.tf’, ’.tfvars’, ’.md’}):
docs = []
for p in Path(path).rglob(’*’):
if p.suffix in extensions:
docs.append((str(p), p.read_text()))
return docs
W ten sposób otwieramy całe drzewo katalogów i zbieramy pliki, które nas interesują.
Tworzenie Embeddingów
Embeddingi tworzymy za pomocą Sentence Transformers, a bazę wektorową trzymamy w ChromaDB:
from sentence_transformers import SentenceTransformer
import chromadb
model = SentenceTransformer(„multi-qa-MiniLM-L6-cos-v1”)
client = chromadb.PersistentClient(path=”embeddings/chroma”)
collection = client.get_or_create_collection(„terraform_docs”)
Kod inicjalizuje dwa kluczowe elementy wykorzystywane w pipelinie RAG, takie jak model do tworzenia embeddingów, plikową bazę danych dla szybszego ładowania informacji o naszych danych (ChromaDB) i późniejszego szybkiego wyszukiwania fragmentów dokumentacji podobnych do zapytania użytkownika. Do śledzenia zmian wykorzystujemy prosty mechanizm hashy plików. Tworzymy plik embeddings/state. json, w którym będziemy zapisywać sumę kontrolną SHA256 każdego zaindeksowanego pliku. W naszym skrypcie za odczytywanie i zapisywanie stanu odpowiada poniższy kod:
import json, hashlib
STATE_FILE = „embeddings/state.json”
def file_hash(content: str) -> str:
return hashlib.sha256(content.encode()).hexdigest()
def load_state():
try:
return json.loads(Path(STATE_FILE).read_text())
except FileNotFoundError:
return {}
def save_state(state):
Path(STATE_FILE).write_text(json.dumps(state, indent=2))
Dodajemy jeszcze funkcję wykrywania plików zmienionych lub usuniętych:
def get_changed_files():
state = load_state()
changed = []
removed = []
current = {}
for path, content in load_files(„data/”):
h = file_hash(content)
current[path] = h
if path not in state or state[path] != h:
changed.append((path, content))
for old_path in state:
if old_path not in current:
removed.append(old_path)
return changed, removed, current
Aktualizacja bazy wiedzy: implementujemy –update i –rebuild
W realnym repozytorium Terraforma pliki zmieniają się często, dlatego nasz skrypt musi obsługiwać aktualizacje. Dodajemy do niego dwie opcje:
- –update – dodaje tylko nowe lub zmienione pliki;
- –rebuild – czyści indeks i buduje go od zera.
Poniższy kod odpowiada za aktualizację indeksów:
def update_index():
changed, removed, current_files = get_changed_files()
Następnie usuwamy embeddingi plików, które zniknęły:
for r in removed:
collection.delete(where={„source”: r})
Dodajemy embeddingi nowych/zmienionych plików:
for path, content in changed:
chunks = chunk_text(content)
for i, chunk in enumerate(chunks):
embedding = model.encode(chunk).tolist()
collection.add(
documents=[chunk],
embeddings=[embedding],
metadatas=[{„source”: path}],
ids=[f”{path}-{i}”]
)
save_state(current_files)
Pełna przebudowa indeksu (–rebuild):
def rebuild_index():
collection.delete(where={})
ingest_documents()
Z kolei na samym końcu skryptu dodamy jeszcze obsługę argumentów przekazywanych przez użytkownika i uruchamiamy żądaną akcję:
if __name__ == „__main__”:
parser = argparse.ArgumentParser()
parser.add_argument(„–update”, action=”store_true”)
parser.add_argument(„–rebuild”, action=”store_true”)
args = parser.parse_args()
if args.rebuild:
print(„Rebuilding full index.. .”)
rebuild_index()
elif args.update:
print(„Updating index.. .”)
update_index()
else:
print(„Building index.. .”)
ingest_documents()
Lokalny pipeline w Pythonie
W tym etapie łączymy wszystkie elementy w jedną całość: otwieramy edytor, piszemy kod w Pythonie i uruchamiamy lokalny pipeline RAG, który potrafi pobierać dane z naszej infrastruktury, wyszukiwać właściwy kontekst i zasilać nim Code Llamę. Dzięki temu model nie generuje wyłącznie ogólnych sugestii, ale realnie odpowiada na pytania zgodnie z naszym
stylem pracy, strukturą repozytoriów i standardami.
Wszystkie biblioteki Pythona mamy już zainstalowane, więc przechodzimy do integracji z lokalną instancją Ollamy. W Pythonie dodajemy prosty wrapper, który pozwala nam wysyłać prompty do modelu i odbierać odpowiedzi. Kod może wyglądać po prostu tak:
from ollama import Client
ollama = Client(host=’http://localhost:11434′)
def ask_model(prompt, context):
return ollama.generate(
model=”codellama”,
prompt=f”## # Kontekst:\n{context}\n\n## # Pytanie-:\n{prompt}\n\n## # Odpowiedź:”
)[„response”]
Jednak dopiero połączenie go ze źródłem wiedzy – czyli naszym zaindeksowanym zbiorem dokumentów – sprawia, że RAG zaczyna mieć sens. Otwieramy więc plik z kodem i dopisujemy obsługę wyszukiwarki wektorowej. Korzystamy tutaj z ChromaDB w trybie persistent, aby cały indeks był przechowywany lokalnie:
import chromadb
chroma = chromadb.PersistentClient(path=”./chroma”)
collection = chroma.get_collection(„tf_docs”)
def search_context(query, n=4):
results = collection.query(
query_texts=[query],
n_results=n
)
return „\n\n”.join(results[„documents”][0])
W tym momencie możemy już uruchomić pełne zapytanie typu RAG. Wchodzimy do interpretera Pythona albo uruchamiamy skrypt, używając komendy ipython. Następnie wpisujemy:
question = „Jak mogę rozszerzyć moduł VPC o dodatkowe publiczne subnety?”
context = search_context(question)
response = ask_model(question, context)
print(response)
Model pobierze fragmenty dokumentacji, kodu Terraforma i notatek projektowych, dopasuje je do pytania, a następnie zbuduje odpowiedź na ich podstawie. Dzięki temu unika halucynowania, a jego sugestie są zgodne z naszym repozytorium i realnym stanem infrastruktury.
W tym miejscu mamy już działający pipeline RAG, który pobiera dane, przetwarza je, wyszukuje kontekst i podaje go lokalnemu modelowi. Warto rozszerzyć ten skrypt o interaktywną obsługę zapytań użytkownika. Jeśli chcemy, możemy go dalej wzbogacić – otwieramy kolejne pliki i dopisujemy obsługę logowania zapytań, wersjonowanie kolekcji, cache embeddingów czy integrację z GitLabem, aby model analizował także zmiany w merge requestach.
Analiza i refaktoryzacja kodu Terraforma
W tej części otwieramy terminal, przygotowujemy kilka zapytań i sprawdzamy, jak zachowuje się model, gdy dostarczamy mu precyzyjny kontekst pobrany z naszej dokumentacji i repozytoriów. To moment, w którym widać największą różnicę między „gołym” LLM-em a takim, który jest wspierany przez wektorową wyszukiwarkę z prawdziwym stanem infrastruktury.
Zaczynamy od przygotowania fragmentu kodu Terraforma, który chcemy przeanalizować. Otwieramy plik, kopiujemy jego zawartość i wklejamy do zmiennej w Pythonie:
tf_snippet = „””
module „vpc” {
source = „../modules/vpc”
cidr_block = „10.10.0.0/16”
}
„””
Następnie piszemy pytanie, które kierujemy do modelu. W tym przypadku chcemy sprawdzić, czy moduł VPC można rozbudować zgodnie z naszym standardem sieciowym. Zakładamy, że dokumentacja tego modułu i przykłady zostały wcześniej zindeksowane przez ingest.py i znajdują się w ChromaDB:
question = „Czy ten moduł VPC powinien obsługiwać publiczne
subnety i jak to dopisać zgodnie z naszym standardem?”
context = search_context(question + „\n” + tf_snippet)
response = ask_model(question + „\n\nKod:\n” + tf_snippet,
context)
print(response)
W tym momencie model zaczyna pracę dwutorowo. Najpierw pobiera z ChromaDB fragmenty naszej dokumentacji: opis modułu VPC, przykłady jego użycia, standard nazewnictwa dla subnetów albo komentarze z poprzednich merge requestów. Następnie łączy je z pytaniem i analizowanym kodem. To sprawia, że odpowiedź nie jest przypadkowa, lecz dopasowana dokładnie do tego, jak wygląda nasza infrastruktura.
Jeżeli w standardzie mamy zasadę, że każdy moduł VPC powinien zawierać trzy publiczne subnety w określonych AZ, to model odnajdzie i uwzględni tę kwestię. Jeżeli w dokumentacji modułu istnieje informacja, że publiczne subnety są opcjonalne i wymagają dodatkowego parametru enable_public_subnets = true, Code Llama również to wykorzysta i zaproponuje poprawki.
W praktyce możemy teraz traktować model jako lokalny odpowiednik recenzenta kodu. Otwieramy edytor, kopiujemy kolejny fragment kodu i pytamy o optymalizację. Możemy poprosić go o refaktoryzację, wykrycie parametrów, które zostaną wkrótce usunięte, lub dopisanie tagów wymaganych przez nasze polityki FinOps czy walidację konwencji nazewnictwa zasobów AWS.
Przykład bardziej szczegółowego zapytania:
question = „Przeanalizuj ten kod. Zgodnie ze standardem, że nasze zasoby muszą mieć tag 'Owner’ i 'Service’. Czego brakuje i jak to poprawić?”
context = search_context(question + „\n” + tf_snippet)
response = ask_model(question + „\n\nKod:\n” + tf_snippet, context)
print(response)
W odpowiedziach model będzie w stanie wykryć brakujące tagi, brak walidacji zmiennych, niekonsekwentne nazwy, a nawet zasugerować poprawki zgodne z udokumentowanymi uzgodnieniami, jeśli te również są częścią zindeksowanej dokumentacji.
Na tym etapie pipeline RAG zaczyna być realnym narzędziem do codziennej pracy – takim, które można odpalić podczas tworzenia lub wprowadzania zmian do modułu, przed wysłaniem do repozytorium albo w trakcie większej refaktoryzacji infrastruktury. Działa lokalnie, jest szybki, nie wymaga wysłania żadnych danych do chmury i zawsze korzysta z aktualnego stanu naszego repozytorium.
Interaktywna praca z RAG-iem
Gdy pipeline RAG jest gotowy, możemy zacząć używać go w trybie interaktywnym. Zamiast wpisywać pytania w kodzie Python i za każdym razem odpalać skrypt, otwieramy terminal, uruchamiamy interaktywny czat i piszemy zapytania wprost do modelu. Dzięki temu asystent Terraform działa jak lokalny konsultant, który zna wszystkie nasze moduły, zmienne i standardy.
Jeśli chcemy, żeby nasz asystent był dostępny nie tylko w terminalu, ale działał także w środowisku VS Code, to warto rozważyć użycie protokołu MCP i odpowiednich rozszerzeń. MCP to standard, który umożliwia lepszą integrację LLM‑ów z narzędziami deweloperskimi: edytorem, plikami, terminalem. Daje to dużo większe możliwości niż prosty czat w terminalu. Dzięki temu, oprócz rozmawiania z modelem, będziemy mogli prosić go o refaktoryzację lub sugestie.
Lokalnie i bezpiecznie
Otworzyliśmy sobie drzwi do wykorzystania narzędzia RAG w środowisku DevOps/SRE, pokazując, jak połączyć lokalny LLM z własną dokumentacją, kodem Terraforma i procedurami operacyjnymi. Pisząc skrypty do tworzenia embeddingów, budujemy bazę wiedzy, która pozwala modelowi odpowiadać precyzyjnie i zgodnie z naszymi standardami. Uruchamiając prosty czat Pythonowy, możemy wchodzić w interakcję z modelem na żywo, zadawać pytania o konfiguracje, moduły, zmienne, a nawet o polityki FinOps czy bezpieczeństwa.
Pokazaliśmy też, jak zintegrować pipeline z VS Code – zarówno przy użyciu prostego terminalowego chatu, jak i w pełni interaktywnie przez MCP. Dzięki temu model staje się naszym lokalnym asystentem, który nie tylko podpowiada zmiany w kodzie, ale potrafi też analizować, sugerować refaktoryzacje i weryfikować standardy w repozytorium.
Cały proces jest lokalny, bezpieczny, szybki i elastyczny: otwieramy projekt, klikamy w plik, piszemy pytanie i otrzymujemy odpowiedź dostosowaną do rzeczywistej infrastruktury. Taki pipeline RAG pozwala zautomatyzować wiele rutynowych zadań, zwiększyć jakość code review i przyspieszyć rozwój infrastruktury, pozostając w pełni pod kontrolą zespołu.
Wszystkie skrypty użyte w tekście znajdują się w repozytorium: github.com/DevOpsFury/rag-codellama
Autor
Grzegorz Adamowicz
Autor jest inżynierem systemów z blisko 20-letnim doświadczeniem. Zajmuje się tematami z pogranicza DevOps/SRE i programowania. Autor książki na tematy związane z DevOpsem w chmurze. Propagator ruchu open source.