fix(insta-lab): ranked가 judge에 보낼 후보를 상위 30개로 cap
미사용 키워드 대량 누적 시 judge 프롬프트/응답이 토큰 한도를 넘어 파싱 실패→claude 신호 전부 null로 degrade되던 문제(프로덕션 확인됨) 해결. base score 상위 JUDGE_CANDIDATE_CAP(30)개만 judge·선별에 적용해 claude 신호 일관 보장. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -153,6 +153,11 @@ def list_keywords(
|
|||||||
return {"items": db.list_trending_keywords(category=category, used=used)}
|
return {"items": db.list_trending_keywords(category=category, used=used)}
|
||||||
|
|
||||||
|
|
||||||
|
# judge(Claude)에 보낼 최대 후보 수 — 미사용 키워드 대량 누적 시 응답 truncation으로
|
||||||
|
# claude 점수가 전부 null로 degrade되는 것을 방지 (base score 상위 N개만 평가).
|
||||||
|
JUDGE_CANDIDATE_CAP = 30
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/insta/keywords/ranked")
|
@app.get("/api/insta/keywords/ranked")
|
||||||
def ranked_keywords(
|
def ranked_keywords(
|
||||||
limit: int = Query(20, ge=1, le=100),
|
limit: int = Query(20, ge=1, le=100),
|
||||||
@@ -162,6 +167,10 @@ def ranked_keywords(
|
|||||||
candidates = db.list_trending_keywords(used=False)
|
candidates = db.list_trending_keywords(used=False)
|
||||||
if not candidates:
|
if not candidates:
|
||||||
return {"items": []}
|
return {"items": []}
|
||||||
|
# base score 상위 JUDGE_CANDIDATE_CAP개로 제한 → judge·선별 동일 집합에 적용(claude 신호 일관)
|
||||||
|
candidates = sorted(
|
||||||
|
candidates, key=lambda c: float(c.get("score", 0.0)), reverse=True
|
||||||
|
)[:JUDGE_CANDIDATE_CAP]
|
||||||
issued = db.list_recent_issued_topics(window_days=dedup_window_days)
|
issued = db.list_recent_issued_topics(window_days=dedup_window_days)
|
||||||
prefs = {p["category"]: p["weight"] for p in db.get_preferences()}
|
prefs = {p["category"]: p["weight"] for p in db.get_preferences()}
|
||||||
claude_scores = selection_judge.judge_candidates(candidates)
|
claude_scores = selection_judge.judge_candidates(candidates)
|
||||||
|
|||||||
@@ -76,3 +76,28 @@ def test_ranked_respects_dedup_window(client):
|
|||||||
hwanul = next((i for i in items if i["keyword"] == "환율"), None)
|
hwanul = next((i for i in items if i["keyword"] == "환율"), None)
|
||||||
assert hwanul is not None, "'환율' 항목이 ranked 응답에 없음"
|
assert hwanul is not None, "'환율' 항목이 ranked 응답에 없음"
|
||||||
assert hwanul["eligible"] is True, "발행 이력 없는 키워드는 eligible True여야 함"
|
assert hwanul["eligible"] is True, "발행 이력 없는 키워드는 eligible True여야 함"
|
||||||
|
|
||||||
|
|
||||||
|
def test_ranked_caps_candidates_to_judge(client, monkeypatch):
|
||||||
|
"""후보가 많아도 judge(Claude)에는 base score 상위 N(JUDGE_CANDIDATE_CAP)개만 전달.
|
||||||
|
|
||||||
|
운영에서 미사용 키워드가 대량 누적되면 judge 프롬프트/응답이 토큰 한도를 넘어
|
||||||
|
파싱 실패 → claude 신호가 전부 null로 degrade되던 문제 방지.
|
||||||
|
"""
|
||||||
|
for i in range(40):
|
||||||
|
db.add_trending_keyword({"keyword": f"kw{i}", "category": "economy", "score": i * 0.01})
|
||||||
|
captured = {}
|
||||||
|
|
||||||
|
def fake_judge(cands):
|
||||||
|
captured["n"] = len(cands)
|
||||||
|
captured["min_score"] = min(c["score"] for c in cands)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
monkeypatch.setattr("app.selection_judge.judge_candidates", fake_judge)
|
||||||
|
r = client.get("/api/insta/keywords/ranked?threshold=0.0&limit=100")
|
||||||
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
from app.main import JUDGE_CANDIDATE_CAP
|
||||||
|
assert captured["n"] == JUDGE_CANDIDATE_CAP, "judge에는 cap 개수만 전달돼야 함 (전체 X)"
|
||||||
|
# 상위 score만 전달됐는지 — 최저 score 후보(0.0)는 제외됐어야 함
|
||||||
|
assert captured["min_score"] > 0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user