Skip to content

Story Worker (Lambda)

개요

truloop-storySQS 기반 서버리스 Lambda 워커로, 룹의 미디어를 AI로 분석하여 리캡(코드: Story)을 자동 생성하는 서비스입니다. truloop-core가 SQS에 발행한 메시지를 소비하여, Gemini Vision으로 이미지를 분석하고 Claude로 블로그 스타일의 스토리를 생성한 뒤 결과를 truloop-core Internal API로 콜백합니다.

완전 Stateless 아키텍처로 DB를 사용하지 않으며, 모든 상태는 truloop-core가 관리합니다.

정보

truloop-ai-server와의 관계: truloop-ai-server는 하이라이트, 포스터, 이미지 분석 등 다양한 AI 콘텐츠를 Celery 기반으로 생성합니다. truloop-story는 리캡 생성만 전담하며, SQS + Lambda 기반의 독립적인 서버리스 아키텍처로 분리되어 있습니다. 리캡 생성의 긴 처리 시간과 독립적인 스케일링 요구사항을 반영한 설계입니다.


기술 스택

항목기술설명
런타임Python 3.13Lambda arm64
IaCServerless Framework 4Lambda + SQS 인프라 관리
AI 프레임워크pydantic-aiAgent 기반 AI 모델 통합
AI 프로바이더OpenRouterGemini Vision + Claude 라우팅
AWS SQS메시지 큐 + DLQ
HTTPhttpx비동기 HTTP 클라이언트
설정Pydantic Settings환경변수 기반 설정 관리
재시도tenacity지수 백오프 재시도
로깅loguruCloudWatch 호환 구조화 로깅

주요 기능

기능설명
이미지 분석Gemini Vision으로 각 이미지를 병렬 분석 (FallbackModel 지원)
이미지 그룹핑타임스탬프 기반 시간순 재배치 및 그룹핑 (30분 gap)
스토리 생성Claude로 text/media 교차 패턴의 블로그 스타일 스토리 생성
다국어 지원한국어, 일본어, 영어 3개 언어 완전 분리 프롬프트
DLQ 처리3회 실패 메시지를 DLQ에서 수신하여 Story를 FAILED로 마킹

프로젝트 구조

truloop-story/
├── handler.py                 # SQS 핸들러 (sqsWorker, dlqWorker)
├── config.py                  # Pydantic Settings 설정
├── models.py                  # Pydantic 모델 (StoryRequest, 블록 모델, API 모델)
├── services/
│   ├── image_analyzer.py      # Gemini Vision 이미지 분석 (pydantic-ai Agent)
│   ├── image_grouper.py       # 타임스탬프 기반 이미지 그룹핑 & 재배치
│   ├── story_generator.py     # Claude 스토리 생성 (pydantic-ai Agent)
│   ├── locale.py              # 로케일별 프롬프트/설정 (ko/ja/en)
│   └── api_client.py          # truloop-core Internal API 클라이언트
├── serverless.yml             # Serverless Framework 인프라 설정
└── pyproject.toml             # 의존성

처리 파이프라인


SQS 메시지 스키마

truloop-core가 발행하는 SQS 메시지의 구조입니다.

json
{
  "request_id": "UUID (멱등성 보장용)",
  "story_eid": "Story EID",
  "locale": "ko | ja | en (nullable)",
  "media_items": [
    {
      "media_url": "https://...",
      "media_eid": "media EID",
      "media_type": "image",
      "width": 1080,
      "height": 1920,
      "timestamp": "2026-03-10T15:30:00+09:00 (timezone-aware, nullable)",
      "comments": "JSON (nullable)",
      "tagged_users": ["이름1", "이름2"]
    }
  ],
  "loop_metadata": {
    "loop_eid": "loop EID",
    "title": "모임 제목",
    "description": "모임 설명",
    "location": {
      "display_name": "장소명",
      "place_id": "...",
      "map_provider": "GOOGLE_PLACES | KAKAO",
      "loc": [127.0, 37.5]
    },
    "datetime": "2026-03-10T18:00:00Z",
    "timezone": "Asia/Seoul",
    "participants": ["참가자1", "참가자2"]
  }
}

API 콜백

처리 완료 후 truloop-core Internal API로 결과를 전송합니다. 인증은 Bearer API Key 방식입니다.

엔드포인트: POST /core/internal/stories/{story_eid}/requests/{request_id}

json
{
  "status": "completed",
  "title": "스토리 제목",
  "thumbnail_url": "첫 번째 이미지 URL",
  "preview_text": "2-3문장 미리보기",
  "content_blocks": {
    "blocks": [
      { "type": "text", "order": 0, "content": "텍스트 내용" },
      { "type": "media", "order": 1, "media_eid": "...", "media_url": "...", "media_type": "image", "width": 1080, "height": 1920 },
      { "type": "text", "order": 2, "content": "다음 텍스트" }
    ]
  }
}

환경변수

변수명타입소스설명
OPENROUTER_API_KEYstringSSMOpenRouter API 키
OPENROUTER_BASE_URLstring기본값https://openrouter.ai/api/v1
IMAGE_ANALYSIS_MODELSstring기본값파이프(|) 구분 fallback 모델 목록
STORY_GENERATION_MODELstring기본값anthropic/claude-3.5-sonnet
TRULOOP_CORE_API_URLstringstage별dev: https://dev-api.truloop.app
INTERNAL_API_KEYstringSSMInternal API 인증 키
STAGEenumServerlessdev | prod
LOG_LEVELstring기본값INFO

정보

SSM Parameter Store 경로: /truloop-story/{stage}/OPENROUTER_API_KEY, /truloop-story/{stage}/INTERNAL_API_KEY


인프라 리소스

Lambda Functions

함수핸들러TimeoutMemory역할
sqsWorkerhandler.sqs_worker_handler300s1024MB메인 스토리 생성 처리
dlqWorkerhandler.dlq_handler30s256MB실패 메시지 처리 (FAILED 마킹)

SQS Queues

이름 패턴설정
Main Queue{stage}-truloop-story-queueVisibilityTimeout 360s, Long polling 20s, 14일 보관
DLQ{stage}-truloop-story-dlqmaxReceiveCount 3, 14일 보관

에러 처리 전략

상황동작SQS 재시도
일부 이미지 분석 실패성공한 이미지만으로 스토리 생성 계속 진행X
전체 이미지 분석 실패API에 FAILED 보고 후 success=True 반환X (재시도 방지)
스토리 생성 실패API에 FAILED 보고 후 success=True 반환X (재시도 방지)
API 보고 자체 실패success=False 반환O (SQS 재시도)
3회 재시도 후 최종 실패DLQ로 이동 → dlqWorker가 FAILED 마킹X (DLQ 처리)

주의

핵심 설계 원칙: 에러를 API에 보고할 수 있으면 success=True를 반환하여 SQS 재시도를 방지합니다. API 보고 자체가 실패한 경우에만 SQS 재시도가 발생합니다.


하위 문서

  • 아키텍처 — 처리 흐름, 이미지 분석/스토리 생성 파이프라인, 다국어 지원, 에러 처리 상세

관련 문서


변경 이력

날짜내용
2026-03-11최초 작성 — truloop-story 서비스 기술 문서