Commit graph

3 commits

Author SHA1 Message Date
changukyu
6be263a69d feat: LLM 을 요약기에서 증거 판독기로 (구조화 인용 + 입력 경계 + 3B)
사용자 결정: LLM 역할을 "요약"에서 "증거 판독 + 구조화 판단"으로 확대,
타깃에 GPU 있음. 최종 판정은 여전히 사람이 한다.

## 입력 경계

프롬프트가 증거 data 딕셔너리를 통째로 f-string 에 박아 제출건당
13,733~62,291 자를 만들고 있었다. 화이트리스트 9개 필드 + 상위 12건으로
제한한다. promotion_reason 을 제외한 것이 핵심 — 모델이 그대로 베껴서
"요약"이 증거 목록 복붙이 됐다. 얼굴 크롭 좌표 / privacy_note /
thumbnail_url 도 모델 입력에서 뺀다(요약 입력이 아니다).

선별은 위험을 실제로 지는 행 우선: 지문 유사도 0.9 이상 -> full/partial/page
매칭과 promoted -> 얼굴 -> 나머지.

## 구조화 인용과 검증

format=json 으로 {claims:[{text, evidence}], needs_check:[]} 를 받고, 각
claim 이 인용한 라벨이 실제로 프롬프트에 들어간 라벨 집합의 부분집합인지
검사해 아니면 버린다. 화면에 렌더되는 텍스트는 검증을 통과한 claim 으로만
조립하므로 근거 없는 문장이 운영자에게 도달할 경로가 없다.

인용을 JSON 필드로 받는 것은 의도적이다. 산문을 문장 분해해 개체(URL, 숫자)를
대조하는 방식은 'confirming the image's authenticity [E3]' 같은 근거 없는
서술어를 100% 통과시켜, 오히려 환각 문장에 "근거 확인됨" 도장을 찍는다.
집합 포함 검사는 그 실패 모드가 구조적으로 발생하지 않는다.

증거 id 대신 [E1] 라벨을 쓴다 — 24자 해시를 소형 모델에게 받아쓰게 하면
오타와 조작을 구별할 수 없다.

grounded_claims / dropped_claims 를 증거에 기록한다. 모델이 지어내기
시작하면 수치로 드러난다.

## 모델과 컨텍스트

기본 모델 qwen2.5:0.5b-instruct -> qwen2.5:3b-instruct. 0.5B 는 출력 계약을
지키지 못했다(영어로 답하고 증거 목록을 반복했다).

num_ctx 를 명시한다. Ollama 기본 컨텍스트는 수천 토큰이고 넘으면 조용히
자르는데, source_urls / source_evidence_ids 는 잘리지 않은 전체 증거에서
뽑혀 붙었다. 즉 모델이 읽은 적 없는 근거를 근거라고 표시하고 있었다.
num_predict / keep_alive / timeout(30->120s) 도 환경변수로 노출.

## 시드 잠금 해제

_ensure_llm_summary 호출 2곳을 _write_lock 밖으로 옮긴다. record_decision 이
같은 락을 쓰므로, 폴더 재적재 중에는 운영자가 보고 있지도 않은 이미지의
요약이 끝날 때까지 판정 버튼이 대기했다. 시드 1회당 20건 상한을 두되 조용히
자르지 않고 로그를 남긴다(only-if-missing 이라 다음 재적재에서 이어진다).

이 이동은 provider 커버리지 회귀를 만들었다 —
_sync_submission_provider_state 가 잠금 구간 안에서, 즉 나레이션 전에 돌아
요약이 존재하는데도 llm 을 not_run 으로 보고했다. 나레이션이 실제로
발생했을 때만 커버리지를 다시 계산한다.

pytest 515 passed.
2026-07-25 23:34:02 +09:00
유창욱
e9a15e8110 fix: harden operator HTTP server
Remove wildcard CORS (prevented cross-origin reads of biometric/case data
from localhost), add optional shared-token auth gate on data routes
(COPYRIGHTER_AUTH_TOKEN; GUI shell + /health stay open), cap request body
size (413), and map malformed JSON to 400 and SQLite lock contention to 503.
2026-06-20 18:18:54 +09:00
유창욱
3f7b3a9cf2 chore: initial commit of copyrighter (rights_filter)
Image rights / copyright detection system: SQLite store, HTTP app,
search integrations (Naver, Google Custom Search, Google Cloud Vision
web detection), image analysis (fingerprints, face/person detection,
evidence enrichment, risk scoring), an admin/review layer, governance
and retention policies, batch jobs, and a browser-based operator GUI.

This baseline incorporates a full code-review remediation pass
(46 fixes; 358 tests passing). Highlights:

CRITICAL
- Prevent evidence cascade-delete during the schema-constraint
  migration by disabling FK enforcement around the table rebuild.

Security
- Sandbox served media (neutralize stored XSS from uploaded/collected
  SVGs) via CSP + nosniff on the untrusted media routes.
- Strip embedded EXIF/GPS from external image derivatives before they
  are sent to third-party APIs.
- Return a clean 404 (not an uncaught StopIteration) for PATCH on an
  unknown provider.

Correctness
- LLM-summary failures no longer add +30 to the risk score.
- Decode only explicit JS escapes so Korean image URLs are not mangled.
- Consume search quota only after a successful request.
- Naver/Google adapters map responses inside the failure boundary, so a
  malformed response degrades to evidence instead of crashing enrichment.
- Domain-aware provider attribution; face-box IoU de-duplication; count
  searches (not result items); per-box crop isolation; clamp evidence
  confidence and Google CSE num; real submittedEpoch; and more.

Robustness
- Offline LLM connect fast-fails (short connect timeout) so seed/reload
  requests are not stalled; full read timeout preserved for generation.
- Malformed numeric env vars fall back to defaults instead of crashing
  startup.

Performance
- Per-submission evidence reads (no full-table scan per rescore),
  audit-log LIMIT, lazy active-store lookup, hoisted timestamps.

Tests
- ~24 regression tests added pinning the above fixes.

Runtime data (data/, outputs/, *.sqlite3, *.log), secrets (.env), and
node_modules are gitignored.
2026-06-09 09:50:31 +09:00