Strona główna / Artykuły / Od inżynierii promptów do agentywnych procesów pracy: budowanie inteligentnych systemów

Od inżynierii promptów do agentywnych procesów pracy: budowanie inteligentnych systemów

Szczegółowy przewodnik od inżynierii promptów do agentywnych procesów pracy: budowanie inteligentnych systemów w oparciu o umowy, sprawdzania oraz gotowe elementy kodu dla zespołów wdrażających ten model.

2274 słów

Poniższe notatki przedstawiają praktyczny przewodnik po temacie „Od inżynierii promptów do agentywnych procesów pracy: budowanie inteligentnych systemów na Dataproc Serverless, część 1”. Nacisk kładziony jest na umowy, sprawdzania oraz miejsca zastępcze dla kodu, a nie na motywacyjne aspekty. Podczas przechodzenia przez etap przeglądu, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Wolno preferować małe, testowalne jednostki zamiast rozbudowanych skryptów. Gdy jakiś krok się nie powiedzie, błąd powinien wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji.

Przegląd serii

Etap przeglądu serii działa najlepiej, gdy traktuje się go jako mierzalną powierzchnię. Zapisz jeden idealny przykład działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Traktuj ten etap jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i odrzuć ciche, częściowe ukończenie zadań. Przydziel budżet tokenów na każdy ruch i sesję. Narzędzia typu agentic intensywnie rozszerzają kontekst; sztywne limity zapobiegają temu, by demonstracje przerodziły się w niespodziewane rachunki.

Część 1: Eliminowanie przeszkód dla programistów Spark — Automatyzacja cyklu życia Git, SBT i chmury

Faza usuwania iskier z Części 1 działa najlepiej, gdy traktuje się ją jako mierzalną powierzchnię. Zapisz jeden idealny przepis, jeden przypadek awarii oraz notatkę o cofnięciu zmian, zanim rozszerzysz zakres pracy. Zapisuj czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy przechodzi się z środowiska demonstracyjnego do współdzielonych środowisk. Ustal budżet tokenów na jeden ruch i na jedną sesję. Narzędzia agentowe intensywnie rozszerzają kontekst; sztywne limity zapobiegają temu, by demonstracje przerodziły się w nieoczekiwane rachunki.

Wprowadzenie: Cierpienie spowodowane pętlą wewnętrzną w inżynierii Spark

Wprowadzenie: „Agony of stage” funkcjonuje najlepiej, gdy traktuje się je jako mierzalną powierzchnię. Zapisz jeden idealny zapis działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Trzymaj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Ustal limit tokenów na rundę i sesję. Narzędzia typu agentic intensywnie rozszerzają kontekst; sztywne ograniczenia zapobiegają temu, by demonstracje przerodziły się w niespodziewane rachunki. Wprowadzenie: „Agony of stage” funkcjonuje najlepiej, gdy traktuje się je jako mierzalną powierzchnię. Zapisz jeden idealny zapis działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Wolij małe, testowalne jednostki nad rozbudowane skrypty. Gdy jakiś krok zawiedzie, awaria powinna wskazywać na konkretną odpowiedzialność, a nie na skomplikowaną sekwencję działań.

Architektura: Rozdzielenie procesu budowania i przesyłania danych dla efektywnego wykorzystania agentów

W ramach architektury rozdzielającej proces budowania i przesyłania danych należy zdefiniować dane wejściowe, osobę odpowiedzialną za daną fazę oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić daną fazę na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu systemu. Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy plikom, zdefiniuj kryteria sukcesu i odrzucaj przypadkowe, częściowe ukończenie zadań. Przy następnej fazie, która polega na tworzeniu kodu lub wywoływaniu narzędzi, preferuj strukturyzowane wyniki z walidacją schematu zamiast tekstów w formie swobodnej.

Krok po kroku: implementacja

W fazie krok po kroku implementacji należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy rejestrować czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do współdzielonych środowisk. W przypadku, gdy następnym krokiem jest kod lub wywołanie narzędzia, lepiej używać ustrukturyzowanych wyników z walidacją schematu niż tekstu w formie swobodnej.

1. Wymuszanie użycia Java 11 i odkrywanie procesu budowania (artifact_builder.py)

W fazie 1 Enforcing Java 11 należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. W przypadku, gdy następnym krokiem jest kod lub wywołanie narzędzia, lepiej używać ustrukturyzowanych wyników z walidacją schematu niż prostego tekstu. W fazie 1 Enforcing Java 11 należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Lepiej stosować małe, testowalne jednostki niż rozbudowane skrypty. Gdy jakiś krok zawiedzie, powinien wskazywać na konkretną odpowiedzialność, a nie na skomplikowaną strukturę procesów.

import os
import subprocess
import logging

class ArtifactBuilder:
    """Builds Scala JAR artifacts from Git repositories using SBT."""

    def __init__(self, repo_url: str, branch: str = "main"):
        self.repo_url = repo_url
        self.branch = branch

    def clone_repo(self, dest_dir: str):
        """Clone the given Git repository to the destination directory."""
        logging.info(f"Cloning {self.repo_url} (branch={self.branch}) into {dest_dir}")
        subprocess.run(["git", "clone", "-b", self.branch, self.repo_url, dest_dir], check=True)

    def find_build_dir(self, root_dir: str) -> str:
        """Recursively search for SBT build file."""
        for dirpath, _, filenames in os.walk(root_dir):
            if "build.sbt" in filenames:
                logging.info(f"Detected SBT build file in {dirpath}")
                return dirpath
        raise FileNotFoundError(f"No build.sbt file found in {root_dir}")

    def check_java_installed(self):
        """Ensure Java 11 is available and active in environment."""
        env = os.environ.copy()
        env["JAVA_HOME"] = "/opt/homebrew/opt/openjdk@11/libexec/openjdk.jdk/Contents/Home"
        env["PATH"] = f"/opt/homebrew/opt/openjdk@11/bin:{env.get('PATH', '')}"

        result = subprocess.run(
            ["java", "-version"],
            check=True,
            env=env,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        output = result.stdout + result.stderr
        if "version" in output:
            version_str = output.split("version")[1].split()[0].strip('"')
            major_version = int(version_str.split(".")[0])
            if major_version != 11:
                raise EnvironmentError(f"Java 11 required, but detected Java {major_version}")
        logging.info("Java 11 verified successfully.")

    def build_artifact(self, repo_dir: str) -> str:
        """Compile and assemble fat JAR."""
        build_dir = self.find_build_dir(repo_dir)
        self.check_java_installed()

        env = os.environ.copy()
        env["JAVA_HOME"] = "/opt/homebrew/opt/openjdk@11/libexec/openjdk.jdk/Contents/Home"
        env["PATH"] = f"/opt/homebrew/opt/openjdk@11/bin:{env.get('PATH', '')}"

        logging.info("Running: sbt clean compile assembly...")
        subprocess.run(["sbt", "clean", "compile", "assembly"], cwd=build_dir, env=env, check=True)

        target_dir = os.path.join(build_dir, "target")
        return self._find_file(target_dir, ".jar")

    def _find_file(self, directory: str, extension: str) -> str:
        for root, _, files in os.walk(directory):
            for f in files:
                if f.endswith(extension):
                    return os.path.join(root, f)
        raise FileNotFoundError(f"No {extension} file found in {directory}")

2. Niezawodne publikowanie w chmurze (uploader.py)

Podczas pracy nad etapem 2 Niezawodnego przechowywania w chmurze najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Traktuj ten etap jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i unikaj cichego ukończenia zadania w sposób niepełny. Zachowuj w pamięci tymczasowej stabilne instrukcje systemu oraz schematy narzędzi. Ponowne wysyłanie identycznego początku komunikatu jest częstą przyczyną problemów.

from google.cloud import storage
import logging
import os

class ArtifactUploader:
    """Handles secure upload of artifacts to Google Cloud Storage."""

    def __init__(self):
        self.client = storage.Client()

    def upload_to_gcs(self, bucket_name: str, artifact_path: str, dest_path: str):
        if not os.path.exists(artifact_path):
            raise FileNotFoundError(f"Artifact not found: {artifact_path}")

        logging.info(f"Uploading {artifact_path} → gs://{bucket_name}/{dest_path}")
        bucket = self.client.bucket(bucket_name)
        blob = bucket.blob(dest_path)
        blob.upload_from_filename(artifact_path)
        logging.info("Upload to GCS completed successfully.")

3. Orkiestracja backendu i otoczka CLI (run_build_and_upload.py)

Gdy przechodzisz przez 3 etapy Backend Orchestration CLI, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zapisz czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do współdzielonych środowisk. Zachowuj w pamięci tymczasowej stabilne instrukcje systemu oraz schematy narzędzi. Ponowne wysyłanie identycznego nagłówka jest częstą przyczyną marnotrawstwa zasobów.

# Executed autonomously in the background by the Agent's backend tool:
python3 artifact_pipeline/run_build_and_upload.py \
  --repo "https://github.com/<username>/demo-pipeline.git" \
  --branch main \
  --bucket "demo-spark-sandbox-bucket" \
  --dest "spark-jobs/spark-serverless-job_test.jar" \
  --cleanup

Wizualizacja procesu budowania w działaniu

Gdy przechodzisz przez etap wizualizacji pipeline budowania, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Trzymaj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Zachowuj w pamięci podręcznej stabilne instrukcje systemowe oraz schematy narzędzi. Ponowne wysyłanie identycznego prefiksu to częsty powód marnotrawstwa zasobów.

1. Weryfikacja chmurowego przechowywania

Gdy przechodzisz przez etap weryfikacji chmurnego przechowywania danych, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zdokumentuj zarówno prawidłowy przebieg działania, jak i ścieżkę naprawczą. Próby ponowne, kontrola przez ludzi oraz obsługa wiadomości błędowych stanowią część produktu, a nie elementy dodawane później. Zachowuj w pamięci podręcznej stabilne instrukcje systemu oraz schematy narzędzi. Ponowne wysyłanie identycznego nagłówka jest częstą przyczyną problemów.

2. Cykl życia wykonywania agenta w Streamlit

Gdy przechodzisz przez etap 2 Agent Execution Lifecycle, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Wolno preferować małe, testowalne jednostki zamiast rozbudowanych skryptów. Gdy jakiś krok się nie powiedzie, błąd powinien wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji. Zachowuj w pamięci tymczasowej stabilne instrukcje systemu oraz schematy narzędzi. Ponowne wysyłanie identycznego prefiksu to częsty powód marnotrawstwa zasobów.

Zarządzanie błędami i diagnostyka w praktyce

Gdy przechodzisz przez etap diagnostyki odpornego radzenia sobie z błędami, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Traktuj ten etap jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i odrzuć możliwość cichego, częściowego ukończenia zadania. Zachowuj w pamięci tymczasowej stabilne instrukcje systemu oraz schematy narzędzi. Ponowne wysyłanie identycznego nagłówka jest częstą przyczyną problemów.

Szczegóły 1: Błędy autoryzacji i klonowania w Git

Gdy przechodzisz przez etap autoryzacji w Git w Scenariuszu 1, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Obok wyników funkcjonalnych zapisz czas wykonywania oraz koszt tokena lub zapytania. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy przechodzi się z środowiska demonstracyjnego do współdzielonych środowisk. Zachowuj w pamięci podręcznej stabilne instrukcje systemu oraz schematy narzędzi. Ponowne wysyłanie identycznego nagłówka jest częstą przyczyną marnotrawstwa zasobów.

Scenariusz 2: Błędy w czasie działania pipeline’u i środowisk

Podczas pracy nad etapem Scenario 2 Pipeline Runtime najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Trzymaj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Zachowuj w pamięci cache stabilnych instrukcji systemowych oraz schematów narzędzi. Ponowne wysyłanie identycznego prefiksu to częsty powód marnotrawstwa zasobów. Podczas pracy nad etapem Scenario 2 Pipeline Runtime najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Wolij małe, testowalne jednostki nad rozbudowane skrypty. Gdy jakiś krok się nie powiedzie, błąd powinien wskazywać na konkretną odpowiedzialność, a nie na skomplikowaną strukturę pipeline’u.

Główne wnioski z części 1

Główne wnioski z tej fazy są najskuteczniejsze, gdy traktuje się ją jako mierzalną powierzchnię do analizy. Zapisz jeden idealny przykład działania, jeden przypadek niepowodzenia oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres pracy. Traktuj tę fazę jako umowę pomiędzy wprowadzanymi danymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i unikaj cichego, częściowego ukończenia zadań. Ustal budżet tokenów na każdą rundę i sesję. Narzędzia typu agentic intensywnie rozszerzają kontekst; sztywne limity zapobiegają temu, by demonstracje przerodziły się w nieoczekiwane rachunki.

Wniosek

Etap podsumowania działa najlepiej, gdy traktuje się go jako mierzalną powierzchnię. Zapisz jeden idealny przykład działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Zapisuj czasy wykonywania zadań oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy przechodzi się z środowiska demonstracyjnego do współdzielonych środowisk. Przydziel budżet tokenów na każdy ruch i na każdą sesję. Narzędzia typu agentic intensywnie rozszerzają kontekst; sztywne limity zapobiegają temu, by demonstracje przerodziły się w nieoczekiwane rachunki.

Referencje i dalsza lektura (Część 1)

Faza „Referencje i dalsza lektura” funkcjonuje najlepiej, gdy traktowana jest jako mierzalna powierzchnia. Zapisz jeden idealny zapis rozmowy, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Trzymaj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności przeglądania całej struktury. Ustal limit tokenów na rundę i sesję. Narzędzia typu agentic intensywnie rozszerzają kontekst; sztywne ograniczenia zapobiegają temu, by demonstracje przerodziły się w niespodziewane rachunki. Faza „Referencje i dalsza lektura” funkcjonuje najlepiej, gdy traktowana jest jako mierzalna powierzchnia. Zapisz jeden idealny zapis rozmowy, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Wolij małe, testowalne jednostki nad rozbudowane skrypty. Gdy jakiś krok zawiedzie, awaria powinna wskazywać na konkretną odpowiedzialność, a nie na skomplikowaną sekwencję działań.

Listwa kontrolna operacyjna

W fazie listy kontrolnej operacyjnej należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie wykonać dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu.

Należy udokumentować zarówno prawidłowy przebieg działania, jak i ścieżkę naprawczą. Próby ponownych wykonania, kontrolne punkty ludzkie oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodawane później.

Zamiast tekstów swobodnego stylu, należy preferować ustrukturyzowane wyniki z walidacją schematu, gdy następnym krokiem jest kod lub wywołanie narzędzia.

Ustawiaj punkty kontrolne po kosztownych krokach. System powinien unikać ponownego naliczania opłat za to samo wywołanie LLM, gdy operator próbuje ponownie wykonać późniejszy element.

Zabezpiecz wersje zależności i zapisz hash obrazu, który służył do uruchomienia demonstracji. Reprodukowalność jest ważniejsza od wiedzy przekazywanej ustnie w grupie.

Zapisuj czas wykonywania oraz koszt tokena lub zapytania obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega niespodziewanym rachunkom, gdy ścieżka przechodzi z wersji demonstracyjnej do środowisk udostępnionych.

Zanim przesuniesz całą architekturę, zamroź wersje, utwórz dokładny zapis dla kluczowych ścieżek oraz potwierdź kroki odwracania zmian. Środowiska udostępnione wymagają ograniczeń szybkości, weryfikacji użytkowników oraz jasno określonego właściciela odpowiedzialnego za rotację haseł. Wolisz nudną niezawodność od sprytnych, jednorazowych demonstracji.

Uwaga dotycząca 671e92168610: trzymaj klucze dostawcy poza repozytorium, ustaw górny limit tokenów na sesję oraz przechowuj zapisy obok narzędzi do oceny, aby późniejsze zmiany modeli pozostały porównywalne.

Literatura pokrewna