PPS 모노레포 · 조달청 기술평가 지원 플랫폼
메일 리포트 주장 → 실제 코드 대조 → 오프라인·실HTTP 검증 → 결함 개선 → QA셋 평가까지 전 과정 정리
메일의 "전 항목 Pass"는 상당수가 커밋된 코드가 아닌 로컬 우회로 달성됨. 실코드 대조 결과 failover·busy-poll은 실제 갭이었고, 이를 개선·검증(오프라인 18테스트 + 실HTTP failover)했다. QA 프록시 평가 결과 정형 문서엔 우수, 실 비정형 문서·기본메타·지연은 목표 미달.
| 자료 | 내용 |
|---|---|
| PPS_로컬_전수테스트_결과.eml | self-report 메일 (C1–C16 전수 Pass 주장, 2026-07-09) |
| pps-test-bundle.zip (243KB) | QA_RESULTS.md · env(.env·override) · scripts(datalab_shim.py·e2e·bench) · results(JSON 7종) · artifacts(pdf·hwpx·ocr) |
번들은 tests/qa_datasets/local_e2e_20260709/ 에 배치. env·eml 시크릿은 .gitignore로 커밋 차단, QA 산출물만 추적.
| # | 리포트 주장 | 실제 코드 | 판정 |
|---|---|---|---|
| G1 | Kanana 다운 시 OpenRouter failover 자동전환 | 자동 전환 로직 전무 (예외 raise). env 토글+재기동 수동절차 | 갭 |
| G2 | busy-poll 병목 → dual restart로 해결 | wait_for_task_completion 2s폴링 + Celery PENDING 무한반환 → 좀비루프 | 실버그 |
| G3 | OCR = Datalab async→OpenAI shim | repo에 datalab 0건. shim은 repo 밖 로컬 HTTP (Chandra 요청→Datalab /convert) | 외부 |
| G4 | OCR 캐시 187배(5.6→0.03s) | 실재. write=_save_ocr_cache, hit=chandra_results_dir (단 opt-in) | 확인 |
| G5 | 포트 5440/6390 override | compose는 5432/6379 하드코딩, override 미커밋 | 외부 |
| G6 | Infisical staging(179키) | dev/prod 불완전 → staging 우회 | env |
정상 확인: 배점표(정성65/정량35)·요구사항(5+기술12) 추출은 실코드로 정상. "0/5 fields"는 별개 경로(기본메타 키워드 파싱)임을 코드로 규명.
primary(Kanana) → 127.0.0.1:62581 HTTP 500 (다운)
fallback → 127.0.0.1:62582 정상
──────────────────────────────────────────
[실코드 로그] LLM failover: primary kanana_llm(kakaocorp)
실패(LLMError) → fallback or_llm(qwen) 재시도
실제 사용 provider: qwen
실제 응답: '[FAILOVER-OK] OpenRouter가 응답함' ✅ PASS
실제 KananaEngine이 진짜 HTTP 500 → LLMError → 실제 QwenEngine(fallback)로 전환 → 실응답 수신. 엔진 생성→HTTP→실패감지→전환→수신 전 경로 동작.
샘플 HWPX → HTML 28,675자 → 마크다운 1,691자 = 기록값 정확일치(동일 입력 확증). 사업명·예산 850M·기술평가 90:10 정확 추출. 규칙기반 요구사항추출 0건 → 핵심추출은 LLM 의존 판명.
| 항목 | 수정 | 테스트 |
|---|---|---|
| G2 busy-poll | TaskLostError+활동기반 grace: worker 생존 감지로 좀비루프 종료, 큐 대기 오판 방지 | 6/6 |
| G1 failover | llm_failover.py: primary 실패 시 등록된 다른 provider로 자동 재시도 | 6/6 |
| G4 캐시 | _lookup_ocr_cache: file_uuid 자동히트(opt-in 불필요) | 6/6 |
| 이슈 | 심각도 | 수정 |
|---|---|---|
| busy-poll이 큐 대기 정상 태스크를 유실 오판 (prefetch=1 배치) | 회귀 | 활동 기반 판정으로 전환 + grace 120→180s |
| 캐시 부분저장 히트 → 잘린 OCR 조용히 반환 | 정확성 | metadata.json 완료마커 + page_count 정합 검증 |
| online.py 미사용 import | 청소 | 제거 |
변경: 소스 3파일(+154/−39) · 신규 llm_failover.py + 테스트 3종
방식: 라이브 363문항은 모델+프로덕션 DB 부재로 실행 불가(질문이 self-contained 아님). 기록된 실 파이프라인 출력을 문서 ground truth+QA 루브릭으로 채점한 2문서 프록시 평가.
| QA | 목표 | 실측 | 판정 |
|---|---|---|---|
| C2 배점 (정형 RFP) | 90/95% exact·sum | 정성65+정량35=100/100, 7/7 항목 | PASS |
| C2 배점 (실 109p) | 90/95% | 0건 추출 | FAIL |
| C1 기본메타 (양 문서) | 90% exact | 0/5 = 0% (값은 문서에 실재) | FAIL |
| C4 요구사항 (정형) | — | req 5 / tech 12 | 양호 |
| C4 요구사항 (실 109p) | — | req 1 / tech 10 | 저조 |
| C6 지연 단일 LLM | 3.5s / 2.2s | Kanana 4.7s (gemini-flash 3.7s) | FAIL |
| C6 임베딩 | — | KURE 52ms | PASS |
| 제안서 e2e | — | 2p→7섹션→7벡터, errors=0 | PASS |
| 단계 성공률 | — | 6/6 SUCCESS (양 문서) | PASS |
| 항목 | 상태 |
|---|---|
| 라이브 e2e (363 QA 전수) | 차단 — 모델노드(promaxgb10) 오프라인·Docker 미기동·프로덕션 DB 부재 |
| C-C 기본메타 0/5 개선 | 정규식 폴백으로 오프라인 개선·검증 가능 (값이 문서에 실재) |
| C-D 실문서 recall | LLM 의존 → 라이브 검증 전제 |
내가 고친 3개(failover·poll·cache)는 인프라 복원력/속도 개선으로 QA 정확도엔 무회귀·중립. QA 점수를 올리는 다음 작업은 C-C·C-D 추출 로직.