1. 개요
완벽한 선물을 쉽게 찾을 수 있는 완구점을 가상 또는 오프라인으로 방문한다고 상상해 보세요. 원하는 것을 설명하거나, 장난감 사진을 업로드하거나, 직접 만든 디자인을 업로드하면 스토어에서 즉시 사용자의 요구사항을 이해하고 맞춤형 환경을 제공합니다. 이것은 미래의 판타지가 아니라 AI, 클라우드 기술, 맞춤형 전자상거래를 위한 비전으로 구현되는 현실입니다.
문제: 상상한 것과 일치하는 완벽한 제품을 찾는 것은 어려울 수 있습니다. 일반적인 검색어, 키워드, 퍼지 검색은 부족한 경우가 많고, 끝없이 이어지는 페이지를 탐색하는 것은 지루할 수 있으며, 상상한 것과 실제로 제공되는 것 사이의 불일치로 인해 답답함을 느낄 수 있습니다.
솔루션: 데모 애플리케이션은 AI의 힘을 활용하여 이 문제를 정면으로 해결합니다. 컨텍스트 검색과 검색 컨텍스트와 일치하는 제품의 맞춤 생성을 통해 진정으로 개인화되고 원활한 환경을 제공합니다.
빌드할 항목
이 실습에서는 다음을 수행합니다.
- AlloyDB 인스턴스 만들기 및 Toys 데이터 세트 로드
- AlloyDB에서 pgvector 및 생성형 AI 모델 확장 프로그램 사용 설정
- 제품 설명에서 임베딩을 생성하고 사용자 검색 텍스트에 대해 실시간 코사인 유사도 검색을 실행합니다.
- Gemini 2.0 Flash를 호출하여 컨텍스트에 맞는 장난감 검색을 위해 사용자가 업로드한 이미지를 설명합니다.
- Imagen 3을 호출하여 사용자의 관심분야에 따라 맞춤 장난감 만들기
- 데이터베이스용 생성형 AI 도구 상자를 사용하여 만든 가격 예측 도구를 호출하여 맞춤 제작된 장난감의 가격 세부정보를 확인합니다.
- 서버리스 Cloud Run Functions에 솔루션 배포
요구사항
2. 아키텍처
데이터 흐름: 시스템을 통해 데이터가 이동하는 방식을 자세히 살펴보겠습니다.
- AI 기반 RAG (검색 증강 생성)를 사용한 컨텍스트 검색
'빨간색 자동차'를 검색하는 대신 시스템은 아래 내용을 이해합니다.
'3세 남자아이에게 적합한 작은 차량'
AlloyDB를 기반으로 사용: Google Cloud의 완전 관리형 PostgreSQL 호환 데이터베이스인 AlloyDB를 사용하여 설명, 이미지 URL, 기타 관련 속성 등 장난감 데이터를 저장합니다.
시맨틱 검색을 위한 pgvector: PostgreSQL 확장 프로그램인 pgvector를 사용하면 장난감 설명과 사용자 검색어의 벡터 임베딩을 모두 저장할 수 있습니다. 이를 통해 시맨틱 검색이 가능해지며, 시스템은 정확한 키워드뿐만 아니라 단어의 의미를 이해합니다.
관련성을 위한 코사인 유사성: Google은 코사인 유사성을 사용하여 사용자의 검색 벡터와 장난감 설명 벡터 간의 의미적 유사성을 측정하고 가장 관련성 높은 결과를 표시합니다.
속도와 정확성을 위한 ScaNN 색인: 장난감 인벤토리가 증가함에 따라 빠르고 정확한 결과를 보장하기 위해 ScaNN (확장 가능한 최근접 이웃) 색인을 통합합니다. 이를 통해 벡터 검색의 효율성과 검색률이 크게 개선됩니다.
- Gemini 2.0 Flash를 사용한 이미지 기반 검색 및 이해
텍스트로 컨텍스트를 입력하는 대신 사용자가 검색하려는 친숙한 장난감 사진을 업로드한다고 가정해 보겠습니다. 사용자는 좋아하는 장난감의 이미지를 업로드하여 관련 기능을 이용할 수 있습니다. LangChain4j를 사용하여 호출되는 Google의 Gemini 2.0 Flash 모델을 활용하여 이미지를 분석하고 장난감의 색상, 재질, 유형, 대상 연령대와 같은 관련 맥락을 추출합니다.
- 생성형 AI로 맞춤설정된 드림 토이 만들기: Imagen 3
사용자가 직접 장난감을 만들기로 결정할 때 진정한 마법이 일어납니다. Imagen 3를 사용하면 간단한 텍스트 프롬프트를 사용하여 꿈에 그리던 장난감을 설명할 수 있습니다. '보라색 날개와 친근한 얼굴을 가진 봉제 용을 원해'라고 말하면 화면에 그 용이 나타나는 것을 상상해 보세요. 그런 다음 Imagen 3가 맞춤 디자인된 장난감의 이미지를 생성하여 사용자에게 창작물을 명확하게 시각화합니다.
- 에이전트 및 데이터베이스용 생성형 AI 툴박스를 기반으로 한 가격 예측
맞춤 디자인 장난감의 제작 비용을 추정하는 가격 예측 기능을 구현했습니다. 이 기능은 정교한 가격 계산 도구가 포함된 에이전트에서 제공합니다.
데이터베이스용 생성형 AI 도구 상자: 이 에이전트는 Google의 새로운 오픈소스 도구인 데이터베이스용 생성형 AI 도구 상자를 사용하여 데이터베이스와 원활하게 통합됩니다. 이를 통해 에이전트는 자재 비용, 제조 프로세스, 기타 관련 요소에 관한 실시간 데이터에 액세스하여 정확한 가격 견적을 제공할 수 있습니다. 자세한 내용은 여기를 참고하세요.
- Java Spring Boot, Gemini Code Assist, Cloud Run을 사용한 간소화된 개발 및 서버리스 배포
전체 애플리케이션은 강력하고 확장 가능한 프레임워크인 Java Spring Boot를 사용하여 빌드됩니다. 개발 과정 전반, 특히 프런트엔드 개발에 Gemini Code Assist를 활용하여 개발 주기를 크게 가속화하고 코드 품질을 개선했습니다. 전체 애플리케이션을 배포하는 데는 Cloud Run을 사용하고, 데이터베이스와 에이전트 기능을 독립적인 엔드포인트로 배포하는 데는 Cloud Run Functions를 사용했습니다.
3. 시작하기 전에
프로젝트 만들기
- Google Cloud 콘솔의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.
- Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다. 프로젝트에 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요 .
- bq가 미리 로드되어 제공되는 Google Cloud에서 실행되는 명령줄 환경인 Cloud Shell을 사용합니다. Google Cloud 콘솔 상단에서 Cloud Shell 활성화를 클릭합니다.

- Cloud Shell에 연결되면 다음 명령어를 사용하여 이미 인증되었는지, 프로젝트가 프로젝트 ID로 설정되었는지 확인합니다.
gcloud auth list
- Cloud Shell에서 다음 명령어를 실행하여 gcloud 명령어가 프로젝트를 알고 있는지 확인합니다.
gcloud config list project
- 프로젝트가 설정되지 않은 경우 다음 명령어를 사용하여 설정합니다.
gcloud config set project <YOUR_PROJECT_ID>
- Cloud Shell 터미널에서 다음 명령어를 하나씩 실행하여 필요한 API를 사용 설정합니다.
아래를 실행하는 단일 명령어는 있지만 무료 체험 계정 사용자는 이러한 기능을 일괄적으로 사용 설정하려고 할 때 할당량 문제가 발생할 수 있습니다. 따라서 명령어는 한 줄에 하나씩 표시됩니다.
gcloud services enable alloydb.googleapis.com
gcloud services enable compute.googleapis.com
gcloud services enable cloudresourcemanager.googleapis.com
gcloud services enable servicenetworking.googleapis.com
gcloud services enable run.googleapis.com
gcloud services enable cloudbuild.googleapis.com
gcloud services enable cloudfunctions.googleapis.com
gcloud services enable aiplatform.googleapis.com
gcloud 명령의 대안은 콘솔을 통해 각 제품을 검색하거나 이 링크를 사용하는 것입니다.
API가 누락된 경우 구현 과정에서 언제든지 사용 설정할 수 있습니다.
gcloud 명령어 및 사용법은 문서를 참조하세요.
4. 데이터베이스 설정
이 실습에서는 AlloyDB를 장난감 가게 데이터를 저장하는 데이터베이스로 사용합니다. 클러스터를 사용하여 데이터베이스, 로그와 같은 모든 리소스를 보유합니다. 각 클러스터에는 데이터에 대한 액세스 포인트를 제공하는 기본 인스턴스가 있습니다. 테이블에는 실제 데이터가 저장됩니다.
전자상거래 데이터 세트가 로드될 AlloyDB 클러스터, 인스턴스, 테이블을 만들어 보겠습니다.
클러스터 및 인스턴스 만들기
- Cloud 콘솔에서 AlloyDB 페이지로 이동합니다. Cloud 콘솔에서 대부분의 페이지를 쉽게 찾으려면 콘솔의 검색창을 사용하여 검색하면 됩니다.
- 해당 페이지에서 클러스터 만들기를 선택합니다.

- 아래와 같은 화면이 표시됩니다. 다음 값으로 클러스터 및 인스턴스를 만듭니다 (저장소에서 애플리케이션 코드를 클론하는 경우 값이 일치하는지 확인).
- 클러스터 ID: '
vector-cluster' - password: "
alloydb" - PostgreSQL 15 호환
- 지역: '
us-central1' - 네트워킹: '
default'

- 기본 네트워크를 선택하면 아래와 같은 화면이 표시됩니다.
연결 설정을 선택합니다.
- 여기에서 '자동으로 할당된 IP 범위 사용'을 선택하고 계속을 클릭합니다. 정보를 검토한 후 연결 만들기를 선택합니다.

- 네트워크가 설정되면 클러스터를 계속 만들 수 있습니다. 클러스터 만들기를 클릭하여 아래와 같이 클러스터 설정을 완료합니다.

인스턴스 ID를 다음으로 변경해야 합니다.
vector-instance
변경할 수 없는 경우 향후 모든 참조에서 인스턴스 ID를 변경해야 합니다.
클러스터를 만드는 데 약 10분이 걸립니다. 성공하면 방금 만든 클러스터의 개요가 표시된 화면이 표시됩니다.
5. 데이터 수집
이제 매장에 관한 데이터가 포함된 표를 추가할 차례입니다. AlloyDB로 이동하여 기본 클러스터와 AlloyDB Studio를 선택합니다.

인스턴스 생성이 완료될 때까지 기다려야 할 수 있습니다. 완료되면 클러스터를 만들 때 만든 사용자 인증 정보를 사용하여 AlloyDB에 로그인합니다. PostgreSQL에 인증하려면 다음 데이터를 사용하세요.
- 사용자 이름 : '
postgres' - 데이터베이스 : '
postgres' - 비밀번호 : '
alloydb'
AlloyDB Studio에 인증되면 편집기에 SQL 명령어가 입력됩니다. 마지막 창 오른쪽에 있는 더하기 기호를 사용하여 여러 편집기 창을 추가할 수 있습니다.

필요에 따라 실행, 형식, 지우기 옵션을 사용하여 편집기 창에 AlloyDB 명령어를 입력합니다.
확장 프로그램 사용 설정
이 앱을 빌드하기 위해 확장 프로그램 pgvector 및 google_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;
데이터베이스에서 사용 설정된 확장 프로그램을 확인하려면 다음 SQL 명령어를 실행합니다.
select extname, extversion from pg_extension;
테이블 만들기
아래 DDL 문을 사용하여 테이블을 만듭니다.
CREATE TABLE toys ( id VARCHAR(25), name VARCHAR(25), description VARCHAR(20000), quantity INT, price FLOAT, image_url VARCHAR(200), text_embeddings vector(768)) ;
위 명령어를 성공적으로 실행하면 데이터베이스에서 테이블을 볼 수 있습니다.
데이터 수집
이 실습에서는 이 SQL 파일에 약 72개의 레코드가 있는 테스트 데이터를 사용합니다. id, name, description, quantity, price, image_url 필드가 포함되어 있습니다. 나머지 필드는 실습 후반부에서 채워집니다.
여기에서 처음 5줄/삽입 문만 복사한 다음 빈 편집기 탭에 붙여넣고 실행을 선택합니다. 무료 체험 결제 계정을 사용하지 않는 경우 모든 삽입 문을 복사하여 실행할 수 있습니다.
표 콘텐츠를 보려면 'apparels'라는 표가 표시될 때까지 탐색기 섹션을 펼칩니다. 세로 점 3개 (⋮)를 선택하여 테이블을 쿼리하는 옵션을 확인합니다. SELECT 문이 새 편집기 탭에서 열립니다.

권한 부여
아래 문을 실행하여 embedding 함수에 대한 실행 권한을 사용자 postgres에게 부여합니다.
GRANT EXECUTE ON FUNCTION embedding TO postgres;
AlloyDB 서비스 계정에 Vertex AI 사용자 역할 부여
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"
6. 컨텍스트의 임베딩 생성
컴퓨터는 텍스트를 처리하는 것보다 숫자를 처리하는 것이 훨씬 쉽습니다. 임베딩 시스템은 문구, 언어 등에 관계없이 텍스트를 나타내는 일련의 부동 소수점 숫자로 텍스트를 변환합니다.
해변 위치를 설명해 줘. '해변', '해안가', '객실에서 바다까지 도보', 'sur la mer', 'на берегу океана' 등 다양한 표현이 사용될 수 있습니다. 이러한 용어는 모두 다르게 보이지만 의미론적 의미 또는 머신러닝 용어인 임베딩은 서로 매우 유사해야 합니다.
데이터와 컨텍스트가 준비되었으므로 SQL을 실행하여 제품 설명의 임베딩을 embedding 필드의 테이블에 추가합니다. 다양한 삽입 모델을 사용할 수 있습니다. Vertex AI의 text-embedding-005를 사용하고 있습니다. 프로젝트 전체에서 동일한 임베딩 모델을 사용해야 합니다.
참고: 오래전에 만든 기존 Google Cloud 프로젝트를 사용하는 경우 textembedding-gecko와 같은 이전 버전의 텍스트 임베딩 모델을 계속 사용해야 할 수 있습니다.
AlloyDB Studio 탭으로 돌아가서 다음 DML을 입력합니다.
UPDATE toys set text_embeddings = embedding( 'text-embedding-005', description);
toys 표를 다시 살펴보면 일부 삽입이 표시됩니다. 변경사항을 확인하려면 SELECT 문을 다시 실행해야 합니다.
SELECT id, name, description, price, quantity, image_url, text_embeddings FROM toys;
그러면 아래와 같이 장난감 설명에 대한 임베딩 벡터(부동 소수점 배열과 유사)가 반환됩니다.

참고: 무료 등급으로 새로 생성된 Google Cloud 프로젝트는 초당 허용되는 삽입 모델의 삽입 요청 수와 관련하여 할당량 문제가 발생할 수 있습니다. ID에 필터 쿼리를 사용한 다음 선택적으로 1~5개의 레코드 등을 선택하여 삽입을 생성하는 것이 좋습니다.
7. 벡터 검색 실행
이제 테이블, 데이터, 임베딩이 모두 준비되었으므로 사용자 검색 텍스트에 대한 실시간 벡터 검색을 실행해 보겠습니다.
사용자가 다음과 같이 질문한다고 가정해 보겠습니다.
'I want a white plush teddy bear toy with a floral pattern'
아래 쿼리를 실행하여 일치하는 항목을 찾을 수 있습니다.
select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 2;
이 쿼리를 자세히 살펴보겠습니다.
이 쿼리에서
- 사용자의 검색 텍스트는 '
I want a white plush teddy bear toy with a floral pattern.'입니다. text-embedding-005모델을 사용하여embedding()메서드에서 임베딩으로 변환합니다. 이 단계는 표의 모든 항목에 삽입 함수를 적용한 마지막 단계와 유사합니다.- '
<=>'는 코사인 유사도 거리 메서드의 사용을 나타냅니다. pgvector 문서에서 사용 가능한 모든 유사성 측정치를 확인할 수 있습니다. - 데이터베이스에 저장된 벡터와 호환되도록 임베딩 메서드의 결과를 벡터 유형으로 변환하고 있습니다.
- LIMIT 5는 검색 텍스트에 대해 5개의 최근접 이웃을 추출하려는 것을 나타냅니다.
결과는 다음과 같습니다.

결과에서 볼 수 있듯이 일치하는 항목은 검색 텍스트와 매우 유사합니다. 텍스트를 변경하여 결과가 어떻게 달라지는지 확인해 보세요.
중요사항:
이제 ScaNN 색인을 사용하여 이 벡터 검색 결과의 성능 (쿼리 시간), 효율성, 재현율을 높이려고 한다고 가정해 보겠습니다. 이 블로그의 단계를 읽고 색인 유무에 따른 결과의 차이를 비교하세요.
선택사항: ScaNN 색인으로 효율성과 재현율 개선
레코드 수가 100개 미만인 경우 이 단계를 무시하세요.
편의를 위해 색인 생성 단계를 여기에 나열합니다.
- 클러스터, 인스턴스, 컨텍스트, 삽입이 이미 생성되었으므로 다음 문을 사용하여 ScaNN 확장 프로그램을 설치하기만 하면 됩니다.
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
- 다음으로 색인 (ScaNN)을 만듭니다.
CREATE INDEX toysearch_index ON toys
USING scann (text_embeddings cosine)
WITH (num_leaves=9);
위 DDL에서 apparel_index는 색인의 이름입니다.
'toys'는 내 테이블이야.
'scann'은 색인 방법입니다.
'embedding'은 색인을 생성할 테이블의 열입니다.
'cosine'은 색인과 함께 사용할 거리 메서드입니다.
'8'은 이 색인에 적용할 파티션 수입니다. 1~1048576 사이의 값으로 설정합니다. 이 값을 결정하는 방법에 대한 자세한 내용은 ScaNN 색인 조정을 참고하세요.
ScaNN 저장소에서 권장하는 대로 데이터 포인트 수의 제곱근을 사용했습니다 (파티셔닝할 때 num_leaves는 데이터 포인트 수의 제곱근과 거의 같아야 함).
- 다음 쿼리를 사용하여 색인이 생성되었는지 확인합니다.
SELECT * FROM pg_stat_ann_indexes;
- 색인 없이 사용한 것과 동일한 쿼리를 사용하여 벡터 검색을 실행합니다.
select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 5;
위 쿼리는 8단계의 실습에서 사용한 쿼리와 동일합니다. 하지만 이제 필드의 색인이 생성되었습니다.
- 색인을 삭제하여 색인이 있는 경우와 없는 경우를 모두 포함하는 간단한 검색어로 테스트합니다.
이 사용 사례에는 레코드가 72개만 있으므로 색인이 실제로 적용되지 않습니다. 다른 사용 사례에서 실시한 테스트의 결과는 다음과 같습니다.
색인화된 임베딩 데이터에 동일한 벡터 검색 쿼리를 실행하면 고품질 검색 결과와 효율성이 제공됩니다. 색인을 사용하면 효율성이 크게 향상됩니다 (실행 시간: ScaNN 없이 10.37ms, ScaNN 사용 0.87ms). 이 주제에 관한 자세한 내용은 이 블로그를 참고하세요.
8. LLM과 일치하는 유효성 검사
다음 단계로 넘어가 애플리케이션에 가장 적합한 결과를 반환하는 서비스를 만들기 전에 생성형 AI 모델을 사용하여 이러한 잠재적 대답이 정말로 관련성이 있고 사용자와 공유해도 안전한지 확인해 보겠습니다.
인스턴스가 Gemini용으로 설정되어 있는지 확인
먼저 클러스터 및 인스턴스에 Google ML 통합이 이미 사용 설정되어 있는지 확인합니다. AlloyDB Studio에서 다음 명령어를 실행합니다.
show google_ml_integration.enable_model_support;
값이 'on'으로 표시되면 다음 두 단계를 건너뛰고 AlloyDB 및 Vertex AI 모델 통합 설정으로 바로 이동하면 됩니다.
- AlloyDB 클러스터의 기본 인스턴스로 이동하여 기본 인스턴스 수정을 클릭합니다.

- 고급 구성 옵션에서 플래그 섹션으로 이동합니다.
google_ml_integration.enable_model_support flag이 아래와 같이 'on'로 설정되어 있는지 확인합니다.

'사용'으로 설정되어 있지 않으면 '사용'으로 설정한 다음 인스턴스 업데이트 버튼을 클릭합니다. 이 단계는 몇 분 정도 걸립니다.
AlloyDB 및 Vertex AI 모델 통합
이제 AlloyDB Studio에 연결하고 다음 DML 문을 실행하여 표시된 위치에 프로젝트 ID를 사용하여 AlloyDB에서 Gemini 모델 액세스를 설정할 수 있습니다. 명령어를 실행하기 전에 문법 오류 경고가 표시될 수 있지만 명령어는 제대로 실행됩니다.
먼저 아래와 같이 Gemini 1.5 모델 연결을 만듭니다. 아래 명령어에서 $PROJECT_ID를 Google Cloud 프로젝트 ID로 바꾸세요.
CALL
google_ml.create_model( model_id => 'gemini-1.5',
model_request_url => 'https://us-central1-aiplatform.googleapis.com/v1/projects/$PROJECT_ID/locations/us-central1/publishers/google/models/gemini-1.5-pro:streamGenerateContent',
model_provider => 'google',
model_auth_type => 'alloydb_service_agent_iam');
AlloyDB Studio에서 다음 명령어를 사용하여 액세스하도록 구성된 모델을 확인할 수 있습니다.
select model_id,model_type from google_ml.model_info_view;
마지막으로 데이터베이스 사용자에게 ml_predict_row 함수를 실행하여 Google Vertex AI 모델을 통해 예측을 실행할 수 있는 권한을 부여해야 합니다. 다음 명령어를 실행합니다.
GRANT EXECUTE ON FUNCTION ml_predict_row to postgres;
참고: 기존 Google Cloud 프로젝트와 오래전에 생성된 AlloyDB의 기존 클러스터/인스턴스를 사용하는 경우 gemini-1.5 모델에 대한 이전 참조를 삭제하고 위의 CALL 문으로 다시 생성해야 할 수 있으며, gemini-1.5의 향후 호출에 문제가 발생하는 경우 함수 ml_predict_row에 대한 실행 권한 부여를 다시 실행해야 할 수 있습니다.
대답 평가
다음 섹션에서는 쿼리의 대답이 합리적인지 확인하기 위해 하나의 큰 쿼리를 사용하지만 쿼리를 이해하기 어려울 수 있습니다. 이제 각 부분을 살펴보고 몇 분 후에 어떻게 결합되는지 알아보겠습니다.
- 먼저 데이터베이스에 요청을 보내 사용자 쿼리와 가장 일치하는 10개의 결과를 가져옵니다.
- 유효한 대답을 확인하기 위해 대답을 평가하는 방법을 설명하는 외부 쿼리를 사용합니다. 검색 텍스트인
recommended_text필드와 내부 테이블의content(장난감 설명 필드)을 쿼리의 일부로 사용합니다. - 이를 사용하여 반환된 대답의 '적절성'을 검토합니다.
predict_row은 결과를 JSON 형식으로 반환합니다. '-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'"' 코드는 해당 JSON에서 실제 텍스트를 추출하는 데 사용됩니다. 반환되는 실제 JSON을 확인하려면 이 코드를 삭제하면 됩니다.- 마지막으로 LLM 응답을 가져오기 위해
REGEXP_REPLACE(gemini_validation,'[^a-zA-Z,: ]','','g')를 사용하여 추출합니다.
SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') AS gemini_validation
FROM (SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
CAST(ARRAY_AGG(LLM_RESPONSE) AS TEXT) AS gemini_validation
FROM (SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
json_array_elements(google_ml.predict_row(model_id => 'gemini-1.5',
request_body => CONCAT('{ "contents": [ { "role": "user", "parts": [ { "text": "User wants to buy a toy and this is the description of the toy they wish to buy: ', recommended_text, '. Check if the following product items from the inventory are close enough to really, contextually match the user description. Here are the items: ', content, '. Return a ONE-LINE response with 3 values: 1) MATCH: if the 2 contexts are reasonably matching in terms of any of the color or color family specified in the list, approximate style match with any of the styles mentioned in the user search text: This should be a simple YES or NO. Choose NO only if it is completely irrelevant to users search criteria. 2) PERCENTAGE: percentage of match, make sure that this percentage is accurate 3) DIFFERENCE: A clear one-line easy description of the difference between the 2 products. Remember if the user search text says that some attribute should not be there, and the record has it, it should be a NO match. " } ] } ] }')::JSON)) -> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text' :: TEXT AS LLM_RESPONSE
FROM (SELECT id,
name,
description AS content,
quantity,
price,
image_url,
'Pink panther standing' AS recommended_text
FROM toys
ORDER BY text_embeddings <=> embedding('text-embedding-005',
'Pink panther standing')::VECTOR
LIMIT 1) AS xyz) AS X
GROUP BY id,
name,
content,
quantity,
price,
image_url,
recommended_text) AS final_matches
-- WHERE REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') LIKE '%MATCH%:%YES%';
여전히 부담스러워 보일 수 있지만, 이제는 조금 더 이해가 되실 겁니다. 결과에는 일치 여부, 일치 비율, 등급에 대한 설명이 표시됩니다.
Gemini 모델은 기본적으로 스트리밍이 사용 설정되어 있으므로 실제 대답은 여러 줄에 걸쳐 표시됩니다.

9. 서버리스로 장난감 검색을 클라우드로 가져오기
이 앱을 웹으로 가져올 준비가 되셨나요? 다음 단계에 따라 Cloud Run 함수를 사용하여 이 Knowledge Engine을 서버리스로 만드세요.
- Google Cloud 콘솔의 Cloud Run Functions로 이동하여 새 Cloud Run 함수를 만들거나 링크(https://console.cloud.google.com/functions/add)를 사용합니다.
- 환경을 'Cloud Run Functions'로 선택합니다. 함수 이름 'get-toys-alloydb'을 입력하고 리전을 'us-central1'로 선택합니다. 인증을 '인증되지 않은 호출 허용'으로 설정하고 다음을 클릭합니다. 런타임으로 Java 17을 선택하고 소스 코드로 인라인 편집기를 선택합니다.
- 기본적으로 진입점은 '
gcfv2.HelloHttpFunction'로 설정됩니다. Cloud Run 함수의HelloHttpFunction.java및pom.xml에 있는 자리표시자 코드를 각각 HelloHttpFunction.java 및 pom.xml의 코드로 바꿉니다. - Java 파일에서 <<YOUR_PROJECT>> 자리표시자와 AlloyDB 연결 사용자 인증 정보를 실제 값으로 변경해야 합니다. AlloyDB 사용자 인증 정보는 이 Codelab을 시작할 때 사용한 사용자 인증 정보입니다. 다른 값을 사용한 경우 Java 파일에서 동일하게 수정하세요.
- 배포를 클릭합니다.
배포가 완료되면 Cloud 함수가 AlloyDB 데이터베이스 인스턴스에 액세스할 수 있도록 VPC 커넥터를 만듭니다.
중요한 단계:
배포를 시작하면 Google Cloud Run Functions 콘솔에서 함수를 확인할 수 있습니다. 새로 만든 함수 (get-toys-alloydb)를 검색하고 클릭한 다음 수정을 클릭하여 다음을 변경합니다.
- 런타임, 빌드, 연결, 보안 설정으로 이동
- 시간 제한을 180초로 늘립니다.
- '연결' 탭으로 이동합니다.

- 인그레스 설정에서 '모든 트래픽 허용'이 선택되어 있는지 확인합니다.
- 이그레스 설정에서 네트워크 드롭다운을 클릭하고 '새 VPC 커넥터 추가' 옵션을 선택한 후 팝업되는 대화상자에 표시된 안내를 따릅니다.

- VPC 커넥터의 이름을 입력하고 리전이 인스턴스와 동일한지 확인합니다. 네트워크 값을 기본값으로 두고 서브넷을 사용 가능한 IP 범위(예: 10.8.0.0)가 있는 '커스텀 IP 범위'로 설정합니다.
- '확장 설정 표시'를 펼치고 구성이 정확히 다음과 같이 설정되어 있는지 확인합니다.

- '만들기'를 클릭하면 이제 이 커넥터가 이그레스 설정에 나열됩니다.
- 새로 만든 커넥터를 선택합니다.
- 이 VPC 커넥터를 통해 모든 트래픽이 라우팅되도록 선택합니다.
- 다음을 클릭한 후 배포를 클릭합니다.
10. Cloud Run 함수 테스트
업데이트된 Cloud 함수가 배포되면 생성된 엔드포인트가 표시됩니다. 이를 복사하여 다음 명령어에서 바꿉니다.
또는 다음과 같이 Cloud Run 함수를 테스트할 수 있습니다.
PROJECT_ID=$(gcloud config get-value project)
curl -X POST <<YOUR_ENDPOINT>> \
-H 'Content-Type: application/json' \
-d '{"search":"I want a standing pink panther toy"}' \
| jq .
결과는 다음과 같습니다.

작업이 끝났습니다. AlloyDB 데이터에서 임베딩 모델을 사용하여 유사성 벡터 검색을 수행하는 것은 매우 간단합니다.
11. 웹 애플리케이션 클라이언트 빌드
이 파트에서는 사용자가 텍스트, 이미지를 기반으로 상호작용하고 일치하는 장난감을 찾고 필요에 따라 새 장난감을 만들 수 있는 웹 애플리케이션을 빌드합니다. 애플리케이션이 이미 빌드되었으므로 아래 단계에 따라 IDE에 복사하고 앱을 실행할 수 있습니다.
- 사용자가 업로드하여 일치하는 장난감을 찾을 수 있는 이미지를 설명하기 위해 Gemini 2.0 Flash를 사용하므로 이 애플리케이션의 API 키를 가져와야 합니다. 이를 위해 https://aistudio.google.com/apikey로 이동하여 이 애플리케이션을 구현하는 활성 Google Cloud 프로젝트의 API 키를 가져와서 어딘가에 저장합니다.

- Cloud Shell 터미널로 이동
- 다음 명령어를 사용하여 저장소를 클론합니다.
git clone https://github.com/AbiramiSukumaran/toysearch
cd toysearch
- 저장소가 클론되면 Cloud Shell 편집기에서 프로젝트에 액세스할 수 있습니다.
- 클론된 프로젝트에서 'get-toys-alloydb' 및 'toolbox-toys' 폴더를 삭제해야 합니다. 이 두 폴더는 필요할 때 저장소에서 참조할 수 있는 Cloud Run Functions 코드이기 때문입니다.
- 웹 폴더에서 GenerateToy.java로 이동하여 다음 줄을 찾아 삭제합니다. 성인 콘텐츠 허용에는 일부 무료 체험 결제 계정에서 사용할 수 없는 특별한 권한이 필요할 수 있기 때문입니다.
paramsMap.put("personGeneration", "allow_adult");
- 앱을 빌드하고 배포하기 전에 필요한 모든 환경 변수가 설정되어 있는지 확인하세요. Cloud Shell 터미널로 이동하여 다음을 실행합니다.
PROJECT_ID=$(gcloud config get-value project)
export PROJECT_ID=$PROJECT_ID
export GOOGLE_API_KEY=<YOUR API KEY that you saved>
- 로컬에서 앱을 빌드하고 실행합니다.
프로젝트 디렉터리에 있는지 확인하고 다음 명령어를 실행합니다.
mvn package
mvn spring-boot:run
- Cloud Run에 배포
gcloud run deploy --source .
12. 생성형 AI 세부정보 이해
별도의 조치를 취하지 않아도 됩니다. 참고로 알려드립니다.
이제 애플리케이션을 배포했으므로 검색 (텍스트 및 이미지)과 생성을 어떻게 수행했는지 잠시 시간을 내어 알아보세요.
- 사용자 텍스트 기반 벡터 검색:
이 문제는 '벡터 검색 애플리케이션 웹 가져오기' 섹션에서 배포한 Cloud Run Functions에서 이미 해결되었습니다.
- 이미지 업로드 기반 벡터 검색:
텍스트로 컨텍스트를 입력하는 대신 사용자가 검색하려는 친숙한 장난감 사진을 업로드한다고 가정해 보겠습니다. 사용자는 좋아하는 장난감의 이미지를 업로드하여 관련 기능을 이용할 수 있습니다.
LangChain4j를 사용하여 호출되는 Google의 Gemini 2.0 Flash 모델을 활용하여 이미지를 분석하고 장난감의 색상, 재질, 유형, 대상 연령대와 같은 관련 맥락을 추출합니다.
단 5단계만으로 사용자 멀티모달 데이터 입력을 오픈소스 프레임워크를 사용한 대규모 언어 모델 호출과 일치하는 결과로 가져왔습니다. 지도를 공개하는 방법에 대해 알아보세요.
package cloudcode.helloworld.web;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
import java.util.Base64;
import java.util.Optional;
public class GeminiCall {
public String imageToBase64String(byte[] imageBytes) {
String base64Img = Base64.getEncoder().encodeToString(imageBytes);
return base64Img;
}
public String callGemini(String base64ImgWithPrefix) throws Exception {
String searchText = "";
// 1. Remove the prefix
String base64Img = base64ImgWithPrefix.replace("data:image/jpeg;base64,", "");
// 2. Decode base64 to bytes
byte[] imageBytes = Base64.getDecoder().decode(base64Img);
String image = imageToBase64String(imageBytes);
// 3. Get API key from environment variable
String apiKey = Optional.ofNullable(System.getenv("GOOGLE_API_KEY"))
.orElseThrow(() -> new IllegalArgumentException("GOOGLE_API_KEY environment variable not set"));
// 4. Invoke Gemini 2.0
ChatLanguageModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(apiKey)
.modelName("gemini-2.0-flash-001")
.build();
Response<AiMessage> response = gemini.generate(
UserMessage.from(
ImageContent.from(image, "image/jpeg"),
TextContent.from(
"The picture has a toy in it. Describe the toy in the image in one line. Do not add any prefix or title to your description. Just describe that toy that you see in the image in one line, do not describe the surroundings and other objects around the toy in the image. If you do not see any toy in the image, send response stating that no toy is found in the input image.")));
// 5. Get the text from the response and send it back to the controller
searchText = response.content().text().trim();
System.out.println("searchText inside Geminicall: " + searchText);
return searchText;
}
}
- Imagen 3를 사용하여 생성형 AI로 사용자 요청에 기반한 맞춤형 장난감을 빌드한 방법을 알아보세요.
그런 다음 Imagen 3가 맞춤 디자인된 장난감의 이미지를 생성하여 사용자에게 창작물을 명확하게 시각화합니다. 다음은 5단계로 진행된 방법입니다.
// Generate an image using a text prompt using an Imagen model
public String generateImage(String projectId, String location, String prompt)
throws ApiException, IOException {
final String endpoint = String.format("%s-aiplatform.googleapis.com:443", location);
PredictionServiceSettings predictionServiceSettings =
PredictionServiceSettings.newBuilder().setEndpoint(endpoint).build();
// 1. Set up the context and prompt
String context = "Generate a photo-realistic image of a toy described in the following input text from the user. Make sure you adhere to all the little details and requirements mentioned in the prompt. Ensure that the user is only describing a toy. If it is anything unrelated to a toy, politely decline the request stating that the request is inappropriate for the current context. ";
prompt = context + prompt;
// 2. Initialize a client that will be used to send requests. This client only needs to be created
// once, and can be reused for multiple requests.
try (PredictionServiceClient predictionServiceClient =
PredictionServiceClient.create(predictionServiceSettings)) {
// 3. Invoke Imagen 3
final EndpointName endpointName =
EndpointName.ofProjectLocationPublisherModelName(
projectId, location, "google", "imagen-3.0-generate-001"); //"imagegeneration@006"; imagen-3.0-generate-001
Map<String, Object> instancesMap = new HashMap<>();
instancesMap.put("prompt", prompt);
Value instances = mapToValue(instancesMap);
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("sampleCount", 1);
paramsMap.put("aspectRatio", "1:1");
paramsMap.put("safetyFilterLevel", "block_few");
paramsMap.put("personGeneration", "allow_adult");
paramsMap.put("guidanceScale", 21);
paramsMap.put("imagenControlScale", 0.95); //Setting imagenControlScale
Value parameters = mapToValue(paramsMap);
// 4. Get prediction response image
PredictResponse predictResponse =
predictionServiceClient.predict(
endpointName, Collections.singletonList(instances), parameters);
// 5. Return the Base64 Encoded String to the controller
for (Value prediction : predictResponse.getPredictionsList()) {
Map<String, Value> fieldsMap = prediction.getStructValue().getFieldsMap();
if (fieldsMap.containsKey("bytesBase64Encoded")) {
bytesBase64EncodedOuput = fieldsMap.get("bytesBase64Encoded").getStringValue();
}
}
return bytesBase64EncodedOuput.toString();
}
}
가격 예측
위의 이전 섹션에서는 Imagen이 사용자가 직접 디자인하고자 하는 장난감 이미지를 생성하는 방법을 설명했습니다. 사용자가 구매할 수 있도록 애플리케이션에서 가격을 설정해야 하며, 맞춤 주문형 장난감의 가격을 정의하기 위해 직관적인 로직을 사용했습니다. 이 로직은 사용자가 디자인한 장난감과 설명이 가장 유사한 상위 5개 장난감의 평균 가격을 사용하는 것입니다.
생성된 장난감의 가격 예측은 이 애플리케이션의 중요한 부분이며, 이를 생성하기 위해 에이전트 접근 방식을 사용했습니다. 데이터베이스용 생성형 AI 도구 상자를 소개합니다.
13. 데이터베이스용 생성형 AI 도구 상자
데이터베이스용 생성형 AI 도구 상자는 데이터베이스와 상호작용하는 생성형 AI 도구를 더 쉽게 빌드할 수 있도록 지원하는 Google의 오픈소스 서버입니다. 연결 풀링, 인증 등의 복잡한 작업을 처리하여 더 쉽고 빠르게 안전하게 도구를 개발할 수 있습니다. 에이전트가 데이터베이스에 있는 데이터에 액세스할 수 있도록 생성형 AI 도구를 빌드하는 데 도움이 됩니다.
도구를 준비하고 애플리케이션을 에이전트화하기 위해 설정해야 하는 단계는 다음과 같습니다. 도구 상자 Codelab 링크
이제 애플리케이션에서 배포된 Cloud Run Functions 엔드포인트를 사용하여 맞춤 주문형 장난감 이미지에 대해 생성된 Imagen 결과와 함께 가격을 채울 수 있습니다.
14. 웹 애플리케이션 테스트
이제 애플리케이션의 모든 구성요소가 빌드되고 배포되었으므로 클라우드에서 제공할 준비가 되었습니다. 모든 시나리오에서 애플리케이션을 테스트합니다. 다음은 예상되는 내용을 보여주는 동영상 링크입니다.
https://www.youtube.com/shorts/ZMqUAWsghYQ
방문 페이지는 다음과 같이 표시됩니다.

15. 삭제
이 게시물에서 사용한 리소스의 비용이 Google Cloud 계정에 청구되지 않도록 하려면 다음 단계를 따르세요.
- Google Cloud 콘솔에서 리소스 관리 페이지로 이동합니다.
- 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
- 대화상자에서 프로젝트 ID를 입력하고 종료를 클릭하여 프로젝트를 삭제합니다.
16. 축하합니다
축하합니다. 오픈소스 라이브러리를 활용하여 강력한 통합을 빌드하는 동시에 AlloyDB, pgvector, Imagen, Gemini 2.0을 사용하여 Toystore 컨텍스트 검색 및 생성을 성공적으로 실행했습니다. AlloyDB, Vertex AI, 벡터 검색의 기능을 결합하여 컨텍스트 및 벡터 검색을 액세스 가능하고 효율적이며 진정으로 의미 기반으로 만드는 데 큰 진전을 이루었습니다.