Пайплайн для перавода з Git у GCS для Spark: стварэнне і завантажэння праекту на Scala з вікорыстаннем SBT, гатовага для агента.
Дэтэрміністычны пайплайн на Python, які клонуе рэпозытарый Scala, створае вялікі JAR-файл на Java 11 за дапамою SBT і завантажае яго у GCS, створаны так, каб агент ADK могаў запускаць яго як інструмент.
Кожны, хто працаваў над праектамі на платформе Spark з лэптопа ў прыметную срэду, ведае такі працэс. Пры гэтым трэба справляцца з командамі Git, працэсамі кампайлінгу SBT, завантажэнням дадзеных у хмарны сховішча і вікорыстанням калькуляваннях інфраструктурных API, пры чым кожны крок выпалюеся вручную. Існуе іншы спосаб: замест таго, каб прасіць ШЫ пра фрагменты коду (тэхніка стварэння запітаў), вы перадаеце агенту ШЫ весь цыкл жыцця праекта і дазволяеце яму самам выпалюваць усі крокі. За дапамогою набора для разработкі агентав Google (ADK), Gemini 2.5 Flash і Dataproc Serverless, большая мова модэля можа выступаць у ролі партнера для працы з DevOps і інжынерыяй дадзеных.
Шосты ўрадок павярнуўся да фундаменту гэтага налашчэння: дэтэрміністычны Python-пайплайн, які запоўняе рэпазітарый Scala/Spark з Git, стварае вялікі JAR-файл за дапамою SBT пад Java 11 і выкладае яго ў Google Cloud Storage. Пайплайн напісаны так, каб агент могаў выкорыстоўваць яго як інструмент. Сам шар агента, з функцыяльнымі інструментамі ADK, праблемай прыемкай падтверджэння, інтэрактыўнымі сесіямі Dataproc Serverless і фронтэндам чату Streamlit, будуецца на яго основе пазней.
Практычны рэзультат — 20-хвілінная, супяжызаная з багамі процедура стварэння і развертання ператвараецца на надзяйны каманды, якую агент можа запускаць на падставе простага англійскага запіту.
Боль внутранняга ціклу ў працы з Spark
Распрацоўшчыкі веб-сайтатов атрымоюць практычна мгновенную адзвеччу пры выкарыстоўванні функцыі гэтага перзагружання модуляў. Інжынеры, якія працуюць з вялікімі масавымі дадзеннямі, рэдка калі атрымоюць такія можлівасці. Цыкл рэдагавання, складання, размешчэння і тэставання, які часта называюць внутранім цыклам, часта зупініваецца на калькі пунктаў:
- Несувярэнныя асяроддзя. Версія Java на ноутбуке не сувярэнна з версіяй на кластэрэ Spark. Класычным прыкладам ёсць Java 17 на локальным апаратзе проты Java 11 у фармаце байткода на Dataproc.
- Дорогі працэс пакавання. Стварэнне вялікага JAR-файла з атрыбутамі
sbt clean compile assemblyможа зайняць калькі хвілін, выкарыстоўваючы значны рэсурс CPU і памяці. - Ручныя дзеянні ў воблаку. Людзі пераходзяць у Google Cloud Console або вводзяць дугія команды CLI, якія лёгка можна спісаць некалькі.
Ідэя такая: што, якбы весь ланцоўка была кодам, адлагоджаным і паданым агенту, які прымае інструкцыі на звычной мове? Наступныя раздзелы опісваюць автаматызаваную лінію пабудовы Git-до-GCS на мовах Scala/SBT, створаную як выконвальны інструмент для агентаў LLM.
Правілы дызайну інструментаў, безпечных для агентаў
Каб AI-агент могаў безпечна запускаць сістэму пабудовы, інструмент павінен адпавядаць трох прынцыпам:
- Дэтэрміністычны выконанне. Скрыпт пераканаецца ў стані сераўісу, напрыклад, падтвердзіўшы наявнасць Java 11, прычым перад тым, як пачаць дорогі крок компіляцыі.
- Чыстая ізоляцыя. Пабудовы ведуцца ў тымчасовых каталогах, якія пазней адмаўляюцца, тады прастор на дыску не накапліваецца.
- Стандартны ўвод і выход. Кожны этап выдруквае логі, якія можна аналізаваць машынай, і вяртае чыстае становішча.
Працэсы стварэння і заваносу залічваюцца окрэмымі, таму кожны з іх можна вызваць, працаваць з яму і практыкаваць занова незалежна.
Адкалэнне, па кроках
Фіксаванне стварэння на Java 11
Рамкі працы Spark на Dataproc Serverless выкалекаваны так, каб была бінарная сумеснасць з Java 11. Камп’ютары распрацоўшчыкаў часта выкорыстоваюць Java 17 або 21, і гэта несувязанасць вызывае памылкі байткода, такія як UnsupportedClassVersionError: 61.0 vs 55.0.
Клас ArtifactBuilder у файле artifact_builder.py запобегае гэтам, пераглядаючы установленыя JDK-ы пад час працы і прыязджаючы JAVA_HOME толькі да падпроцэса стварэння, таму сістэмныя настройкі залішаюцца некалечанымі:
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}")
Надзяйна публікацыя у хмарны ўтрымванне дадзеных
Выклікі gsutil або gcloud на рэвэлі Shell можу мовчкі збыцца пад некалькама сітуацыямі аутантыкацыі. Таму модуль uploader.py вікорыстоўвае офіцыйную бібліятэку-кліента Python для Google Cloud Storage, якая стварае рэальныя асывенты, якія можа пазначыць і запісаць канвей:
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.")
Аб’яднанне як будувальніка, так і завантажувача ў CLI
У падсумку run_build_and_upload.py аб’яднвае будувальнік і завантажувач у адны ArtifactPipeline з дэтэрміністычным інтэрфейсам у командной лініі.
Няхто не павінен запам’ятовваць чы ручнаўляць гэтыя команды. Распрацоўшчык выказвае сваю мету у фронтэне чату, напрыклад: «Збудаваць мой рэпазітарый у версіі main і завантажыць яго ў мой бакет GCS». Бэкенд агента выкарыстоўвае параметры з гэтага запісу і запускае відпаведную команду. Гэта тое, што ён выканае у фонавым рэжыме:
# 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
Лог тэрміналу адзюнктнага слоя з рэальнага запуску паказвае, як агент Google ADK работае з Gemini 2.5 Flash у Vertex AI. Ён чытае паведамленне карыстальніка і автаматычна вызывае artifact_pipeline/run_build_and_upload.py з правымі параметрамі.
Следзіце за запускам пайплайна
Пераканаленне ў Cloud Storage. Калі працэс загрузкі завершыцца, файл у фармаце JAR будзе знаходзіцца ў выбранай корзіне, гатовы для прыўязкі да Dataproc. У консолі скомпіляваны файл Spark JAR з’яўляецца па такам шляху, як gs://<bucket-name>/spark-jobs/spark-serverless-job_test.jar, з розмерам 186,8 MB.
Кераванне з інтэрфейса чату. За дапамою інтэрфейса Streamlit, стварэнне і аплодзіць вядуцца выключна за дапамою нейтральнай мовы. Распрацоўшчык задае рэпазітарый Git, галузь, бакет і шлях падачы, а агент плануе і выконвае інструменты стварэння. Калі завершаецца стварэнне артыкла і аплодзіць у GCS, агент следуе за сваёй машынай станоў і просі падтверджэння перш чым запускаць серверлессны нотбук.
Адпаведна дзеянне у разы невясковасці
Пайплайн, прызначаны для практычнага вжытку, павінен адпавядаць у чытальны спосаб. Калі выйшае з ладу стварэнне або серавер, агент перэхопляе паказаныя кроці выключнення і дае практычныя настановы замест таго, каб програма зламалася.
Проблемы з аутантыкацыяй у Git. Як толькі пайплайн пераключаецца на прыватны рэпазітары, выйшае брак у аутантыкацыі, і ўтвараецца блокаванне стандартнага выходу Git (на прыклад, Invalid username or token / Password authentication is not supported). Пасля чаго паказваеся, як настроіць SSH або токен.
Памылкі пад час выканання і ў сяродовыце. Калі ў сяродовыце Python выйдзе якаясь нечаканая проблема, напрыклад, відсутная залежнасць або ModuleNotFoundError, агент ізолюе проблему, паказвае точны трабек-трэйс і пропануе способы вярнуць працу. Сесія чату застаецца актываючайся, з інтэракtywnымі запросамі на пракату, а не заканчваецца разам з бракам.
Урокі, якія трэба запам’ятаваць
- Складнасць домэну трэба спроставаць. Калі працэс складаецца з чатырох разных крокаў, іх трэба аднаць у адны ідэмпатны каманду з чысткіма вхіднамі і выходнымі данымі.
Надзейныя агентскія сістэмы растуць знізу вверх. Замена ручных крокаў компіляцыі ізольаваным, сумэнным з Java 11 Python-двіжкам ператварае медленны, кранглы бутлнэк у надзейную будуючую елемент. Калі пайплайн Git-до-GCS будзе запакаваны як самастойны, ідэмпатэнтны інструмент, ён можа служыць рукамі чыта ногамі AI-агента.