요청 배경
대화 캡처 이미지 1장에서 장소명 후보, 근거, 맥락 후보를 추출하고 카카오 로컬 API로 실제 장소 후보를 검색하는 기능을 추가하려고 합니다.
전체 호출 흐름은 다음과 같습니다.
Front
-> Spring POST /api/core/v1/places/suggestions
-> FastAPI POST /internal/v1/place-suggestions
-> GMS 비전 분석
-> 카카오 로컬 장소 검색
-> FastAPI가 raw 결과를 Spring에 반환
-> Spring이 공통 응답 형식으로 Front에 반환
FastAPI는 외부에 공개하지 않고 기존과 동일하게 Spring만 호출합니다. 기존 X-Internal-Secret 인증과 ingress.enabled: false도 유지합니다.
이번 확인 요청은 Front UX나 Spring 공개 API가 아니라, 기존 맥락 임베딩 및 키워드 추출을 수행하는 AI 서버에 이미지 장소 제안 모듈을 함께 조립해도 되는지를 확인하기 위한 것입니다.
제안하는 통합 방식
image2text의 독립 FastAPI 앱 전체를 합치지 않고 다음 모듈만 기존 ai 애플리케이션으로 옮깁니다.
- 이미지 형식·크기·픽셀 검증
- GMS 비전 모델 클라이언트
- 카카오 로컬 API 클라이언트
- 장소 제안 서비스 및 스키마
/internal/v1/place-suggestions 라우터
서비스와 외부 HTTP 클라이언트는 기존 ai/app/main.py의 lifespan에서 조립하고 app.state.place_suggestion_service로 제공합니다.
확인 과정에서 발견한 사항
1. lifespan 자원 정리 범위
현재 AI lifespan은 DB 연결과 Preset 적재가 끝난 뒤에 try/finally가 시작됩니다. 따라서 Preset 0건 등으로 yield 전에 기동이 중단되면 DB 정리가 실행되지 않을 가능성이 있습니다.
여기에 비전·카카오용 httpx.AsyncClient를 단순 추가하면 기동 실패 시 HTTP 연결 자원도 정리되지 않을 수 있습니다.
다음과 같이 AsyncExitStack 또는 동등한 구조로 DB와 HTTP 클라이언트를 생성 직후부터 관리하는 방식을 제안합니다.
lifespan 시작
-> DB 연결 및 종료 callback 등록
-> 장소 제안 전용 AsyncClient 생성 및 종료 등록
-> 기존 EmbeddingClient·LLMClient 생성
-> Preset 적재
-> 기존 서비스 + 장소 제안 서비스 조립
-> yield
-> 역순으로 HTTP 클라이언트 및 DB 종료
2. 장소 제안 전용 HTTP 클라이언트
비전 GMS와 카카오 호출에는 lifespan에서 생성한 장소 제안 전용 httpx.AsyncClient 하나를 공유하려고 합니다.
- 모듈 전역 클라이언트는 사용하지 않습니다.
- 기존 임베딩·키워드 판정 클라이언트의 연결 풀과 분리합니다.
- 카카오 후보 최대 3건을 병렬 검색할 수 있도록 연결 수를 설정합니다.
- GMS와 카카오의 제한 시간은 각 호출에 별도로 적용합니다.
- 클라이언트 생성 시에는 외부 요청을 보내지 않습니다.
3. 이미지 작업의 동시성 및 CPU 작업 분리
기존 /search, /context/process가 이미지 처리 때문에 지연되지 않도록 다음 기준을 적용하려고 합니다.
- 비전 장소 제안은 AI 인스턴스당 동시에 1건만 실행합니다.
- 두 번째 요청은 오래 대기시키지 않고 즉시 503으로 반환합니다.
- Pillow 기반 이미지 검증·리사이즈·압축은
asyncio.to_thread()로 이벤트 루프 밖에서 실행합니다.
- GMS 비전 호출에는 기존 키워드 판정의 3회 재시도 정책을 상속하지 않습니다.
- 카카오 후보 검색만 후보별로 병렬 실행합니다.
현재 운영 구성이 Uvicorn 1 worker, replica 1이면 서버 전체 1건 제한이 됩니다. worker 또는 replica가 늘어나면 제한도 인스턴스 수만큼 늘어납니다.
4. 기존 설정과 신규 설정
GMS에는 기존 설정을 재사용합니다.
신규 설정 후보는 다음과 같습니다.
KAKAO_REST_API_KEY
PINLOG_IMAGE_MODEL
IMAGE_MODEL_TIMEOUT_SEC
KAKAO_TIMEOUT_SEC
VISION_MAX_CONCURRENCY
IMAGE_MAX_BYTES
KAKAO_REST_API_KEY를 필수 설정으로 추가하면 모듈 import 과정의 create_app()에서 바로 검증될 수 있습니다. 따라서 Infra runtime Secret, strict key 검증기, 테스트 환경변수를 AI 코드와 같은 배포 단위로 반영해야 합니다.
추가 런타임 의존성은 다음과 같습니다.
5. 기존 오류 체계 사용
새로운 독립 오류 envelope을 만들지 않고 기존 AI 오류 분류를 사용하려고 합니다.
GMS 429·5xx·timeout -> TransientError -> FastAPI 503
GMS 3xx·4xx -> PermanentError -> FastAPI 502
예상하지 못한 결함 -> 500 유지
카카오 후보 일부 실패 -> 전체 실패가 아닌 200 + warning
비전 동시성 초과 -> 503
GMS 및 카카오 응답 본문, 이미지 base64, 추출 근거와 맥락은 로그에 남기지 않습니다. 기존 GMS 계측에는 kind=vision을 추가하는 방향을 생각하고 있습니다.
6. 내부 응답 계약
FastAPI는 Spring 공통 envelope을 만들지 않고 다음 raw 데이터만 반환합니다.
{
"requestId": "req_...",
"candidates": [],
"warnings": []
}
최종 success/data envelope은 Spring의 ApiResponseBodyAdvice가 한 번만 생성합니다.
AI 담당자 확인 요청
다음 사항을 확인 부탁드립니다.
- 기존 lifespan을
AsyncExitStack 기반으로 정리하고 장소 제안 전용 AsyncClient를 추가해도 기존 DB·Preset·임베딩·키워드 초기화 계약과 충돌하지 않는지
- 장소 제안 전용 HTTP 연결 풀을 기존
EmbeddingClient, LLMClient와 분리하는 방향이 적절한지
- 비전 작업을 인스턴스당 1건으로 제한하고 Pillow 작업을
asyncio.to_thread()로 분리하면 기존 /search, /context/process 보호에 충분한지
- GMS 비전 호출이 기존 키워드 판정 재시도 정책을 상속하지 않고 1회 호출 후 오류를 반환하는 방향이 적절한지
- 기존
TransientError·PermanentError 및 GMS call meter에 kind=vision을 추가하는 방식이 기존 오류·계측 계약과 맞는지
- 같은
GMS_API_KEY를 사용하되 HTTP 클라이언트만 분리하는 경우 기존 임베딩·키워드 판정 쿼터나 처리 흐름에 추가로 보호해야 할 부분이 있는지
KAKAO_REST_API_KEY를 필수 설정으로 추가할 때 AI 코드, 테스트, Infra Secret을 어떤 순서 또는 같은 배포 단위로 반영해야 하는지
- lifespan 기동 실패 상황에서 DB와 HTTP 클라이언트가 모두 정리되는 테스트를 추가하는 것으로 충분한지
제안하는 테스트 항목
- 정상 기동 시
place_suggestion_service가 app.state에 조립되는지
- 종료 시 장소 제안용
AsyncClient와 DB가 모두 닫히는지
- Preset 적재 실패 등
yield 이전 오류에서도 두 자원이 모두 닫히는지
- lifespan 기동만으로 GMS·카카오 외부 요청이 발생하지 않는지
- 비전 요청 1건 실행 중 두 번째 요청이 즉시 503을 받는지
- 이미지 압축 중에도
/search 또는 이벤트 루프의 가벼운 작업이 진행되는지
- GMS 429·5xx·4xx·timeout이 기존 오류 체계에 맞게 분류되는지
- 카카오 일부 실패가 성공한 후보까지 제거하지 않고
200 + warning으로 반환되는지
- 로그에 이미지, base64, GMS·카카오 응답 본문, 추출 근거·맥락이 남지 않는지
결정 요청
위 통합 방식에 문제가 없다면 해당 기준으로 AI 내부 API를 기존 ai 애플리케이션에 병합하려고 합니다. 기존 맥락 임베딩·키워드 추출 경로와 충돌하거나 먼저 수정해야 할 항목이 있다면 의견 부탁드립니다.
요청 배경
대화 캡처 이미지 1장에서 장소명 후보, 근거, 맥락 후보를 추출하고 카카오 로컬 API로 실제 장소 후보를 검색하는 기능을 추가하려고 합니다.
전체 호출 흐름은 다음과 같습니다.
FastAPI는 외부에 공개하지 않고 기존과 동일하게 Spring만 호출합니다. 기존
X-Internal-Secret인증과ingress.enabled: false도 유지합니다.이번 확인 요청은 Front UX나 Spring 공개 API가 아니라, 기존 맥락 임베딩 및 키워드 추출을 수행하는 AI 서버에 이미지 장소 제안 모듈을 함께 조립해도 되는지를 확인하기 위한 것입니다.
제안하는 통합 방식
image2text의 독립 FastAPI 앱 전체를 합치지 않고 다음 모듈만 기존ai애플리케이션으로 옮깁니다./internal/v1/place-suggestions라우터서비스와 외부 HTTP 클라이언트는 기존
ai/app/main.py의 lifespan에서 조립하고app.state.place_suggestion_service로 제공합니다.확인 과정에서 발견한 사항
1. lifespan 자원 정리 범위
현재 AI lifespan은 DB 연결과 Preset 적재가 끝난 뒤에
try/finally가 시작됩니다. 따라서 Preset 0건 등으로yield전에 기동이 중단되면 DB 정리가 실행되지 않을 가능성이 있습니다.여기에 비전·카카오용
httpx.AsyncClient를 단순 추가하면 기동 실패 시 HTTP 연결 자원도 정리되지 않을 수 있습니다.다음과 같이
AsyncExitStack또는 동등한 구조로 DB와 HTTP 클라이언트를 생성 직후부터 관리하는 방식을 제안합니다.2. 장소 제안 전용 HTTP 클라이언트
비전 GMS와 카카오 호출에는 lifespan에서 생성한 장소 제안 전용
httpx.AsyncClient하나를 공유하려고 합니다.3. 이미지 작업의 동시성 및 CPU 작업 분리
기존
/search,/context/process가 이미지 처리 때문에 지연되지 않도록 다음 기준을 적용하려고 합니다.asyncio.to_thread()로 이벤트 루프 밖에서 실행합니다.현재 운영 구성이 Uvicorn 1 worker, replica 1이면 서버 전체 1건 제한이 됩니다. worker 또는 replica가 늘어나면 제한도 인스턴스 수만큼 늘어납니다.
4. 기존 설정과 신규 설정
GMS에는 기존 설정을 재사용합니다.
신규 설정 후보는 다음과 같습니다.
KAKAO_REST_API_KEY를 필수 설정으로 추가하면 모듈 import 과정의create_app()에서 바로 검증될 수 있습니다. 따라서 Infra runtime Secret, strict key 검증기, 테스트 환경변수를 AI 코드와 같은 배포 단위로 반영해야 합니다.추가 런타임 의존성은 다음과 같습니다.
5. 기존 오류 체계 사용
새로운 독립 오류 envelope을 만들지 않고 기존 AI 오류 분류를 사용하려고 합니다.
GMS 및 카카오 응답 본문, 이미지 base64, 추출 근거와 맥락은 로그에 남기지 않습니다. 기존 GMS 계측에는
kind=vision을 추가하는 방향을 생각하고 있습니다.6. 내부 응답 계약
FastAPI는 Spring 공통 envelope을 만들지 않고 다음 raw 데이터만 반환합니다.
{ "requestId": "req_...", "candidates": [], "warnings": [] }최종
success/dataenvelope은 Spring의ApiResponseBodyAdvice가 한 번만 생성합니다.AI 담당자 확인 요청
다음 사항을 확인 부탁드립니다.
AsyncExitStack기반으로 정리하고 장소 제안 전용AsyncClient를 추가해도 기존 DB·Preset·임베딩·키워드 초기화 계약과 충돌하지 않는지EmbeddingClient,LLMClient와 분리하는 방향이 적절한지asyncio.to_thread()로 분리하면 기존/search,/context/process보호에 충분한지TransientError·PermanentError및 GMS call meter에kind=vision을 추가하는 방식이 기존 오류·계측 계약과 맞는지GMS_API_KEY를 사용하되 HTTP 클라이언트만 분리하는 경우 기존 임베딩·키워드 판정 쿼터나 처리 흐름에 추가로 보호해야 할 부분이 있는지KAKAO_REST_API_KEY를 필수 설정으로 추가할 때 AI 코드, 테스트, Infra Secret을 어떤 순서 또는 같은 배포 단위로 반영해야 하는지제안하는 테스트 항목
place_suggestion_service가app.state에 조립되는지AsyncClient와 DB가 모두 닫히는지yield이전 오류에서도 두 자원이 모두 닫히는지/search또는 이벤트 루프의 가벼운 작업이 진행되는지200 + warning으로 반환되는지결정 요청
위 통합 방식에 문제가 없다면 해당 기준으로 AI 내부 API를 기존
ai애플리케이션에 병합하려고 합니다. 기존 맥락 임베딩·키워드 추출 경로와 충돌하거나 먼저 수정해야 할 항목이 있다면 의견 부탁드립니다.