fix(优先组): #18 密钥明文直配 config(弃 .env 依赖);#5 appearances 改 distinct 视频数覆盖校准(防 reconcile 累加膨胀);#9 oracle_sync 拉取加锁防 trigger_now 与后台并发双拉;#11 LLM 合并命名解析 target 已有 canonical(统一显示名)

This commit is contained in:
ericwyuan
2026-08-21 14:22:28 +08:00
parent 6afdda582b
commit 034dca92b2
4 changed files with 43 additions and 9 deletions

View File

@@ -243,6 +243,26 @@ class OracleDB:
"""手动命名设置规范名label 可视为别名)。"""
self.upsert_person(label, canonical_name, source='manual')
def set_person_appearances(self, label: str, count: int, source: str = 'llm'):
"""覆盖设置出现次数reconcile 时用 distinct 视频数校准,避免累加膨胀)。"""
now = _now_iso()
row = self._conn.execute("SELECT * FROM people WHERE label=?", (label,)).fetchone()
if row:
if source == 'manual' or row['source'] != 'manual':
self._conn.execute(
"UPDATE people SET appearances=?, source=?, updated_at=? WHERE label=?",
(int(count), source, now, label))
else:
self._conn.execute(
"UPDATE people SET appearances=?, updated_at=? WHERE label=?",
(int(count), now, label))
else:
self._conn.execute(
"INSERT INTO people (label, canonical_name, first_seen, appearances, "
"source, updated_at) VALUES (?,?,?,?,?,?)",
(label, '', now, int(count), source, now))
self._conn.commit()
def get_people(self) -> List[sqlite3.Row]:
return self._conn.execute("SELECT * FROM people ORDER BY id ASC").fetchall()

View File

@@ -43,7 +43,8 @@ class PersonService:
# ------------------------------------------------------------------
def reconcile(self):
"""汇总 + LLM 合并一次。可由定时或手动触发。"""
# 1. 先把所有视频的 people_mentioned 同步进 people 表(标签级
# 1. 统计每个人物标签出现过的视频数(去重),校准 appearances防每次 reconcile 累加膨胀
label_videos: Dict[str, set] = {}
for v in self.db.get_all_videos():
try:
people = json.loads(v['people_json'] or '[]')
@@ -51,7 +52,9 @@ class PersonService:
people = []
for p in people:
if p and p not in ('无人', ''):
self.db.upsert_person(p, source='llm')
label_videos.setdefault(p, set()).add(v['id'])
for label, vids in label_videos.items():
self.db.set_person_appearances(label, len(vids), source='llm')
# 2. 收集未命名(无 canonical 或 canonical==label的标签 + 描述样本
rows = self.db.get_people()
@@ -66,12 +69,17 @@ class PersonService:
if not mapping:
return
# 3. 落库canonical 若是另一个 labeltarget解析为其已有 canonical保证同一身份统一显示名
label_to_canonical = {r['label']: (r['canonical_name'] or r['label']) for r in rows}
updated = 0
for label, canonical in mapping.items():
if label in manual:
continue # 手动命名优先
if canonical and canonical != label:
self.db.set_canonical(label, canonical, source='llm')
logger.info(f"PersonService: LLM 合并完成,更新 {len(mapping)}")
resolved = label_to_canonical.get(canonical, canonical)
self.db.set_canonical(label, resolved, source='llm')
updated += 1
logger.info(f"PersonService: LLM 合并完成,更新 {updated}")
def _collect_descriptions(self, labels: List[str]) -> Dict[str, List[str]]:
"""从 events 表收集每个标签出现时的描述样本。"""