Files
ExperionCrawler/dxf-graph-checkby-gemma4.md

101 lines
7.8 KiB
Markdown

# MCP 서버 DXF Graph 로직 분석 보고서 (by Gemma 4)
## 1. DXF Graph 생성 파이프라인 흐름
전체 프로세스는 **추출 $\rightarrow$ 위상 구축 $\rightarrow$ 매핑 $\rightarrow$ 분석**의 4단계로 구성됨.
1. **`PidGeometricExtractor` (extractor.py)**:
* DXF 파일에서 `TEXT`, `MTEXT`, `LINE`, `LWPOLYLINE`, `CIRCLE`, `ARC` 엔티티 추출.
* 각 엔티티의 Bounding Box(BBox) 및 좌표 정보 계산.
* 결과를 기하학적 데이터 JSON으로 저장.
2. **`PidTopologyBuilder` (topology.py)**:
* 추출된 엔티티를 NetworkX 그래프의 노드로 생성.
* **태그-설비 연결**: `TEXT` 노드와 가장 가까운 설비 노드를 `associated_with` 엣지로 연결.
* **물리적 연결**: `LINE`/`LWPOLYLINE`의 양 끝점(Endpoints)이 설비 BBox 내에 있거나 임계값 이내인 경우 `pipe` 엣지로 연결.
3. **`IntelligentMapper` (mapper.py)**:
* 도면의 텍스트 태그를 실제 시스템 태그(Experion Tags)와 매핑.
* `RapidFuzz`로 1차 후보를 뽑고, LLM(Qwen3)에 주변 위상 맥락(Neighbors)을 제공하여 최종 매핑 결정.
4. **`PidAnalysisEngine` (analyzer.py)**:
* 구축된 그래프와 매핑 데이터를 로드.
* `flow_direction``valve_status`를 고려하여 장애 전파 경로(Impact Analysis) 분석.
---
## 2. 주요 문제점 및 취약점 분석
### 🔴 심각도: HIGH (정확도 및 신뢰성 문제)
* **단순 거리 기반 태그 매핑 (`_find_nearest_equipment`)**:
* **문제**: 단순히 가장 가까운 설비를 찾으므로, 도면이 밀집된 구역에서 엉뚱한 설비에 태그가 할당될 가능성이 매우 높음.
* **영향**: 잘못된 태그 매핑 $\rightarrow$ 잘못된 영향도 분석 결과 도출.
* **단순화된 배관 방향성 추론 (`build_graph`)**:
* **문제**: `connected_nodes[0] \rightarrow connected_nodes[1]` 식으로 단순히 발견 순서대로 방향을 설정함. 실제 공정 흐름(Flow)을 전혀 반영하지 못함.
* **영향**: `analyzer.py`에서 `flow_direction`을 기반으로 분석하지만, 데이터 생성 단계에서 이미 무작위 방향성이 부여되어 분석 결과가 무의미함.
### 🟠 심각도: MED (로직 결함 및 누락)
* **배관-설비 연결의 한계**:
* **문제**: `LINE`의 양 끝점만 체크함. 배관이 설비 BBox를 관통하거나, 중간에 꺾이는 `LWPOLYLINE`의 경우 끝점이 설비 밖에 있으면 연결이 누락됨.
* **영향**: 그래프의 단절(Disconnected Components) 발생 $\rightarrow$ 영향도 분석 범위 축소.
* **LLM 매핑의 컨텍스트 부족**:
* **문제**: `get_node_context`가 단순히 연결된 노드의 값만 나열함. 설비의 종류, 계층 구조, 도면 내 상대적 위치 등의 풍부한 정보가 부족함.
* **영향**: 매핑 정확도 저하.
### 🟡 심각도: LOW (유지보수 및 성능)
* **하드코딩된 임계값**:
* **문제**: `dist_threshold=50.0`, `tag_threshold=100.0` 등이 하드코딩되어 있음. 도면의 스케일(Scale)이 달라지면 작동하지 않음.
* **영향**: 도면마다 설정값을 수동으로 조정해야 하는 번거로움.
* **에러 처리 미흡**:
* **문제**: `extractor.py` 등에서 `try-except``print`만 수행하고 계속 진행함.
* **영향**: 일부 엔티티 누락 시 원인 파악이 어려움.
---
## 3. 개선 방향 제안
1. **위상 기반 태그 매핑**: 단순 거리 $\rightarrow$ (거리 + 텍스트 방향 + 인접 배관 연결성)을 종합한 가중치 모델 도입.
2. **흐름 방향 추론 고도화**:
* 체크밸브, 펌프 등 방향성이 명확한 심볼을 기준으로 흐름 방향 전파(Propagation).
* LLM에게 배관 연결 리스트를 주고 공정 상식에 기반한 방향성 추론 요청.
3. **기하학적 연결 검사 강화**: `Point.distance` 대신 `LineString.intersects(Box)` 또는 `LineString.distance(Box)`를 사용하여 관통/접촉 여부를 정밀하게 판별.
4. **동적 스케일링**: 도면의 전체 크기나 표준 심볼 크기를 기준으로 임계값을 자동 계산하는 로직 추가.
---
## 4. [추가 진단] End-to-End 프로세스 및 UI 반환 흐름 진단
`diagnosis-checklist.md` 규칙에 따라 UI부터 파이프라인, MCP 서버까지의 전체 호출 계층을 진단한 결과입니다.
### 호출 계층 지도 (Call Hierarchy)
`UI (pid-viewer.js)` $\rightarrow$ `Web API (PidGraphController.cs)` $\rightarrow$ `Service (PidGraphService.cs)` $\rightarrow$ `MCP Client (McpClient.cs)` $\rightarrow$ `MCP Server (server.py)` $\rightarrow$ `Worker (pid_worker.py)` $\rightarrow$ `Pipeline (extractor/topology/mapper)` $\rightarrow$ `Storage (JSON files)` $\rightarrow$ `UI (Polling/Fetch)`
### 🔴 HIGH: 런타임 및 데이터 무결성 위험
1. **상태 관리의 휘발성 (In-Memory Store)**
- **문제**: `PidGraphController.cs:17`에서 `_statusStore``static ConcurrentDictionary`로 관리함.
- **영향**: 서버 재시작 시 모든 진행 중인 `taskId`가 소멸됨. UI(`pid-viewer.js`)는 `localStorage``taskId`를 저장하고 복구를 시도하지만, 서버에 데이터가 없어 `NotFound` 발생 및 사용자 혼란 초래.
- **수정**: Redis 또는 DB 기반의 상태 저장소 도입 필요.
2. **One-Shot 워커의 강제 종료 타이밍 (Race Condition)**
- **문제**: `pid_worker.py:430``_schedule_shutdown``asyncio.sleep(0.5)``os.kill`을 수행함.
- **영향**: 네트워크 지연이나 응답 크기가 클 경우, HTTP 응답이 클라이언트에 완전히 전달되기 전에 프로세스가 종료되어 `Connection Reset` 또는 `Empty Response` 발생 가능성 높음.
- **수정**: FastAPI의 `BackgroundTasks`를 사용하여 응답 전송 완료 후 종료하거나, MCP 서버가 워커의 종료를 확인하는 핸드셰이크 메커니즘 도입.
### 🟠 MED: 동시성 및 리소스 효율성
1. **P&ID 워커의 직렬화 병목 (Semaphore)**
- **문제**: `server.py:1455` 등에서 `_pid_sem = asyncio.Semaphore(1)`을 사용하여 모든 P&ID 관련 도구를 전역적으로 직렬화함.
- **영향**: 여러 사용자가 서로 다른 도면을 처리하려 해도 한 번에 하나의 요청만 처리 가능. 특히 `build_pid_graph_parallel`처럼 오래 걸리는 작업이 있을 때 전체 시스템 응답성 저하.
- **수정**: `graph_id` 또는 `filepath` 단위로 Lock을 세분화하여 서로 다른 도면 작업은 병렬로 처리 가능하게 개선.
2. **UI-서버 간의 비효율적 폴링 (Polling Overhead)**
- **문제**: `pid-viewer.js:70`에서 1초 간격으로 `/api/pidgraph/status/{taskId}`를 호출함.
- **영향**: 작업 시간이 길어질 경우 불필요한 HTTP 요청이 누적되며, 서버 리소스 낭비.
- **수정**: WebSocket 또는 Server-Sent Events(SSE)를 도입하여 서버가 상태 변경 시 푸시하도록 변경.
### 🟡 LOW: 코드 구조 및 사용자 경험
1. **하드코딩된 Graph ID (UI)**
- **문제**: `pid-viewer.js:342`에서 `graphId = "No-10_Plant_PID_graph.json"`가 하드코딩되어 있음.
- **영향**: 다른 도면을 로드했을 때 영향도 분석 요청이 항상 특정 파일로 고정되어 잘못된 결과 반환.
- **수정**: `pidLoadDrawing` 시점에 서버로부터 받은 `graphId`를 전역 변수에 저장하고 이를 사용하도록 수정.
2. **에러 응답 형식의 불일치**
- **문제**: `PidGraphController.cs``new { error = ... }` 형식을 사용하지만, MCP 서버/워커는 `{"success": false, "error": ...}` 형식을 사용함.
- **영향**: 프론트엔드에서 에러 처리 로직을 작성할 때 응답 구조에 따라 분기 처리가 복잡해짐.
- **수정**: 전사적인 공통 응답 DTO(Standard Response Format) 정의 및 적용.