POSA_Copyrighter/docs/brainstorms/2026-07-25-ai-capability-expansion-brainstorm.md
changukyu 6bbaaf6e23 fix: 형식 예시 반향 차단, 기본 모델을 실측으로 qwen2.5:7b 로 확정
실모델 프로브에서 인용 검증만으로는 못 잡는 실패 모드가 드러났다.

## 형식 예시 반향(template echo)

qwen2.5:0.5b 는 프롬프트의 형식 예시를 그대로 돌려주면서(claim text 가
"<한국어 문장>") 실제 라벨 12개를 전부 인용했다. 집합 포함 검사는 이것을
완벽히 통과시킨다 — 인용은 전부 진짜이기 때문이다. 결과적으로 완전히 비어
있는 출력이 "근거 있음" 판정을 받았다.

그라운딩 검증은 "인용이 진짜인가"와 "내용이 진짜인가"를 둘 다 봐야 한다.
형식 예시 문자열을 llm_prompts 의 상수로 두고 그 반향을 거부한다. 앵글
브래킷으로 감싼 다른 스탠드인도 같은 실패로 취급한다.

가짜 transport 테스트로는 이 결함을 잡을 수 없었다 — 스크립트된 응답은
언제나 잘 만들어진 JSON 이기 때문이다.

## 기본 모델

저장된 실제 증거(제출물 #3, 54행 중 12행 선별, 프롬프트 1,507자)로 GPU
타깃에서 측정:

  qwen2.5:0.5b-instruct  55.7s  형식 예시 반향, 계약 위반
  qwen2.5:7b              8.8s  계약 준수, claim 2 + 확인필요 1   <- 채택
  qwen3:8b               35.7s  계약 준수, thinking 모델이라 4배 느림

3b 는 빌드 머신에 없고 7b 는 이미 있어 에어갭 반입 비용이 0 이다. 브레인스토밍
문서의 "7B 는 인터랙티브 불가" 는 CPU 전제였고 이 배포에는 해당하지 않는다.

pytest 517 passed.
2026-07-25 23:48:23 +09:00

68 KiB
Raw Permalink Blame History

2026-07-25 AI 기능 확장 브레인스토밍

세션: 5렌즈 발산 30건 → 항목별 적대적 실현가능성 검증(코드·DB 실측 동반, 6건 REFUTED 탈락) → 생존 24건을 심사 3인(운영자·기술리드·제품책임자)이 0~10 채점. 기준 HEAD: 8b5b3f5 (fix/2026-07-11-review-remediation). 작성일 2026-07-25.

이 시스템에서 "AI"라고 부를 수 있는 것은 사실상 하나뿐이다 — analysis/llm_assistance.py의 Ollama qwen2.5:0.5b-instruct 요약이고, 그것마저 risk_scoring.py가 EvidenceSource.LLM_SUMMARY를 명시적으로 0점 처리해 판정에 기여하지 않는다. 나머지 지능은 전부 손으로 쓴 규칙이다: 검색 쿼리 생성 321줄 템플릿(analysis/search_query_generation.py), 위험 점수 174줄 정수 가중치(analysis/risk_scoring.py), 2001년식 Haar cascade 얼굴 검출(analysis/face_person_detection.py). 이 문서는 "더 큰 모델을 붙이자"가 아니라 지금 이미 켜져 있는 AI가 조용히 고장나 있는 지점을 먼저 고치고(프롬프트가 증거의 90%를 잘라먹는 문제, 시드 경로가 쓰기 락을 잡은 채 LLM을 도는 문제), 그 위에 의존성 0짜리 낭독형 신호(EXIF/IPTC 권리 필드, YuNet 검출 신뢰도)를 얹은 뒤, 판정 라벨이 실제로 쌓인 다음에야 학습·자동화로 간다는 3단계 순서를 제안한다. 심사 3인은 각각 다른 렌즈(운영자·기술리드·제품책임자)에서 출발했지만 1순위가 만장일치로 같았다 — 「LLM 예산·계측·입력 경계」(평균 7.90, 3인 중 최고점 단독 1위).

이 심사가 준 가장 중요한 신호: "새 AI 모델을 붙이는" 계열은 전부 하위권으로 내려갔고(의미지문 인덱스 2.70, 판정예측 트리아지 2.30, 워터마크 검출기 4.50), 상위권은 이미 있는 AI를 정직하게 만드는 것과 모델 없이 얻는 신호(EXIF 권리필드 7.23, YuNet 7.30, 방송캡처 포렌식 6.53)가 차지했다. "AI를 적극 활용한다"의 이 코드베이스에서의 실제 번역은 더 큰 모델이 아니라 ① 지금 켜져 있는 LLM이 근거를 실제로 읽게 만들고 ② 모델 없이 얻을 수 있는 신호를 먼저 전부 줍고 ③ 판정 라벨이 쌓인 뒤에 학습을 켠다는 순서다.

현재 AI 표면 진단

축 현재 구현 실제 성격 사각지대
생성형 LLM analysis/llm_assistance.py:22 OllamaGenerateLlmClient (qwen2.5:0.5b, temp 0.1, timeout 30s) 요약 1종. risk_scoring.py에서 0점 options에 num_ctx 없음 → 증거 대부분이 모델에 도달 전 절단. 저장된 llm 증거 9행에 summary 키 자체가 없다 — 요약 본문이 영속화되지 않아 운영자에게 한 번도 보인 적이 없다 ※

※ 이 한 줄은 미커밋 작업 트리에서 이미 고쳐지고 있다. store_serialization.py의 _evidence_payload에 summary/sourceUrls가 추가돼 있다(현재 git diff에 존재). DB의 9행이 비어 있는 것은 2026-05-26에 생성된 과거 데이터다. 다만 이 수정은 문제의 방향을 바꾼다 — 요약이 드디어 화면에 보이기 시작하므로, 잘린 증거로 만든 요약의 품질 결함이 이제 운영자 눈앞에 노출된다. 1위 항목(입력 경계)의 시급성은 이 수정으로 낮아지는 게 아니라 높아진다. | 프롬프트 | llm_assistance.py:133 _evidence_prompt | data={item.data} 딕셔너리 통째 f-string | 제출건당 13,73362,291자(최대 63행). 근거 링크는 잘리지 않은 전체 목록에서 뽑혀 붙음 | | 위험 점수 | analysis/risk_scoring.py 174줄 | 손튜닝 정수 가중치 + 밴드 70/30 하드코딩 | 학습·캘리브레이션 0. 실측 밴드 분포 high 4 / medium 2 / low 0 — 트리아지 변별력 붕괴 | | 쿼리 생성 | analysis/search_query_generation.py 321줄 | 한/영 템플릿 규칙 + generic 블록리스트 | plan()의 priority가 API 표면에 안 나옴. _is_generic_local_query 필터 결과가 GUI 추천에 반영 안 됨 | | 로컬 CV(얼굴) | analysis/face_person_detection.py:_detect_with_opencv | Haar frontal + profile 2종 | person_count = face_count — 사람 축이 사실상 부재. confidence는 internal_analyzer.py에서 0.8 하드코딩. 제출 1건당 detect가 2회 호출됨(internal_analyzer.py:78, store_enrichment.py:184) | | 로컬 CV(지문) | analysis/fingerprints.py:_perceptual_hash | 이름은 pHash, 실제는 8x8 average hash | 크롭 한 번에 무력화. 실측: 무관한 제출 이미지 쌍이 이미 0.250.781을 냄 | | 텍스트/로고 | 없음 | — | 이미지 표면에 저작권자가 직접 써 놓은 워터마크·ⓒ를 읽는 경로가 0줄 | | 원본 메타데이터 | analysis/preprocessing.py:169-171 | "raw pixel 재구성으로 EXIF/GPS/ICC 전량 파기" | 파기 전에 읽는 코드가 없음. getexif/getiptcinfo src 전역 0건 | | 사람 라벨 활용 | risk_scoring.py:15 하드 스킵 + export 컬럼 1개 | operator_status를 점수에서 빼는 용도만 | 통계로 되먹이는 코드 0줄 | | 계측·재현성 | 없음 | — | model/prompt/latency 필드가 증거 payload 40여 개 중 0개. COPYRIGHTER_LLM_DAILY_LIMIT은 화면 표시용이고 아무것도 막지 않음 | | 죽은 코드 | analysis/evidence_enrichment.py:EvidenceEnricher, jobs/review_enrichment_job.py | 테스트에서만 인스턴스화 | 실제 LLM 호출 지점은 server/store_operations.py:471 _ensure_llm_summary 한 곳 — 발산 단계 아이디어 3건이 이 죽은 코드를 수정 대상으로 오지목했다 |

검증 단계에서 확정된 데이터 실측치 (이 문서의 모든 라벨 의존 판단의 근거):

  • submissions 6건 — 전부 decision_status='unreviewed'. 'Operator decision created' 감사 이벤트 0건.
  • evidence 292행 — operatorStatus 전부 None. 백업 DB에 false_positive 2행이 전 이력.
  • queryStrategy 유의미 값 0행(None 288 / '' 4). entity 0행, promoted 0행, domainSource 전부 None.
  • 즉 "사람이 만든 라벨과 근거 페어가 이미 축적 중"이라는 전제는 이 레포에서 확인되지 않는다.

제약 조건

  1. 폐쇄망 번들 — 런타임 서드파티는 numpy / opencv-python-headless / pillow 3개뿐이고 requirements.txt 헤더가 그것을 명시 선언한다. 4번째 의존성 추가(onnxruntime 4055MB, defusedxml 25KB)는 문서 한 줄 수정이 아니라 반입 심사 안건이다. 모델 자산은 별도: 3B Q4_K_M +약 1.9GB, 7B +4.7GB, OCR ONNX 5672MB(제안된 "12MB"는 약 5배 과소), 롤백 태그 유지 시 +379MiB.
  2. CPU 지연 — 발산 단계 6개 아이디어가 전부 프리필(prompt eval)을 무시했다. 생성 속도만 대역폭으로 추정했으나 프리필은 연산 바운드다. 실측: 현행 프롬프트·증거 10건에서 20스레드 5.9초 / 2스레드 20.6초, 증거 20건이면 28.2초 / 72.7초로 timeout=30을 확정적으로 초과. 3B Q4는 압축 프롬프트에서도 60~180초. 7B는 현재 근거로 인터랙티브 불가.
  3. 거버넌스 — (a) governance/policies.py:86 assert_no_biometric_template은 top-level 키 3개만 보는 얕은 검사지만, 하드 바운더리는 코드가 아니라 사양 문서 두 곳(docs/operations/image-rights-risk-filter.md:16, docs/project-introduction-and-technical-implementation.md:209)의 "Do not store face embeddings ... or biometric templates" 문구다. 전용 테이블 우회는 코드는 통과해도 사양은 통과하지 못한다. (b) DataClass.LLM_SUMMARY의 근거링크 강제는 현재 형식적으로만 통과 중 — 링크는 전체 증거에서, 요약은 잘린 증거에서 나온다. (c) 신청자 노출 경계: memo가 APPLICANT_EXPORT_FIELDS에 포함되므로 LLM 출력이 decision_memo_presets에 착지하면 신청자에게 그대로 나간다.
  4. 판정 주체는 사람 — 프롬프트 계약(llm_assistance.py:43-49)에 명시. 개별 판정뿐 아니라 판정 규칙 생성도 이 계약에 걸린다(규칙은 판정의 집합 형태).
  5. 배관 비용 — 새 EvidenceSource 1종을 추가하려면 records.py / _ui_source / _ui_group / _evidence_payload / _domain_evidence_from_ui / _domain_source_from_ui / risk_scoring 분기 / app.js 3곳 / store_operations.py:488 LLM 입력 필터 / operator-labels.js — 12곳을 한 커밋에 고쳐야 하고, 라운드트립 점수 보존 테스트가 선행돼야 한다. _ui_source의 폴백이 "fingerprint"라 누락 시 조용히 오분류된다.
  6. 인프라 부재 — src 전역에 백그라운드 스레드 0개, 잡 큐·상태 테이블·폴링 엔드포인트 0개. 463개 테스트 중 스레드를 다루는 것 0개. SQLite는 WAL이 아니다(store_persistence.py:19). rerun_enrichment는 명시적으로 동기다. "비동기 경로에 태운다"는 경로 변경이 아니라 없는 서브시스템을 만드는 것이다. (정정: 발산·검증 단계에서 "SQLite가 WAL이 아니다"라는 서술이 나왔으나 사실이 아니다. sqlite_store.py:162 initialize()가 pragma journal_mode = wal을 설정하고 이 설정은 DB 파일에 영속되므로 _connect()가 매번 다시 걸 필요가 없다 — 실제 data/copyrighter.sqlite3도 wal을 반환한다. 워커 쓰기가 HTTP 읽기를 블록하는 위험은 이미 해소돼 있다.)

후보 전체 (심사 점수순)

심사 3인(운영자 / 기술리드 / 제품책임자)이 각각 독립으로 0~10 채점한 평균. 점수 인플레 금지 조건을 걸어 전체 평균은 4.7이다.

순위 아이디어 렌즈 평균 운영자 기술 제품 검증 verdict 공수(원안→수정후) 오늘 착수
1 LLM 예산·계측·입력 경계 비용·쿼터·감사 7.90 7.5 8.2 8.0 FEASIBLE M (입력경계 단독 S) 가능
2 YuNet 초상권 정밀화 로컬 비전 7.30 7.0 7.4 7.5 FWC M 가능
3 원본 메타 권리추출 로컬 비전 7.23 7.2 7.0 7.5 FWC S→M (EXIF+IPTC만 S) 가능
4 방송캡처 포렌식 로컬 비전 6.53 6.8 6.8 6.0 FWC S→M 가능(0점 라벨)
5 모델 번들 무결성 검증 폐쇄망 번들링 6.10 5.0 6.8 6.5 FWC M 가능
6 AI 선연산 잡 큐 지연·배치 6.07 6.2 6.0 6.0 FWC M 부분(락 이동만)
7 근거공백 액션 플래너 자동 다음 액션 6.00 5.8 6.2 6.0 FWC M→S 가능
8 작업별 모델 라우터 LLM 파이프라인 5.73 5.5 6.2 5.5 FWC M→L (1단계 S) 부분
9 판정 골든셋 회귀게이트 사람 판정 학습 5.63 4.5 6.4 6.0 FWC M 부분(각인만)
10 모델·프롬프트 버전 스탬프 재현성·감사 5.37 3.5 6.6 6.0 FEASIBLE S 가능
11 증거 영역 오버레이 증거 하이라이트 5.33 5.0 6.0 5.0 FWC M→L 좌표계 수정 선행
12 골든 케이스 회귀 하네스 오프라인 평가 5.03 3.8 5.8 5.5 FWC M→S 가능
13 요약 상태 배지(5단→3단) AI 실패 UX 4.90 4.8 5.4 4.5 FWC M 가능
14 인용형 증거팩 요약 LLM 파이프라인 4.50 4.2 4.8 4.5 FWC M→L 1위 선행 필수
15 워터마크·ⓒ 검출기 로컬 비전 4.50 4.8 3.2 5.5 FWC L→XL 모델 반입 심사
16 밴드 임계값 스윕·섀도 리스코어 사람 판정 학습 4.30 2.8 4.6 5.5 검증 미수행 M(원안) 라벨 대기
17 페이지 권리표기 추출 LLM 파이프라인 3.87 4.0 3.6 4.0 FWC L→XL 페치 재설계 선행
18 근거 신뢰도 재보정 사람 판정 학습 3.57 3.2 4.0 3.5 FWC S 가능(n=0 안전)
19 문장별 근거 인용 검증기 그라운딩 검증 3.03 2.3 3.8 3.0 FWC M→L 1위 선행 필수
20 근거한정 케이스 Q&A LLM 파이프라인 2.97 2.5 3.4 3.0 FWC L 라우터·캐시 선행
21 AI 수용률 계기판 AI 신뢰도 측정 2.87 2.2 3.4 3.0 FWC M→S 라벨 대기
22 의미지문 인덱스 로컬 비전 2.70 3.0 2.6 2.5 FWC L→XL 거버넌스 승인 필요
23 판정예측 트리아지 레인 트리아지 2.30 2.7 2.2 2.0 FWC L→XL 라벨 대기
24 근거프로파일 일관성 감시 사람 판정 학습 2.27 2.0 2.8 2.0 FWC L 라벨 대기

FWC = FEASIBLE_WITH_CHANGES(수정 조건부 실현가능). 24건 중 verdict가 무조건 FEASIBLE인 것은 1위와 10위 둘뿐이다. 16위는 검증 단계에서 판정이 기록되지 않았다 — 미검증 상태로 표기하며, 착수 전 별도 검증이 필요하다.

심사자 의견이 갈린 항목(참고용 — 착수 결정 시 이 갈림이 곧 리스크다):

  • 15위 워터마크·ⓒ 검출기 — 제품 5.5 vs 기술 3.2. 제품 렌즈는 "이미지 표면의 저작권 표기를 못 읽는 것이 가장 큰 탐지 공백"으로 보고, 기술 렌즈는 실측 템플릿 매칭 2,347ms/장과 로고 수에 선형인 비용을 근거로 반대했다.
  • 10위 모델·프롬프트 버전 스탬프 — 기술 6.6 vs 운영자 3.5. 운영자에게는 화면에 글씨 하나 느는 일이고, 기술 렌즈에는 이후 모든 모델 변경 판단의 전제다.
  • 12위 골든 케이스 회귀 하네스 / 9위 판정 골든셋 회귀게이트 — 두 항목 모두 기술·제품 대비 운영자 점수가 1.5~2.0점 낮다. 품질 기반 작업의 전형적 패턴으로, 운영자 체감이 0이라는 뜻이지 가치가 없다는 뜻은 아니다.

상위 후보 상세

배열 순서는 점수순이 아니라 착수 순서(선행조건 의존 관계)다. 각 제목에 심사 순위와 평균을 함께 표기했다. 점수 상위권 중 이 절에 상세가 없는 3건(4위 방송캡처 포렌식, 5위 모델 번들 무결성 검증, 8위 작업별 모델 라우터)은 바로 아래 「상위권 중 상세 미수록 3건」에 요약했고, 로드맵 2단계에 항목으로 들어가 있다.

1. LLM 예산·계측·입력 경계 (심사 1위 · 평균 7.90 · 1순위 만장일치 3/3, 공수 M → 입력경계 단독 S)

무엇을 — 세 가지 묶음이지만 입력 경계만 떼어 먼저 낸다(검증자 권고). (a) _evidence_prompt가 data 딕셔너리를 통째로 f-string에 박는 것을 화이트리스트(reason/match/similarity/page_title/domain/query)로 교체, (b) 증거 상한(위험 기여 상위 12건), (c) options에 num_ctx 명시. 이어서 별건으로 예산 집행(LlmCallPolicy)과 계측(p95 링버퍼).

왜 — 지연 감소가 아니라 거버넌스 결함 수정이다. 프롬프트는 잘리는데 source_urls/source_evidence_ids는 잘리지 않은 전체 증거에서 뽑혀 붙는다. 결과적으로 assert_operator_evidence_payload_allowed(LLM_SUMMARY)는 통과하지만 화면의 "연결 근거 N건"에서 N은 거짓이다. 모델이 본 적 없는 근거를 근거라고 표시하는 상태이며, 다른 23건 중 어느 것도 이 구멍을 닫지 않는다.

AI 구성요소·모델·지연 — 모델 변경 없음(qwen2.5:0.5b 유지). 추론 추가 0. 24건 중 유일하게 지연을 줄인다: 실측 20.6초 → 1.58.3초(중앙값 23초). 부수 효과로 축약 프롬프트에서 모델이 한국어 2단('요약:'/'확인 필요:') 형식을 지키기 시작했다(현행 프롬프트에서는 형식·언어 계약을 전부 무시하고 영어로 답했다).

코드 근거 — llm_assistance.py:133 _evidence_prompt(data 통째 덤프) · llm_assistance.py:52-53(options에 temperature만) · env_clients.py:57(COPYRIGHTER_LLM_DAILY_LIMIT이 파싱만 되고 naver/google과 달리 daily_limit=으로 배선되지 않음) · store_persistence.py:231-246 _apply_provider_usage_delta(usage 누적만, 상한 대조 없음) · store_operations.py:471 _ensure_llm_summary(실제 유일한 호출 지점).

폐쇄망 번들 — 증분 0바이트, 신규 의존성 0, 스키마 마이그레이션 0, 새 EvidenceSource 0. 텔레메트리 송출 없음(전부 로컬 SQLite).

가드레일 — face_crop_search/crop_index/privacy_note/thumbnail_url을 모델 입력에서 제외해 초상 경계를 프롬프트 레벨에서도 지킨다(assert_no_biometric_template과 정합). 쿼터 소진은 오류가 아니라 강등(SEARCH_SKIPPED 처리 방식과 동일).

검증자가 지적한 조건 — (1) 세 가지를 한 커밋에 묶지 말 것. (2) 화이트리스트만으로 부족 — 축약해도 56건이면 3,000토큰대라 증거 상한이 필수이고, num_ctx만 올리고 상한을 안 두면 2스레드 기준 프리필에만 약 17초가 들어 순증가한다. (3) promotion_reason은 화이트리스트에서 뺀다 — 모델이 그대로 베껴 요약이 증거 목록 복붙이 된다(실측 출력이 정확히 그랬다). (4) 예산 카운터의 진실원은 ExternalApiPolicy 복제가 아니라 SQLite provider payload로 — 복제하면 프로세스 메모리 카운터라 재시작 시 0으로 리셋돼 화면 usage와 집행 기준이 갈라진다. (5) p95 표본은 최근 200건 링버퍼로 상한 명시.

함께 넣을 것 — 심사자 3인이 공통 지목한 5줄 수술: _seed_from_image_store_locked(sqlite_store.py:310) 안의 _ensure_llm_summaries_for_existing_source_evidence 호출을 _write_lock 밖으로 이동. record_decision도 같은 락을 쓰므로, 지금은 폴더 재적재 중에 운영자가 판정 버튼을 눌러도 락 대기에 걸린다. store_operations.py:457-458이 이미 "락 밖에서 LLM 호출" 선례를 갖고 있다.

2. 원본 메타 권리추출 (심사 3위 · 평균 7.23, 공수 S→M / EXIF+IPTC만이면 S)

무엇을 — 외부 전송용 파생본을 만들며 메타데이터를 파기하기 전에, 원본 바이트에서 권리 필드만 화이트리스트로 읽어 증거로 만든다. EXIF 0x8298 Copyright / 0x013B Artist + IPTC CopyrightNotice(2:116)/Credit(2:110)/Source(2:115).

왜 — 저작권자가 파일 안에 직접 써 넣은 정답을 지금 100% 버리고 있다. 추론이 아니라 낭독이라 오탐 가능성이 사실상 없고, 히트하면 외부 API 왕복(수 초 + 쿼터) 없이 판정 근거가 선다. 미스하면 섹션이 아예 안 뜬다 — 리스크 구조가 이 목록에서 가장 깨끗하다(운영자 심사자 평).

AI 구성요소·모델·지연 — AI 모델 없음(컨테이너 파싱). 실측 0.1ms 미만/장. 픽셀 기반 AI-생성 판별기는 의도적으로 배제 — CPU/폐쇄망에서 도메인 시프트에 취약하고 반증 가능성이 없다.

코드 근거 — preprocessing.py:169-171 _upright_derivative("Rebuild from raw pixels so NO metadata survives") · preprocessing.py:build_external_derivative · image_store.py:60-68(metadata가 submission_id/title/file/format 4개뿐) · governance/policies.py:82 policy_for(미등록 DataClass는 KeyError → 신규 RIGHTS_METADATA 등록 강제).

폐쇄망 번들 — EXIF+IPTC는 pillow 12.2.0에 이미 포함, 증분 0. 단 XMP는 폐쇄망에서 조용히 죽는다: Pillow의 Image.getxmp()는 defusedxml이 없으면 경고와 함께 빈 dict를 반환하는데(직접 차단 실측), 이 개발 머신의 defusedxml은 anaconda/nbconvert/Scrapy가 끌고 온 것이고 requirements.txt에는 없다. 납품 타깃에서 XMP dc:rights·digitalSourceType·C2PA 선언 탐지는 전부 no-op이 된다.

가드레일 — GPS·카메라 시리얼·소유자명·XMP-mwg-rs Face regions는 읽지도 저장하지도 않는다(화이트리스트를 코드 상수로 두고 테스트로 고정). 메타데이터는 위조 가능하므로 "자기신고 정보"로 라벨링하고 자동 반려하지 않는다.

검증자가 지적한 조건 — (1) XMP/C2PA는 defusedxml 반입 안건으로 분리하고, 추가 전까지는 "이 배포에서는 XMP 미판독"을 기동 로그에 명시. (2) 스톡 자산 ID +70(단독 high)은 논리적으로 틀렸다 — Getty/Adobe 자산 ID는 정품 라이선스 구매본에도 그대로 박혀 배포된다. 올바른 귀결은 반려가 아니라 "라이선스 증빙 요구"이므로 +30(medium)으로 낮춘다. (3) recall이 낮다 — 카카오톡/인스타/트위터를 거친 이미지는 메타가 이미 제거돼 있어 히트율이 한 자릿수~십몇 %일 가능성. 따라서 GUI 섹션은 추출값이 하나라도 있을 때만 렌더(빈 섹션 금지). (4) 추출 훅은 InternalAnalyzer.analyze 진입부(원본 ImagePayload.content가 살아 있는 지점)에 둔다 — 파이프라인 순서 변경 불필요. (5) .svg가 SUPPORTED_IMAGE_SUFFIXES에 있으나 Pillow가 못 여니 추출 실패를 예외가 아니라 "메타 없음"으로 degrade.

3. YuNet 초상권 정밀화 (심사 2위 · 평균 7.30, 공수 M)

무엇을 — Haar cascade를 cv2.FaceDetectorYN(YuNet)로 교체하고 검출 신뢰도를 실제 값으로 채운다. 얼굴 면적 비율 + 검출 수로 "식별 가능한 근접 인물"과 "배경 군중"을 등급 분리. FacePersonSignal 인터페이스와 face_boxes 계약은 유지해 크롭·얼굴 웹탐지 경로 무변경.

왜 — 두 방향 동시 이득. 놓침: 뒷모습·마스크·군중·저조도 인물이 지금 전부 신호 0. 낭비: Haar 오탐 박스가 +35와 face_crop 파일을 소비한다. 그리고 밴드 분포가 이미 붕괴했다(실측 high 4 / medium 2 / low 0) — FACE_PERSON +35가 사람 있는 사진 전부에 붙어 medium 문턱을 혼자 넘기기 때문이다. 등급화는 트리아지 변별력을 직접 회복시킨다.

AI 구성요소·모델·지연 — YuNet ONNX 약 230~345KB, CPU 640x480 약 10ms. 현재보다 빨라진다: 실측 Haar 2캐스케이드 1920x1080 = 218ms/호출 × 건당 2회 = 약 440ms → YuNet 약 20ms. 순감 약 0.4초/장.

코드 근거 — face_person_detection.py:_detect_with_opencv(scaleFactor 1.1 / minNeighbors 4 고정) · face_person_detection.py:103(person_count = face_count) · face_person_detection.py:57-67(OpenCV 5.x가 CascadeClassifier를 제거한다는 경고가 이미 코드에 있음 — 이 교체가 그 미래 호환 문제까지 해소) · internal_analyzer.py:78과 store_enrichment.py:184(detect 중복 호출) · internal_analyzer.py(confidence 0.8 하드코딩).

폐쇄망 번들 — 파이썬 의존성 0(설치본 opencv-python-headless 4.13.0에 cv2.FaceDetectorYN 존재를 직접 확인). 모델 자산 345KB로 사실상 무시 가능.

가드레일 — YuNet이 반환하는 5점 랜드마크는 읽는 즉시 폐기하고 저장하지 않는다. 나아가 assert_no_biometric_template 금지 키에 landmarks/face_landmarks/face_descriptor 추가(호출부가 2곳뿐이라 기존 데이터 로드 무영향 — 정책 강화 방향, 무충돌).

검증자가 지적한 조건 — (1) 사람 검출기(YOLOv8n/MobileNet-SSD)는 별건으로 분리. cv2.dnn으로 YOLOv8 ONNX를 쓰려면 NMS를 직접 구현해야 하고 저사양 CPU에서 0.3~1.5s/장이다. YuNet 345KB/10ms와 같은 묶음으로 취급하면 안 된다. (2) 같은 커밋에서 detect() 중복 호출을 제거한다. (3) tier(등급 라벨)와 실제 신뢰도를 _evidence_payload와 _domain_evidence_from_ui 양쪽에 추가하고 라운드트립 점수 보존 테스트를 먼저 작성 — 안 하면 재채점 때마다 전 건이 +35로 복귀한다. (4) 등급화는 기존 medium 다수를 low로 이동시켜 decision_stats의 캘리브레이션 기준선을 리셋한다. 판정 0건인 지금이 이 비용을 치르기 가장 싼 시점이다(나중이면 판정 이력과 기준선이 어긋난다).

4. 판정 골든셋 회귀게이트 (심사 9위 · 평균 5.63 — 운영자 4.5 vs 기술 6.4, 공수 M / 각인 부분만 오늘 즉시)

무엇을 — 원안은 결정 케이스를 evidence 스냅샷과 함께 내보내 verify에서 재채점하는 하네스. 검증 결과 원안의 절반은 오늘 성립하지 않고, 나머지 절반은 오늘 당장 실효가 있다.

왜 (오늘 유효한 절반) — 검증자가 24건 전체에서 "가장 실효가 큰 한 줄짜리 지적"으로 꼽은 것: _rescore_submission이 12곳에서 riskScore를 덮어쓰므로 저장된 점수는 이미 왕복 점수다. 그것을 다시 왕복 채점과 비교하면 항상 일치한다. 즉 각인 없이 만든 점수 재현 테스트는 항상 통과하는 빈 테스트다. 막으려는 실제 버그(search_image 왕복 뒤집힘, risk_scoring.py:105-112 주석)는 원본 채점 vs 왕복 채점의 차이인데 원본 점수가 어디에도 저장되지 않는다.

AI 구성요소·모델·지연 — 모델 없음(평가 하네스). verify 단계에서만 도는 오프라인 하네스라 운영자 체감 0. 실측 1000건 전량 재채점 0.3초 미만.

코드 근거 — store_search_candidates.py:721-731 _rescore_submission 및 호출 12곳(sqlite_store.py:309,406,743 / store_enrichment.py:125,167,298,358 / store_operations.py:324,385,462 / store_search_candidates.py:160) · store_serialization.py:124(fileFacts.analysis가 리터럴 'v1', AnalysisRun.analysis_version은 submission payload에 각인되지 않음) · store_serialization.py:271-275(라운드트립 불변식이 주석으로만 존재).

폐쇄망 번들 — 신규 의존성 0. 비용은 번들이 아니라 반출 리스크: 실측 evidence 평균 632바이트 × 케이스당 약 49행 = 약 31KB/건, 1000건이면 약 31MB짜리 파일이 result URL·pageTitle·thumbnailUrl·내부 riskScore를 통째로 담는다.

가드레일 — 골든셋은 APPLICANT_EXPORT_FIELDS 경로와 물리적으로 다른 모듈에 두고, .gitignore + 납품 번들 생성 스크립트 제외 목록 + offline-install.md에 3중 명시.

검증자가 지적한 조건 — (1) 선행 필수: initialRiskScore / initialRiskBand / scorerVersion을 _submission_payload(store_serialization.py:106)에 추가하고 _rescore_submission이 절대 덮어쓰지 않게 한다. (2) 골든셋 생성에 export_decisions를 재사용하지 말 것 — 호출마다 'Decisions exported' 감사이벤트를 남기고 audit_events()는 최신 500행으로 잘리므로 verify가 돌 때마다 운영자 감사 화면이 노이즈로 밀려난다. (3) 지표 회귀는 CI 실패가 아니라 차이 리포트 출력만 — highBandApprovalRate는 코드 변경뿐 아니라 케이스 믹스 변화로도 움직인다. (4) 판정 100건 미만이면 계산하지 않는다. (5) 왕복 복원의 취약 지점(weak_hint가 저장되지 않고 제목 접두사 'Best guess label ' 문자열로 재구성됨, domainSource 미기록 레거시 행)을 스키마에 명시 필드로 굳혀 제목 문자열 의존을 끊는다. (6) 판정 0건이어도 (1)+(5)만으로 오늘 즉시 가치가 있다 — 라벨 없이 실제 버그를 잡을 수 있는 유일한 학습계 항목.

5. 모델·프롬프트 버전 스탬프 (심사 10위 · 평균 5.37 — 기술 6.6 vs 운영자 3.5, 공수 S · verdict FEASIBLE)

무엇을 — 프롬프트 리터럴을 analysis/llm_prompts.py로 분리하고 PROMPT_ID/PROMPT_VERSION 부여. /api/generate 응답의 model과 /api/tags의 digest를 Evidence.data에 심고, _evidence_payload에 modelVersion/promptVersion/latencyMs를 추가해 GUI에 "모델 qwen2.5:0.5b · 프롬프트 ko-v2 · 1.4s"를 작은 글씨로 노출.

왜 — 재현성 필드가 코드 전체에 단 하나도 없다. 모델을 올리거나 프롬프트를 고친 뒤 과거 요약과 신규 요약이 DB에 섞여도 구분이 불가능하다. 모델을 바꾸자는 모든 후속 제안이 이 스탬프 없이는 전후 비교를 못 한다. 부수 효과로 latency_ms가 쌓여 운영자 대기시간 실측치가 처음으로 생긴다.

AI 구성요소·모델·지연 — 모델 변경 없음. 응답 메타데이터 파싱만. 추론 증가 0, /api/tags는 프로세스당 1회 수십 ms.

코드 근거 — llm_assistance.py:38-61 summarize_evidence(모델명을 보내지만 응답의 model을 _response_text:143-154가 버림) · llm_assistance.py:41-50(버전 없는 시스템 프롬프트 리터럴) · store_serialization.py:256-322 _evidence_payload(40여 필드 중 모델/프롬프트 0개) · store_serialization.py:428-430 _evidence_id.

폐쇄망 번들 — 증분 0. 프롬프트 파일도 코드 동봉.

가드레일 — 스탬프는 메타데이터일 뿐 판정 무영향(LLM_SUMMARY 0점 유지). 모델명은 admin 전용 PROVIDER_METADATA 정책과 정합하게 노출.

검증자가 지적한 조건 — (1) id 해시 오염 주의: _evidence_id는 data 전체의 sha256이라 latency_ms/generated_at을 data에 넣으면 버전이 바뀔 때가 아니라 매 호출마다 id가 바뀐다. model/model_digest/prompt_version만 data에, 시간 관련 필드는 _evidence_payload 전용으로 분리. (2) digest는 /api/generate 응답에 없다 — /api/tags에만 있고(실측 a8b0c51577010a27..., 397,821,319바이트, Q4_K_M) 그것도 blob 해시가 아니라 매니페스트 sha256이므로 "매니페스트 digest"라고 정직하게 명명. (3) 정책 필수화는 신규 생성 경로에만 적용하고 legacy 행은 prompt_version='legacy'로 1회 마이그레이션. (4) 반환 타입을 dataclass로 바꾸면 FakeInternalLlmClient와 기존 테스트 3건이 함께 바뀐다.

6. 근거공백 액션 플래너 (심사 7위 · 평균 6.00, 공수 M→S)

무엇을 — 원안은 신규 엔드포인트 + LLM 재랭킹이었으나 검증 결과 둘 다 제거되고, 서버 plan() 결과를 (query, strategy, source, priority)로 기존 review() 응답에 실어 보내고 _is_generic_local_query가 걸러낸 쿼리를 화면에서 숨기는 것으로 축소됐다.

왜 — 파일명이 IMG_2831인 케이스에 GUI가 추천하는 쿼리는 IMG_2831 저작권이다. 실행 버튼이 그대로 노출되어 운영자가 헛검색을 돌리고 외부 쿼터를 태운다. _is_generic_local_query의 정규식 (img|image|photo|picture|portrait|screenshot|download|copy|scan|file)[\s_-]*\d*가 이 계열을 정확히 잡는데, 그 필터가 서버에만 있고 GUI 추천에는 반영되지 않는다.

AI 구성요소·모델·지연 — LLM 없음(검증자 권고로 재랭킹 제거). plan()은 순수 정규식으로 μs 단위, review() 응답에 얹으면 체감 0.

코드 근거 — search_query_generation.py:19 plan() · search_query_generation.py:295 _is_generic_local_query · evidence-guidance.js:suggestedEvidenceQueries(서버와 무관하게 seed+['저작권','공식','이미지 출처'] 템플릿) · app.js:1083 renderEvidenceNextActions · store_serialization.py:285(query_priority가 data엔 들어가지만 _evidence_payload가 매핑 안 함).

폐쇄망 번들 — 증분 0.

가드레일 — 추천 쿼리 실행은 여전히 운영자 클릭이 필요하고 SearchApiPolicy 쿼터를 그대로 통과.

검증자가 지적한 조건 — (1) 원안의 "plan()의 strategy가 API 표면에 나온 적 없다"는 사실이 아니다 — store_enrichment.py:447,620이 이미 plan()을 호출하고 queryStrategy로 직렬화해 app.js:1886이 화면에 그린다. 실제로 빠진 것은 priority 하나뿐이다. (2) 인용된 evidence_enrichment.py:58 enrich_latest는 죽은 코드다. (3) knowledge_entries를 plan()에 넘기면 오히려 나빠진다 — 관련성 검사 없이 활성 엔트리 전부에 대해 priority 70/68 후보를 만들어 로컬 메타(54)를 밀어낸다. 프로덕션 두 호출부가 모두 []를 넘기는 것은 우연이 아니다. (4) LLM 재랭킹은 후보 총량이 애초에 3~5개라 정보 이득이 사실상 0인 전형적 과설계. (5) 폴백 전제가 거짓 — env_clients._llm_assistant()는 도달성 검사 없이 무조건 어시스턴트를 반환하므로 llm_assistant is None 분기는 절대 실행되지 않는다.

7. 골든 케이스 회귀 하네스 (심사 12위 · 평균 5.03 — 운영자 3.8, 공수 M→S)

무엇을 — 원안 3축 중 축(2) 라운드트립만 남기되 케이스 고정이 아니라 속성 기반 테스트로 만든다: match/promoted/weak_hint/domainSource/knowledge_entry_type/similarity 조합을 열거해 _evidence_payload → _domain_evidence_from_ui → RiskScorer 점수 보존을 전수 검사.

왜 — risk_scoring.py와 store_serialization.py 주석이 기록한 실제 회귀 이력이 전부 이 조합에서 나왔다(FACE_PERSON 중복 +35, naver 중복 +40, search_image 왕복 0점→+45). 고정 케이스 몇 개로는 조합 폭발을 못 덮는다.

AI 구성요소·모델·지연 — 모델 없음. 평시 추론 0초, 밀리초 단위.

코드 근거 — risk_scoring.py:7-99(밴드 임계 70/30 하드코딩, 캘리브레이션 테스트 없음) · tests/rights_filter/analysis/test_llm_assistance.py(전체 47줄, FakeInternalLlmClient만 사용 — 실제 모델 출력 회귀 0건) · store_serialization.py:271-275(불변식이 주석으로만 존재).

폐쇄망 번들 — 증분 0(pytest는 requirements-dev에 이미 있음). 픽스처는 합성 JSON 수십 KB.

가드레일 — 픽스처는 운영 DB 익명화 추출이 아니라 합성한다 — 점수 기여 키만 필요하고 실제 URL·블로거명은 필요 없다. 원안의 "이미지가 안 들어가니 안전"은 틀렸다(evidence payload에 bloggerName/bloggerLink/pageTitle/url이 그대로 들어간다).

검증자가 지적한 조건 — (1) 축(1) 밴드 정확도는 정답 라벨의 출처가 없다 — 사람이 기대 밴드를 손으로 적으면 "손튜닝 가중치를 손으로 만든 기대값에 맞추는" 동어반복. "정확도 임계"가 아니라 **"가중치 변경 시 전 제출건 밴드 변동 diff"**로 바꾼다. (2) 축(3) 환각률은 14번(인용 검증기)이 안정화되기 전에는 만들지 않고, 만들 때도 test_llm_output_snapshot으로 정직하게 명명한다(녹화 재생은 환각률 회귀가 아니라 문자열 diff이고, 실측 편차가 임계 0.1보다 크다).

8. AI 선연산 잡 큐 (심사 6위 · 평균 6.07, 공수 M / 락 이동 5줄만 오늘 즉시)

무엇을 — 원안은 ai_jobs 테이블 + 백그라운드 워커. 검증 결과 **"잡 큐보다 먼저 5줄로 얻을 것을 얻어라"**로 축소 권고: 시드의 LLM 순회를 _write_lock 밖으로 옮기고 시드 1회당 처리 건수 상한(예: 20건)을 둔다.

왜 — 현재 seed_from_image_store가 _write_lock을 잡은 채 제출건마다 Ollama를 부르고, record_decision도 같은 락을 쓴다. 즉 폴더 재적재 중에는 판정 버튼이 락 대기에 걸린다. 실측 1건당 20.6초 기준 제출 100건이면 약 34분간 모든 쓰기가 잠긴다. 게다가 Ollama가 아예 없어도 비용이 든다 — 이 Windows 환경 실측에서 127.0.0.1:11434 연결은 즉시 refuse가 아니라 2초 타임아웃이었다(100건 × 2초 = 200초 락 점유).

AI 구성요소·모델·지연 — 모델 변경 없음. 바뀌는 것은 추론의 위치와 시점.

코드 근거 — sqlite_store.py:293 seed_from_image_store가 _write_lock 획득 → :310 LLM 순회 · sqlite_store.py:688 record_decision이 같은 락 · store_operations.py:457-458(수동 검색은 이미 "락 밖에서 호출"을 명시 — 같은 레포에 상반된 두 패턴) · store_serialization.py:220 _provider_item_has_result(source=='failure'에 False를 반환해 only_if_missing이 실패 행을 "요약 없음"으로 봄 → 매 reload마다 실패 전건 무한 재시도) · store_persistence.py:19 _connect()(WAL 미설정).

폐쇄망 번들 — 증분 0(threading + sqlite3 stdlib). 새 ai_jobs 테이블은 store_schema.py DDL + store_constants.py:18 STORE_TABLES 등록 필요(store_serialization.py:480 _validate_table이 미등록 테이블을 차단). 백업은 Connection.backup이 파일 전체를 뜨므로 자동 포함.

가드레일 — 워커는 생성(HTTP) 구간을 반드시 락 밖에서 수행하고 저장 순간에만 락을 잡는다. '생성 중' 상태가 판정 버튼을 막지 않는다.

검증자가 지적한 조건 — (1) 1번(프롬프트 축소)이 선행이다 — 축소 없이 잡 큐만 넣으면 '생성 중' 칩이 영구히 붙어 있는 화면이 된다. (2) 실패 백오프 추가(현재는 매 reload마다 전건 재시도). (3) env_clients._llm_assistant()에 도달성 프로브 추가 — 여러 아이디어가 "None이면 폴백"을 전제하는데 그 전제가 현재 거짓이다. (4) _connect()에 journal_mode=WAL 추가 — 이 조건은 취소한다. 확인 결과 WAL은 이미 켜져 있다(sqlite_store.py:162 initialize(), DB 파일에 영속). store_backup.py docstring이 WAL을 전제로 서술한 것도 정확했다. 워커 쓰기가 HTTP 읽기를 블록하는 위험은 존재하지 않는다. (5) 워커는 run_once()를 노출해 스레드 없이 테스트 가능하게 하고, 부팅 시 running→pending 리셋을 넣는다.

상위권 중 상세 미수록 3건 (요약)

4위 · 방송캡처 포렌식 (평균 6.53, 공수 S→M) — 방송 화면·앱 스크린샷을 결정적 영상처리(휘도 투영 프로파일, Canny 엣지 밀도, HSV 채도, 히스토그램 균일도)만으로 판별해 라벨을 붙인다. ML 모델 0, 의존성 0, 실측 2ms/장. 지금 코드에 캡처를 식별하는 줄이 하나도 없고, 오히려 _is_generic_best_guess_label이 'screenshot'을 블록리스트로 버린다. 심사 3인이 공통으로 "관측 가능성이 로컬비전 6건 중 최고"(오탐 원인이 사람이 읽는 규칙으로 남는다)라고 평가했다. 조건: 1차는 반드시 0점 라벨로 출시하고 decision_stats() 교차표로 실제 반려와의 상관을 본 뒤 점수를 부여할 것. 레터박스·하단 텍스트밴드는 시네마틱 크롭·밈·PPT 캡처와 구분되지 않으므로 "서로 독립인 카테고리 2개 이상"을 검출 조건으로 둘 것. → 로드맵 2-f.

5위 · 모델 번들 무결성 검증 (평균 6.10, 공수 M) — /api/tags 응답 기반 models.lock.json + 비차단 프리플라이트 + enabled와 분리된 modelReady 필드. 핵심 발견: env_clients._llm_assistant()가 도달성 검사 없이 무조건 어시스턴트를 반환해 llm provider가 항상 enabled=True이고, offline-install.md는 정반대로 서술한다. 즉 provider 패널이 지금 거짓말을 하고 있다. 이 산출식 하나를 고치면 다른 4건이 공통으로 깔고 있는 "LLM이 없으면 폴백" 거짓 전제까지 한 번에 참이 된다. 조건: 프리플라이트가 기동을 막으면 안 되고(DROP 방화벽에서 기동 정지), 운영자 긴급 비활성 토글인 enabled를 자동 강등이 덮어써서도 안 된다. → 로드맵 1-f + 2-b.

8위 · 작업별 모델 라우터 (평균 5.73, 공수 M→L / 1단계 S) — 태스크별 Ollama 파라미터 프로필과 모델 등급 분리(0.5B 분류 / 3B 주력 / 7B 옵션). 심사 3인이 공통으로 "태스크가 문자 그대로 하나뿐인 지금 라우터는 전형적 선행 추상화"라고 감점했고, 동시에 그 1단계(num_ctx/num_predict/keep_alive/timeout을 환경변수로 노출 + 시드 LLM 호출을 _write_lock 밖으로)는 "최우선급 수술"로 평가했다. 결론: 라우터가 아니라 1단계만 떼어내 쓴다(로드맵 1-b·2-d). 7B(+4.7GB)는 1단계 실측 tok/s가 나오기 전 번들 반입 금지.

반증되어 제외된 것

통째로 REFUTED — 6건 (심사 대상에서 제외)

발산 30건 중 6건이 적대적 검증에서 전제 자체가 무너져 심사에 올라가지 못했다. 이 6건은 "나중에 하자"가 아니라 "이 형태로는 하지 말자"이다.

탈락 아이디어 렌즈 무너진 전제
LLM 쿼리 리라이터 LLM 파이프라인 입력으로 쓰겠다는 지식엔트리가 파이프라인에 안 들어온다 — 프로덕션 두 호출부가 plan(evidence, [], ...)로 빈 리스트를 넘긴다(store_enrichment.py:447,620). 그런데 plan()은 이미 entry.aliases로 별칭 쿼리를 만든다. 별칭 확장 기계는 이미 있고 입력선만 끊겨 있다 — 한 줄 수정이 LLM보다 먼저다. 또 가드레일("입력 토큰 1개 이상 공유")과 가치(활동명↔본명·로마자 = 입력에 없는 외부 지식)가 서로를 무효화한다
반려 메모 초안기 LLM 파이프라인 코퍼스가 존재하지 않는다(판정 메모 0건). 그리고 memo는 APPLICANT_EXPORT_FIELDS에 포함돼 LLM 생성 텍스트가 신청자에게 나가는 경로를 새로 여는 유일한 아이디어다. 반려 사유는 분쟁 시 문서 증거다. 효용도 역전 — 3B 초안 대기 4090초 vs 수작업 타이핑 3090초
로컬 VLM 쿼리 시드 로컬 비전 "배치라 대기 경로 밖"이 거짓(시드는 _write_lock 안 동기 POST). 환각이 점수로 새는 경로 2개 실재 — VLM priority 58이 유료 쿼터를 잠식하고, 헛쿼리 검색 실패는 FAILURE +30으로 환각이 곧바로 위험점수를 올린다. 지연도 과소추정(이미지 프리필만 15~50초 > timeout=30)
판정역산 가중치 학습기 사람 판정 학습 n=0. 더 근본적으로 라벨 순환이 구조적이다 — 큐가 riskScore 내림차순 정렬이고 화면이 점수·근거를 먼저 보여준 뒤 판정을 받으므로, 학습된 계수는 "이 근거가 반려를 예측한다"가 아니라 "운영자가 내가 보여준 점수에 동의했다"를 재학습한다. 제안 피처 3개는 현 코퍼스에서 상수 0 컬럼
라벨가치 우선 큐 사람 판정 학습 입력인 p(반려)가 없다(위 항목이 REFUTED). 능동학습의 경제학이 성립하지 않는다 — 큐의 모든 케이스가 결국 판정되므로 재정렬은 라벨이 언제 오는지만 바꾸고 몇 개 오는지는 못 바꾼다. 게다가 riskScore 내림차순은 정보 순서가 아니라 위험 트리아지 순서이고 미탐 비용이 비대칭이라 정당하다. 가치 있는 절반(중복그룹 우선)은 duplicateGroup으로 이미 클라이언트에 도착해 있다
시리즈 묶음 심사 카드 운영자 효율 0.880.97 밴드가 목표를 못 맞춘다(실측 반증). 실제 제출 이미지로 측정: 20% 크롭 0.8120.859, 좌우반전 0.406~0.844로 목표는 밴드 아래로 빠지는데, 무관한 이미지 쌍이 이미 0.781을 낸다. 원인은 fingerprints.py:47이 DCT pHash가 아니라 8x8 평균해시라는 것. 게다가 묶음 LLM 요약은 8건 전체에서 근거 링크를 뽑으면서 실제로는 1건 남짓만 읽어 근거 없는 요약 하나를 8건에 확산시킨다

이 6건이 남긴 재사용 가능한 교훈 3가지: ① 이 레포에서 "배치니까 괜찮다"는 거짓이다 — 시드가 _write_lock 안에서 돈다. ② 실패 증거에 +30이 붙으므로 AI가 틀리면 위험점수가 올라간다(환각→가점 경로). ③ _perceptual_hash는 이름만 pHash이고 실제로는 평균해시라, 유사도 임계값에 기대는 모든 설계가 이 위에서 흔들린다.

살아남은 아이디어에서 잘려나간 구성요소

아래는 항목 자체는 살았지만 내부의 특정 주장·구성요소가 반증되어 잘려나간 것들이다. 착수 시 되살리면 안 된다.

잘려나간 것 소속 반증 근거
ⓒ/© OCR 매칭 워터마크 검출기 OpenCV 배포 CRNN crnn.onnx의 알파벳은 alphabet_36(0-9, a-z)뿐이라 대문자·괄호·기호가 없다. 94자 crnn_cs.onnx에도 U+00A9가 없다. '(C) 2024' 정규식 매칭률 0, stock.adobe.com은 stockadobecom으로 복원
전체 프레임 로고 템플릿 매칭 워터마크 검출기 개발 머신 실측 1920x1080 · 로고 10개 × 6스케일 = 2,347ms. KB에 로고 50개면 ×5 → 저사양 30~100초/장. 비용이 "운영자가 등록한 로고 수"에 선형이고 상한이 없다
타일 aHash 부분매칭 국소화 증거 영역 오버레이 match='partial'은 Google Vision의 원격 URL 목록에서만 생성되며 우리는 그 픽셀을 갖고 있지 않다. 게다가 8x8 average hash는 실측에서 무관한 제출 이미지 쌍이 이미 0.25~0.781을 낸다 — 1/16 타일은 더 심하다. 틀린 영역을 칠하는 것은 아무것도 안 칠하는 것보다 나쁘다
LLM 정책메모 초안 근거프로파일 일관성 감시 착지점 decision_memo_presets의 본문이 memo를 거쳐 APPLICANT_EXPORT_FIELDS로 신청자에게 나간다. 또 "이 조합에서는 …인 경우 반려"는 시스템 프롬프트가 금지하는 판정 권고의 집합 형태
LLM 후보 재랭킹 근거공백 액션 플래너 후보 총량이 3~5개(쿼터 상한). 5개 중 3개 고르기에 0.5B + 파싱 폴백 + 범위 검증 + 배지를 붙이는 과설계. priority가 이미 순서를 준다
"추가 네트워크 비용 0" 페이지 권리표기 추출 page_fetcher 호출부는 유사도 비교의 네 번째 폴백이라, 이미지가 실제로 걸린 페이지는 애초에 HTML을 받지 않는다. 게다가 페치 바이트는 어디에도 캐시되지 않는다
"screenshot 블록리스트는 자해" 방송캡처 포렌식 과장된 오독. _is_generic_best_guess_label은 검색 쿼리 적합성 판단 함수이고 'screenshot' 한 단어로 검색하는 것은 실제로 무의미하다. 차단은 옳은 설계
"수천 번 클릭한 오탐 라벨" 근거 신뢰도 재보정 실측 evidence 292행 전부 operatorStatus=None, 백업 DB에 false_positive 2행이 전 이력
"pending 프리마킹은 점수 불변이 코드로 보장" 근거 신뢰도 재보정 mark_evidence_status(sqlite_store.py:731-734)가 NON_CONTRIBUTING이 아니면 contributed를 True로 복원하고 재채점한다. 이전에 false_positive였던 FINGERPRINT 행을 pending으로 바꾸면 +80이 되살아난다
"운영자별 편향 감지" 근거프로파일 일관성 감시 actor='rights.ops' 상수가 문제가 아니라 시스템에 사용자 개념 자체가 없다(_require_auth는 단일 공유 bearer 토큰 하나). 이 선행 작업 없이 편향 감지를 주장하면 허위
인용 검증기의 "환각 차단" 가치 문장별 인용 검증기 실측 환각은 서술어(의미)에 있고 제안된 검사는 개체(URL·숫자·연도)에 있다. 실측 출력 'confirming the image's authenticity [E3]' 같은 근거 없는 단정이 화이트리스트 대조를 100% 통과 → 오히려 환각 문장에 "근거 확인됨" 도장을 찍는다
"문장 끝 라벨" 파서 계약 문장별 인용 검증기 실측 5회 전부 라벨이 불릿 앞에 붙었고 문장 끝은 0건. 출력이 마침표 문장이 아니라 개행 덩어리라 한국어 문장 분해가 성립하지 않는다
"검증된 폐기 경로 재사용" 문장별 인용 검증기 _source_evidence_ids는 항상 비지 않으므로 verified는 프로덕션에서 항상 True. policies.py:110-114의 LLM_SUMMARY 분기와 폐기 분기는 한 번도 실행된 적이 없다
"llm_assistant가 None이면 폴백" 6·9·16·23 공통 env_clients._llm_assistant()는 도달성 검사 없이 무조건 반환. is None 분기는 절대 실행되지 않고 llm provider는 항상 enabled=True
EvidenceEnricher / ReviewEnrichmentJob 수정 6·9·16 공통 프로덕션 미배선 죽은 코드. 실제 호출 지점은 store_operations.py:471. 테스트 2건 + test_public_module_layout.py 임포트 검사가 붙어 있어 이 범위에서 손대면 안 됨
"7B로 메모 초안" 작업별 모델 라우터 CPU 프리필 100초+ 생성 40~50초. 1단계 실측 tok/s가 나오기 전에는 번들에 넣지 말 것(+4.7GB)
"새 저장 인프라 없음" 의미지문 인덱스 신규 테이블은 STORE_TABLES·CONSTRAINED_TABLE_SCHEMAS·LEGACY_REBUILD_ORDER·TYPED_COLUMNS·SCHEMA_VERSION·_validate_table을 전부 건드리고, 벡터 BLOB이 백업 zip 10개에 곱해져 들어간다

미검증 1건: 16위 「밴드 임계값 스윕·섀도 리스코어」는 적대적 검증 단계에서 판정이 기록되지 않았다(verdict: null). 다른 항목과 같은 수준의 반증을 거치지 않았으므로 착수 전 별도 검증이 필요하다. 특히 원안이 전제하는 "결정된 케이스 전체"가 현재 0건이라는 점, _rescore_all_submissions가 항상 쓰기까지 한다는 점(read-only 경로 분리 필요)이 검증 대상이다.

단계별 로드맵 제안

이 로드맵은 심사 점수와 독립적으로 선행조건 의존 관계로 짰지만, 결과적으로 정합한다: 1단계는 심사 1·3·6·7·10·12위를 포함하고, 라벨 의존 항목(21·23·24위)은 전부 3단계로 밀린다. 점수 하위권이 3단계로 가는 이유는 "가치가 없어서"가 아니라 지금은 입력이 없어서다.

1단계 — 지금 당장(라벨 0건·의존성 0·모델 반입 0에서 작동)

한 스프린트짜리 "기반 정리" 묶음. 이 단계는 AI를 늘리지 않는다. 이미 켜져 있는 AI의 거짓말과 세금을 끄는 작업이다.

항목 내용 공수
1-a LLM 입력 경계 단독 커밋 — 프롬프트 화이트리스트 + 증거 상한 12건 + num_ctx 명시 (promotion_reason 제외) S
1-b 시드 LLM 순회를 _write_lock 밖으로 + 시드당 처리 상한 20건 + 실패 백오프 S
1-c initialRiskScore/initialRiskBand/scorerVersion 각인 + 라운드트립 속성 기반 테스트 S
1-d 모델·프롬프트 버전 스탬프 (id 해시 오염 회피 규칙 준수) S
1-e 원본 메타 권리추출 EXIF+IPTC만 (XMP는 별건, 스톡 ID는 +30 라벨) S
1-f env_clients._llm_assistant() 도달성 프로브 + provider enabled 산출식 정정 + offline-install.md의 잘못된 서술 정정 S
1-g 요약 상태 3단 배지(정상 / 생성 안 됨+원인 / 실패) — 5단은 과설계 S
1-h 액션 플래너 축소판 — plan() 결과를 review() 응답에 싣고 _is_generic_local_query 탈락 쿼리 숨김 + query_priority 매핑 1줄 S
1-i app.js:726-746 영→한 if-체인을 사전 테이블로 리팩터 (같은 항목에 있던 "_connect()에 WAL 추가"는 이미 적용돼 있어 삭제) S

2단계로 가기 위한 선행조건

  • 1-d의 latency_ms가 실제 타깃 하드웨어에서 2주 이상 쌓여 케이스당 실측 지연 분포가 나올 것.
  • 1-c의 라운드트립 속성 테스트가 그린이고, 새 EvidenceSource 배관 12곳 체크리스트가 문서로 확정될 것.
  • 타깃 하드웨어 사양(코어/스레드/메모리대역/GPU 유무)이 확정될 것 — 실측이 2스레드 20.6초 vs 20스레드 5.9초로 갈렸다. 이 값 없이는 3B 채택 여부를 결정할 수 없다.
  • 모델 번들 크기 상한이 승인될 것.

2단계 — 모델 도입을 수반하는 중기

항목 내용 비고
2-a YuNet 교체 + detect 중복 호출 제거 + tier 라운드트립 모델 345KB, 지연 순감. 밴드 재캘리브레이션 비용을 판정 라벨이 쌓이기 전에 치른다
2-b 모델 번들 무결성 검증 — models.lock.json(/api/tags 응답 기반) + 비차단 프리플라이트 + enabled와 분리된 modelReady 필드 차단형 프리플라이트 금지(DROP 방화벽에서 기동 정지)
2-c 잡 큐 + 백그라운드 워커 (run_once() 테스트 모드, 부팅 시 running→pending 리셋) 1-b로 급한 불을 끈 뒤, 태스크가 2개 이상 생긴 다음
2-d 모델 라우터 1단계 — num_ctx/num_predict/keep_alive/timeout을 환경변수 프로필로 노출 + 3B 실험 + 감사 로깅(모델명·소요시간·토큰수) 7B는 실측 tok/s 확인 전 번들 금지
2-e 증거팩 직렬화 + 인용 칩 (3B) — 줄 단위 출력([E<n>]로 시작), 라벨 없는 줄만 드롭, 미라벨 줄은 렌더하지 않음(취소선 금지) 1-a·2-d 선행 필수
2-f 방송캡처 포렌식 — 실측 2.0ms/장, 모델 0. 단 1차는 0점 라벨로만 출시하고 워터마크와 같은 EvidenceSource·같은 GUI 그룹으로 배관 비용을 한 번만 지불 독립 카테고리 2개 이상 성립 규칙
2-g 워터마크 로고 템플릿 매칭 — ROI를 4모서리 + 하단 20%로 한정, 활성 로고 12개 상한, 스케일 3단계, 점수 +45(단독 high 금지), 크롭은 face-crop과 분리된 보관 경로 OCR(56~72MB) 반입은 로고 경로 검출률을 본 뒤 결정

3단계로 가기 위한 선행조건

  • 판정 라벨이 실제로 축적될 것 — 이것이 절대적 게이트다. 현재 0건. 프로파일 군집은 자체 규칙(군집 5건 이상)까지 감안하면 수백 건이 필요하고, 쿼리전략별 통계는 queryStrategy가 채워지기 시작한 이후 수개월이 걸린다.
  • decision_stats()의 밴드×판정 교차표가 유의미한 표본(최소 100건)을 가질 것.
  • 골든셋 diff 하네스가 돌아 "가중치를 바꾸면 큐가 어떻게 변하는가"를 숫자로 낼 수 있을 것.
  • 임베딩 저장 금지 정책의 개정 여부가 사용자 결정으로 확정될 것(아래 열린 질문 참조).

3단계 — 학습·자동화로 가는 장기

항목 내용 진입 조건
3-a 근거 신뢰도 카운트 배지 — (source, matchType, domainSource) 3키 집계, n≥15에서만 표시. isotonic 보정표·confidence 주입은 만들지 않는다 사실상 오늘 출시 가능(n=0에서 조용히 사라짐). 오히려 라벨 수집 인센티브
3-b AI 수용률 축소판 — 새 테이블·새 화면 없이 decision_stats에 llmDraftPulled/llmDraftMedianRetention 두 필드. 단 _latest_decision_memo의 ' · ' partition 버그 선수정 판정 라벨 축적
3-c 밴드 임계값 스윕 + 섀도 리스코어 — 쓰기를 타지 않는 read-only 경로로 분리, 적용은 항상 사람 클릭 + 감사 이벤트 결정 케이스 100건 + 별도 검증(현재 미검증)
3-d 근거프로파일 일관성 감시(A안만) — 시그니처는 현행 코드가 실제로 채우는 필드로만. LLM 정책메모 초안은 폐기. actor/다중 사용자 인증은 분리 발주 판정 수백 건
3-e 트리아지 레인 — 규칙 우선 — 1차는 모델 없이 decision_stats 교차표 규칙("같은 밴드+같은 providerState에서 과거 승인률>80% & n≥10")으로, 동결 라벨 200건 초과 시 로지스틱 교체 피처 동결(판정 시점 스냅샷) + 워치리스트 역류 제외가 선행
3-f 의미지문 인덱스 — 1단계는 제출물+KB 참조만(검색 후보 제외), cv2.dnn + CNN 백본, 벡터는 SQLite BLOB이 아니라 .npy 외부 저장, 점수 0 + '변형 의심' 라벨 거버넌스 사전 승인 없이는 착수 금지
3-g 페이지 권리표기 추출 — 유사도 폴백 체인에 얹지 말고 "점수 기여 상위 N개 URL"에 대한 독립 페치 단계(케이스당 2건)로. 1차는 정규식 전용, LLM 없음 페치 커버리지 재설계
3-h 케이스 Q&A — 고정 질문 사전 캐시 — 자유 질문 동기 대기(40120초)는 기능의 사망 원인. 증거 확정 시점에 35개 고정 질문을 미리 생성해 클릭 시 0초 잡 큐(2-c) + 별도 캐시 테이블(TTL, 감사 대상 아님)

결정 반영 실행안 (2026-07-25 확정)

아래 열린 질문 중 4개가 답변되었다. 이 답변들이 위 로드맵의 전제를 바꾸므로, 실행 시에는 이 절이 로드맵보다 우선한다.

질문 결정
LLM 역할 범위 증거 판독 + 구조화 판단 (요약만 유지 아님) — 3B 이상 승급
타깃 하드웨어 GPU 있음
새 신호의 점수 개입 0점 라벨로 시작, decision_stats 상관 확인 후 가중치 부여
실사용 투입 곧 투입 예정

이 결정들이 무효화한 분석

  1. CPU 지연 분석 전체가 이 배포에는 적용되지 않는다. 제약 2절의 실측(2스레드 20.6초 / 20스레드 5.9초, 3B 60~180초, "7B는 인터랙티브 불가")은 CPU-only 전제였다. GPU에서는 3B가 인터랙티브이고 7B도 후보다. 다만 지연이 사라져도 _write_lock 점유 문제는 남는다 — 락은 추론 시간에 비례할 뿐 원인이 아니다.
  2. "잡 큐/배치 선연산이 필수"라는 전제가 약해졌다. 2-c(백그라운드 워커)의 우선순위를 낮추고, 1단계의 락 이동(5줄)만으로 당분간 버틴다.
  3. 워터마크 OCR의 재검토 여지가 생겼다. 단 반증된 결함(배포 CRNN 알파벳에 ©·대문자 없음)은 하드웨어와 무관하므로, 재검토한다면 다른 OCR 모델 선정이 전제다.
  4. 의미지문 인덱스는 여전히 막혀 있다. 막는 것이 성능이 아니라 거버넌스(생체 템플릿 저장 금지 사양)이기 때문. GPU는 이 항목을 풀어주지 않는다.

🔴 실사용 투입 전에 끝내야 하는 것 — 되돌릴 수 없는 창

"곧 투입"이 만든 가장 중요한 제약이다. 아래 4건은 운영자가 첫 판정을 누르는 순간 소급이 불가능하거나 비용이 급등한다.

# 항목 왜 투입 전이어야 하는가
T0-1 initialRiskScore / initialRiskBand / scorerVersion 각인 (_submission_payload) _rescore_submission이 12곳에서 riskScore를 덮어쓰므로 저장된 점수는 이미 왕복 점수다. 원본 점수를 지금 각인하지 않으면 판정이 시작된 뒤에는 영원히 복원할 수 없고, 모든 점수 회귀 테스트가 항상 통과하는 빈 테스트로 남는다
T0-2 YuNet 교체 + 밴드 재캘리브레이션 등급화는 기존 medium 다수를 low로 옮겨 밴드 분포를 바꾼다. 판정 이력이 쌓인 뒤에 하면 decision_stats의 캘리브레이션 기준선이 판정 이력과 어긋난다. 판정 0건인 지금이 이 비용이 가장 싼 시점이고, 그 시점은 곧 닫힌다
T0-3 판정 시점 피처 스냅샷 각인 (라벨 순환 차단) 큐가 riskScore 내림차순 정렬이고(app.js:1267) 화면이 점수·밴드·reasons를 보여준 뒤 판정을 받는다. 이 구조에서 모은 라벨은 "이 근거가 반려를 예측한다"가 아니라 **"운영자가 내가 보여준 점수에 동의했다"**를 기록한다. 판정 순간 운영자가 실제로 본 것(점수·밴드·표시된 근거 목록)을 스냅샷으로 남겨야 나중에 순환 보정이 가능하다. 투입 후에 붙이면 그 이전 라벨은 전부 용도 불명이 된다
T0-4 모델·프롬프트 버전 + latency_ms 스탬프 투입 후 모델을 3B로 올리면 과거 요약과 신규 요약이 DB에 섞이는데 구분할 필드가 없다. 지금 스탬프를 넣으면 승급 전후 비교가 성립한다

T0-3은 이번 심사에서 나온 항목이 아니라, "곧 투입"이라는 답변이 새로 만든 요구사항이다. 학습 계열 7건 전부의 미래 가치가 여기 걸려 있고, 비용은 S다.

실행 순서 (확정)

A. 투입 전 (T0) — initialRiskScore 각인 · YuNet + 재밴딩 · 판정 스냅샷 각인 · 버전/지연 스탬프

B. LLM을 판독기로 — GPU 전제로 공격적으로 간다

  • B-1 입력 경계(화이트리스트 + 상위 12건 + num_ctx) — promotion_reason 제외 필수
  • B-2 3B 승급 + GPU 프로필(num_ctx/num_predict/keep_alive/num_gpu), 7B는 B-2 실측 후 판단
  • B-3 시드 LLM 호출을 _write_lock 밖으로
  • B-4 구조화 증거 판독 — format=json으로 {claims:[{text, evidence_ids[], kind}], needs_check:[...]} 스키마 강제. 인용 검증은 문장 파싱이 아니라 스키마 필드 검증으로 한다 — evidence_ids가 실제로 프롬프트에 들어간 증거 id 집합의 부분집합인지만 대조하고, 아니면 그 claim을 렌더하지 않는다. 이 설계는 심사에서 3.03점을 받은 「문장별 근거 인용 검증기」의 반증 사유를 우회한다: 그 안은 자유 텍스트를 문장 분해해 개체(URL·숫자)를 대조하는 방식이라 'confirming the image's authenticity [E3]' 같은 근거 없는 서술어에 "확인됨" 도장을 찍는 문제가 있었다. 스키마 필드 대조는 모델이 claim 단위로 id를 스스로 붙이게 하므로 이 실패 모드가 구조적으로 발생하지 않는다.

C. 모델 없는 신호 (전부 0점 라벨) — EXIF/IPTC 권리추출 · 방송캡처 포렌식. 두 항목은 같은 EvidenceSource·같은 GUI 그룹으로 묶어 배관 12곳 비용을 한 번만 지불한다.

D. 투입 후 라벨 축적 뒤 — decision_stats 교차표로 0점 라벨들의 실제 반려 상관 측정 → 가중치 부여 → 임계값 스윕. 학습 모델은 T0-3 스냅샷이 수백 건 쌓인 뒤.

실기동 실측 (2026-07-25, GPU 타깃) — 추정치를 대체함

이 문서의 지연 수치는 전부 CPU 개발 머신 실측의 외삽이었다. 실제 GPU 타깃에서 저장된 실제 증거(제출물 #3, 증거 54행 중 12행 선별, 프롬프트 1,507자)로 재측정한 결과:

모델 지연 계약 준수 비고
qwen2.5:0.5b-instruct 55.7s ❌ 프롬프트의 형식 예시(<한국어 문장>)를 그대로 출력하면서 실제 라벨 12개를 전부 인용
qwen2.5:7b 8.8s ✅ 근거 claim 2건 + 확인필요 1건. 채택
qwen3:8b 35.7s ✅ claim 4건이지만 4배 느림(thinking 모델)

이 실측이 바꾼 것 3가지:

  1. 기본 모델을 3B가 아니라 qwen2.5:7b로 정했다. 3B는 이 빌드 머신에 없고 7B는 이미 있어서 에어갭 반입 비용이 0이며, GPU에서 8.8초면 인터랙티브다. 문서 상단의 "7B는 현재 근거로 인터랙티브 불가"는 CPU 전제였고 이 배포에는 적용되지 않는다.
  2. 인용 검증만으로는 부족하다는 것이 드러났다. 0.5B의 출력은 실제 라벨을 인용했으므로 집합 포함 검사를 완벽히 통과했다 — 완전히 비어 있는데도 "근거 있음"으로 판정됐다. 형식 예시 문자열을 상수로 두고 그 반향(echo)을 거부하는 가드를 추가했다. 교훈: 그라운딩 검증은 "인용이 진짜인가"와 "내용이 진짜인가"를 둘 다 봐야 한다.
  3. 가짜 transport 테스트만으로는 이 결함을 절대 못 잡았다. 스크립트된 응답은 언제나 잘 만들어진 JSON이기 때문이다. 실모델 프로브가 필요했다.

남은 결정 1개

임베딩 저장 금지 정책의 개정 여부 — 의미지문 인덱스(22위)만 이 결정에 걸려 있고, 나머지 A~D는 전부 이 결정 없이 진행된다. 급하지 않으므로 D 시점에 재론해도 된다.

열린 질문 (사용자 결정 필요)

아래는 근거로 답할 수 없고 사람이 정해줘야 다음 단계가 진행되는 항목이다.

  1. 타깃 하드웨어 사양 — CPU 코어/스레드 수, 메모리 대역폭, GPU 유무. 실측이 2스레드에서 20.6초, 20스레드에서 5.9초로 3배 이상 갈렸다. 이 값 없이는 3B 채택 여부(2-d)도, "케이스당 몇 초까지 허용되는가"도 결정할 수 없다. 현재 문서의 모든 저사양 추정치는 개발 머신 실측의 외삽이며 추정이다.
  2. 임베딩 저장 금지 정책의 개정 여부 — 3-f(의미지문 인덱스)는 사양 문서 두 곳(docs/operations/image-rights-risk-filter.md:16, docs/project-introduction-and-technical-implementation.md:209)의 "생체 템플릿 저장 금지" 문구와 정면으로 만난다. 전용 테이블 우회는 코드는 통과해도 사양은 통과하지 못한다. 결정 옵션: (a) 개정하지 않고 3-f 영구 배제, (b) "장면 벡터는 SEMANTIC_INDEX로 별도 관리"를 명문화하되 얼굴 박스 검은 마스킹 + 인물이 프레임 N% 이상이면 인덱싱 스킵 + 원본 삭제 시 연쇄 삭제를 조건으로. (b)를 택하면 사양 문서 2곳 동시 개정이 필요하다.
  3. 케이스당 허용 가능한 추가 지연 — 동기 요청 경로에서 몇 초까지 운영자가 기다릴 수 있는가? 현재 timeout=30은 0.5B조차 여유가 없는 값이다. 이 숫자가 정해져야 (a) 어느 기능이 동기이고 어느 것이 배치인지, (b) 3B를 쓸 수 있는지가 결정된다.
  4. 모델 번들 크기 상한 — 후보별 증분: 3B Q4_K_M +약 1.9GB / 7B +4.7GB / OCR ONNX 56~72MB / YuNet 345KB / 롤백 태그 유지 +379MiB. Ollama 런타임과 0.5B는 현행 구성이 이미 요구하므로 증분이 아니다. 반입 매체와 절차 기준으로 상한을 정해달라.
  5. 4번째 런타임 의존성 반입 승인 여부 — requirements.txt 헤더가 "서드파티는 PIL/cv2/numpy 뿐"이라고 선언하고 있다. 후보: defusedxml(25KB, XMP/C2PA 판독에 필수 — 없으면 폐쇄망에서 조용히 no-op), onnxruntime(40~55MB, CLIP/ViT 계열에 필수). 각각 별도 심사 안건이다.
  6. AI 제안의 판정 개입 범위 — 새 신호를 (a) 0점 라벨로만 낼 것인가, (b) 캘리브레이션 후 점수를 줄 것인가, (c) 준다면 단독 high 승격을 허용할 것인가. 검증자들은 워터마크 +70, 스톡 자산 ID +70, 방송캡처 +30, 의미유사도 +30을 전부 낮추거나 0점 라벨로 시작하라고 권고했다. 판정 라벨이 0건인 현재로선 어떤 가중치도 근거가 없다.
  7. 새 EvidenceSource 배관 12곳을 언제 한 번에 지불할 것인가 — 워터마크·방송캡처·권리메타·권리표기가 각각 새 소스를 요구한다. 운영자 관점에서는 넷 다 같은 질문("이 이미지가 어디서 왔나")에 답하므로 하나의 소스·하나의 GUI 그룹으로 묶어 배관 비용을 한 번만 지불하는 안이 검증 단계에서 제시됐다. 묶을지 나눌지는 화면 설계 결정이다.
  8. 다중 사용자 인증(actor)을 별도 발주할 것인가 — 현재 시스템에 사용자 개념이 없어(단일 공유 bearer 토큰) "운영자별 편향 감지"·"프리마킹과 사람 라벨 구분"이 원리적으로 불가능하다. 계정·로그인·세션→actor 전파(add_audit_event 호출부 약 10곳) + 감사 로그 마이그레이션은 별도 프로젝트이며 보안 리뷰 대상이다.
  9. 골든셋 파일의 반출 통제 수준 — 1000건 기준 약 31MB, 내부 riskScore·result URL·pageTitle을 담는다. .gitignore만으로 충분한가, 아니면 납품 번들 생성 스크립트 제외 목록 + offline-install.md + delivery-audit 체크리스트까지 3중으로 갈 것인가.
  10. 판정 라벨을 언제까지 얼마나 모을 것인가 — 3단계 전체(3-a~3-h)가 이 값에 걸려 있다. "몇 건이 모이면 학습계를 켠다"는 목표치를 정하지 않으면, 라벨 의존 기능 7건이 영구히 비어 있는 화면으로 남는다.