Cymbal Transit: LangChain4J 및 MCP 도구 상자 Java SDK를 사용하는 멀티 에이전트 시스템

1. 개요

오늘날의 여행객은 대화형 경험을 기대합니다. 복잡한 UI 필터를 탐색하는 대신 '오전 9시 버스를 타고 보스턴에 갈 때 강아지를 데려갈 수 있나요?'라고 묻고 싶어 합니다. 이를 위해서는 비정형 데이터 (PDF 정책)와 정형 데이터 (SQL 일정)를 모두 추론할 수 있는 에이전트가 필요합니다.

이 실습에서는 다음을 사용하여 Cymbal Transit Agent를 빌드합니다.

  • LangChain4j: AI 조정을 위한 최고의 Java 프레임워크입니다.
  • AlloyDB: 고성능 PostgreSQL 호환 데이터베이스입니다.
  • MCP 도구 상자 Java SDK: Java 에이전트를 외부 도구 및 데이터 소스에 연결하는 표준화된 방법입니다.

빌드할 항목

e68388d533c9997e.png

다음으로 구성된 Java Spring Boot 애플리케이션인 Cymbal Bus Agent

  1. AlloyDB 데이터베이스 및 에이전트와의 도구 오케스트레이션을 위한 MCP 도구 상자 Java SDK
  2. 도구 상자 배포 및 애플리케이션 (에이전트 배포)용 Cloud Run
  3. Java 17을 사용하는 Spring Boot 애플리케이션의 에이전트 및 LLM 프레임워크용 LangChain4J 라이브러리

학습할 내용

  • LangChain4J를 사용하여 데이터베이스용 MCP 도구 상자 Java SDK를 사용하여 오케스트레이션된 특수 에이전트 및 하위 에이전트를 만드는 방법
  • 데이터 및 AI를 위해 AlloyDB를 설정하고 사용하는 방법
  • MCP 도구 상자를 사용하여 에이전트를 AlloyDB 데이터 도구에 연결하는 방법
  • Cloud Run을 사용하여 솔루션을 배포하거나 로컬로 실행하는 방법

아키텍처

  1. PostgreSQL용 AlloyDB: 경로, 정책, 예약 기록을 보유하는 고성능 운영 데이터베이스 역할을 합니다. 벡터 검색 및 검색을 지원합니다.
  2. 데이터베이스용 MCP 도구 상자 Java SDK: '오케스트레이션 마에스트로' 역할을 하며, 에이전트가 호출할 수 있는 실행 가능한 도구로 AlloyDB 데이터를 노출합니다.

MCP 도구 상자 Java SDK를 사용하면 엔터프라이즈급 애플리케이션을 위해 데이터베이스 도구로 에이전트를 손쉽게 오케스트레이션할 수 있습니다.

  1. LangChain4J: 대규모 언어 모델 (LLM)을 Java 애플리케이션에 더 쉽게 통합할 수 있도록 지원하는 오픈소스 Java 라이브러리입니다. 챗봇, 에이전트, 검색 증강 생성 (RAG) 시스템 등 AI 기반 애플리케이션을 빌드하기 위한 도구와 추상화를 제공합니다.
  2. Cloud Run: 모든 언어, 라이브러리, 바이너리로 앱 또는 웹사이트를 빠르게 빌드하고 배포할 수 있는 완전 관리형 서버리스 플랫폼입니다. 원하는 언어, 프레임워크, 라이브러리를 사용하여 코드를 작성하고 컨테이너로 패키징하고 'gcloud run deploy'를 실행하면 앱이 활성화됩니다. 프로덕션 환경에서 실행하는 데 필요한 모든 것이 제공됩니다. 컨테이너 빌드는 전적으로 선택사항입니다. Go, Node.js, Python, Java, .NET Core, Ruby를 사용하는 경우 사용 중인 언어의 권장사항에 따라 컨테이너를 빌드하는 소스 기반 배포 옵션을 사용할 수 있습니다.

요구사항

  • 브라우저(Chrome, Firefox 등)
  • 결제가 사용 설정된 Google Cloud 프로젝트.
  • SQL 및 Java에 대한 기본 지식

2. 시작하기 전에

프로젝트 만들기

  1. Google Cloud 콘솔의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.
  2. Cloud 프로젝트에 결제가 사용 설정되어 있어야 하므로 프로젝트에 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요.
  1. Google Cloud에서 실행되는 명령줄 환경인 Cloud Shell을 사용합니다. Google Cloud 콘솔 상단에서 Cloud Shell 활성화를 클릭합니다.

Cloud Shell 활성화 버튼 이미지

  1. Cloud Shell에 연결되면 다음 명령어를 사용하여 이미 인증되었고 프로젝트가 프로젝트 ID로 설정되었는지 확인합니다.
gcloud auth list
  1. Cloud Shell에서 다음 명령어를 실행하여 gcloud 명령어가 프로젝트를 알고 있는지 확인합니다.
gcloud config list project
  1. 프로젝트가 설정되지 않은 경우 다음 명령어를 사용하여 설정합니다.
gcloud config set project <YOUR_PROJECT_ID>
  1. 필요한 API 사용 설정: 링크를 따라 API를 사용 설정합니다.

또는 gcloud 명령어를 사용할 수 있습니다. gcloud 명령어 및 사용법은 문서를 참조하세요.

주의사항 및 문제 해결

'유령 프로젝트' 증후군

gcloud config set project를 실행했지만 Console UI에서 다른 프로젝트를 보고 있습니다. 왼쪽 상단의 드롭다운에서 프로젝트 ID를 확인하세요.

결제 바리케이드

프로젝트를 사용 설정했지만 결제 계정을 잊었습니다. AlloyDB는 고성능 엔진이므로 '연료 탱크' (결제)가 비어 있으면 시작되지 않습니다.

API 전파 지연

'API 사용 설정'을 클릭했지만 명령줄에 여전히 Service Not Enabled라고 표시됩니다. 60초 동안 기다립니다. 클라우드에서 뉴런을 활성화하는 데 시간이 걸립니다.

할당량 Quags

새 체험판 계정을 사용하는 경우 AlloyDB 인스턴스의 리전별 할당량에 도달할 수 있습니다. us-central1가 실패하면 us-east1를 시도하세요.

'숨겨진' 서비스 에이전트

AlloyDB 서비스 에이전트에는 aiplatform.user 역할이 자동으로 부여되지 않는 경우가 있습니다. 나중에 SQL 쿼리가 Gemini와 통신할 수 없는 경우 일반적으로 이 문제가 원인입니다.

3. 데이터베이스 설정

애플리케이션의 핵심은 PostgreSQL용 AlloyDB입니다. 강력한 벡터 기능을 활용하고 열 기반 엔진을 통합하여 50,000개 이상의 SCM 레코드의 삽입을 생성했습니다. 이를 통해 거의 실시간 벡터 분석이 가능해져 상담사가 대규모 데이터 세트에서 몇 밀리초 만에 인벤토리 이상치 또는 물류 위험을 식별할 수 있습니다.

이 실습에서는 AlloyDB를 테스트 데이터의 데이터베이스로 사용합니다. 클러스터를 사용하여 데이터베이스, 로그와 같은 모든 리소스를 보유합니다. 각 클러스터에는 데이터에 대한 액세스 포인트를 제공하는 기본 인스턴스가 있습니다. 테이블에는 실제 데이터가 저장됩니다.

테스트 데이터 세트가 로드될 AlloyDB 클러스터, 인스턴스, 테이블을 만들어 보겠습니다.

  1. 아래 버튼을 클릭하거나 Google Cloud 콘솔 사용자가 로그인한 브라우저에 링크를 복사합니다.

또는 결제 계정을 사용한 프로젝트에서 Cloud Shell 터미널로 이동하여 GitHub 저장소 를 클론하고 아래 명령어를 사용하여 프로젝트로 이동합니다.

git clone https://github.com/AbiramiSukumaran/easy-alloydb-setup

cd easy-alloydb-setup
  1. 이 단계를 완료하면 저장소가 로컬 Cloud Shell 편집기에 클론되고 프로젝트 폴더에서 아래 명령어를 실행할 수 있습니다 (프로젝트 디렉터리에 있는지 확인하는 것이 중요함).
sh run.sh
  1. 이제 UI를 사용하여 터미널에서 링크를 클릭하거나 터미널에서 '웹에서 미리보기' 링크를 클릭합니다.
  2. 시작하려면 프로젝트 ID, 클러스터, 인스턴스 이름을 입력하세요.
  3. 로그가 스크롤되는 동안 커피를 마시세요. 여기에서 백그라운드에서 어떻게 작동하는지 자세히 알아볼 수 있습니다.

주의사항 및 문제 해결

'인내심' 문제

데이터베이스 클러스터는 무거운 인프라입니다. 페이지를 새로고침하거나 '멈춘 것 같아' Cloud Shell 세션을 종료하면 부분적으로 프로비저닝되어 수동 개입 없이는 삭제할 수 없는 '고스트' 인스턴스가 생성될 수 있습니다.

지역 불일치

us-central1에서 API를 사용 설정했지만 asia-south1에서 클러스터를 프로비저닝하려고 하면 할당량 문제나 서비스 계정 권한 지연이 발생할 수 있습니다. 실습 전체에서 하나의 리전을 사용하세요.

좀비 클러스터

이전에 클러스터에 동일한 이름을 사용했고 삭제하지 않은 경우 스크립트에서 클러스터 이름이 이미 있다고 표시할 수 있습니다. 클러스터 이름은 프로젝트 내에서 고유해야 합니다.

Cloud Shell 시간 제한

커피를 마시는 데 30분이 걸리면 Cloud Shell이 절전 모드로 전환되어 sh run.sh 프로세스가 연결 해제될 수 있습니다. 탭을 활성 상태로 유지하세요.

4. 스키마 프로비저닝

AlloyDB 클러스터와 인스턴스가 실행되면 AlloyDB Studio SQL 편집기로 이동하여 AI 확장 프로그램을 사용 설정하고 스키마를 프로비저닝합니다.

1e3ac974b18a8113.png

인스턴스 생성이 완료될 때까지 기다려야 할 수 있습니다. 완료되면 클러스터를 만들 때 만든 사용자 인증 정보를 사용하여 AlloyDB에 로그인합니다. PostgreSQL에 인증하려면 다음 데이터를 사용하세요.

  • 사용자 이름 : 'postgres'
  • 데이터베이스 : 'postgres'
  • 비밀번호 : 'alloydb' (또는 생성 시 설정한 비밀번호)

AlloyDB Studio에 인증되면 편집기에 SQL 명령어가 입력됩니다. 마지막 창 오른쪽에 있는 더하기 기호를 사용하여 여러 편집기 창을 추가할 수 있습니다.

28cb9a8b6aa0789f.png

필요에 따라 실행, 형식 지정, 지우기 옵션을 사용하여 편집기 창에 AlloyDB 명령어를 입력합니다.

확장 프로그램 사용 설정

이 앱을 빌드하기 위해 확장 프로그램 pgvectorgoogle_ml_integration를 사용합니다. pgvector 확장 프로그램을 사용하면 벡터 임베딩을 저장하고 검색할 수 있습니다. google_ml_integration 확장 프로그램은 SQL에서 예측을 수행하기 위해 Vertex AI 예측 엔드포인트에 액세스하는 데 사용하는 함수를 제공합니다. 다음 DDL을 실행하여 이러한 확장 프로그램을 사용 설정합니다.

CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;

권한 부여

아래 문을 실행하여 'embedding' 함수에 대한 실행 권한을 부여합니다.

GRANT EXECUTE ON FUNCTION embedding TO postgres;

AlloyDB 서비스 계정에 Vertex AI 사용자 역할 부여

Google Cloud IAM 콘솔에서 AlloyDB 서비스 계정 (service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com)에 'Vertex AI 사용자' 역할에 대한 액세스 권한을 부여합니다. PROJECT_NUMBER에는 프로젝트 번호가 표시됩니다.

또는 Cloud Shell 터미널에서 아래 명령어를 실행할 수 있습니다.

PROJECT_ID=$(gcloud config get-value project)


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"

테이블 만들기

AlloyDB Studio에서 아래 DDL 문을 사용하여 테이블을 만들 수 있습니다.

DROP TABLE IF EXISTS transit_policies;
DROP TABLE IF EXISTS bus_schedules;
DROP TABLE IF EXISTS bookings;

-- Table 1: Transit Policies (Unstructured Data for RAG)
CREATE TABLE transit_policies (
    policy_id SERIAL PRIMARY KEY,
    category VARCHAR(50),
    policy_text TEXT,
    policy_embedding vector(768) 
);

-- Table 2: Intercity Bus Schedules (Structured Data)
CREATE TABLE bus_schedules (
    trip_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    origin_city VARCHAR(100),
    destination_city VARCHAR(100),
    departure_time TIMESTAMP,
    arrival_time TIMESTAMP,
    available_seats INT DEFAULT 50,
    ticket_price DECIMAL(6,2)
);

-- Table 3: Booking Ledger (Transactional Action Data)
CREATE TABLE bookings (
    booking_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    trip_id UUID REFERENCES bus_schedules(trip_id),
    passenger_id VARCHAR(100),
    status VARCHAR(20) DEFAULT 'CONFIRMED',
    booking_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

policy_embedding 열은 일부 텍스트 필드의 벡터 값을 저장할 수 있습니다.

데이터 수집

아래 SQL 문 집합을 실행하여 각 테이블에 레코드를 일괄 삽입합니다.

  1. AlloyDB에서 기본적으로 구조화되지 않은 정책 삽입 및 실제 임베딩 생성
-- 1. Insert Unstructured Policies and GENERATE REAL EMBEDDINGS natively in AlloyDB

INSERT INTO transit_policies (category, policy_text, policy_embedding) 
VALUES 
('Pets', 'Service animals are always welcome. Small pets (under 25 lbs) are allowed in secure carriers for a $25 fee. Large dogs are not permitted on standard coaches.', embedding('text-embedding-005', 'Service animals are always welcome. Small pets (under 25 lbs) are allowed in secure carriers for a $25 fee. Large dogs are not permitted on standard coaches.')),
('Luggage', 'Each passenger is allowed one carry-on (up to 15 lbs) and two stowed bags (up to 50 lbs each) free of charge. Additional bags cost $15 each.', embedding('text-embedding-005', 'Each passenger is allowed one carry-on (up to 15 lbs) and two stowed bags (up to 50 lbs each) free of charge. Additional bags cost $15 each.')),
('Refunds', 'Tickets are fully refundable up to 24 hours before departure. Within 24 hours, tickets can be exchanged for travel credit only.', embedding('text-embedding-005', 'Tickets are fully refundable up to 24 hours before departure. Within 24 hours, tickets can be exchanged for travel credit only.'));
  1. generate_series를 사용하여 7일 동안의 현실적인 일정 200개 이상 생성
-- 2. Generate 200+ Realistic Schedules for the Next 7 Days using generate_series

INSERT INTO bus_schedules (origin_city, destination_city, departure_time, arrival_time, ticket_price, available_seats)
SELECT 
    origin,
    destination,
    -- Generate departures every 4 hours starting from tomorrow
    (CURRENT_DATE + 1) + (interval '4 hours' * seq) AS dep_time,
    (CURRENT_DATE + 1) + (interval '4 hours' * seq) + interval '4.5 hours' AS arr_time,
    ROUND((RANDOM() * 30 + 25)::numeric, 2) AS price, -- Random price between $25 and $55
    FLOOR(RANDOM() * 50 + 1) AS seats -- Random seats between 1 and 50
FROM 
    (VALUES 
        ('New York', 'Boston'), ('Boston', 'New York'),
        ('Philadelphia', 'Washington DC'), ('Washington DC', 'Philadelphia'),
        ('Seattle', 'Portland'), ('Portland', 'Seattle')
    ) AS routes(origin, destination)
CROSS JOIN generate_series(1, 40) AS seq; -- 6 routes * 40 time slots = 240 distinct trips ingested!

임베딩 생성

임베딩은 'embedding('text-embedding-005', '<<policytext>>')' 함수를 사용하여 transit_policies 테이블의 삽입 문에 자동으로 포함됩니다.

주의사항 및 문제 해결

'비밀번호 기억 상실증' 루프

'원클릭' 설정을 사용했고 비밀번호가 기억나지 않는 경우 콘솔의 인스턴스 기본 정보 페이지로 이동하여 '수정'을 클릭하여 postgres 비밀번호를 재설정합니다.

'확장 프로그램을 찾을 수 없음' 오류

CREATE EXTENSION가 실패하는 경우 초기 프로비저닝으로 인해 인스턴스가 아직 '유지보수' 또는 '업데이트' 상태인 경우가 많습니다. 인스턴스 생성 단계가 완료되었는지 확인하고 필요한 경우 몇 초 정도 기다립니다.

IAM 전파 문제

gcloud IAM 명령어를 실행했지만 SQL CALL이 여전히 권한 오류와 함께 실패합니다. IAM 변경사항이 Google 백본을 통해 전파되는 데 다소 시간이 걸릴 수 있습니다. 숨을 쉬세요.***중요:

  1. AlloyDB 서비스 계정이 권한 단계에서 사용한 기존 형식과 다를 수 있습니다. AlloyDB 서비스 계정에 Vertex AI 사용자 역할이 있는지 확실하게 확인하려면 Google Cloud 콘솔의 AlloyDB 클러스터 페이지로 이동하세요. 클러스터를 클릭하고 개요 탭에서 서비스 계정이라는 필드를 찾습니다.
    값을 복사한 다음 IAM으로 이동하여 Vertex AI 사용자 역할을 추가합니다.
  2. 또한 '시작하기 전' 섹션에서 'API 사용 설정' 단계를 건너뛴 경우 AlloyDB에서 삽입에 액세스하는 데 문제가 발생합니다.

벡터 차원 불일치

transit_policies 테이블의 policy_embedding 열이 VECTOR(768)로 설정됩니다. 나중에 다른 모델 (예: 1536차원 모델)을 사용하려고 하면 삽입이 폭발합니다. text-embedding-005를 사용하세요.

프로젝트 ID 오타

create_model 호출에서 괄호 « »를 그대로 두거나 프로젝트 ID를 잘못 입력하면 모델 등록이 성공한 것처럼 보이지만 첫 번째 실제 쿼리 중에 실패합니다. 문자열을 다시 확인하세요.

5. 도구 및 도구 상자 설정

데이터베이스용 MCP 도구 상자는 데이터베이스용 오픈소스 MCP 서버입니다. 연결 풀링, 인증, 기타와 같은 복잡한 작업을 처리하여 더 쉽고 빠르고 안전하게 도구를 개발할 수 있습니다. 도구 상자를 사용하면 에이전트가 데이터베이스의 데이터에 액세스할 수 있는 생성형 AI 도구를 빌드할 수 있습니다.

데이터베이스용 모델 컨텍스트 프로토콜 (MCP) 도구 상자를 '컨덕터'로 사용합니다. 에이전트와 AlloyDB 간의 표준화된 미들웨어 역할을 합니다. tools.yaml 구성을 정의하면 툴박스가 복잡한 데이터베이스 작업을 find-bus-schedules and routes 또는 query-schedules for specific routes와 같은 깔끔한 실행 가능한 도구로 자동 노출하고 book-ticket와 같은 자율 작업을 실행합니다. 이렇게 하면 에이전트 로직 내에서 수동 연결 풀링이나 상용구 SQL이 필요하지 않습니다.

Toolbox 서버 설치

Cloud Shell 터미널에서 새 도구 yaml 파일과 툴박스 바이너리를 저장할 폴더를 만듭니다.

mkdir cymbal-bus-toolbox

cd cymbal-bus-toolbox

새 폴더 내에서 다음 명령어 집합을 실행합니다.

# see releases page for other versions
export VERSION=0.27.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox

그런 다음 Cloud Shell 편집기로 이동하여 새 폴더 내에 tools.yaml 파일을 만들고 이 repo 파일의 콘텐츠를 tools.yaml 파일에 복사합니다.

... (Refer to entire file in the repo)

tools:

   find-bus-schedules:
    kind: postgres-sql
    source: alloydb
    description: Find all available bus schedules.
    statement: |
      SELECT CAST(trip_id AS TEXT) trip_id, departure_time, arrival_time, ticket_price, available_seats , origin_city, destination_city 
      FROM bus_schedules;

   query-schedules:
    kind: postgres-sql
    source: alloydb
    description: Find available bus schedules between an origin and destination city.
    parameters:
      - name: origin
        type: string
        description: The departure city name.
      - name: destination
        type: string
        description: The arrival city name.
    statement: |
      SELECT CAST(trip_id AS TEXT) trip_id, departure_time, arrival_time, ticket_price, available_seats 
      FROM bus_schedules 
      WHERE lower(origin_city) = lower($1) 
        AND lower(destination_city) = lower($2) 
        AND available_seats > 0 
      ORDER BY departure_time ASC 
      LIMIT 5;

   book-ticket:
    kind: postgres-sql
    source: alloydb
    description: Books a ticket for a specific trip, decrementing available seats and generating a confirmed booking record.
    parameters:
      - name: trip_id
        type: string
        description: The UUID of the trip schedule to book.
      - name: passenger_name
        type: string
        description: Name or ID of the passenger (Bound securely via backend or AuthToken).
        authServices:
          - name: google_auth
            field: sub
    statement: |
      WITH updated_schedule AS (
          UPDATE bus_schedules 
          SET available_seats = available_seats - 1 
          WHERE trip_id = CAST($1 AS UUID) AND available_seats > 0
          RETURNING trip_id
      )
      INSERT INTO bookings (trip_id, passenger_id)
      SELECT trip_id, $2 
      FROM updated_schedule
      RETURNING CAST(booking_id as TEXT) as booking_id, trip_id, passenger_id, status, booking_time;

   search-policies:
    kind: postgres-sql
    source: alloydb
    description: Semantic search for transit policies regarding luggage, pets, refunds, and general rules.
    parameters:
      - name: search_query
        type: string
        description: The user's question about transit policies to be embedded and searched.
    statement: |
      SELECT category, policy_text 
      FROM transit_policies 
      ORDER BY policy_embedding <=> CAST(embedding('text-embedding-005', $1) AS vector(768))
      LIMIT 2;

참고:

  1. tools.yaml 설정에서 alloydb 소스 구성에 ipType: "private"을 포함해야 합니다.
  2. 인증 서비스 구성의 clientId 매개변수에 MCP 도구 상자 서비스 URL도 포함해야 합니다. 초기 배포 후에만 링크가 제공될 수 있으므로 인증된 도구 사용 사례가 작동하는지 확인하려면 배포 단계를 두 번 실행해야 합니다.
  3. AlloyDB 연결이 비공개로 설정된 경우 아래 옵션을 사용하여 로컬에서 툴박스를 테스트하면 작동하지 않습니다. 로컬에서 테스트하려면 공개로 설정하거나 연결에 프록시를 사용해야 합니다. 하지만 걱정하지 마세요. 이 경우 Cloud Run에 직접 배포한 후 테스트합니다.

로컬 서버에서 tools.yaml 파일을 테스트하려면 다음 단계를 따르세요.

./toolbox --tools-file "tools.yaml"

UI에서 테스트할 수도 있습니다.

./toolbox --ui

다음과 같이 Cloud Run에 배포해 보겠습니다.

Cloud Run 배포

  1. PROJECT_ID 환경 변수를 설정합니다.
export PROJECT_ID="my-project-id"
  1. gcloud CLI를 초기화합니다.
gcloud init
gcloud config set project $PROJECT_ID
  1. 다음 API가 사용 설정되어 있어야 합니다.
gcloud services enable run.googleapis.com \
                       cloudbuild.googleapis.com \
                       artifactregistry.googleapis.com \
                       iam.googleapis.com \
                       secretmanager.googleapis.com
  1. 아직 백엔드 서비스 계정이 없는 경우 다음 단계를 따라 만듭니다.
gcloud iam service-accounts create toolbox-identity
  1. Secret Manager 사용 권한을 부여합니다.
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/secretmanager.secretAccessor
  1. AlloyDB 소스 (roles/alloydb.client 및 roles/serviceusage.serviceUsageConsumer)에 특정한 서비스 계정에 추가 권한 부여
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/alloydb.client


gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/serviceusage.serviceUsageConsumer
  1. tools.yaml을 보안 비밀로 업로드합니다.
gcloud secrets create tools-cymbal-transit --data-file=tools.yaml
  1. 이미 보안 비밀이 있고 보안 비밀 버전을 업데이트하려면 다음을 실행하세요.
gcloud secrets versions add tools-cymbal-transit --data-file=tools.yaml
  1. Cloud Run에 사용할 컨테이너 이미지에 환경 변수를 설정합니다.
export IMAGE=us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest
  1. 다음 명령어를 사용하여 Cloud Run에 도구 상자를 배포합니다.

AlloyDB 인스턴스에서 공개 액세스를 사용 설정한 경우 Cloud Run에 배포하려면 아래 명령어를 따르세요.

gcloud run deploy toolbox-cymbal-transit \
    --image $IMAGE \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools-cymbal-transit:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --allow-unauthenticated

VPC 네트워크를 사용하는 경우 아래 명령어를 사용합니다.

gcloud run deploy toolbox-cymbal-transit \
    --image $IMAGE \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools-cymbal-transit:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --network <<YOUR_NETWORK_NAME>> \
    --subnet <<YOUR_SUBNET_NAME>> \
    --allow-unauthenticated

참고: 배포 후 Cloud Run 서비스 목록 으로 이동하여 해당 서비스의 보안 탭에서 '공개 액세스 허용'이 선택되어 있는지 확인합니다.

6. 에이전트 애플리케이션 설정

이 저장소를 프로젝트에 클론하고 살펴보겠습니다.

GitHub 저장소

이를 클론하려면 Cloud Shell 터미널 (루트 디렉터리 또는 이 프로젝트를 만들려는 위치)에서 다음 명령어를 실행합니다.

git clone https://github.com/googleapis/mcp-toolbox-sdk-java

위 명령어는 실제로 mcp-toolbox-sdk-java 전체를 클론합니다. 샘플 프로젝트만 필요합니다. 따라서 저장소 내에서 프로젝트의 루트 디렉터리로 이동합니다.

cd mcp-toolbox-sdk-java/demo-applications/cymbal-transit
  1. 이렇게 하면 프로젝트가 생성되며 Cloud Shell 편집기에서 이를 확인할 수 있습니다.

a494664032904c77.png

  1. CymbalTransitController.java를 열고 환경 변수를 설정합니다.
  2. GCP_PROJECT_ID
  3. GCP_REGION
  4. GEMINI_MODEL_NAME
  5. MCP_TOOLBOX_URL

또는 (개발 목적으로만) 해당 대체 값 자리표시자를 바꿀 수도 있습니다.

7. 코드 둘러보기

CymbalTransitController는 Cloud Run 서비스의 진입점 역할을 합니다. 대화 흐름을 관리하고 상담사가 사용자의 현재 요청에 액세스할 수 있도록 합니다.

이 구현은 AI 조정, 도구 브리징, 하위 수준 MCP 통신을 분리하는 계층화된 아키텍처를 따릅니다.

1. AI 에이전트 구성 (AgentConfiguration)

이 클래스는 Spring의 @Configuration를 사용하여 AI 구성요소를 부트스트랩합니다. VertexAiGeminiChatModel을 초기화하고 에이전트 인터페이스에 바인딩합니다.

@Bean
ChatLanguageModel geminiChatModel() {
    return VertexAiGeminiChatModel.builder()
        .project(projectId)
        .location(region)
        .modelName(modelName)
        .build();
}

@Bean
TransitAgent transitAgent(ChatLanguageModel chatLanguageModel, TransitAgentTools tools) {
    return AiServices.builder(TransitAgent.class)
        .chatLanguageModel(chatLanguageModel)
        .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(20))
        .tools(tools) 
        .build();
}

중요성: AiServices은 인터페이스를 LLM에 바인딩합니다. MessageWindowChatMemory를 사용하면 에이전트가 단일 세션 내에서 최대 20개의 메시지에 대해 사용자 환경설정 (예: 앞에서 언급한 반려동물 운반장)을 기억합니다.

2. AI 에이전트 인터페이스 (TransitAgent)

@SystemMessage 주석은 '페르소나'와 운영 제약 조건, 특히 라우팅 전략을 정의합니다.

@SystemMessage({
    "You are the Cymbal Transit Concierge.",
    "CRITICAL INSTRUCTION: On your very first interaction, you MUST use the 'findAllSchedules' tool to fetch and memorize the broad bus routes.",
    "ONLY if the user asks a specifically narrowed-down question... should you route to the specific tools like 'querySchedules', 'bookTicket', 'searchPolicies'.",
    "Don't show any asterisks while listing results. Keep it formatted and numbered or bulleted."
})
String chat(@MemoryId String sessionId, @UserMessage String userMessage);

중요도: 이 전략은 지연 시간을 최소화합니다. 광범위한 데이터를 먼저 가져오면 에이전트가 중복된 백엔드 호출 없이 내부 컨텍스트를 사용하여 일반적인 라우팅 질문에 답변할 수 있습니다.

3. 도구 상자 브리지 (TransitAgentTools)

이 서비스는 에이전트의 '손' 역할을 하며 LangChain4j 도구 호출을 실행 로직으로 변환합니다.

@Tool("Fetches the initial, broad dataset of all available bus schedules and routes.")
public String findAllSchedules() {
    return mcpService.findAllSchedules().join();
}


@Tool("Book a ticket for a passenger using a specific trip ID.")
public String bookTicket(String tripId, String passengerName) {
    return mcpService.bookTicket(tripId, passengerName).join();
}

동기 실행: MCP 호출은 비동기식 (CompletableFuture 반환)이지만 LLM은 '사고' 프로세스를 계속하기 전에 결과가 필요합니다. .join()를 사용하여 동기 결과를 에이전트에게 다시 제공합니다.

4. MCP 도구 상자 서비스 (McpToolboxService)

이는 MCP 도구 상자 Java SDK를 사용하여 AlloyDB 백엔드와 상호작용하는 통신 레이어입니다.

// Identity Management: Fetching OIDC ID Token for Auth
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
this.idToken = ((IdTokenProvider) credentials)
    .idTokenWithAudience(targetUrl, Collections.emptyList())
    .getTokenValue();

// Dynamic Invocation: Executing a tool by name
public CompletableFuture<String> findAllSchedules() {
    return mcpClient.invokeTool("find-bus-schedules", Collections.emptyMap()).thenApply(result -> {
        return result.content().stream()
            .map(content -> content.text())
            .collect(Collectors.joining(", ", "[", "]"));
    });
}

중요성: McpToolboxClient는 JSON-RPC 통신의 무거운 작업을 처리합니다. bookTicket 메서드는 복잡한 매개변수를 동적으로 바인딩하는 SDK의 기능을 구체적으로 보여줍니다.

5. REST 컨트롤러 (TransitAgentController)

LangChain4j가 상태와 로직을 관리하므로 최종 엔드포인트가 크게 간소화됩니다.

@PostMapping("/chat")
public ResponseEntity<String> handleUserChat(@RequestBody String userMessage, HttpSession session) {
    String sessionId = session.getId();
    String agentResponse = transitAgent.chat(sessionId, userMessage);
    return ResponseEntity.ok(agentResponse);
}

중요성: HttpSession ID를 @MemoryId에 매핑하면 컨트롤러 코드를 깔끔하고 읽기 쉬운 상태로 유지하면서 서로 다른 사용자의 여행 계획이 혼동되지 않습니다.

8. MCP 도구 상자: 중요성 및 Java SDK

MCP란 무엇인가요?

모델 컨텍스트 프로토콜 (MCP)은 AI를 위한 범용 번역기라고 생각하면 됩니다. AI 모델이 외부 도구 및 데이터 세트에 연결하는 방식을 표준화하기 위해 만들어진 MCP는 맞춤형의 단편적인 통합 스크립트를 안전한 범용 프로토콜로 대체합니다. 에이전트가 트랜잭션 SQL 쿼리를 실행해야 하든, 수천 개의 정책 문서를 검색해야 하든, REST API를 트리거해야 하든 MCP는 단일 통합 인터페이스를 제공합니다.

데이터베이스용 MCP 도구 상자

엔지니어링팀은 단순한 챗봇을 넘어 미션 크리티컬 데이터베이스와 직접 상호작용하는 에이전트 시스템을 구축하고 있습니다. 하지만 이러한 엔터프라이즈 에이전트를 구축하려면 맞춤 접착 코드, 취약한 API, 복잡한 데이터베이스 로직의 통합 장벽에 부딪히는 경우가 많습니다.

하드코딩된 병목 현상을 안전하고 통합된 컨트롤 플레인으로 대체하기 위해 데이터베이스용 모델 컨텍스트 프로토콜 (MCP) 도구 상자의 Java SDK를 발표하게 되어 기쁩니다. 이번 출시를 통해 세계에서 가장 널리 채택된 엔터프라이즈 생태계에 최고 수준의 타입 안전 에이전트 오케스트레이션이 제공됩니다. Java의 성숙한 아키텍처는 이러한 엄격한 요구사항을 위해 특별히 설계되었으며, 프로덕션에서 업무상 중요한 AI 에이전트를 안전하게 확장하는 데 필요한 높은 동시성, 엄격한 트랜잭션 무결성, 강력한 상태 관리를 제공합니다.

Java SDK를 사용하는 이유

MCP Toolbox Java SDK를 사용하면 Java 개발자가 다음 작업을 할 수 있습니다.

  1. 도구 사용: MCP 서버 (예: AlloyDB용 MCP 도구 상자)에 연결하고 해당 기능을 LangChain4j가 이해하는 Java 메서드로 자동 변환합니다.
  2. 타입 안전성: 도구 매개변수에 Java의 강력한 타이핑을 활용하여 도구 호출에서 런타임 '할루시네이션' 오류를 줄입니다.
  3. 엔터프라이즈 지원: Spring Boot, Quarkus, Micronaut 등과 쉽게 통합
  4. 손쉽게 연결: 상용구 JSON-RPC 코드를 작성하지 않아도 됩니다.
  5. 인증 표준화: Google Cloud OIDC 토큰을 기본적으로 지원하여 도구를 안전하게 실행할 수 있습니다.

더 많은 기능을 제공합니다.

종속 항목: pom.xml 구성

최신 MCP Toolbox Java SDK를 포함하려면 Maven 프로젝트에 다음 종속 항목을 추가하세요.

   <dependency>
        <groupId>com.google.cloud.mcp</groupId>
        <artifactId>mcp-toolbox-sdk-java</artifactId>
        <version>0.2.0</version>
    </dependency>

LangChain4j 아티팩트를 포함하려면 Maven 프로젝트에 다음 종속 항목을 추가하세요.

     <!-- LangChain4j Core & Gemini -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j</artifactId>
        <version>0.35.0</version>
    </dependency>

이제 끝났습니다. 프로젝트를 성공적으로 클론하고 에이전트, MCP Toolbox Java SDK, 컨텍스트의 세부정보를 살펴봤습니다.

9. 로컬에서 실행

머신에서 에이전트를 테스트하려면 배포된 MCP 도구 상자 서버를 가리켜야 합니다.

  1. 환경 변수를 설정합니다.
export GCP_PROJECT_ID="<<YOUR_PROJECT_ID>>"
export GCP_REGION="us-central1"
export GEMINI_MODEL_NAME="gemini-2.5-flash"
export MCP_TOOLBOX_URL="<<YOUR_TOOLBOX_ENDPOINT_URL>>/mcp"
  1. Maven으로 실행:
mvn compile

mvn spring-boot:run

이렇게 하면 에이전트가 로컬에서 시작되고 테스트할 수 있습니다.

10. Cloud Run에 배포해 보겠습니다.

프로젝트가 클론되고 프로젝트의 루트 폴더 내에 있는지 확인한 Cloud Shell 터미널에서 다음 명령어를 실행하여 Cloud Run에 배포합니다.

현재 프로젝트의 루트 폴더에 있지 않은 경우 Cloud Shell 터미널에서 다음을 실행합니다.

cd cymbal-transit

이미 cymbal-transit 루트에 있는 경우 아래 명령어를 실행하여 Cloud Run에 앱을 직접 배포합니다.

gcloud run deploy cymbal-transit --source . --set-env-vars GCP_PROJECT_ID=<<YOUR_PROJECT_ID>>,GCP_REGION=us-central1,GEMINI_MODEL_NAME=gemini-2.5-flash,MCP_TOOLBOX_URL=<<YOUR_MCP_TOOLBOX_URL>> --allow-unauthenticated

자리표시자 <<YOUR_PROJECT>> and <<YOUR_MCP_TOOLBOX_URL>>의 값을 바꿉니다.

명령어가 완료되면 서비스 URL이 출력됩니다. 복사합니다.

Cloud Run 서비스 계정에 AlloyDB 클라이언트 역할을 부여합니다.이렇게 하면 서버리스 애플리케이션이 데이터베이스로 안전하게 터널링할 수 있습니다.

Cloud Shell 터미널에서 다음을 실행합니다.

# 1. Get your Project ID and Project Number
PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")

# 2. Grant the AlloyDB Client role
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
--role="roles/alloydb.client"

참고: 배포 후 Cloud Run 서비스 목록 으로 이동하여 해당 서비스의 보안 탭에서 '공개 액세스 허용'이 선택되어 있는지 확인합니다.

이제 서비스 URL (앞서 복사한 Cloud Run 엔드포인트)을 사용하여 앱을 테스트합니다.

참고: 서비스 문제가 발생하고 메모리가 이유로 언급된 경우 할당된 메모리 한도를 1GiB로 늘려 테스트해 보세요.

11. 데모

상담사에게 '내일 아침 뉴욕에서 보스턴으로 가야 해. 골든 리트리버를 데려가도 되나요?' 에이전트가 다음을 수행하는 방식을 관찰합니다.

  1. 대형견 정책을 검색합니다.
  2. 특정 일정을 찾습니다.
  3. 여행 ID로 가장 빠른 여행을 요약합니다.
  4. 또한 해당 작업 요청을 후속 조치하는 경우 티켓을 예약합니다.

aa0408a81074d0fc.png

12. 삭제

이 실습을 완료한 후에는 AlloyDB 클러스터와 인스턴스를 삭제해야 합니다.

인스턴스와 함께 클러스터를 정리해야 합니다.

13. 축하합니다

정교한 Java 기반 대중교통 에이전트를 빌드했습니다. 오케스트레이션을 위해 LangChain4j를 활용하고 데이터 연결을 위해 MCP 도구 상자 Java SDK를 활용하여 에이전트, 도구, 데이터 소스 전반에서 추론할 수 있는 시스템을 만들었습니다. 여러 데이터베이스(플랫폼 간 포함)에서 데이터베이스용 MCP 도구 상자를 사용하여 에이전트형 애플리케이션 오케스트레이션을 시작하려면 지금 Java SDK를 시작하세요. 라이브러리에 관한 자세한 내용은 출시 공지 블로그를 참고하세요. 이러한 애플리케이션을 직접 빌드하고 싶다면 https://codevipassana.dev에서 Code Vipassana에 가입하세요. 무료로 원하는 속도로 강사의 안내를 받을 수 있습니다.