-
[초기화] 등록된 기관의 임베딩 사전 저장
- 기관 정보 -> 텍스트 변환 -> bge-m3 -> pgvector 저장
- IVFFlat 인덱스 생성으로 검색 최적화
-
[업데이트] 등록 시 동기 생성, 수정 시 비동기 배치 업데이트
- 등록: 즉시 임베딩 생성 및 저장 (동기)
- 수정: dirty flag 설정 후 배치 처리 (비동기)
- 배치 작업: 매일 자정 또는 1시간마다 플래그된 기관만 재생성
- Blue-Green 방식: 새 임베딩 생성 후 교체
-
[요청] Spring으로부터 Member, ElderlyProfile, text(추가 입력 받은 텍스트)를 전달 받는다.
- 파라미터: member, elderlyProfile, text(추가 텍스트)
-
[생성] Member + ElderlyProfile + text 를 통해서 기관의 임베딩과 동일한 공간에 위치하도록 텍스트를 생성한다.
- Member + ElderlyProdile + text 결합 텍스트 생성
- 기관의 텍스트와 비슷한 템플릿으로 변환
-
[임베딩] 사용자 텍스트를 임베딩으로 변환
- bge-m3 모델 사용 (1024차원)
- 재시도 로직: 3회 시도, 실패 시 5XX 반환
-
[검색] pgvector로 유사 기관 검색
-
1: 사전 필터링 (SQL WHERE절)
- 입소 가능 여부 (정원 초과 제외)
- 선호 서비스 타입 매칭
- 결과: 10,000개 → 약 3,000~5,000개로 축소
-
2: 코사인 유사도 계산
ORDER BY embedding <=> $1 LIMIT 5- IVFFlat 인덱스 활용으로 O(log n) 검색
-
-
[분석] 매칭 이유 키워드 추출
- LLM 배치 호출
- 5개 기관을 1번의 LLM 호출로 처리
- GPT-4o-mini 사용 (저렴, 빠름)
- 각 기관당 핵심 키워드 3개 추출
- 예상 시간: 300-800ms
- LLM 배치 호출
-
[응답] 추천 결과 반환
- 응답 형식:
{ "recommendations": [ { "institutionId": 1, "matchScore": 0.92, "keywords": ["치매 전문", "24시간 간호사", "정원"] }, { "institutionId": 5, "matchScore": 0.89, "keywords": ["당뇨 관리", "친절한 직원", "예배실"] } ], "responseTime": 850 }- 추천의 결과 db 저장
- 응답 시간을 목표로 얼마나 잡을까 ?
- 등록된 기관의 임베딩 데이터 생성 및 저장 기능
-
기관 정보 → 구조화된 텍스트 변환
-
bge-m3 모델 인코딩 (1024차원)
-
pgvector에 저장 + IVFFlat 인덱스 생성
-
기관
- 기본 정보: - 기관명: 사랑재 및 이용요양원 - 기관 유형: 요양원 - 서울시 송파구 인근 - 운영 및 이용 정보: - 운영 시간: 평일 08:00~20:00 토요일 09:00~15:00 - 기관 태그: - 전문 질환 태그: "치매", "당뇨" - 서비스 유형 태그: "주간 보호", "장기 요양" - 운영 특성 태그: "치매 전담", "24시간 간호사" - 환경 시설 태그: "정원", "예배실" - 리뷰 유형 태그: "친절한 직원" - 기관 설명: "치매 교육을 받은 요양보호사가 다수 근무하며 친절을 강조하는 기관입니다. 기독교 요양원으로서 예배실이 구비되어 있습니다." -
- 임베딩 배치 업데이트 기능
- 스케줄러: APScheduler 사용
- 주기: 매일 자정 또는 1시간마다
- dirty flag 확인 후 변경된 기관만 재생성
- 배치 처리로 API 응답 시간에 영향 없음
- 기관 추천 API
- 기관 추천 API 기능
POST /api/v1/recommendations- 요청:
{memberId, elderlyProfileId, text} - 응답:
{recommendations: [{institutionId, matchScore, keywords}], responseTime}
- 사용자 데이터 조회
- Member + ElderlyProfile JOIN 쿼리
- 캐싱: Redis (TTL: 5분)
- 에러 처리: 사용자 NotFound 404 반환
- 사용자 텍스트 생성 유틸
- 조회된 사용자와 어르신 데이터를 통해 텍스트 생성
- 중요: 사용자와 기관 모두 같은 관점으로 텍스트 작성
- 건강 상태, 인지 수준, 활동 수준
- 서비스 유형, 지역, 선호 태그
- 어르신 프로필 템플릿:
[어르신 프로필] - 기본 정보: - 연령: 만 82세 - 성별: 여성 - 건강 및 기능 상태: - 활동 수준: - HIGH: "혼자 외출 가능하고 일상생활 대부분 스스로 가능" - MID: "실내 활동 위주, 짧은 거리 보행 가능" - LOW: "거동이 많이 불편하고 이동 시 도움 필요" - BEDRIDDEN: "침대에서만 생활" - 인지 수준: 치매 초기로 가끔 기억력 저하와 길 잃음이 있습니다. - NORMAL: "인지 기능에 큰 문제 없음" - MILD_COGNITIVE_IMPAIRMENT: "경미한 기억력 저하가 있습니다." - MILD_DEMENTIA: "일상생활에 약간의 지장가는 수준입니다." - MODERATE_DEMENTIA: "치매 초기, 가끔 기억력 저하와 길 잃습니다." - SEVERE_DEMENTIA: "인지 저하가 심하고 지속적인 보호가 필요합니다." - 장기 요양 등급: 장기텍스트:요양 2등급 - 선호 태그: - 전문 질환 태그: "치매", "당뇨" - 서비스 태그: "주간 보호", "장기 요양" - 운영 특성 태그: "24시간 간호사" - 환경 시설 태그: "정텍 - 리뷰 유형 태그: "친절한 직원" - 추가 요청 텍스트: "저희 어머니가 사람 많은 곳을 힘들어 하세요"
- 텍스트 → 임베딩 변환
- bge-m3 모델 사용 (SentenceTransformer)
- normalize_embeddings=True로 코사인 유사도 최적화
- 재시도 로직: 3회, exponential backoff
- 타임아웃: 10초
- 사전 필터링 (SQL WHERE절)
- 제외 조건:
- 정원 초과 기관
- 휴업/폐업 상태
- 선호 조건:
- 서비스 타입 매칭 (MemberPreferenceTag)
- 지역 범위 (예: 사용자 주소 기준 반경 20km)
- 성능: 10,000개 → 3,000~5,000개로 축소
- 제외 조건:
- pgvector 코사인 유사도 계산
- SQL:
ORDER BY embedding <=> $1::vector LIMIT 5 - 인덱스:
USING ivfflat (embedding vector_cosine_ops) - 유사도 점수:
1 - (embedding <=> $1)(0~1 범위)
- SQL:
- 매칭 키워드 추출 (LLM 활용)
- 5개 기관을 1회 LLM 호출로 배치 처리
- 프롬프트:
사용자 프로필: {user_text} 매칭된 기관 5개: 1. {institution_1} 2. {institution_2} ... 각 기관이 사용자와 매칭된 핵심 이유를 키워드 3개로 요약. JSON 형식으로 반환: {"1": ["키워드1", "키워드2", "키워드3"], ...}- 모델: GPT-4o-mini (비용 효율적)
- 예상 시간: 300-800ms
- 직접 호스팅 방법
- HuggingFace Inference API 사용