# ๐Ÿ”Œ Graph Pipeline Phase 5: MCP ์„œ๋ฒ„ ํ†ตํ•ฉ ๋ฐ ๊ณ ์„ฑ๋Šฅ ๋ณ‘๋ ฌ ์•„ํ‚คํ…์ฒ˜ (MCP Integration & Parallel Processing) ์ด ๋ฌธ์„œ๋Š” ์•ž์„œ ์„ค๊ณ„ํ•œ 1~4๋‹จ๊ณ„์˜ Graph Pipeline์„ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์˜ **Unified MCP Server (`mcp-server/server.py`)**์— ํ†ตํ•ฉํ•˜๋Š” ๋ฐฉ์•ˆ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ํŠนํžˆ, ๋Œ€์šฉ๋Ÿ‰ ๋„๋ฉด ์ฒ˜๋ฆฌ ์‹œ ๋ฐœ์ƒํ•˜๋Š” ์ง€์—ฐ๊ณผ ๋ฒ„ํผ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด `PID_Parser_Plan_Revision.md`์˜ **๋ถ„์‚ฐ ์ฒ˜๋ฆฌ ๊ธฐ๋ฒ•**๊ณผ vLLM์˜ **Continuous Batching** ํŠน์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•œ ๋ณ‘๋ ฌ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. --- ## ๐Ÿ—๏ธ 1. ํ†ตํ•ฉ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ### 1.1 ๊ณ ์„ฑ๋Šฅ ๋ณ‘๋ ฌ ๋ฐ์ดํ„ฐ ํ๋ฆ„ (Parallel End-to-End Flow) ๋‹จ์ผ ์ˆœ์ฐจ ์š”์ฒญ ๋ฐฉ์‹์—์„œ ๋ฒ—์–ด๋‚˜, **[์ „์ฒ˜๋ฆฌ $\rightarrow$ ๋ณ‘๋ ฌ ๋ถ„์‚ฐ ์ถ”์ถœ $\rightarrow$ ํ†ตํ•ฉ ํ›„์ฒ˜๋ฆฌ]** ๊ตฌ์กฐ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. `Frontend (UI)` $\rightarrow$ `C# Server (API)` $\rightarrow$ `MCP Server (Orchestrator)` $\rightarrow$ `Parallel Worker Tools (vLLM Batching)` $\rightarrow$ `Result Aggregator` $\rightarrow$ `C# Server` 1. **์š”์ฒญ:** ์‚ฌ์šฉ์ž๊ฐ€ UI์—์„œ ๋„๋ฉด ๋ถ„์„ ์‹œ์ž‘ ๋ฒ„ํŠผ ํด๋ฆญ. 2. **์ „์ฒ˜๋ฆฌ (Orchestrator):** MCP ์„œ๋ฒ„๊ฐ€ DXF๋ฅผ ๋กœ๋“œํ•˜์—ฌ ๊ธฐํ•˜ํ•™์  ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜๊ณ , ๋ถ„์„ ๋Œ€์ƒ(Transmitter, Valve, Pump ๋“ฑ)๋ณ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค. 3. **๋ณ‘๋ ฌ ํ˜ธ์ถœ (Continuous Batching):** * ๋ถ„ํ• ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์—ฌ๋Ÿฌ ๊ฐœ์˜ MCP ํˆด(๋˜๋Š” ๋™์ผ ํˆด์˜ ๋‹ค์ค‘ ์š”์ฒญ)์„ **๋™์‹œ์—(Asynchronously)** ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. * vLLM ์„œ๋ฒ„๋Š” ์ด ๋‹ค์ˆ˜์˜ ์š”์ฒญ์„ **Continuous Batching**์œผ๋กœ ๋ฌถ์–ด ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ, ๊ฐœ๋ณ„ ์š”์ฒญ ์‹œ๋ณด๋‹ค ์ „์ฒด ์ฒ˜๋ฆฌ๋Ÿ‰(Throughput)์„ ํš๊ธฐ์ ์œผ๋กœ ๋†’์ž…๋‹ˆ๋‹ค. 4. **ํ†ตํ•ฉ ๋ฐ ์ €์žฅ (Aggregator):** ๊ฐ ๋ถ„์‚ฐ ํˆด์ด ๋ฐ˜ํ™˜ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ทจํ•ฉํ•˜์—ฌ ์ตœ์ข… ์œ„์ƒ ๊ทธ๋ž˜ํ”„๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  DB์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ### 1.2 MCP ์„œ๋ฒ„ ๋‚ด ์—ญํ•  ๋ถ„๋‹ด (๋ถ„์‚ฐ ์ฒ˜๋ฆฌ ๋ชจ๋ธ) `PID_Parser_Plan_Revision.md`๋ฅผ ๋ฐ˜์˜ํ•˜์—ฌ, ๊ธฐ๋Šฅ์„ ์„ธ๋ถ„ํ™”ํ•˜๊ณ  ๋ณ‘๋ ฌ ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๊ฒŒ ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค. | ๊ตฌ๋ถ„ | MCP Tool / Module | ์—ญํ•  | ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์ „๋žต | |---|---|---|---| | **Orchestrator** | `orchestrate_pid_pipeline` | ์ „์ฒด ๊ณต์ • ์ œ์–ด, ๋ฐ์ดํ„ฐ ๋ถ„ํ•  ๋ฐ ๊ฒฐ๊ณผ ์ทจํ•ฉ | Asyncio ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ์ œ์–ด | | **Worker 1** | `extract_transmitters` | FIT, FT, LT, PT, TE ์ถ”์ถœ | vLLM Batching ์š”์ฒญ | | **Worker 2** | `extract_valves` | FCV, LCV, TCV, PCV, XV ์ถ”์ถœ | vLLM Batching ์š”์ฒญ | | **Worker 3** | `extract_gauges` | PG, TG, LG ์ถ”์ถœ | vLLM Batching ์š”์ฒญ | | **Worker 4** | `extract_equipment` | Column, Tank, Filter, Drum, Heat Exchanger ๋“ฑ ์ถ”์ถœ | vLLM Batching ์š”์ฒญ | | **Worker 5** | `extract_pumps` | P-xxxx, VP-xxxx ์ถ”์ถœ | vLLM Batching ์š”์ฒญ | | **Analyzer** | `analyze_pid_impact` | ๊ตฌ์ถ•๋œ ๊ทธ๋ž˜ํ”„ ๊ธฐ๋ฐ˜ ์˜ํ–ฅ๋„ ๋ถ„์„ | Graph Algorithm (CPU) | --- ## ๐Ÿ’ป 2. MCP ์„œ๋ฒ„ ํ†ตํ•ฉ ๊ตฌํ˜„ ๊ฐ€์ด๋“œ ### 2.1 ๋น„๋™๊ธฐ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์„ค๊ณ„ (Asyncio + vLLM Batching) `FastMCP` ํ™˜๊ฒฝ์—์„œ `asyncio.gather`๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ์ถ”์ถœ ํˆด์„ ๋™์‹œ์— ํ˜ธ์ถœํ•จ์œผ๋กœ์จ vLLM์˜ Continuous Batching ํšจ์œจ์„ ๊ทน๋Œ€ํ™”ํ•ฉ๋‹ˆ๋‹ค. ```python # mcp-server/server.py ํ†ตํ•ฉ ์„ค๊ณ„ (๊ฐœ๋… ์ฝ”๋“œ) import asyncio from typing import List async def run_parallel_extraction(geo_data): """ ๋ถ„๋ฅ˜๋ณ„ ์ถ”์ถœ ํˆด์„ ๋ณ‘๋ ฌ๋กœ ํ˜ธ์ถœํ•˜์—ฌ vLLM Batching ์œ ๋„ """ # ๊ฐ ๋ถ„๋ฅ˜๋ณ„ ํ”„๋กฌํ”„ํŠธ์™€ ๋ฐ์ดํ„ฐ ์ค€๋น„ tasks = [ extract_transmitters_async(geo_data), extract_valves_async(geo_data), extract_gauges_async(geo_data), extract_equipment_async(geo_data), extract_pumps_async(geo_data) ] # ๋™์‹œ์— ์š”์ฒญ์„ ๋˜์ ธ vLLM์ด ๋‚ด๋ถ€์ ์œผ๋กœ Batch ์ฒ˜๋ฆฌํ•˜๊ฒŒ ํ•จ results = await asyncio.gather(*tasks) return results @mcp.tool() async def build_pid_graph_parallel(filepath: str) -> str: """ ๋ถ„์‚ฐ ์ฒ˜๋ฆฌ ๊ธฐ๋ฒ•์„ ์ ์šฉํ•œ P&ID ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ ํˆด """ # 1. ์ „์ฒ˜๋ฆฌ (Phase 1) extractor = PidGeometricExtractor(filepath) geo_data = extractor.extract_all() # 2. ๋ณ‘๋ ฌ ๋ถ„์‚ฐ ์ถ”์ถœ (vLLM Batching ํ™œ์šฉ) # ๊ฐ Worker ํˆด๋“ค์ด LLM์— ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ vLLM์ด ์ด๋ฅผ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•จ extracted_parts = await run_parallel_extraction(geo_data) # 3. ๊ฒฐ๊ณผ ํ†ตํ•ฉ ๋ฐ ์œ„์ƒ ๋ชจ๋ธ๋ง (Phase 2) all_tags = flatten_results(extracted_parts) builder = PidTopologyBuilder(geo_data, all_tags) builder.build_graph() # 4. ์ €์žฅ graph_id = os.path.basename(filepath).replace(".dxf", "_graph.json") nx.write_graphml(builder.G, f"storage/{graph_id}") return json.dumps({"success": True, "graph_id": graph_id, "nodes": builder.G.number_of_nodes()}) ``` ### 2.2 C# ์„œ๋ฒ„์™€์˜ ์ธํ„ฐํŽ˜์ด์Šค (`McpClient` ํ™œ์šฉ) C# ์„œ๋ฒ„๋Š” `src/Infrastructure/Mcp/McpClient.cs`๋ฅผ ํ†ตํ•ด ์œ„ ํˆด๋“ค์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ### 2.2 C# ์„œ๋ฒ„์™€์˜ ์ธํ„ฐํŽ˜์ด์Šค (`McpClient` ํ™œ์šฉ) C# ์„œ๋ฒ„๋Š” `src/Infrastructure/Mcp/McpClient.cs`๋ฅผ ํ†ตํ•ด ์œ„ ํˆด๋“ค์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ```csharp // src/Core/Application/Services/PidGraphService.cs (์‹ ๊ทœ ์„œ๋น„์Šค) public async Task GetImpactAnalysisAsync(string graphId, string nodeId) { var request = new McpToolRequest { ToolName = "analyze_pid_impact", Arguments = new { graph_id = graphId, start_node_id = nodeId } }; var jsonResponse = await _mcpClient.CallToolAsync(request); return JsonSerializer.Deserialize(jsonResponse); } ``` --- ## ๐Ÿ› ๏ธ 3. ํ”„๋กœ๊ทธ๋žจ ๊ตฌ์„ฑ ๋ฐ ๋ฐฐํฌ ์ „๋žต ### 3.1 ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ํ™•์žฅ ```text mcp-server/ โ”œโ”€โ”€ server.py # MCP ๋ฉ”์ธ ์„œ๋ฒ„ (ํˆด ์ •์˜) โ”œโ”€โ”€ pipeline/ # Graph Pipeline ํ•ต์‹ฌ ๋กœ์ง (Phase 1~4) โ”‚ โ”œโ”€โ”€ __init__.py โ”‚ โ”œโ”€โ”€ extractor.py # Phase 1: Geometric Extraction โ”‚ โ”œโ”€โ”€ topology.py # Phase 2: Topology Modeling โ”‚ โ”œโ”€โ”€ mapper.py # Phase 3: Intelligent Mapping โ”‚ โ””โ”€โ”€ analyzer.py # Phase 4: Impact Analysis โ””โ”€โ”€ storage/ # ์ƒ์„ฑ๋œ ๊ทธ๋ž˜ํ”„ ํŒŒ์ผ (.graphml) ์ €์žฅ์†Œ ``` ### 3.2 ์‹คํ–‰ ํ”„๋กœ์„ธ์Šค 1. **MCP ์„œ๋ฒ„ ๊ธฐ๋™:** `python mcp-server/server.py --http` (ํฌํŠธ 5001) 2. **C# ์„œ๋ฒ„ ๊ธฐ๋™:** `dotnet run` (ํฌํŠธ 5000) 3. **ํ†ต์‹ :** C# ์„œ๋ฒ„ $\xrightarrow{HTTP/JSON}$ MCP ์„œ๋ฒ„ $\xrightarrow{Python\ Libs}$ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜. --- ## ๐Ÿš€ 4. ์ตœ์ข… ์™„๋ฃŒ ๊ธฐ์ค€ (Definition of Done) - [ ] `mcp-server/server.py`์— `build_pid_graph`, `analyze_pid_impact` ๋“ฑ ํ•ต์‹ฌ ํˆด์ด ์ •์˜๋˜์—ˆ๋Š”๊ฐ€? - [ ] Phase 1~4์˜ Python ๋กœ์ง์ด `mcp-server/pipeline/` ๋ชจ๋“ˆ๋กœ ๊ตฌ์กฐํ™”๋˜์–ด ํ†ตํ•ฉ๋˜์—ˆ๋Š”๊ฐ€? - [ ] C# `McpClient`๋ฅผ ํ†ตํ•ด MCP ์„œ๋ฒ„์˜ ๊ทธ๋ž˜ํ”„ ๋ถ„์„ ํˆด์„ ํ˜ธ์ถœํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€? - [ ] ๋„๋ฉด ์—…๋กœ๋“œ $\rightarrow$ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ $\rightarrow$ ํƒœ๊ทธ ๋งคํ•‘ $\rightarrow$ ์˜ํ–ฅ๋„ ๋ถ„์„์œผ๋กœ ์ด์–ด์ง€๋Š” **End-to-End ํŒŒ์ดํ”„๋ผ์ธ**์ด ์™„์„ฑ๋˜์—ˆ๋Š”๊ฐ€? - [ ] ๋ชจ๋“  ๊ณผ์ •์ด `json_response=True` ๋ฐ `stateless_http=True` ์„ค์ • ํ•˜์— ์•ˆ์ •์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”๊ฐ€?