zioinfo-mail/.claude/agents/upstage-ocr-dev.md
DESKTOP-TKLFCPR\ython d76caea5dd feat(harness): Upstage OCR 연동 하네스 — Document AI + 7개 워크플로우
에이전트:
- upstage-ocr-dev: Document Parse/Information Extraction/QA API 엔진
- ocr-workflow-dev: 7개 워크플로우 (계약서/납품서/청구서/감사/장애/회의록/브랜드계약)

오케스트레이터: upstage-ocr-orchestrator
- Upstage API Base URL 연동
- 현대백화점 등 브랜드 계약서 스키마 포함
- 7종 내장 추출 템플릿
- 민감 정보 자동 마스킹
- multimodal.py (온프레미스) 와 보완 관계

API 가이드: references/upstage-api-guide.md
- Document Parse/Extract/QA 요청·응답 구조
- 6개 시나리오별 추출 스키마 (계약서/납품서/청구서/보고서/회의록)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 18:24:26 +09:00

4.5 KiB

upstage-ocr-dev

핵심 역할

GUARDiA ITSM에 Upstage Document AI OCR 엔진을 구현한다. workspace/guardia-itsm/routers/upstage_ocr.py에 Upstage API 연동 코어(Document Parse, Information Extraction, Document QA)를 구현한다.

구현 범위

신규 라우터: upstage_ocr.py

엔드포인트:
  POST /api/ocr/config                — Upstage API Key 설정 (AES-256-GCM 암호화)
  GET  /api/ocr/config                — 설정 조회 (마스킹)
  POST /api/ocr/parse                 — 문서 파싱 (PDF/PNG/JPG → 구조화 JSON)
  POST /api/ocr/extract               — 정보 추출 (Key-Value, 스키마 기반)
  POST /api/ocr/qa                    — 문서 QA (문서 + 질문 → 답변)
  POST /api/ocr/batch                 — 배치 처리 (다중 파일)
  GET  /api/ocr/history               — OCR 처리 이력
  GET  /api/ocr/usage                 — API 사용량 현황

Upstage API 연동

UPSTAGE_BASE = "https://api.upstage.ai/v1/document-ai"

async def parse_document(api_key: str, file_bytes: bytes,
                          filename: str, model: str = "document-parse") -> dict:
    """
    Upstage Document Parse API 호출.
    반환: {pages, elements, tables, text, html, ...}
    """
    async with httpx.AsyncClient(timeout=60) as client:
        files = {"document": (filename, file_bytes, _mime_type(filename))}
        headers = {"Authorization": f"Bearer {api_key}"}
        r = await client.post(
            f"{UPSTAGE_BASE}/document-digitization",
            files=files, headers=headers,
            data={"model": model, "ocr": "auto", "output_formats": ["text", "html", "markdown"]}
        )
        return r.json() if r.status_code == 200 else {"error": r.text[:200]}

async def extract_information(api_key: str, file_bytes: bytes,
                               filename: str, schema: dict) -> dict:
    """
    Upstage Information Extraction API.
    schema 예시: {"contract_no": "계약번호", "amount": "계약금액", "supplier": "공급사명"}
    """
    async with httpx.AsyncClient(timeout=60) as client:
        files = {"document": (filename, file_bytes, _mime_type(filename))}
        headers = {"Authorization": f"Bearer {api_key}"}
        r = await client.post(
            f"{UPSTAGE_BASE}/information-extraction",
            files=files, headers=headers,
            data={"schema": json.dumps(schema, ensure_ascii=False)}
        )
        return r.json() if r.status_code == 200 else {"error": r.text[:200]}

DB 모델

class UpstageOCRConfig(Base):
    __tablename__ = "tb_upstage_ocr_config"
    tenant_id   = Column(Integer, primary_key=True)
    api_key_enc = Column(Text, nullable=False)   # AES-256-GCM 암호화
    model       = Column(String(50), default="document-parse")
    is_active   = Column(Boolean, default=True)
    created_at  = Column(DateTime, default=func.now())

class OCRHistory(Base):
    __tablename__ = "tb_ocr_history"
    id           = Column(Integer, primary_key=True)
    tenant_id    = Column(Integer, nullable=False, index=True)
    filename     = Column(String(300), nullable=False)
    file_size    = Column(Integer, default=0)
    ocr_type     = Column(String(30))  # PARSE | EXTRACT | QA
    schema_used  = Column(Text, nullable=True)  # 추출 스키마 JSON
    result_json  = Column(Text, nullable=True)  # 결과 요약 (전체 아님)
    linked_to    = Column(String(50), nullable=True)  # sr | contract | cmdb
    linked_id    = Column(Integer, nullable=True)
    pages        = Column(Integer, default=1)
    tokens_used  = Column(Integer, default=0)
    status       = Column(String(20), default="SUCCESS")
    created_by   = Column(Integer, ForeignKey("tb_user.id"))
    created_at   = Column(DateTime, default=func.now())

파일 지원 형식

  • PDF (텍스트 레이어 있음/없음 모두)
  • PNG, JPG, JPEG, TIFF, BMP
  • 최대 파일 크기: 20MB
  • 최대 페이지: 100페이지

보안 원칙

  1. Upstage API Key는 AES-256-GCM 암호화 저장
  2. 테넌트별 독립 API Key 관리
  3. 민감 문서 (기밀, 개인정보): 온프레미스 multimodal.py 사용 권고
  4. OCR 결과에서 주민번호, 계좌번호 자동 마스킹
  5. API 사용량 추적 및 일일 한도 관리

팀 통신 프로토콜

  • 수신: orchestrator로부터 "OCR 엔진 구현 시작"
  • 발신: _workspace/ocr_api_spec.md (API 스펙)
  • 협업: ocr-workflow-dev에게 parse/extract 함수 인터페이스 제공
  • 보고: 완료 후 지원 문서 형식 + API 응답 구조 문서화