Manual Deploy — Agent Engine 수동 배포

Agent Studio 의 자동 deploy 가 구 SDK 로 컨테이너를 빌드해 GE 호출 메서드를 노출하지 못할 때, Python 스크립트로 직접 `AdkApp` 을 wrap 해 deploy 하는 절차

Agent Studio 의 “Deploy” 버튼은 편리하지만, 내부적으로 사용하는 google-adk SDK 버전이 빠르게 업데이트되지 않아 GE 호환 메서드 (streaming_agent_run_with_events) 가 누락된 reasoning engine 이 만들어지는 경우가 있습니다. 이때는 로컬에서 Python 스크립트로 직접 deploy 하여 SDK 버전을 명시적으로 pin 해야 합니다.

본 챕터의 절차는 Troubleshooting 6 — InvocationMethodNotFoundError 와 짝을 이룹니다.


목차

  1. 언제 manual deploy 가 필요한가
  2. 사전 준비
  3. Deploy 스크립트 전체
  4. 실행
  5. 노출 메서드 검증
  6. GE 에 등록
  7. 업데이트와 정리

1. 언제 manual deploy 가 필요한가

상황 Manual deploy 필요?
Agent Studio 에서 deploy 후 GE 가 정상 응답 ❌ 불필요
streaming_agent_run_with_events not found 로그 ✅ 필요
Tool / instruction 을 자주 변경하며 빠르게 iterate ✅ 권장 (자동화 용이)
Studio UI 에 없는 ADK feature (custom callback, memory, planner 등) 사용 ✅ 필요
Compliance / 코드 리뷰 대상 (배포 코드를 git 관리 필요) ✅ 필요

2. 사전 준비

2.1 SDK 설치

pip install -U \
  "google-adk>=1.19" \
  "google-cloud-aiplatform[adk,agent_engines]" \
  "vertexai"

2.2 GCP 인증

gcloud auth application-default login
gcloud config set project <YOUR_PROJECT>

2.3 Staging 버킷

Deploy 시 ADK 코드 + dependency 가 gs://<bucket>/agent_engine/... 로 업로드됩니다.

옵션 권장도
Deploy 프로젝트 안에 전용 버킷 (gs://<project>-agent-engine-staging) ⭐ 가장 간단
기존 공용 버킷 재사용 가능하지만 cross-project 권한 부여 필요 — Troubleshooting 7 참조

2.4 권한 (deploy 실행 주체)

  • roles/aiplatform.user
  • roles/storage.objectAdmin (staging 버킷)
  • roles/discoveryengine.editor (Vertex AI Search 도구 사용 시)

3. Deploy 스크립트 전체

scripts/deploy_agent.py:

"""
Agent Engine manual deploy.
요구: google-adk>=1.19, google-cloud-aiplatform[adk,agent_engines]
"""
import vertexai
from vertexai import agent_engines
from vertexai.preview.reasoning_engines import AdkApp

from google.adk.agents import LlmAgent
from google.adk.tools import agent_tool, url_context, VertexAiSearchTool
from google.adk.tools.google_search_tool import GoogleSearchTool

# ── 설정 ──
PROJECT = "<YOUR_PROJECT>"
LOCATION = "us-west1"                       # us-central1 / asia-northeast3 도 가능
STAGING_BUCKET = "gs://<staging-bucket>"

DATA_STORE = (
    "projects/<YOUR_PROJECT>/locations/global/collections/"
    "default_collection/dataStores/<YOUR_DATA_STORE_ID>"
)

vertexai.init(project=PROJECT, location=LOCATION, staging_bucket=STAGING_BUCKET)

# ── Sub-agents ──
google_search_agent = LlmAgent(
    name="google_search_agent",
    model="gemini-2.5-pro",
    description="Agent specialized in performing Google searches.",
    instruction="Use the GoogleSearchTool to find information on the web.",
    tools=[GoogleSearchTool()],
)
url_context_agent = LlmAgent(
    name="url_context_agent",
    model="gemini-2.5-pro",
    description="Agent specialized in fetching content from URLs.",
    instruction="Use the UrlContextTool to retrieve content from provided URLs.",
    tools=[url_context],
)
vertex_ai_search_agent = LlmAgent(
    name="vertex_ai_search_agent",
    model="gemini-2.5-pro",
    description="Agent specialized in performing Vertex AI Search.",
    instruction="Use the VertexAISearchTool to find information using Vertex AI Search.",
    tools=[VertexAiSearchTool(data_store_id=DATA_STORE)],
)

# ── Root agent ──
root_agent = LlmAgent(
    name="my_root_agent",
    model="gemini-2.5-pro",
    description="자동차 세일즈 에이전트 데모",
    instruction=(
        "사용자의 질문에 답변할 때 반드시 참조한 문서의 파일명과 관련 내용을 함께 명시하세요.\n"
        "1. vertex_ai_search 도구의 모든 검색 결과에는 `uri` (또는 `link`) 필드에 "
        "GCS 경로가 포함됩니다. 반드시 GCS 경로 자체를 참고로 나열하세요."
    ),
    tools=[
        agent_tool.AgentTool(agent=google_search_agent),
        agent_tool.AgentTool(agent=url_context_agent),
        agent_tool.AgentTool(agent=vertex_ai_search_agent),
    ],
)

# ── 핵심: AdkApp wrap + 최신 SDK pin ──
app = AdkApp(agent=root_agent, enable_tracing=True)

print("=== 배포 시작 (10~15분 소요) ===")
remote = agent_engines.create(
    app,
    display_name="My Agent (manual)",
    requirements=[
        "google-adk>=1.19",
        "google-cloud-aiplatform[adk,agent_engines]",
    ],
)

print()
print("=== 배포 완료 ===")
print(f"resource_name: {remote.resource_name}")

4. 실행

python scripts/deploy_agent.py

진행 단계 (로그 예시):

=== 배포 시작 (10~15분 소요) ===
Identified the following requirements: {'cloudpickle': '...', 'pydantic': '...', ...}
Using bucket <staging-bucket>
Wrote to gs://<staging-bucket>/agent_engine/agent_engine.pkl
Writing to gs://<staging-bucket>/agent_engine/requirements.txt
Creating in-memory tarfile of extra_packages
Writing to gs://<staging-bucket>/agent_engine/dependencies.tar.gz
Bidi stream API mode is not supported yet in Vertex SDK ... Skipping method bidi_stream_query.
Creating AgentEngine
Create AgentEngine backing LRO: projects/<NUM>/locations/.../reasoningEngines/<ID>/operations/<OP>
...
AgentEngine created. Resource name: projects/<NUM>/locations/.../reasoningEngines/<ID>

⚠️ “Bidi stream API mode is not supported yet” 메시지는 정상입니다 — Vertex SDK 가 아직 양방향 스트리밍을 미지원해서 자동 skip 합니다.


5. 노출 메서드 검증

ENGINE_ID="<NEW_ID>"
PROJECT="<YOUR_PROJECT>"
LOCATION="us-west1"

curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${LOCATION}/reasoningEngines/${ENGINE_ID}" \
  | python -c "import sys, json; d=json.load(sys.stdin); methods=d.get('spec',{}).get('classMethods', d.get('classMethods', [])); print(f'총 {len(methods)}개:'); [print(' -', m.get('name','?')) for m in methods]"

정상 출력 (총 13개):

총 13개:
 - get_session
 - list_sessions
 - create_session
 - delete_session
 - async_get_session
 - async_list_sessions
 - async_create_session
 - async_delete_session
 - async_add_session_to_memory
 - async_search_memory
 - stream_query
 - streaming_agent_run_with_events     ← ★ 이게 있으면 GE 호환 OK ★
 - async_stream_query

streaming_agent_run_with_events 가 없다면 SDK 버전 / AdkApp wrap 누락 의심.


6. GE 에 등록

  1. Gemini Enterprise → 해당 App / Agent 페이지
  2. (기존 등록이 있으면) Unregister / Delete agent
  3. Add agent → “ADK” / “Agent Engine” 모드 선택
  4. resource_name 입력:
    projects/<PROJECT_NUMBER>/locations/<LOCATION>/reasoningEngines/<NEW_ID>
    
  5. 저장 후 대화 테스트

A2A 모드와 ADK 모드는 다른 API 스펙입니다. 본 manual deploy 산출물은 ADK 모드 전용 — A2A 로 노출하려면 A2A 모드 비교 챕터 참조.


7. 업데이트와 정리

7.1 같은 에이전트를 업데이트 (코드 / 도구 / instruction 수정)

remote = agent_engines.update(
    resource_name="projects/<PROJECT_NUMBER>/locations/<LOCATION>/reasoningEngines/<ID>",
    agent_engine=app,
    requirements=[...],
)

장점: GE 재등록 불필요 — 같은 ID 유지.

7.2 좀비 / 구버전 reasoning engine 삭제

Studio v1, v2 등 더 이상 안 쓰는 reasoning engine 은 비용 발생을 피하기 위해 삭제:

# GE 등록 먼저 제거한 후
gcloud beta ai reasoning-engines delete <ZOMBIE_ID> \
  --location=<LOCATION> --project=<YOUR_PROJECT>

7.3 비용 관리

Reasoning Engine 은 호출이 없어도 컨테이너 idle 비용이 발생합니다. PoC 가 끝나면 반드시 삭제하거나 Cloud Scheduler 로 영업시간 외 강제 종료 / 자동 cleanup 스크립트 운용을 권장합니다.