AI 예상지출은 호출마다 40만~500만으로 흔들려 신뢰성 부족. 코멘트/팁만 AI 유지. 예상 지출은 프론트에서 결정적 계산으로 대체. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,8 +17,6 @@ public class AiCommentRequest {
|
||||
private long prevExpense; // 직전 달 지출 합계(대비용)
|
||||
private long budget; // 이번 달 예산 합계
|
||||
private long spent; // 이번 달 예산 대비 소비액
|
||||
private int elapsedDays; // 이번 달 경과일(현재 달일 때만 >0, 아니면 0)
|
||||
private int totalDays; // 이번 달 총일수
|
||||
private List<CategoryAmount> categories; // 분류별 지출 상위
|
||||
|
||||
@Data
|
||||
|
||||
@@ -14,6 +14,4 @@ public class AiCommentResponse {
|
||||
|
||||
private List<String> bullets; // 현황 관찰/코멘트
|
||||
private List<String> tips; // 절약 가이드(실행 가능한 제안)
|
||||
private Long forecast; // 월말 예상 지출(원) — 일회성 큰 지출 제외 추정. 없으면 null
|
||||
private String forecastNote; // 예상 지출 근거 한 문장
|
||||
}
|
||||
|
||||
@@ -114,18 +114,14 @@ public class AiEntryParser {
|
||||
if (!enabled() || summaryJson == null || summaryJson.isBlank()) return emptyComment();
|
||||
try {
|
||||
String system = """
|
||||
너는 한국어 개인 가계부 코치다. 아래 사용자의 이번 달 집계(JSON, 금액 단위 원)를 보고 세 가지를 만든다.
|
||||
너는 한국어 개인 가계부 코치다. 아래 사용자의 이번 달 집계(JSON, 금액 단위 원)를 보고 두 가지를 만든다.
|
||||
1) bullets: 현황 관찰 2~3개. 각 한 문장, 구체적 숫자 인용, 잔소리·과장 없이 담백하게.
|
||||
(예: 전월 대비 지출 증감, 예산 대비 소비 속도, 특정 분류 과다, 수입 대비 지출 비율)
|
||||
2) tips: 이 사용자의 지출 패턴에 딱 맞춘 '실행 가능한' 절약 제안 2~3개. 두루뭉술한 원칙 금지,
|
||||
구체적 분류·습관을 지목(예: "카페 지출이 8만원인데 주 2회로 줄이면 약 3만원 절약"). 각 한 문장, 제안형.
|
||||
3) forecast: 월말 예상 지출(원, 정수). 계산법 = '지금까지 실제 지출(expense)' + '남은 날 × 평소 하루 지출'.
|
||||
단, 노트북·가전·여행 등 '일회성 큰 지출'로 보이는 분류는 남은 날에 반복되지 않으므로 하루 지출 산정에서 제외한다.
|
||||
(평소 하루 지출 ≈ (expense − 일회성 큰 지출) ÷ elapsedDays). forecastNote 에 한 문장 근거(무엇을 일회성으로 봤는지).
|
||||
elapsedDays 가 0 이면(과거/미래 달) forecast=0, forecastNote="".
|
||||
반드시 아래 JSON '하나만' 출력한다(마크다운/설명 금지):
|
||||
{"bullets":["관찰1","관찰2"],"tips":["절약제안1","절약제안2"],"forecast":정수,"forecastNote":"근거 한 문장"}
|
||||
데이터가 빈약하면 bullets/tips 는 빈 배열, forecast=0.
|
||||
{"bullets":["관찰1","관찰2"],"tips":["절약제안1","절약제안2"]}
|
||||
데이터가 빈약하면 해당 배열을 빈 배열로.
|
||||
""";
|
||||
Map<String, Object> reqBody = Map.of(
|
||||
"model", model,
|
||||
@@ -147,14 +143,8 @@ public class AiEntryParser {
|
||||
JsonNode root = om.readTree(json);
|
||||
List<String> bullets = strArray(root.path("bullets"));
|
||||
List<String> tips = strArray(root.path("tips"));
|
||||
long forecast = Math.max(0, root.path("forecast").asLong(0));
|
||||
String forecastNote = root.path("forecastNote").asText("").trim();
|
||||
log.info("[ai-comment] 결과 — 코멘트 {}개, 절약팁 {}개, 예상지출 {}", bullets.size(), tips.size(), forecast);
|
||||
return AiCommentResponse.builder()
|
||||
.bullets(bullets).tips(tips)
|
||||
.forecast(forecast > 0 ? forecast : null)
|
||||
.forecastNote(forecast > 0 ? forecastNote : null)
|
||||
.build();
|
||||
log.info("[ai-comment] 결과 — 코멘트 {}개, 절약팁 {}개", bullets.size(), tips.size());
|
||||
return AiCommentResponse.builder().bullets(bullets).tips(tips).build();
|
||||
} catch (Exception e) {
|
||||
log.warn("[ai-comment] 실패: {}", e.toString());
|
||||
return emptyComment();
|
||||
|
||||
Reference in New Issue
Block a user