AI 추천 생성 엔드포인트 구현 - #10
Open
cfcromn wants to merge 4 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
✨ 작업 내용
POST /internal/ai/recommendations를 구현했습니다. Spring 이 보낸 후보 종목 중 상승 확률이 가장 높은 하나를 골라 추천 카드로 반환하고, 결정 로그를 남깁니다.app/recommendation/)decision_type컬럼 추가 —TRADING/RECOMMENDATIONparticipant_id/ai_strategynullable 완화 — 추천은 챌린지 단위라 둘 다 없음record_decision()을 사용기존 인증(
app/shared/http.py), 에러 포맷, camelCase 스키마 베이스, LightGBM 추론(app/trading_ai/predictor.py)은 그대로 재사용했습니다.🔍 리뷰 시 참고사항
기존 명세의 요청은
{challengeId, market}뿐인데, 이것만으로는 고를 종목도 반환할symbolId도 알 수 없습니다. Spring 이 후보를 실어 보내는 형태로 바꿔야 합니다:{ "challengeId": 1, "market": "US", "candidates": [ {"symbolId": 10, "symbol": "AAPL", "features": {"rsi": 55.0, "volatility": 0.3}} ] }symbolId는 Spring symbol 테이블 PK 를 ID 로만 참조하고(크로스 DB, FK 없음),features는trading-decisions와 같은 형태입니다. 응답은 명세 그대로입니다.이 합의 전까지 Spring 쪽 클라이언트는 붙지 않습니다. 다른 형태를 원하시면 말씀해 주세요.
스키마를 왜 건드렸는지
추천도
decisionId를 반환해야 하니decision_log를 남겨야 하는데,participant_id와ai_strategy가 NOT NULL 이라 쓸 수가 없었습니다. 추천은 챌린지 단위로 생성되므로 둘 다 nullable 로 완화했습니다.decision_type을 추가한 건 재학습 때문입니다. 매매 결정과 추천이 구분 없이 한 테이블에 쌓이면 학습 데이터가 오염됩니다.추천 로그의
action은BUY고정입니다추천 카드는 매수 후보 제시라는 뜻이고, 전략이 없어
decide_action()의 시장별 임계값을 쓸 수 없습니다.reasonText는 템플릿입니다GPT 를 쓰지 않았습니다 — CLAUDE.md 상 추천은 LightGBM 담당이고 GPT 는 피드백/리포트 전용입니다. SHAP 가 붙으면 상위 기여 피처를 문장에 넣도록
ponytail:주석으로 자리를 표시해 두었습니다.동작 확인 결과
200 {"symbolId":10,"reasonText":"US 시장 AAPL의 상승 확률이 50%로 후보 중 가장 높습니다.","probability":0.5,"decisionId":1}200 {"decisionId":2,"action":"HOLD",...}— 안 깨짐candidates422 body.candidates: List should have at least 1 item...401decision_logs에 두 타입이 구분되어 남는 것을 확인했습니다:마이그레이션은
upgrade → downgrade -1 → upgrade왕복을 확인했고, downgrade 가 행을 지우지 않는 것도 확인했습니다 (NULL 이0/STABLE로 메워짐).알려진 한계
downgrade -1후 다시upgrade하면 추천 행의decision_type이server_default인TRADING으로 돌아옵니다. 컬럼을 drop 하면서 값이 사라지기 때문입니다. 되감았다 되돌린 DB 로 재학습을 돌리면 추천 로그가 매매 결정으로 섞이는데, 백업 테이블을 두는 건 과해 보여 그대로 뒀습니다.✅ 체크리스트
.env.example등) 변경이 필요한 경우 작성 또는 수정했나요? (.env.example은 변경 없음. 다만 CLAUDE.md 의 "피처 엔지니어링은 이 서비스에서 계산" 문구가 실제 구조와 어긋나 수정이 필요한데, 코드와 섞지 않으려고 별도 작업으로 미뤘습니다)📎 관련 이슈(선택)