feat(ml): CCIP references exclude unconfirmed auto character tags + confirm trips detectors (m139)
Completes "no self-training": unconfirmed auto-applied character tags no longer seed CCIP references — character_references + the prototype builder (_current_fingerprints/_rebuild_one) gain a shared _positive_char_tag filter (human-applied OR operator-confirmed), mirroring the head-positive exclusion. Confirming a tag also has to move the change-detectors, or an incremental refresh/Retrain right after a confirm wouldn't fold the tag in (only the nightly full pass would): the CCIP global gate now counts character confirmations, and the head training fingerprint counts confirmations. Test for the CCIP path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
@@ -13,7 +13,7 @@ exact CCIP difference metric/threshold gets validated against the model during
|
||||
the hands-on eval. numpy is imported lazily (API worker has it via pgvector).
|
||||
"""
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy import exists, func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ...models import (
|
||||
@@ -23,8 +23,10 @@ from ...models import (
|
||||
MLSettings,
|
||||
Tag,
|
||||
TagKind,
|
||||
TagPositiveConfirmation,
|
||||
)
|
||||
from ...models.tag import image_tag
|
||||
from .training_data import _AUTO_SOURCES
|
||||
|
||||
# Cosine-similarity floor to call a figure the same character. The live setting
|
||||
# (ml_settings.ccip_match_threshold) drives it; this is only the fallback when no
|
||||
@@ -111,6 +113,17 @@ async def _ref_signature(session: AsyncSession) -> tuple:
|
||||
return (n_tags, n_regs, max_id, n_hygiene)
|
||||
|
||||
|
||||
def _positive_char_tag():
|
||||
"""Condition on the joined character image_tag: HUMAN-applied or operator-
|
||||
confirmed — NOT an unconfirmed auto-apply. Keeps an auto-tagged character from
|
||||
self-seeding CCIP references, so a ccip_auto misfire can't reinforce itself
|
||||
(milestone 139) — mirrors the head-training positive exclusion."""
|
||||
return image_tag.c.source.not_in(_AUTO_SOURCES) | exists().where(
|
||||
TagPositiveConfirmation.image_record_id == image_tag.c.image_record_id,
|
||||
TagPositiveConfirmation.tag_id == image_tag.c.tag_id,
|
||||
)
|
||||
|
||||
|
||||
async def character_references(session: AsyncSession) -> dict[int, list]:
|
||||
"""Per character-tag CCIP reference vectors: figure/face-region CCIP
|
||||
embeddings on UNAMBIGUOUS (single-character) images carrying that tag.
|
||||
@@ -128,6 +141,7 @@ async def character_references(session: AsyncSession) -> dict[int, list]:
|
||||
)
|
||||
.join(Tag, Tag.id == image_tag.c.tag_id)
|
||||
.where(Tag.kind == TagKind.character)
|
||||
.where(_positive_char_tag())
|
||||
.where(ImageRegion.kind.in_(_FIGURE_KINDS))
|
||||
.where(ImageRegion.ccip_embedding.is_not(None))
|
||||
.where(ImageRegion.image_record_id.in_(_single_character_images()))
|
||||
|
||||
Reference in New Issue
Block a user