26Y27a3

QK·2026년 7월 26일

Confluence 문서 내 표(Table), 그림(PNG/JPG), 구성도(Draw.io)는 LLM의 RAG 시각과 텍스트 시각 모두에서 매우 중요한 맥락(Context)을 가지고 있습니다. Air-Gapped 환경에서 이를 손실 없이 Markdown화하고 인덱싱하는 구체적인 처리 전략과 구현 방법입니다.


1. 요소별 Markdown 변환 및 저장 전략 요약

요소Markdown 변환 방식Git 저장 방식RAG / Hybrid Search 처리 방식
표 (Table)Clean Markdown Table (`------
이미지 (PNG/JPG)Relative Image Tag (![alt](./assets/img.png))Git 저장소 내 assets/ 폴더에 바이너리 다운로드Local Vision LLM (VLM)을 이용해 Image Captioning(설명 텍스트) 생성 후 MD에 포함
구성도 (Draw.io)SVG/PNG 변환 + Mermaid.js (텍스트 코드)Git 저장소 내 이미지 및 .drawio 원본 파일Mermaid 코드로 변환 시 아키텍처 구조 자체가 텍스트화되어 완벽한 RAG 검색 가능

2. 요소별 세부 구현 가이드

① 표 (Table) 처리

html2text 기본 설정으로 변환하면 복잡한 표가 깨지는 경우가 많습니다. BeautifulSoup으로 표 내부의 병합된 셀(colspan, rowspan)이나 깨진 태그를 정제한 후 표준 Markdown 표 형식으로 변환합니다.

  • Python 구현:
import html2text

h2t = html2text.HTML2Text()
h2t.ignore_tables = False  # 표를 Markdown 표로 변환 활성화
h2t.body_width = 0         # 표 내부 강제 줄바꿈 방지
  • RAG 팁: 표 바로 위에 표의 제목(Heading)이나 상위 헤더 문맥이 유지되도록 Header Chunking을 적용하면, 표 내용만 단독으로 쪼개져 문맥을 잃어버리는 현상을 방지할 수 있습니다.

② 일반 그림/캡처 (PNG, JPG) 처리

Air-Gapped 환경이므로 이미지 URL을 외부로 연결할 수 없습니다. 따라서 Confluence Attachment API를 통해 이미지를 로컬 다운로드하여 Git 저장소에 넣고, Local Vision LLM을 이용해 텍스트 설명(Caption)을 추출하여 메타데이터로 함께 저장합니다.

  1. 이미지 다운로드 및 Git 저장 경로 구조:
ops-knowledge/
└── OPS/
    ├── CONF-10429_Cilium_BGP.md
    └── assets/
        ├── CONF-10429_fig1.png
        └── CONF-10429_fig2.png
  1. Python 파이프라인 (Attachment Download + Local VLM Captioning):
import os
import requests

# 망분리 내 배포된 Local Vision LLM (e.g., Qwen2-VL, Llama-3.2-Vision)
LOCAL_VLM_URL = "http://vllm-vision.internal:8000/v1/chat/completions"

def process_confluence_images(page_id: str, soup: BeautifulSoup, space: str) -> BeautifulSoup:
    assets_dir = f"/workspace/ops-knowledge-repo/ops-knowledge/{space}/assets"
    os.makedirs(assets_dir, exist_ok=True)

    for img_tag in soup.find_all('img'):
        src = img_tag.get('src', '')
        # Confluence 첨부파일 이미지인 경우
        if 'download/attachments' in src or 'attachments' in src:
            img_url = f"http://confluence.internal{src}"
            img_name = f"{page_id}_{os.path.basename(src.split('?')[0])}"
            local_img_path = os.path.join(assets_dir, img_name)

            # 1. 이미지 바이너리 다운로드
            res = requests.get(img_url, headers={"Authorization": "Bearer TOKEN"})
            if res.status_code == 200:
                with open(local_img_path, 'wb') as f:
                    f.write(res.content)

                # 2. Local VLM을 이용해 이미지 요약/설명 텍스트 생성 (선택 사항)
                img_caption = generate_image_caption(local_img_path)

                # 3. HTML 태그를 상대 경로 Markdown 이미지 태그 + Caption으로 대체
                markdown_img_str = f"\n![{img_caption}](./assets/{img_name})\n*그림 설명: {img_caption}*\n"
                img_tag.replace_with(markdown_img_str)

    return soup

③ 아키텍처 구성도 (Draw.io / Lucidchart) 처리

구성도 디아그램은 AIOps 시스템에서 노드 간 연결 관계, 네트워크 경로, 서비스 의존성을 파악하는 핵심 자산입니다. 단순 이미지로만 변환하면 LLM이 구조를 파악하기 어렵습니다.

추천 처리 방식 (3단계 파이프라인)

  1. Draw.io 원본 파일 및 PNG 내보내기:
    Confluence의 Draw.io 매크로는 첨부파일에 .drawio (또는 .xml) 파일과 렌더링된 .png를 동시에 저장합니다. 이를 Git assets/ 폴더에 함께 보관합니다.
  2. Mermaid.js (텍스트 코드) 변환 추출:
    Draw.io XML 데이터 구조(노드/엣지 연결 관계)를 정규식이나 간단한 Parser로 Mermaid.js 코드 블록으로 변환하여 Markdown에 삽입합니다.
  • Markdown 변환 예시:
### 아키텍처 구성도

```mermaid
graph TD
    A[Client Ingress] --> B[Cilium eBPF Router]
    B --> C[MinIO AIStor Storage Pool]
    B --> D[Vault Secret Engine]

원본 파일: [CONF-10429_arch.drawio](https://www.google.com/search?q=./assets/CONF-10429_arch.drawio)


  1. RAG 시의 압도적 장점:
    Mermaid.js 코드로 변환되어 작성된 구성도는 완전한 순수 텍스트(Text)가 됩니다. 따라서:
  • OpenSearch(키워드) 및 Qdrant(Vector) 인덱스에 그대로 인덱싱됩니다.
  • LLM에게 "Cilium eBPF Router와 연결된 스토리지 컴포넌트가 뭐야?"라고 물었을 때, LLM이 Mermaid 코드를 읽고 정확한 관계를 답변할 수 있게 됩니다.

3. 전체 모듈(converter.py) 통합 적용 방식

기존 converter.py 흐름에 위에서 구현한 [표 정제 \rightarrow 이미지 다운로드 및 VLM Captioning \rightarrow Draw.io / Mermaid 변환]을 추가한 통합 프로세스 순서입니다.

def convert_confluence_html_to_md(page_data: dict, raw_html: str) -> str:
    soup = BeautifulSoup(raw_html, 'html.parser')

    # 1. 문서 속성 표(Page Properties) 파싱 및 제거
    human_meta = extract_human_metadata_from_table(soup)

    # 2. Draw.io / 다이어그램 매크로 정제 및 Mermaid/텍스트 추출
    soup = process_drawio_diagrams(soup, page_data["id"], page_data["space"]["key"])

    # 3. 일반 이미지 다운로드 & Local VLM 설명 추출 & 상대 경로 교체
    soup = process_confluence_images(page_data["id"], soup, page_data["space"]["key"])

    # 4. Clean Markdown 변환 (표 변환 옵션 포함)
    h2t = html2text.HTML2Text()
    h2t.ignore_tables = False
    h2t.body_width = 0
    clean_md_body = h2t.handle(str(soup))

    # 5. 메타데이터 최종 합성 (YAML Front-Matter)
    # ... (기존 동일)
    
    return yaml_frontmatter + clean_md_body

핵심 정리

  1. 표: html2text.ignore_tables = False 설정으로 텍스트 표로 100% 보존합니다.
  2. 그림: 이미지는 assets/ 디렉토리에 다운로드 후 상대 경로로 링크하며, Local VLM을 통해 1~2줄 텍스트 캡션을 생성해 추가하면 검색 정밀도가 올라갑니다.
  3. 구성도(Draw.io): 원본 .drawio 파일 보관과 함께, 노드 간 연결 관계를 Mermaid.js 코드 블록으로 변환하여 Markdown에 넣으면 LLM이 아키텍처 관계를 완전하게 추론할 수 있게 됩니다.
profile
engineer

0개의 댓글