From d76caea5dd35c63e9ade692bfbabd6f7bd676428 Mon Sep 17 00:00:00 2001 From: "DESKTOP-TKLFCPR\\ython" Date: Tue, 2 Jun 2026 18:24:26 +0900 Subject: [PATCH] =?UTF-8?q?feat(harness):=20Upstage=20OCR=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=20=ED=95=98=EB=84=A4=EC=8A=A4=20=E2=80=94=20Document?= =?UTF-8?q?=20AI=20+=207=EA=B0=9C=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 에이전트: - 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 --- .claude/agents/ocr-workflow-dev.md | 200 ++++++++++++ .claude/agents/upstage-ocr-dev.md | 108 +++++++ .../skills/upstage-ocr-orchestrator/SKILL.md | 245 ++++++++++++++ .../references/upstage-api-guide.md | 305 ++++++++++++++++++ CLAUDE.md | 28 ++ 5 files changed, 886 insertions(+) create mode 100644 .claude/agents/ocr-workflow-dev.md create mode 100644 .claude/agents/upstage-ocr-dev.md create mode 100644 .claude/skills/upstage-ocr-orchestrator/SKILL.md create mode 100644 .claude/skills/upstage-ocr-orchestrator/references/upstage-api-guide.md diff --git a/.claude/agents/ocr-workflow-dev.md b/.claude/agents/ocr-workflow-dev.md new file mode 100644 index 00000000..2879acea --- /dev/null +++ b/.claude/agents/ocr-workflow-dev.md @@ -0,0 +1,200 @@ +# 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개 시나리오 + 브랜드 계약 워크플로우 완료 보고 diff --git a/.claude/agents/upstage-ocr-dev.md b/.claude/agents/upstage-ocr-dev.md new file mode 100644 index 00000000..46b53b0d --- /dev/null +++ b/.claude/agents/upstage-ocr-dev.md @@ -0,0 +1,108 @@ +# 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 연동 + +```python +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 모델 + +```python +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 응답 구조 문서화 diff --git a/.claude/skills/upstage-ocr-orchestrator/SKILL.md b/.claude/skills/upstage-ocr-orchestrator/SKILL.md new file mode 100644 index 00000000..7301a077 --- /dev/null +++ b/.claude/skills/upstage-ocr-orchestrator/SKILL.md @@ -0,0 +1,245 @@ +--- +name: upstage-ocr-orchestrator +description: > + Upstage Document AI OCR 연동 및 GUARDiA ITSM 워크플로우 자동화 오케스트레이터. + Upstage API(Document Parse, Information Extraction, Document QA)를 연동하여 + 계약서·납품서·청구서·장애보고서·감사보고서 등을 자동 처리한다. + 현대백화점 같은 기업 브랜드 계약서를 포함한 일반 계약서도 처리한다. + 처리 결과를 ITSM 기능(조달관리·CMDB·과금·CSAP·SR)에 자동 연동한다. + 다음 상황에서 반드시 사용: + (1) 'OCR', 'Upstage', '문서 파싱', '문서 자동 처리', '계약서 OCR'; + (2) '납품서 CMDB 등록', '청구서 자동 파싱', '계약서 자동 등록'; + (3) '현대백화점 계약', '브랜드 계약서', '일반 계약서 처리'; + (4) '장애보고서 이미지 → SR', '회의록 → 액션아이템'; + (5) 'CSAP 보고서 자동 업데이트', '감사 문서 처리'; + (6) 다시 실행, 업데이트, 수정, 보완. +--- + +# Upstage OCR 연동 오케스트레이터 + +**실행 모드:** 에이전트 팀 +- 팀 구성: upstage-ocr-dev (OCR 엔진) + ocr-workflow-dev (ITSM 연동) +- Phase 1: OCR 엔진 구현 +- Phase 2: 워크플로우 구현 (병렬) +- Phase 3: DB 모델 + main.py 등록 + +--- + +## 시스템 개요 + +``` +사용자/시스템 + ↓ 문서 업로드 (PDF/PNG/JPG) +upstage_ocr.py + ↓ Upstage API 호출 + ├── Document Parse → 구조화 JSON (레이아웃/텍스트/테이블) + ├── Information Extraction → Key-Value (스키마 기반) + └── Document QA → 자연어 답변 +doc_workflow.py + ↓ 결과 → ITSM 자동 연동 + ├── 계약서 → e_procurement.py (ProcurementRecord) + ├── 납품서 → cmdb.py (Server 등록) + ├── 청구서 → billing.py (Invoice) + ├── 감사보고서 → compliance.py (CSAP 업데이트) + ├── 장애보고서 → tasks.py (SR 생성) + ├── 회의록 → tasks.py (SR + 작업) + └── 브랜드 계약서 → ProcurementRecord +``` + +--- + +## Phase 0: 컨텍스트 확인 + +``` +_workspace/ 없음 → 초기 구현 +있음 + OCR 엔진만 → upstage-ocr-dev 재실행 +있음 + 워크플로우만 → ocr-workflow-dev 재실행 +``` + +--- + +## Phase 1: OCR 엔진 구현 (upstage-ocr-dev) + +### 구현 파일: `workspace/guardia-itsm/routers/upstage_ocr.py` + +**핵심 구현:** + +```python +UPSTAGE_BASE = "https://api.upstage.ai/v1/document-ai" + +# 지원 형식 +SUPPORTED_MIME = { + ".pdf": "application/pdf", + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".tiff": "image/tiff", + ".bmp": "image/bmp", + ".heic": "image/heic", +} + +# 민감 정보 마스킹 패턴 +SENSITIVE_PATTERNS = [ + (r'\d{6}-[1-4]\d{6}', '######-#######'), # 주민번호 + (r'\d{3}-\d{2}-\d{5}', '###-##-#####'), # 사업자번호 (보존) + (r'\d{3,4}-\d{4}-\d{4}', '****-****-****'), # 전화번호 뒷자리 +] +``` + +**엔드포인트:** + +| 엔드포인트 | 설명 | +|-----------|------| +| `POST /api/ocr/config` | Upstage API Key 저장 (AES-256-GCM) | +| `GET /api/ocr/config` | 설정 조회 (키 마스킹) | +| `POST /api/ocr/parse` | 문서 파싱 → 구조화 JSON | +| `POST /api/ocr/extract` | 정보 추출 → Key-Value | +| `POST /api/ocr/qa` | 문서 QA → 자연어 답변 | +| `POST /api/ocr/batch` | 다중 파일 배치 처리 | +| `GET /api/ocr/history` | 처리 이력 | +| `GET /api/ocr/usage` | API 사용량 | + +--- + +## Phase 2: 워크플로우 구현 (ocr-workflow-dev) + +### 구현 파일 1: `workspace/guardia-itsm/routers/doc_workflow.py` + +| 엔드포인트 | 워크플로우 | 연동 | +|-----------|---------|------| +| `POST /api/docflow/contract` | 나라장터 계약서 | e_procurement.py | +| `POST /api/docflow/server-spec` | 서버납품서 | cmdb.py | +| `POST /api/docflow/invoice` | 청구서/세금계산서 | billing.py | +| `POST /api/docflow/audit-report` | 감사·CSAP 보고서 | compliance.py | +| `POST /api/docflow/incident-report` | 장애보고서 이미지 | tasks.py (SR) | +| `POST /api/docflow/meeting-minutes` | 회의록 | tasks.py (SR+작업) | +| `POST /api/docflow/brand-contract` | **기업 브랜드 계약서** | ProcurementRecord | +| `GET /api/docflow/jobs` | 작업 목록 | — | +| `GET /api/docflow/jobs/{id}` | 작업 상세 | — | + +#### 브랜드 계약서 처리 (현대백화점 등) + +```python +BRAND_CONTRACT_SCHEMA = { + "contract_title": "계약서 제목", + "party_a": "갑(발주사/브랜드사)", + "party_b": "을(수주사/공급사)", + "contract_amount": "계약금액", + "currency": "통화", + "contract_period": "계약기간", + "effective_date": "계약일", + "expiry_date": "만료일", + "payment_terms": "지급조건", + "contract_items": "계약품목/서비스", + "royalty_rate": "수수료율/로열티", + "territory": "적용지역", + "exclusive": "독점여부", + "renewal_clause": "갱신조항", + "termination": "해지조건", + "penalty_clause": "위약금", + "contact_a": "갑 담당자", + "contact_b": "을 담당자", + "special_terms": "특약사항", +} +``` + +### 구현 파일 2: `workspace/guardia-itsm/routers/doc_template.py` + +내장 템플릿 + 커스텀 템플릿 관리 + +**내장 템플릿 7종:** +1. `narasajang_contract` — 나라장터 계약서 +2. `server_delivery` — 서버 납품 명세서 +3. `brand_contract` — 기업 브랜드 계약서 (현대백화점 등) +4. `invoice` — 세금계산서/청구서 +5. `incident_report` — 장애 보고서 +6. `csap_report` — CSAP 점검 보고서 +7. `meeting_minutes` — 회의록 + +--- + +## Phase 3: DB 모델 + main.py 등록 + +```python +# 신규 모델 +class UpstageOCRConfig(Base): ... # API Key 설정 +class OCRHistory(Base): ... # 처리 이력 +class DocWorkflowJob(Base): ... # 워크플로우 작업 +class DocTemplate(Base): ... # 추출 템플릿 + +# main.py 등록 +from routers import upstage_ocr, doc_workflow, doc_template +app.include_router(upstage_ocr.router) +app.include_router(doc_workflow.router) +app.include_router(doc_template.router) +``` + +--- + +## ITSM 사이드바 메뉴 추가 + +```javascript +// index.html에 추가 +{ label: '문서 AI (OCR)', icon: '📄', children: [ + { nav: 'ocr_parse', label: 'Upstage OCR 파싱' }, + { nav: 'ocr_contract', label: '계약서 자동 처리' }, + { nav: 'ocr_invoice', label: '청구서 처리' }, + { nav: 'ocr_history', label: 'OCR 처리 이력' }, + { nav: 'doc_templates', label: '추출 템플릿 관리' }, +]} +``` + +--- + +## 데이터 흐름 + +``` +_workspace/ +├── ocr_api_spec.md ← upstage-ocr-dev +└── workflow_spec.md ← ocr-workflow-dev +``` + +--- + +## 에러 핸들링 + +| 에러 | 처리 | +|------|------| +| Upstage API Key 없음 | 설정 페이지 안내 | +| API 한도 초과 | 큐잉 후 재시도 | +| 지원 안 되는 파일 형식 | 400 오류 + 지원 형식 안내 | +| 추출 실패 (낮은 신뢰도) | 수동 검토 큐 등록 + SR 생성 | +| 민감 정보 감지 | 자동 마스킹 후 처리 | +| 파일 크기 초과 | 20MB 한도 안내 | + +--- + +## 테스트 시나리오 + +**정상 흐름 (브랜드 계약서):** +1. POST /api/docflow/brand-contract + 현대백화점_계약서.pdf +2. Upstage Extract API 호출 → 계약번호, 금액, 기간 추출 +3. ProcurementRecord 자동 생성 +4. 응답: {"ok": true, "contract_no": "HDC-2026-001", "amount": 50000000, ...} + +**정상 흐름 (장애보고서 이미지):** +1. POST /api/docflow/incident-report + 장애화면.png +2. Upstage Document Parse → 에러 텍스트 추출 +3. SR 자동 생성 (category=INCIDENT, priority=HIGH) +4. 응답: {"ok": true, "sr_id": 1234} + +--- + +## should-trigger + +- "OCR 연동", "Upstage 설정", "문서 파싱" +- "계약서 자동 등록", "현대백화점 계약", "브랜드 계약서" +- "납품서 CMDB 등록", "청구서 자동 처리" +- "장애보고서 이미지 SR", "회의록 액션아이템" +- "다시 실행", "수정", "보완" + +## should-NOT-trigger + +- "multimodal 이미지 분석" → multimodal.py (온프레미스) +- "SR 처리" → guardia-orchestrator +- "CMDB 관리" → cmdb.py 직접 diff --git a/.claude/skills/upstage-ocr-orchestrator/references/upstage-api-guide.md b/.claude/skills/upstage-ocr-orchestrator/references/upstage-api-guide.md new file mode 100644 index 00000000..b3c5f9da --- /dev/null +++ b/.claude/skills/upstage-ocr-orchestrator/references/upstage-api-guide.md @@ -0,0 +1,305 @@ +# Upstage Document AI API 가이드 + +## 기본 정보 + +| 항목 | 값 | +|------|-----| +| Base URL | `https://api.upstage.ai/v1/document-ai` | +| 인증 | Bearer Token (API Key) | +| 최대 파일 크기 | 20MB | +| 지원 형식 | PDF, PNG, JPG, JPEG, TIFF, BMP, HEIC | +| 최대 페이지 | 100페이지/요청 | + +--- + +## 1. Document Parse (문서 파싱) + +### 요청 +```http +POST https://api.upstage.ai/v1/document-ai/document-digitization +Authorization: Bearer {API_KEY} +Content-Type: multipart/form-data + +document: (파일 바이너리) +model: document-parse # 텍스트 레이어 있는 PDF + document-parse-ocr # 이미지/스캔 PDF +ocr: auto # auto: 자동 판단 +output_formats: ["text", "html", "markdown"] +``` + +### 응답 구조 +```json +{ + "api": "2.0", + "model": "document-parse", + "usage": {"pages": 3, "tokens": 1200}, + "content": { + "markdown": "# 계약서\n\n...", + "html": "...", + "text": "계약서\n\n당사는 ..." + }, + "elements": [ + { + "category": "paragraph", // paragraph | table | figure | header | footer + "content": {"markdown": "...", "html": "...", "text": "..."}, + "coordinates": [{"x": 0.1, "y": 0.1, "w": 0.8, "h": 0.05}], + "page": 1 + }, + { + "category": "table", + "content": {"markdown": "| 항목 | 금액 |\n|---|---|\n...", "html": "...
"}, + "page": 2 + } + ], + "pages": [{"page": 1, "width": 595, "height": 842}] +} +``` + +--- + +## 2. Information Extraction (정보 추출) + +### 요청 +```http +POST https://api.upstage.ai/v1/document-ai/information-extraction +Authorization: Bearer {API_KEY} +Content-Type: multipart/form-data + +document: (파일 바이너리) +schema: { + "contract_no": "계약번호", + "amount": "계약금액", + "supplier": "공급사명" +} +``` + +### 응답 구조 +```json +{ + "api": "2.0", + "usage": {"pages": 1, "tokens": 800}, + "result": { + "contract_no": { + "value": "2026-IT-0042", + "confidence": 0.97, + "coordinates": {"x": 0.2, "y": 0.15, "page": 1} + }, + "amount": { + "value": "₩50,000,000", + "confidence": 0.95, + "normalized": 50000000 + }, + "supplier": { + "value": "(주)지오정보기술", + "confidence": 0.98 + } + } +} +``` + +--- + +## 3. Document QA (문서 질의응답) + +### 요청 +```http +POST https://api.upstage.ai/v1/document-ai/document-qa +Authorization: Bearer {API_KEY} +Content-Type: multipart/form-data + +document: (파일 바이너리) +question: "이 계약서의 계약 기간은 언제까지인가요?" +``` + +### 응답 +```json +{ + "answer": "본 계약의 계약기간은 2026년 6월 1일부터 2027년 5월 31일까지입니다.", + "confidence": 0.93, + "source": {"page": 2, "text": "제3조 (계약기간) 본 계약의 기간은..."} +} +``` + +--- + +## 활용 시나리오별 스키마 + +### 나라장터 계약서 +```json +{ + "contract_no": "계약번호", + "contract_name": "계약품명", + "supplier": "공급사명", + "supplier_biz_no":"사업자등록번호", + "amount": "계약금액(원)", + "vat": "부가세", + "start_date": "계약시작일(YYYY-MM-DD)", + "end_date": "계약종료일(YYYY-MM-DD)", + "institution": "발주기관명", + "manager": "담당자", + "payment_terms": "납부조건" +} +``` + +### 서버 납품 명세서 +```json +{ + "hostname": "호스트명/서버명", + "manufacturer": "제조사", + "model_no": "모델번호", + "serial_no": "시리얼번호", + "cpu_model": "CPU 모델", + "cpu_cores": "CPU 코어 수", + "memory_gb": "메모리 용량(GB)", + "disk_config": "스토리지 구성", + "os": "운영체제", + "ip_addr": "IP주소", + "rack_location": "랙 위치", + "warranty_until": "보증기간 만료일", + "delivery_date": "납품일" +} +``` + +### 기업 브랜드 계약서 (현대백화점 등) +```json +{ + "contract_title": "계약서 제목", + "party_a": "갑(브랜드사/발주사)", + "party_a_biz_no": "갑 사업자번호", + "party_b": "을(공급사/입점사)", + "party_b_biz_no": "을 사업자번호", + "contract_amount": "계약금액", + "currency": "통화(KRW/USD)", + "effective_date": "계약일", + "expiry_date": "만료일", + "auto_renewal": "자동갱신여부", + "payment_terms": "지급조건", + "contract_items": "계약품목/서비스", + "royalty_rate": "수수료율", + "territory": "적용지역/매장", + "exclusive": "독점여부", + "termination": "해지조건", + "penalty_clause": "위약금", + "special_terms": "특약사항", + "contact_a": "갑 담당자", + "contact_b": "을 담당자" +} +``` + +### 세금계산서/청구서 +```json +{ + "invoice_no": "세금계산서번호/청구번호", + "issue_date": "발행일", + "supplier_name": "공급자 상호", + "supplier_biz_no": "공급자 사업자번호", + "buyer_name": "공급받는자 상호", + "buyer_biz_no": "공급받는자 사업자번호", + "supply_amount": "공급가액", + "vat_amount": "세액", + "total_amount": "합계금액", + "items": "품목/내역", + "payment_due": "결제기한", + "bank_account": "입금계좌(마스킹)" +} +``` + +### 장애 보고서 +```json +{ + "incident_date": "발생일시", + "incident_type": "장애유형", + "affected_system": "영향 시스템", + "error_message": "오류 메시지", + "root_cause": "근본원인", + "impact_scope": "영향 범위", + "resolution": "조치사항", + "downtime_minutes": "다운타임(분)", + "reporter": "보고자" +} +``` + +### CSAP 점검 보고서 +```json +{ + "institution": "기관명", + "check_date": "점검일", + "total_items": "총 점검항목 수", + "passed_items": "적합 항목 수", + "failed_items": "부적합 항목 수", + "compliance_rate": "준수율(%)", + "major_findings": "주요 발견사항", + "recommendations": "권고사항" +} +``` + +### 회의록 +```json +{ + "meeting_date": "회의일시", + "meeting_place": "장소", + "participants": "참석자", + "agenda": "안건", + "decisions": "결정사항", + "action_items": "액션아이템(담당자/기한)", + "next_meeting": "차기 회의일" +} +``` + +--- + +## 비용 가이드 (참고) + +| API | 페이지당 비용 | +|-----|------------| +| Document Parse | ~$0.01/페이지 | +| Information Extraction | ~$0.02/페이지 | +| Document QA | ~$0.02/요청 | + +> 최신 요금은 https://www.upstage.ai/pricing 참조 + +--- + +## Python 구현 예시 + +```python +import httpx, base64, json +from pathlib import Path + +async def upstage_parse(api_key: str, file_path: str) -> dict: + with open(file_path, "rb") as f: + file_bytes = f.read() + + filename = Path(file_path).name + ext = Path(file_path).suffix.lower() + mime = {"pdf": "application/pdf", "png": "image/png", "jpg": "image/jpeg"}.get(ext[1:], "application/octet-stream") + + async with httpx.AsyncClient(timeout=120) as client: + response = await client.post( + "https://api.upstage.ai/v1/document-ai/document-digitization", + headers={"Authorization": f"Bearer {api_key}"}, + files={"document": (filename, file_bytes, mime)}, + data={ + "model": "document-parse-ocr", + "ocr": "auto", + "output_formats": '["text", "html", "markdown"]' + } + ) + return response.json() + + +async def upstage_extract(api_key: str, file_path: str, schema: dict) -> dict: + with open(file_path, "rb") as f: + file_bytes = f.read() + + filename = Path(file_path).name + + async with httpx.AsyncClient(timeout=120) as client: + response = await client.post( + "https://api.upstage.ai/v1/document-ai/information-extraction", + headers={"Authorization": f"Bearer {api_key}"}, + files={"document": (filename, file_bytes, "application/pdf")}, + data={"schema": json.dumps(schema, ensure_ascii=False)} + ) + return response.json() +``` diff --git a/CLAUDE.md b/CLAUDE.md index 8be9fb66..b876afc0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -279,6 +279,34 @@ GUARDiA ITSM (허브, :9001/:8443) --- +## 하네스: Upstage OCR 연동 + +**목표:** Upstage Document AI API(Document Parse·Information Extraction·Document QA)를 연동하여 계약서·납품서·청구서·장애보고서·감사보고서·회의록을 자동 처리하고 ITSM 기능에 연동. 현대백화점 등 기업 브랜드 계약서 포함. + +**트리거:** OCR, Upstage, 문서 파싱, 계약서 자동 처리, 현대백화점 계약, 브랜드 계약서, 납품서 CMDB 등록, 청구서 자동 처리, 장애보고서 이미지→SR 요청 시 `upstage-ocr-orchestrator` 스킬을 사용하라. + +**에이전트:** upstage-ocr-dev (Document AI API 엔진) · ocr-workflow-dev (6개 워크플로우 + 브랜드 계약 연동) + +**신규 라우터:** upstage_ocr.py · doc_workflow.py · doc_template.py + +**지원 시나리오:** +1. 나라장터 계약서 → ProcurementRecord 자동 등록 +2. 서버납품서 → CMDB 자동 등록 +3. 브랜드 계약서 (현대백화점 등) → 계약 이력 관리 +4. 세금계산서 → 과금 연동 +5. 장애보고서 이미지 → SR 자동 생성 +6. 회의록 → 액션아이템 SR 생성 +7. CSAP 보고서 → 준수율 자동 업데이트 + +**API 가이드:** `.claude/skills/upstage-ocr-orchestrator/references/upstage-api-guide.md` + +**변경 이력:** +| 날짜 | 변경 내용 | 대상 | 사유 | +|------|----------|------|------| +| 2026-06-02 | 초기 하네스 구성 | 전체 | Upstage OCR 연동 + 기업 계약서 처리 | + +--- + ## 하네스: GUARDiA 고급 확장 **목표:** GUARDiA ITSM 현재 104개 라우터(667 엔드포인트)에서 20개 신규 라우터 추가. CMDB 자동 발견·Text-to-SQL·구성 드리프트·멀티클라우드·공공기관 특화 5개 영역 확장. 목표: 124개 라우터, ~764개 엔드포인트.