# ocr-workflow-dev ## 핵심 역할 Upstage OCR 결과를 **GUARDiA ITSM 워크플로우에 자동 연동**한다. 6개 시나리오 + 기업 계약서 처리를 구현하여 문서 → 자동 등록/분류/SR 생성 전 과정을 자동화한다. ## 구현 범위 ### 신규 라우터: `doc_workflow.py` ``` 엔드포인트: POST /api/docflow/contract — 계약서 OCR → 조달 자동 등록 POST /api/docflow/server-spec — 서버납품서 OCR → CMDB 자동 등록 POST /api/docflow/invoice — 청구서/세금계산서 → 과금 연동 POST /api/docflow/audit-report — 감사보고서 → CSAP 자동 업데이트 POST /api/docflow/incident-report — 장애보고서 이미지 → SR 자동 생성 POST /api/docflow/meeting-minutes — 회의록 → SR/작업 자동 생성 POST /api/docflow/brand-contract — 브랜드 계약서 (현대백화점 등) 처리 GET /api/docflow/jobs — 워크플로우 작업 목록 GET /api/docflow/jobs/{id} — 작업 상세·결과 ``` ### 신규 라우터: `doc_template.py` ``` 엔드포인트: GET /api/doctemplate/ — 추출 템플릿 목록 POST /api/doctemplate/ — 템플릿 생성 PUT /api/doctemplate/{id} — 템플릿 수정 DELETE /api/doctemplate/{id} — 템플릿 삭제 GET /api/doctemplate/builtin — 내장 템플릿 목록 POST /api/doctemplate/apply-builtin — 내장 템플릿 적용 ``` ### 시나리오 구현 #### 시나리오 1: 나라장터 계약서 자동 등록 ```python async def process_contract(file_bytes, filename, api_key, tenant_id, db): # 1. Upstage Information Extraction schema = { "contract_no": "계약번호", "contract_name": "계약품명", "supplier": "공급사명", "supplier_biz_no": "공급사사업자번호", "amount": "계약금액", "start_date": "계약시작일", "end_date": "계약종료일", "institution": "발주기관명", } extracted = await extract_information(api_key, file_bytes, filename, schema) # 2. e_procurement.py 자동 등록 if extracted.get("contract_no"): await db.execute(insert(ProcurementRecord).values( tenant_id=tenant_id, contract_no=extracted["contract_no"], contract_name=extracted["contract_name"], supplier=extracted["supplier"], amount=parse_amount(extracted["amount"]), start_date=parse_date(extracted["start_date"]), end_date=parse_date(extracted["end_date"]), )) return extracted ``` #### 시나리오 2: 서버 납품서 → CMDB 자동 등록 ```python # 납품서에서 서버 사양 추출 → tb_server_info 자동 등록 schema = { "hostname": "호스트명", "model": "서버모델", "cpu": "CPU 사양", "memory_gb": "메모리(GB)", "disk_tb": "스토리지(TB)", "ip_addr": "IP주소", "serial_no": "시리얼번호", "warranty_until": "보증기간", } ``` #### 시나리오 3: 브랜드 계약서 (현대백화점 등) ```python # 일반 기업 계약서 템플릿 schema = { "contract_title": "계약서명", "party_a": "갑(발주사)", "party_b": "을(수주사)", "contract_amount": "계약금액", "contract_period": "계약기간", "payment_terms": "지급조건", "effective_date": "계약일", "contract_items": "계약품목", "special_conditions": "특수조건", "penalty_clause": "위약금 조항", "contact_a": "갑 담당자", "contact_b": "을 담당자", } # 추출 후 → ProcurementRecord + SR 연계 또는 별도 BrandContract 테이블 ``` #### 시나리오 4: 장애보고서 이미지 → SR 자동 생성 ```python # Document Parse로 에러 내용 추출 → SRRequest 자동 생성 extracted_text = await parse_document(api_key, file_bytes, filename) error_summary = extract_error_from_text(extracted_text["text"]) sr = SRRequest( title=f"[장애보고서] {error_summary[:80]}", description=extracted_text["text"][:1000], category="INCIDENT", priority="HIGH", status=SRStatus.OPEN, ) ``` ### 내장 추출 템플릿 ```python BUILTIN_TEMPLATES = { "narasajang_contract": { "name": "나라장터 계약서", "schema": {"contract_no": "계약번호", "amount": "계약금액", ...}, "workflow": "e_procurement", }, "server_delivery": { "name": "서버 납품 명세서", "schema": {"hostname": "호스트명", "cpu": "CPU", "memory_gb": "메모리(GB)", ...}, "workflow": "cmdb_register", }, "brand_contract": { "name": "일반 기업 계약서 (현대백화점 등)", "schema": {"party_a": "갑", "party_b": "을", "contract_amount": "계약금액", ...}, "workflow": "procurement_record", }, "invoice": { "name": "세금계산서/청구서", "schema": {"invoice_no": "세금계산서번호", "supplier": "공급자", "amount": "공급가액", ...}, "workflow": "billing", }, "incident_report": { "name": "장애 보고서", "schema": {"error_type": "오류유형", "affected_system": "영향 시스템", ...}, "workflow": "sr_create", }, "csap_report": { "name": "CSAP 점검 보고서", "schema": {"check_item": "점검항목", "result": "점검결과", "score": "점수", ...}, "workflow": "compliance_update", }, "meeting_minutes": { "name": "회의록", "schema": {"date": "회의일", "participants": "참석자", "decisions": "결정사항", "actions": "액션아이템"}, "workflow": "sr_create", }, } ``` ## DB 모델 추가 ```python class DocWorkflowJob(Base): __tablename__ = "tb_doc_workflow_job" id = Column(Integer, primary_key=True) tenant_id = Column(Integer, nullable=False, index=True) workflow_type = Column(String(50)) # contract|server_spec|invoice|... filename = Column(String(300)) template_id = Column(Integer, nullable=True) status = Column(String(20), default="PROCESSING") extracted_data = Column(JSON, nullable=True) linked_record_id = Column(Integer, nullable=True) linked_table = Column(String(50), nullable=True) error_message = Column(Text, nullable=True) created_by = Column(Integer, ForeignKey("tb_user.id")) created_at = Column(DateTime, default=func.now()) completed_at = Column(DateTime, nullable=True) class DocTemplate(Base): __tablename__ = "tb_doc_template" id = Column(Integer, primary_key=True) tenant_id = Column(Integer, nullable=False, index=True) name = Column(String(200), nullable=False) description = Column(Text, nullable=True) schema_json = Column(Text, nullable=False) # 추출 스키마 workflow = Column(String(50), nullable=True) # 연동 워크플로우 is_builtin = Column(Boolean, default=False) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=func.now()) ``` ## 작업 원칙 1. 민감 정보(주민번호·계좌번호) 자동 마스킹 후 저장 2. 추출 실패 시 수동 검토 큐에 등록 (SR 생성) 3. 기업 계약서는 brand_contract 템플릿으로 표준화 4. 워크플로우 결과는 반드시 원본 파일과 연계 추적 ## 팀 통신 프로토콜 - **수신**: orchestrator로부터 "워크플로우 구현 시작"; upstage-ocr-dev에서 API 스펙 수신 - **발신**: `_workspace/workflow_spec.md` - **협업**: upstage-ocr-dev의 parse/extract 함수 재사용 - **보고**: 6개 시나리오 + 브랜드 계약 워크플로우 완료 보고