Google ADK 에이전트 배포하기

Google ADK 에이전트 배포하기

deployments-wrap-sdk 패키지를 사용해 Google Agent Development Kit(ADK) 에이전트를 LangSmith Agent Server에 배포해요.

이 가이드는 deployments-wrap-sdk 패키지를 사용해 Google Agent Development Kit (ADK) 에이전트를 LangSmith Agent Server에 배포하는 방법을 보여줘요.

deployments-wrap-sdk는 구성된 ADK Runner를 LangGraph 호환 그래프로 바꿔주는 얇은 래퍼를 제공해서, Functional API 글루 코드를 직접 작성하지 않고도 ADK 에이전트를 배포할 수 있게 해요. 래퍼는:

  • ADK 세션을 Agent Server의 체크포인트 영속성으로 연결해서, 세션 상태가 재시작 후에도 유지되고 런 사이에 이어지도록 해요.
  • ADK 토큰 이벤트를 LangGraph의 스트리밍 파이프라인을 통해 전달해서, 부분 토큰이 stream_mode="messages"LangSmith Studio에 나타나게 해요.
  • LANGSMITH_TRACING이 설정돼 있으면 ADK에 대한 LangSmith 추적을 자동으로 활성화해요.

출처: 문서

본문

사전 요구 사항

설치

google-adk 엑스트라로 패키지를 설치하세요. 이 엑스트라는 래퍼에 필요한 google-adk와 기타 의존성을 끌어와요:

pip install "deployments-wrap-sdk[google-adk]"

PyPI 배포 이름은 deployments-wrap-sdk이지만, Python import 경로는 saf_sdk예요. 둘 다 같은 패키지를 가리켜요.

빠른 시작

이 최소 예시는 입력을 그대로 응답으로 반환하고 모델 API 키가 필요 없는 에이전트를 만들어요. 에이전트가 LLM 호출을 건너뛰므로, 실제 모델을 연결하기 전에 배포가 올바르게 작동하는지 확인할 수 있어요.

agent.py 생성:

from google.adk.agents import Agent
from google.adk.models.llm_response import LlmResponse
from google.adk.runners import Runner
from google.genai.types import Content, Part
from saf_sdk.adk import LangsmithSessionService, wrap


def echo_callback(callback_context, llm_request):
    """Return the user's message instead of calling a real model."""
    user_text = ""
    if callback_context.user_content and callback_context.user_content.parts:
        for part in callback_context.user_content.parts:
            if part.text:
                user_text += part.text
    return LlmResponse(
        content=Content(role="model", parts=[Part(text=f"echo: {user_text}")])
    )


agent = wrap(
    Runner(
        agent=Agent(
            name="echo_agent",
            model="gemini-2.5-flash",
            instruction="Echo the user message.",
            before_model_callback=echo_callback,
        ),
        app_name="adk_echo",
        session_service=LangsmithSessionService(),
    )
)

두 가지가 필수예요:

  1. LangsmithSessionService() 러너의 session_service로 전달하세요. 잊으면 wrap()TypeError를 일으켜요. Agent Server는 이 훅이 있어야 체크포인터를 통해 ADK 세션 상태를 로드하고 저장해요.
  2. 래핑된 agent 모듈 레벨 변수로 내보내세요. Agent Server는 그래프를 서빙할 때 이 심볼을 import해요.

실제 에이전트의 경우 before_model_callback을 제거하고 모델을 직접 구성하세요. 예를 들어 GOOGLE_API_KEY를 설정한 채 model="gemini-2.5-flash"로 Gemini를 사용하거나, ADK의 LiteLLM 어댑터(google.adk.models.lite_llm.LiteLlm, google-adk[extensions]로 사용 가능)를 통해 Claude/OpenAI를 사용하세요.

기능 및 제한 사항

wrap()은 ADK 런타임의 정의된 하위 집합을 Agent Server로 연결해요. 기존 ADK 에이전트를 이식하기 전에 아래 경계를 검토하세요. 일부 ADK 기능은 변경 없이 통과되는 반면, 다른 기능은 의도적으로 지원되지 않기 때문이에요.

지원됨

  • 에이전트 프리미티브: Agent, SequentialAgent, ParallelAgent, 그리고 sub_agents 파라미터를 통한 중첩 하위 에이전트 위임을 포함해요.
  • 도구: Python 함수 도구와 LongRunningFunctionTool.
  • 모델: Gemini 모델 직접, 그리고 ADK의 LiteLLM 어댑터(google.adk.models.lite_llm.LiteLlm, google-adk[extensions]로 사용 가능)가 지원하는 모든 모델. 배포에 프로바이더 API 키를 설정하세요.
  • 토큰 스트리밍: ADK 부분 이벤트가 LangGraph의 async callback manager를 통해 전달되어, 토큰 청크가 stream_mode="messages"를 소비하는 클라이언트와 Studio 채팅 뷰에 도달해요.
  • 구조화된 출력: output_schemaoutput_key로 구성된 에이전트는 messages에 더해 그래프의 응답에 타입화된 값을 노출해요.
  • 세션 영속성: LangsmithSessionService는 ADK 세션 상태를 배포의 체크포인트 스토어에 저장해요. 상태는 재시작 후에도 유지되고 같은 스레드의 각 후속 턴에서 로드돼요.
  • 추적: LANGSMITH_TRACING=true일 때 래퍼가 configure_google_adk()를 자동으로 호출해요 (추적 활성화 참고).
  • 인증: Agent Server 인증이 활성화돼 있으면 인증된 사용자 id가 ADK의 user_id가 돼요. 그렇지 않으면 사용자 id는 "anonymous"예요.

지원되지 않음

  • 멀티모달 입력: 래퍼는 messages[-1].content만 단일 텍스트 부분으로 전달해요. 인바운드 이미지, 파일, 오디오 또는 인라인 바이너리 블록은 ADK 러너에 전달되지 않아요.
  • 턴당 여러 개의 새 메시지: messages의 마지막 항목만 새 사용자 메시지로 취급돼요. 대화 기록은 LangGraph 메시지 목록이 아니라 ADK 세션 상태에서 재구성돼요.
  • 양방향 / 라이브 스트리밍: 래퍼는 RunConfig(streaming_mode=StreamingMode.SSE)를 하드코딩해요. 오디오나 음성 에이전트용 Runner.run_live()와 양방향 스트리밍 모드는 호출되지 않으므로, 라이브 오디오 및 음성 에이전트는 wrap()으로 배포할 수 없어요.
  • 비텍스트 출력 부분: ADK 이벤트에서 part.text 값만 수집돼요. 에이전트가 만든 인라인 이미지, 오디오 또는 파일은 그래프의 messages 출력에 표면화되지 않아요.
  • 메시지로 나타나는 중간 이벤트: 응답은 연결된 텍스트를 포함하는 단일 AIMessage로 방출돼요. 도구 호출, 도구 결과, 중간 하위 에이전트 턴은 그래프의 messages 필드에 별도 항목으로 노출되지 않아요. 대신 LangSmith 트레이스에서 검사하세요.
  • 대체 ADK 세션 서비스: runner.session_serviceLangsmithSessionService여야 해요. ADK의 InMemorySessionService, DatabaseSessionService, VertexAiSessionService는 세션 상태가 LangGraph 체크포인트에 보관되므로 TypeError로 거부돼요.
  • 네이티브 LangGraph 인터럽트: 래퍼는 LangGraph의 interrupt 또는 Command(resume=...) 메커니즘을 노출하지 않아요. LongRunningFunctionTool을 기반으로 한 인간-인-더-루프 흐름은 ADK 자체 패턴을 따라요: 도구가 pending_approval 같은 상태를 반환하고, 에이전트가 응답하며, 후속 턴이 보류 중인 호출을 해결해요.

프로젝트 구조

배포 가능한 프로젝트는 세 파일이 필요해요:

my-adk-agent/
├── agent.py              # exports the wrapped agent
├── langgraph.json        # Agent Server config
└── pyproject.toml        # Python dependencies

langgraph.json은 Agent Server를 내보낸 심볼로 안내해요:

{
  "$schema": "https://langgra.ph/schema.json",
  "dependencies": ["."],
  "graphs": {
    "adk_echo": "./agent.py:agent"
  },
  "env": ".env"
}

pyproject.toml은 의존성을 선언해요:

[project]
name = "my-adk-agent"
version = "0.0.1"
requires-python = ">=3.11"
dependencies = [
    "deployments-wrap-sdk[google-adk]>=0.0.1",
]

의존성 설치

pip install -e .

로컬에서 실행

LangGraph CLI로 로컬 Agent Server를 시작하세요:

langgraph dev

이것은 http://127.0.0.1:2024에서 에이전트를 서빙하고 LangSmith Studio를 열어 에이전트와 채팅할 수 있게 해요. curl로 직접 요청을 보내세요:

# Create a thread
THREAD=$(curl -s -X POST http://127.0.0.1:2024/threads \
  -H "Content-Type: application/json" -d '{}' | python -c "import sys, json; print(json.load(sys.stdin)['thread_id'])")

# Run the agent and wait for the final response
curl -s -X POST "http://127.0.0.1:2024/threads/$THREAD/runs/wait" \
  -H "Content-Type: application/json" \
  -d '{
    "assistant_id": "adk_echo",
    "input": {"messages": [{"type": "human", "content": "Hello"}]}
  }'

LangSmith에 배포

에이전트가 로컬에서 실행되면 langgraph deploy로 LangSmith에 배포하세요:

langgraph deploy --name my-adk-agent

환경 구성, 배포 유형, 리비전 관리에 대해서는 클라우드에 배포를 참고하세요. 셀프 호스팅 설정은 셀프 호스팅 배포를 참고하세요.

추적 활성화

wrap()은 LangSmith 추적이 활성화될 때마다 langsmith.integrations.google_adk.configure_google_adk()를 자동으로 호출하므로, 배포에 환경 변수를 설정하기만 하면 돼요:

LANGSMITH_API_KEY=your-langsmith-api-key
LANGSMITH_TRACING=true
LANGSMITH_PROJECT=my-adk-agent     # optional
GOOGLE_API_KEY=your-google-api-key

트레이스LangSmith UI에서 에이전트 호출, 도구 호출, LLM 상호작용을 보여줘요. 기본 추적 통합에 대한 자세한 내용은 Google ADK 애플리케이션 추적을 참고하세요.

API 참조

wrap(runner)

구성된 google.adk.runners.Runner를 래핑하고 모듈에서 내보내 Agent Server가 서빙할 수 있는 LangGraph Pregel 그래프를 반환해요.

인수 유형 설명
runner google.adk.runners.Runner 구성된 ADK Runner. session_service반드시 LangsmithSessionService여야 해요.

반환값: 이름이 runner.app_namePregel 그래프.

발생: runner.session_serviceLangsmithSessionService가 아니면 TypeError.

runner.agentoutput_key를 정의하면 그 키의 값도 messages에 더해 그래프의 출력에 노출돼요. 이것이 ADK 구조화된 출력 에이전트(output_schema=..., output_key=...)가 Studio와 /runs/wait 응답에서 작동하게 하는 이유예요.

LangsmithSessionService

Agent Server의 체크포인트 스토어를 기반으로 하는 google.adk.sessions.BaseSessionService 구현. 래퍼가 세션 수명 주기를 자동으로 관리해요. 스레드의 첫 턴에서 세션을 만들고, 후속 턴에 체크포인트에서 로드하며, 런이 완료되면 업데이트된 세션을 다시 써요.

Runner마다 새 인스턴스를 사용하세요:

session_service = LangsmithSessionService()

메서드를 직접 호출할 필요는 없어야 해요. wrap()이 ADK의 일반 세션 수명 주기를 통해 이것들을 구동해요.

ADKInput

래핑된 에이전트의 기본 입력 스키마.

필드 유형 설명
messages list[AnyMessage] (필수) 대화 메시지; 래퍼는 messages[-1].content를 새 사용자 메시지로 ADK 러너에 보내요.
state_delta dict[str, Any] | None (선택) 이 턴의 ADK 세션 상태를 변경하기 위해 runner.run_async(state_delta=...)로 전달됨.

ADKOutput

래핑된 에이전트의 기본 출력 스키마.

필드 유형 설명
messages list[AnyMessage] 에이전트의 응답 메시지; LangGraph의 add_messages reducer를 통해 스레드에 추가됨.

messages를 (평범한 dict가 아니라) 타입화된 필드로 노출하는 것이 Studio가 그래프를 채팅 호환으로 감지하고 채팅 모드 토글을 활성화하게 하는 이유예요.

작동 방식

런이 도착하면:

  1. 래핑된 그래프가 런 구성에서 thread_id를 읽고 이를 ADK session_id로 사용해요. 인증이 활성화되면 인증된 사용자의 id가 ADK user_id가 되고, 그렇지 않으면 사용자 id는 "anonymous"예요.
  2. 래퍼가 (있으면) 이전 세션을 LangGraph 체크포인트에서 LangsmithSessionService로 로드한 다음, 러너가 최신 메시지를 처리하게 해요.
  3. 러너가 ADK 이벤트를 방출해요. 래퍼는 부분 토큰 이벤트를 LangGraph의 async callback manager를 통해 전달해 stream_mode="messages"로 스트리밍되게 하고, 응답 메시지용 최종 텍스트를 수집해요.
  4. 런이 끝나면 래퍼가 ADK 세션을 직렬화하고 entrypoint.final(save=...)를 통해 체크포인트에 저장해요. 같은 스레드의 다음 런은 그 상태에서 이어져요.

즉, 배포가 표준 Agent Server 기능(내구성 있는 런, 스트리밍, 멀티 스레드 영속성, 추적)을 얻는 동안 ADK 자체의 세션/상태 의미론이 처음부터 끝까지 보존돼요.

더 알아보기 (Learn more)