Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 운영 DB는 저장소에 커밋하지 않는다. 배포 환경의 실제 경로를 지정한다.
ML_CATALOG_DB=./soundlog.db

ML_ALPHA=0.3
ML_AXIS_MIN_TRACKS=40
ML_AXIS_MIN_SOURCES=10
ML_PER_VIDEO_CAP=3
ML_PER_ARTIST_CAP=2
ML_POSITION_DECAY=0.01
ML_MAX_SEQ_LEN=256
ML_EMBED_MODEL=nlpai-lab/KURE-v1
ML_EXCLUDE_CHANNELS=

# 관광공사에서 발급받은 키를 배포 환경에서만 넣는다.
TOUR_API_KEY=
ML_TOUR_API=1
ML_TOUR_FALLBACK=1
ML_TOUR_DETAIL=1
ML_TOUR_BUDGET=2.0
ML_TOUR_DETAIL_BLOCKING=0
ML_TOUR_OVERVIEW_QUERY=0
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @KimJaegeol1 @manNomi
15 changes: 15 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## 변경 내용

- 변경 내용을 작성해주세요.

## 확인 방법

- [ ] `python -m compileall -q .`
- [ ] `python tools/make_fixture_db.py`
- [ ] `python tests/test_swap.py`

## 운영 영향

- [ ] `/recommend` 응답 계약 변경 여부를 `API.md`에 반영했습니다.
- [ ] 운영 DB 또는 임베딩 산출물 변경 여부를 설명했습니다.
- [ ] 환경 변수 변경 여부를 `.env.example`에 반영했습니다.
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: CI

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

jobs:
logic-regression:
name: Logic regression
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: requirements-ci.txt

- name: Install light dependencies
run: python -m pip install -r requirements-ci.txt

- name: Compile Python sources
run: python -m compileall -q -x '/\.venv/' .

- name: Build fixture database
run: python tools/make_fixture_db.py

- name: Run logic regression
run: python tests/test_swap.py
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Soundlog 모델 서비스

[![CI](https://github.com/SoundLogTeam/soundlog-ml/actions/workflows/ci.yml/badge.svg)](https://github.com/SoundLogTeam/soundlog-ml/actions/workflows/ci.yml)

이 저장소는 `SoundLogTeam`이 관리하는 공식 ML 저장소다. 운영 API는
[`SoundLogServer`](https://github.com/SoundLogTeam/SoundLogServer)가 담당하며
이 서비스의 `/recommend` 응답을 사용한다. 저장소 운영 원칙과 배포 자산의
경계는 [`docs/REPOSITORY_OPERATIONS.md`](docs/REPOSITORY_OPERATIONS.md)에 정리했다.

좌표 + 온보딩(여행 상태·무드) → **추천 곡 10 + 배경 사진 1**을 돌려주는 FastAPI 서비스.
2026 관광데이터 활용 공모전 출품작의 ML 컴포넌트다.

Expand All @@ -26,9 +33,9 @@ POST /recommend {x, y, state, mood}
```

곡과 사진이 **같은 POI**를 본다 — 배경은 해수욕장인데 곡은 옆 음식점
기준이 되는 어긋남을 없애기 위해서다. 관광공사 TourAPI는 더 이상 부르지
않는다(대표 장소를 구조적으로 누락해서 자체 카탈로그로 피벗 —
`recommend.py` 상단 주석에 근거).
기준이 되는 어긋남을 없애기 위해서다. 대표 POI는 자체 카탈로그에서 먼저
고른다. 카탈로그 범위를 벗어난 좌표에는 관광공사 TourAPI를 폴백으로 사용한다.
`recommend.py` 상단 주석에 설계 근거가 있다.

## 폴더 지도

Expand All @@ -52,6 +59,10 @@ POST /recommend {x, y, state, mood}

## 실행

운영 실행에는 Git에 포함되지 않는 `soundlog.db`와 `data/photo_embs.npy`가
필요하다. `.env.example`을 `.env`로 복사한 뒤 실제 자산 경로와 관광공사 키를
설정한다. 운영 자산은 저장소에 커밋하지 않는다.

```bat
:: 로컬 (Windows)
.venv\Scripts\activate
Expand All @@ -69,6 +80,22 @@ nohup .venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 >> server.log 2>&1 &
장소 축 지원표(4/8), POI 카탈로그·별칭·부모 수가 전부 찍힌다.
침묵하는 단계가 없으니, 로그에 없는 건 안 돈 것이다.

## 모델 없이 회귀 검사

CI는 대용량 KURE 모델과 운영 DB를 내려받지 않는다. 대신 저장소에 포함된
fixture 생성기로 같은 스키마의 소형 DB를 만든 뒤 계약과 추천 로직을 검사한다.

```bash
python -m venv .venv
. .venv/bin/activate
pip install -r requirements-ci.txt
python tools/make_fixture_db.py
python tests/test_swap.py
```

이 검사는 코드 계약을 검증한다. 실제 추천 품질과 운영 데이터 정합성은
`TESTING.md`의 실전 경로로 별도 확인해야 한다.

## 배포가 제대로 됐는지 30초 확인

```
Expand Down Expand Up @@ -115,6 +142,7 @@ DIST_HALF`. POI 선택이 카탈로그로 옮겨가 요청당 호출 예산이

- **TESTING.md** — 기능별 테스트 명령·기대 출력·트러블슈팅
- **API.md** — `/recommend` 응답 계약. 백엔드에 이 파일만 전달하면 된다
- **docs/REPOSITORY_OPERATIONS.md** — 조직 소유권, 운영 자산, 변경 절차
- 설계 근거는 각 파일 **상단 주석**에 그 자리에서 남겼다
(왜 TourAPI를 버렸나 → recommend.py, 왜 부족 축은 안 섞나 →
soundlog_place.py, 왜 POI 선택에 임베딩을 안 쓰나 → poi_select.py,
Expand Down
7 changes: 7 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# 보안 문제 제보

비밀 키 노출과 인증 우회와 원격 코드 실행 가능성처럼 공개하면 위험한 문제는
일반 이슈로 등록하지 않는다. GitHub 저장소의 Security 탭에서 비공개 보안
제보를 사용하거나 SoundLogTeam 조직 관리자에게 직접 알린다.

일반적인 기능 오류와 문서 오류는 GitHub 이슈로 등록할 수 있다.
54 changes: 54 additions & 0 deletions docs/REPOSITORY_OPERATIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# SoundLog ML 저장소 운영 원칙

## 기준 저장소

공식 ML 저장소는 `SoundLogTeam/soundlog-ml`이다. 개인 계정의 기존 저장소는
조직 이관 전 이력을 확인하는 용도로만 남긴다. 새 기능과 이슈와 배포 기준은
조직 저장소에서 관리한다.

운영 API의 기준 저장소는 `SoundLogTeam/SoundLogServer`다. 과거 API 개발
스냅샷은 `SoundLogTeam/soundlog-api`에 이관 기록으로 보존한다. 두 API
저장소를 동시에 운영하지 않는다.

## 권한과 변경 절차

`KimJaegeol1`은 이 저장소의 관리자다. 저장소 설정과 시크릿과 배포 환경을
관리할 수 있다. 코드 변경은 작업 브랜치에서 시작하고 pull request의 CI를
통과한 뒤 병합한다. `main`에 직접 푸시하지 않는다.

## Git에 포함하지 않는 운영 자산

다음 파일은 크기가 크거나 배포 환경에서 생성되는 산출물이므로 Git에 넣지
않는다.

- `soundlog.db` 또는 `soundlog_serve.db`
- `data/photo_embs.npy`
- `.env`
- 모델 캐시와 서버 로그

운영 배포는 위 자산이 준비됐는지 별도로 확인해야 한다. `soundlog.db`는 현재
추적된 CSV만으로 운영 데이터 전체를 재생성할 수 없다. 자산이 없는데도 배포가
가능하다고 판단하면 안 된다.

사진 임베딩은 `data/photos_ready.csv`와 행 순서가 일치해야 한다. 두 파일의
행 수가 다르면 시작 과정에서 재계산될 수 있고 시작 시간이 크게 늘어난다.

## 검증 단계

pull request의 CI는 fixture DB와 가짜 인코더를 사용한다. 이 검사는 다음
항목을 확인한다.

- Python 소스가 컴파일되는지 확인한다.
- 추천 데이터 스키마와 교정 규칙이 유지되는지 확인한다.
- `/recommend` 응답에 `backgroundImageUrl`이 항상 존재하는지 확인한다.
- POI가 없는 경우에도 음악 추천이 실패하지 않는지 확인한다.

CI는 실제 KURE 모델의 추천 품질과 운영 DB의 내용과 외부 TourAPI 연결을
증명하지 않는다. 배포 전에는 `TESTING.md`의 실제 모델 테스트와 HTTP 스모크를
추가로 수행한다.

## 비밀 정보

`.env.example`에는 변수 이름과 안전한 기본값만 둔다. 관광공사 키와 서버
접속 정보와 토큰은 GitHub 환경 또는 서버 비밀 저장소에서 관리한다. 키가
커밋되면 즉시 폐기하고 새 키를 발급한다.
6 changes: 6 additions & 0 deletions requirements-ci.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# 대용량 모델을 받지 않는 CI 및 로직 회귀 검사 의존성
fastapi>=0.115,<1
python-dotenv>=1,<2
numpy>=1.26,<3
pandas>=2.2,<3
pydantic>=2.9,<3
5 changes: 3 additions & 2 deletions tests/test_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@

import numpy as np

_ROOT = os.path.normpath(
os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir))
os.environ.setdefault("ML_CATALOG_DB",
os.path.join(os.path.dirname(os.path.abspath(__file__)),
"soundlog_serve.db"))
os.path.join(_ROOT, "soundlog_serve.db"))

DIM = 1024

Expand Down
Loading