포트폴리오로 돌아가기
hamster_locally — AI 아키텍처
한국 여행 팁 문화 로컬라이즈 서비스 · 데이터 계층부터 다중턴 대화까지
실제 동작 화면
hamster_locally 동작 화면 — 일본어 문화 로컬라이즈 검색(좌) + 중국어 여행 일정 생성(우)

일본어 검색 "一人でも入りやすい?" — 한국 1인 식당 문화를 일본 규범 기준으로 설명 · SOURCE TIPS · FOLLOW-UP 자동 생성 (좌)  /  중국어 3일 서울 여행 일정 생성 (우)

1. 전체 데이터 흐름
Google Sheets
Master DB
KO 원본 팁 127개
장소·카테고리 구조화
Apps Script API
Sheets 웹앱 엔드포인트
sheet 파라미터 라우팅
JSON 직렬화 반환
loader.py
Pydantic 모델 파싱
로컬 캐시 (sheets_tips.json)
중복 Sheets 호출 방지
PostgreSQL (Supabase)
tips — KO 마스터
tip_translations — 번역 캐시
(tip_id, lang) PK upsert
2. 문화 로컬라이즈 파이프라인 (Batch)
번역 철학 — 단순 번역이 아님
"이 나라 사람에게 한국이 어떻게 다른가"를 설명하는 관점으로 재작성.

1. 자국과 뭐가 다른지 — 명시적으로
2. 자국 기준 어색할 수 있는 부분 — 이름 붙이기
3. 그래도 괜찮다는 안심 — 자신감 주기
모델 설정
gemini-2.5-pro

temperature 0.3 — 낮추면 번역투, 높이면 사실 왜곡
maxOutputTokens 8192
responseMimeType application/json (구조화 강제)
KO 원본 팁 ↓ [프롬프트 조립] tip 7개 필드 (situation · action · why · foreigner_trap · solutions ×3) + lang_name + reader 프로필 + home_norm (자국 규범 상세) + key_friction (마찰 포인트) ↓ Gemini Pro API 1회 호출 ↓ JSON 파싱 → tip_translations upsert (tip_id, lang) PK → 중복 안전 총 127팁 × 3언어 = 최대 381회 이미 번역된 건 DB에서 직접 읽기
언어Reader 프로필home_norm 핵심key_friction
EN Western traveler (US/UK/AU) 직원이 먼저 옴, 팁 필수 15-20%, 첫 줄 서기 신성 큰 소리로 직원 부르기, 팁 없음, T-money
ZH 中国大陆游客 위챗/알리페이 만능, 흥정 가능, 배달 앱 생활화 QR페이 제한, 흥정 불가, VPN 없으면 중국 앱 차단
JA 日本人旅行者 벨/버튼으로 조용히 호출, 큰 소리 실례, Suica 카드 큰 소리 호출 강한 거부감, T-money 별도 구매, 조용히 기다리다 놓침

3. 2-pass RAG 검색 — 토큰 효율 + 품질 분리
핵심 아이디어: Compact Index → Full Data 2단 분리
팁 127개 전체를 한 번에 Pro에 넣으면 컨텍스트 낭비 + 속도 저하.
1차에서 Flash로 빠르게 관련 ID만 고른 뒤, 2차에서 선택된 팁만 Pro에 보내 품질 극대화.
사용자 쿼리 입력
lang 감지
ChatSession.search() 진입
1차: 팁 ID 선택
Flash
Compact 인덱스 (ID + category + situation 80자)
thinking OFF · temperature 0.0
→ 관련 tip_id max 3개 + confidence 반환
2차: 답변 생성
Flash (thinking ON)
선택된 팁 전체 필드 + 문화 컨텍스트
temperature 0.4 · max 16000 tokens
→ title · answer · follow_up 구조화 반환
Search Cache
키: {lang}:{정규화 쿼리}
TTL: 7일
쿼리 정규화 (소문자·특수문자 제거)
동일 쿼리 AI 재호출 없음
DB 미스 시 폴백
팁 DB에 매칭 없어도 문화 컨텍스트(home_norm)는 항상 주입.
AI가 Korea 지식 + reader 문화 차이를 기반으로 답변 생성.
confidence: "low"로 표시.
thinking 예산 제어
1차 선택: thinkingBudget: 0 → 빠른 ID 선택만
2차 답변: thinking 활성화 → 문화 맥락 추론 품질 극대화
속도 vs 품질을 패스별로 명시적으로 분리.

4. 다중턴 대화 세션 — 폴백 체인 + 히스토리 관리
모델 폴백 체인
GLM-4 (1차) → 실패 시 → Gemini 2.5 Pro (폴백)

GLM-4: Z.AI API (구름 교육과정 무료 제공 키 활용)
Gemini: GCP Vertex AI (서비스 계정 토큰)
히스토리 관리
최대 20턴 유지 → 초과 시 오래된 순 자동 제거
컨텍스트 윈도우 과부하 방지

ask() — 팁 컨텍스트 주입 + 구조화 JSON 강제
reply() — 순수 대화, 구조화 없이 자유 응답
search() — 2-pass RAG (위 3번 플로우)
ChatSession(lang="en") ↓ ask(query, tip_context=[tip1, tip2]) ↓ [시스템 프롬프트 조립] 페르소나 (언어별 4종) + 팁 컨텍스트 (번역 적용된 버전) + JSON 출력 형식 강제 ↓ history + 현재 user message → AI 호출 GLM-4 시도 → 실패 시 Gemini Pro 폴백 ↓ 응답 history에 누적 (최대 20턴) ↓ AIResponse { title, answer, tips_used, confidence, follow_up (2-3개 자동 생성) }

5. 3계층 캐시 전략
L1 — Sheets 캐시
sheets_tips.json
Google Sheets API 호출 결과 로컬 저장.
use_cache=True면 Sheets 호출 없이 파일 직접 읽기.
tips / all 시트별 파일 분리 (덮어쓰기 방지).
L2 — 번역 캐시 (DB)
tip_translations (PG)
(tip_id, lang) PRIMARY KEY → upsert 안전.
Pro 모델 배치 번역 결과 영구 저장.
동일 팁 재번역 없음 — 381회 → 0회 수렴.
L3 — 검색 캐시
search_cache.json
키: {lang}:{정규화 쿼리} — 소문자·특수문자·공백 정규화.
TTL 7일 (여행 팁은 자주 안 바뀜).
만료 엔트리 접근 시 자동 삭제 후 재생성.

6. 일일 큐레이션 — 결정론적 날짜 시드
결정론적 선택 보장
날짜 → MD5 해시 → 정수 시드.
같은 날 어떤 서버에서 호출해도 항상 같은 팁 반환.
카테고리 7종 순환: food → transport → manner → daily → shopping → place → hospital
pick_daily(tips, lang="en") ↓ seed = MD5(date.isoformat()) → int category = CYCLE[seed % 7] pool = tips[category == cat] tip = pool[seed % len(pool)] ↓ get_translation(tip.id, lang) ← L2 캐시 히트 ↓ DailyTipResponse 반환

7. AI 핸들링 패턴 — 설계 의사결정
패턴 적용 위치 의도
모델 티어링 배치 번역: Pro / 실시간 검색: Flash 비용 × 품질 트레이드오프 명시적 분리. 1회성 고품질 vs 매 요청 저비용.
2-pass RAG search() — 1차 ID 선택, 2차 답변 생성 전체 팁 컨텍스트 낭비 방지. 관련 팁만 Pro에 노출해 응답 품질 유지.
thinking 예산 제어 1차: thinkingBudget=0 / 2차: thinking ON 선택 단계는 속도, 생성 단계는 추론 품질. 각 패스의 역할에 맞게 명시적 설정.
문화 컨텍스트 주입 번역 프롬프트 + 답변 생성 모두 home_norm + key_friction으로 AI가 "차이"를 판단하는 근거 제공. DB 미스 시에도 적용.
구조화 출력 강제 번역 · 검색 · 대화 모든 AI 호출 responseMimeType=application/json + 출력 형식 명시. 파싱 실패 방지.
폴백 체인 ChatSession — GLM-4 → Gemini Pro 단일 모델 의존성 제거. GLM-4 장애·할당량 초과 시 무중단 서비스 유지.
히스토리 슬라이딩 윈도우 ChatSession.history (max 20턴) 컨텍스트 무한 증가 방지. 오래된 턴 자동 제거로 토큰 비용 통제.
결정론적 큐레이션 curator.pick_daily() — MD5 날짜 시드 AI 호출 없이 날짜만으로 재현 가능한 결과. CDN 캐싱 / 서버리스 환경에 유리.