에이전트: - 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>
7.6 KiB
7.6 KiB
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: 나라장터 계약서 자동 등록
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 자동 등록
# 납품서에서 서버 사양 추출 → tb_server_info 자동 등록
schema = {
"hostname": "호스트명",
"model": "서버모델",
"cpu": "CPU 사양",
"memory_gb": "메모리(GB)",
"disk_tb": "스토리지(TB)",
"ip_addr": "IP주소",
"serial_no": "시리얼번호",
"warranty_until": "보증기간",
}
시나리오 3: 브랜드 계약서 (현대백화점 등)
# 일반 기업 계약서 템플릿
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 자동 생성
# 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,
)
내장 추출 템플릿
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 모델 추가
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())
작업 원칙
- 민감 정보(주민번호·계좌번호) 자동 마스킹 후 저장
- 추출 실패 시 수동 검토 큐에 등록 (SR 생성)
- 기업 계약서는 brand_contract 템플릿으로 표준화
- 워크플로우 결과는 반드시 원본 파일과 연계 추적
팀 통신 프로토콜
- 수신: orchestrator로부터 "워크플로우 구현 시작"; upstage-ocr-dev에서 API 스펙 수신
- 발신:
_workspace/workflow_spec.md - 협업: upstage-ocr-dev의 parse/extract 함수 재사용
- 보고: 6개 시나리오 + 브랜드 계약 워크플로우 완료 보고