ac7de71ecd
auto_review_worker(interval 3분·배치 300): review_status='pending' + ai_domain + ai_confidence>=0.9 인 문서를 review_status='approved' 자동승인 + audit (source_metadata.auto_reviewed). 저신뢰/미분류는 수동 큐 잔류. 재-LLM 호출 없음 (classify confidence 게이트 = 맥미니 부하 0). review_status 는 검색/RAG/digest 필터 미사용(게이트 실측) → 노출 변동 없이 검토 큐만 드레인. 되돌리기=audit 마커로 식별. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
73 lines
3.0 KiB
Python
73 lines
3.0 KiB
Python
"""검토 대기(review_status='pending') 자동 검토 — 고신뢰 자동승인 + 저신뢰 수동 잔류.
|
|
|
|
classify 가 이미 부여한 ai_confidence 를 게이트로 사용 — **재-LLM 호출 없음**(대량 2천건에
|
|
맥미니/GPU 부하 0, 분류 confidence 가 곧 AI 의 자기-신뢰도). ai_domain 보유 +
|
|
ai_confidence >= THRESHOLD 인 pending 문서를 review_status='approved' 로 자동승인하고
|
|
audit(source_metadata.auto_reviewed)를 남긴다. 저신뢰/미분류는 그대로 두어 수동 검토
|
|
큐(/inbox)에 잔류.
|
|
|
|
설계 근거(게이트 실측):
|
|
- review_status 는 inbox 카운트(dashboard) + 수집기 ingest 에서만 사용, 검색/RAG/digest/
|
|
ask 경로 필터에 **미사용** → 자동승인은 노출(검색결과) 변동 없이 검토 큐만 비운다.
|
|
- pending 2,161 중 ai_suggestion 보유 0 → 이 큐는 '분류 변경 제안'(accept_suggestion)이
|
|
아니라 '미검토 자동분류'. 승인 = review_status 플립.
|
|
배치·interval 점진 드레인(관찰·중단 가능). 되돌리기 = source_metadata.auto_reviewed 마커로
|
|
대상 식별 후 review_status='pending' 복원.
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import select
|
|
|
|
from core.database import async_session
|
|
from models.document import Document
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# 고신뢰 자동승인 바 (튜닝 가능). 실측 분포: >=0.9 → 1,981건 자동 / 저신뢰·미분류 ~180건 수동 잔류.
|
|
_CONFIDENCE_THRESHOLD = 0.9
|
|
# 한 틱 처리량 — 순수 DB UPDATE(LLM 없음)라 가볍지만, 2천 행 일괄 락 회피 위해 배치.
|
|
_BATCH = 300
|
|
|
|
|
|
async def run() -> None:
|
|
"""pending 고신뢰 문서를 배치 자동승인 (interval job, no-arg)."""
|
|
async with async_session() as session:
|
|
rows = (
|
|
await session.execute(
|
|
select(Document)
|
|
.where(
|
|
Document.review_status == "pending",
|
|
Document.deleted_at.is_(None),
|
|
Document.ai_domain.isnot(None),
|
|
Document.ai_confidence.isnot(None),
|
|
Document.ai_confidence >= _CONFIDENCE_THRESHOLD,
|
|
)
|
|
.order_by(Document.id)
|
|
.limit(_BATCH)
|
|
)
|
|
).scalars().all()
|
|
if not rows:
|
|
return
|
|
|
|
now = datetime.now(timezone.utc)
|
|
for doc in rows:
|
|
doc.review_status = "approved"
|
|
doc.source_metadata = {
|
|
**(doc.source_metadata or {}),
|
|
"auto_reviewed": {
|
|
"by": "confidence_gate",
|
|
"confidence": float(doc.ai_confidence),
|
|
"threshold": _CONFIDENCE_THRESHOLD,
|
|
"at": now.isoformat(),
|
|
},
|
|
}
|
|
doc.updated_at = now
|
|
await session.commit()
|
|
logger.info(
|
|
"auto_review: approved %d pending docs (ai_confidence >= %.2f)",
|
|
len(rows),
|
|
_CONFIDENCE_THRESHOLD,
|
|
)
|