feat: P&ID 연결 분석, LLM 에이전트 모드, KB 확장, MCP 서버 리팩토링
- P&ID: 연결 분석 API, Prefix 규칙 관리, 카테고리 분류, DXF 그래프 빌드 - LLM: 대화 요약, tool card 영구 보존, 시계열 차트(uPlot), 에이전트 모드 - KB: 청크 미리보기, Field Instrument Inference, 인증/Qdrant 클라이언트 - MCP: 서버 기능 확장, 파이프라인 수정, timeout 개선 - Frontend: P&ID UI, LLM UI, KB UI, OPC UA Write 탭 추가 - 설정: AGENTS.md, plant_context, README, opencode.json 업데이트 - 정리: 진단 체크리스트 문서 삭제
This commit is contained in:
@@ -13,7 +13,7 @@ class MappingResult(BaseModel):
|
||||
confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence score from 0 to 1")
|
||||
|
||||
class IntelligentMapper:
|
||||
def __init__(self, graph: nx.Graph, system_tags: List[str], api_client: Optional[AsyncOpenAI] = None, model_name: str = "Qwen3.6-27B-FP8"):
|
||||
def __init__(self, graph: nx.Graph, system_tags: List[str], api_client: Optional[AsyncOpenAI] = None, model_name: str = "Qwen3.6-35B-A3B-FP8"):
|
||||
self.graph = graph
|
||||
self.system_tags = system_tags
|
||||
self.client = api_client
|
||||
|
||||
@@ -114,6 +114,12 @@ class PidTopologyBuilder:
|
||||
grid.add(nid, self.G.nodes[nid]['bbox'])
|
||||
return grid
|
||||
|
||||
# 시그널 레이어 이름 집합 (ELECTRIC SIGNAL, INSTRUMENT signal선 등)
|
||||
_SIGNAL_LAYERS = frozenset({'ELECTRIC SIGNAL', 'SIGNAL', 'ELEC', 'CABLE', 'WIRE'})
|
||||
|
||||
def _relation_for_layer(self, layer: str) -> str:
|
||||
return 'signal' if (layer or '').upper() in {s.upper() for s in self._SIGNAL_LAYERS} else 'pipe'
|
||||
|
||||
def build_graph(self):
|
||||
# 1. 모든 객체를 노드로 추가
|
||||
for item in self.data:
|
||||
@@ -150,7 +156,7 @@ class PidTopologyBuilder:
|
||||
if best_match:
|
||||
self.G.add_edge(tag, best_match, relation='associated_with')
|
||||
|
||||
# 4. 배관 기반 물리적 연결 (Pipe) — SpatialGrid 사용
|
||||
# 4. 배관/시그널 기반 연결 — SpatialGrid 사용
|
||||
lines = [n for n, d in self.G.nodes(data=True) if d['type'] in ['LINE', 'LWPOLYLINE']]
|
||||
|
||||
for line_id in lines:
|
||||
@@ -161,6 +167,8 @@ class PidTopologyBuilder:
|
||||
coords = original_item['coordinates']
|
||||
line_geom = LineString(coords)
|
||||
line_bbox = line_geom.bounds
|
||||
layer = original_item.get('layer', '')
|
||||
relation = self._relation_for_layer(layer)
|
||||
|
||||
# SpatialGrid로 후보 집합 조회 (O(1) 그리드 셀 기반)
|
||||
nearby_equipment_ids = eq_grid.query(
|
||||
@@ -183,27 +191,22 @@ class PidTopologyBuilder:
|
||||
connected_nodes = list(set(connected_nodes))
|
||||
|
||||
if len(connected_nodes) >= 2:
|
||||
# 개선: 단순 순서가 아닌, 기하학적 좌표 기반의 흐름 방향 추론 (왼쪽 -> 오른쪽, 위 -> 아래 우선)
|
||||
# 실제 공정 도면의 일반적인 흐름 방향을 반영
|
||||
node0_bbox = self.G.nodes[connected_nodes[0]]['bbox']
|
||||
node1_bbox = self.G.nodes[connected_nodes[1]]['bbox']
|
||||
|
||||
center0 = ((node0_bbox.bounds[0] + node0_bbox.bounds[2])/2, (node0_bbox.bounds[1] + node0_bbox.bounds[3])/2)
|
||||
center1 = ((node1_bbox.bounds[0] + node1_bbox.bounds[2])/2, (node1_bbox.bounds[1] + node1_bbox.bounds[3])/2)
|
||||
|
||||
# X축 차이가 Y축 차이보다 크면 X축 기준, 아니면 Y축 기준으로 방향 결정
|
||||
if abs(center1[0] - center0[0]) > abs(center1[1] - center0[1]):
|
||||
# X축 기준: 왼쪽 -> 오른쪽
|
||||
if center0[0] < center1[0]:
|
||||
self.G.add_edge(connected_nodes[0], connected_nodes[1], relation='pipe', flow_direction='forward')
|
||||
self.G.add_edge(connected_nodes[0], connected_nodes[1], relation=relation, flow_direction='forward')
|
||||
else:
|
||||
self.G.add_edge(connected_nodes[1], connected_nodes[0], relation='pipe', flow_direction='forward')
|
||||
self.G.add_edge(connected_nodes[1], connected_nodes[0], relation=relation, flow_direction='forward')
|
||||
else:
|
||||
# Y축 기준: 위 -> 아래 (도면 좌표계에 따라 다를 수 있으나 일반적인 관례 적용)
|
||||
if center0[1] > center1[1]:
|
||||
self.G.add_edge(connected_nodes[0], connected_nodes[1], relation='pipe', flow_direction='forward')
|
||||
self.G.add_edge(connected_nodes[0], connected_nodes[1], relation=relation, flow_direction='forward')
|
||||
else:
|
||||
self.G.add_edge(connected_nodes[1], connected_nodes[0], relation='pipe', flow_direction='forward')
|
||||
self.G.add_edge(connected_nodes[1], connected_nodes[0], relation=relation, flow_direction='forward')
|
||||
elif len(connected_nodes) == 1:
|
||||
# 단일 연결의 경우, 일단 엣지를 생성하되 방향은 미정(undirected-like)으로 처리하거나
|
||||
# 추후 전파 로직에서 결정하도록 함
|
||||
|
||||
Reference in New Issue
Block a user