From 7c74fec079a3ca94d485e0d0437e55bc4c824855 Mon Sep 17 00:00:00 2001 From: "hehihoho3@gmail.com" Date: Tue, 4 Aug 2026 18:47:55 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=BB=B7=EB=B3=84=20=EB=8C=93=EA=B8=80?= =?UTF-8?q?=20=EB=B0=B0=EC=A0=95=EC=9D=84=20=EC=BB=B7=20=EC=9A=B0=EC=84=A0?= =?UTF-8?q?=EC=9D=B4=20=EC=95=84=EB=8B=8C=20=EC=88=9C=EC=9C=84(=EB=9D=BC?= =?UTF-8?q?=EC=9A=B4=EB=93=9C)=20=EC=9A=B0=EC=84=A0=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 컷 하나를 4순위까지 다 채운 뒤 다음 컷으로 넘어가면, 앞 컷의 약한 근거(4순위 좋아요)가 뒤 컷의 강한 근거(3순위 단어 겹침)보다 먼저 댓글을 가로챈다. 라운드를 바깥 루프로 두어(전 컷의 ts → 전 컷의 ai → 전 컷의 word → 전 컷의 like) 순위가 컷 순서를 이기게 했다. Co-Authored-By: Claude Opus 5 (1M context) --- capcut_agent/recommend.py | 53 +++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/capcut_agent/recommend.py b/capcut_agent/recommend.py index c9911a2..002c70e 100644 --- a/capcut_agent/recommend.py +++ b/capcut_agent/recommend.py @@ -97,7 +97,12 @@ def build_cut_picks(cuts, comments, ai_picks: Optional[Dict[int, List[int]]], 단어 겹침이 AI 다음인 이유: Gemini 는 문맥까지 보고 고르니 더 정확하지만, 503 등으로 실패하거나(`ai_picks=None`) 문맥상 연결을 놓칠 때(예: "와인 뱉는 장면" ↔ "싱크대로 달려간 이유"는 겹치는 단어가 없다) 그물을 하나 더 치는 것 — 네트워크 없이도 항상 동작한다. - 중복은 앞 컷이 가져간다(뒤 컷은 다음 후보로 밀린다). + + ⚠ 컷별로 4단계를 다 채우고 다음 컷으로 넘어가면 안 된다 — 앞 컷의 약한 근거(4순위 + 좋아요)가 뒤 컷의 강한 근거(3순위 단어 겹침)보다 먼저 댓글을 가져가 버린다. 그래서 + **단계(라운드)를 바깥 루프, 컷을 안쪽 루프**로 둔다 — 전 컷의 ts를 다 채운 뒤에야 + 전 컷의 ai로, 그다음에야 word로 넘어간다. 순위가 컷 순서보다 우선한다. + 중복은 앞 컷이 가져간다(뒤 컷은 다음 후보로 밀린다) — `used` 는 라운드·컷을 통틀어 공유. ai_picks: {컷인덱스: [댓글idx …]} — Gemini 실패 시 None/{} 어느 쪽을 넘겨도 타임스탬프만으로 채운다(`ai_pick_cuts` 는 실패를 None 으로 알린다). @@ -107,33 +112,43 @@ def build_cut_picks(cuts, comments, ai_picks: Optional[Dict[int, List[int]]], valid = {c["idx"] for c in comments} no_ts = [c for c in comments if not c.get("times")] used: set = set() - out: List[List[dict]] = [] + quota = [quotas[i] if i < len(quotas) else 0 for i in range(len(cuts))] + out: List[List[dict]] = [[] for _ in cuts] + + # 1라운드: ⭐시각 for i, cut in enumerate(cuts): - quota = quotas[i] if i < len(quotas) else 0 - picks: List[dict] = [] for idx in match_ranges(comments, [(cut["start"], cut["end"])]): - if len(picks) >= quota: + if len(out[i]) >= quota[i]: break if idx not in used: - picks.append({"idx": idx, "why": "ts"}) + out[i].append({"idx": idx, "why": "ts"}) used.add(idx) + # 2라운드: 🤖AI + for i in range(len(cuts)): + if len(out[i]) >= quota[i]: + continue for idx in (ai_picks.get(i) or []): - if len(picks) >= quota: + if len(out[i]) >= quota[i]: break if idx in valid and idx not in used: - picks.append({"idx": idx, "why": "ai"}) + out[i].append({"idx": idx, "why": "ai"}) used.add(idx) - if len(picks) < quota: - for idx in _word_match_ranked(cut, comments, used): - if len(picks) >= quota: - break - picks.append({"idx": idx, "why": "word"}) - used.add(idx) - if len(picks) < quota: - for idx in top_liked(no_ts, used, quota - len(picks)): - picks.append({"idx": idx, "why": "like"}) - used.add(idx) - out.append(picks) + # 3라운드: 🔤단어겹침 + for i, cut in enumerate(cuts): + if len(out[i]) >= quota[i]: + continue + for idx in _word_match_ranked(cut, comments, used): + if len(out[i]) >= quota[i]: + break + out[i].append({"idx": idx, "why": "word"}) + used.add(idx) + # 4라운드: ➕좋아요 + for i in range(len(cuts)): + if len(out[i]) >= quota[i]: + continue + for idx in top_liked(no_ts, used, quota[i] - len(out[i])): + out[i].append({"idx": idx, "why": "like"}) + used.add(idx) return out