Skip to content

AI 추천 생성 엔드포인트 구현 - #10

Open
cfcromn wants to merge 4 commits into
developfrom
feature/recommendation-endpoint
Open

AI 추천 생성 엔드포인트 구현#10
cfcromn wants to merge 4 commits into
developfrom
feature/recommendation-endpoint

Conversation

@cfcromn

@cfcromn cfcromn commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

✨ 작업 내용

이번 PR에서 어떤 작업을 했는지 간단히 요약해주세요.

POST /internal/ai/recommendations 를 구현했습니다. Spring 이 보낸 후보 종목 중 상승 확률이 가장 높은 하나를 골라 추천 카드로 반환하고, 결정 로그를 남깁니다.

  • 추천 엔드포인트 — 후보별 추론 → 최고 확률 선택 → 로그 기록 → 응답 (app/recommendation/)
  • decision_type 컬럼 추가TRADING / RECOMMENDATION
  • participant_id / ai_strategy nullable 완화 — 추천은 챌린지 단위라 둘 다 없음
  • 결정 로깅 유틸 확장 — 매매 결정과 추천이 같은 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 없음), featurestrading-decisions 와 같은 형태입니다. 응답은 명세 그대로입니다.

이 합의 전까지 Spring 쪽 클라이언트는 붙지 않습니다. 다른 형태를 원하시면 말씀해 주세요.

스키마를 왜 건드렸는지

추천도 decisionId 를 반환해야 하니 decision_log 를 남겨야 하는데, participant_idai_strategy 가 NOT NULL 이라 쓸 수가 없었습니다. 추천은 챌린지 단위로 생성되므로 둘 다 nullable 로 완화했습니다.

decision_type 을 추가한 건 재학습 때문입니다. 매매 결정과 추천이 구분 없이 한 테이블에 쌓이면 학습 데이터가 오염됩니다.

추천 로그의 actionBUY 고정입니다

추천 카드는 매수 후보 제시라는 뜻이고, 전략이 없어 decide_action() 의 시장별 임계값을 쓸 수 없습니다.

reasonText 는 템플릿입니다

GPT 를 쓰지 않았습니다 — CLAUDE.md 상 추천은 LightGBM 담당이고 GPT 는 피드백/리포트 전용입니다. SHAP 가 붙으면 상위 기여 피처를 문장에 넣도록 ponytail: 주석으로 자리를 표시해 두었습니다.

동작 확인 결과

요청 응답
추천 (후보 2개) 200 {"symbolId":10,"reasonText":"US 시장 AAPL의 상승 확률이 50%로 후보 중 가장 높습니다.","probability":0.5,"decisionId":1}
매매 결정 (회귀 확인) 200 {"decisionId":2,"action":"HOLD",...} — 안 깨짐
candidates 422 body.candidates: List should have at least 1 item...
키 없음 401

decision_logs 에 두 타입이 구분되어 남는 것을 확인했습니다:

 id | decision_type  | participant_id | ai_strategy | symbol_code | action
  1 | RECOMMENDATION |         (null) |      (null) | AAPL        | BUY
  2 | TRADING        |              7 | STABLE      | AAPL        | HOLD

마이그레이션은 upgrade → downgrade -1 → upgrade 왕복을 확인했고, downgrade 가 행을 지우지 않는 것도 확인했습니다 (NULL 이 0 / STABLE 로 메워짐).

알려진 한계

downgrade -1 후 다시 upgrade 하면 추천 행의 decision_typeserver_defaultTRADING 으로 돌아옵니다. 컬럼을 drop 하면서 값이 사라지기 때문입니다. 되감았다 되돌린 DB 로 재학습을 돌리면 추천 로그가 매매 결정으로 섞이는데, 백업 테이블을 두는 건 과해 보여 그대로 뒀습니다.


✅ 체크리스트

  • 문서(README, .env.example 등) 변경이 필요한 경우 작성 또는 수정했나요? (.env.example 은 변경 없음. 다만 CLAUDE.md 의 "피처 엔지니어링은 이 서비스에서 계산" 문구가 실제 구조와 어긋나 수정이 필요한데, 코드와 섞지 않으려고 별도 작업으로 미뤘습니다)
  • 작업한 코드가 정상적으로 동작하는 것을 직접 확인했나요?
  • 필요한 경우 테스트 코드를 작성하거나 수정했나요?
  • Merge 대상 브랜치를 올바르게 설정했나요?
  • PR에 관련 없는 작업이 포함되지 않았나요?
  • 적절한 라벨과 리뷰어를 설정했나요? (라벨만 설정, 리뷰어 미지정)

📎 관련 이슈(선택)

@cfcromn cfcromn added ✅ Test Test관련사항 ✨ Feature 신규 기능 labels Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 신규 기능 ✅ Test Test관련사항

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant