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:
@@ -0,0 +1,15 @@
|
||||
파워포인트 파일 작성, 아래 요구사항에 따라서
|
||||
1) AX 도입의 필연성 및 효율성 (1 페이지)
|
||||
2) AX화 를 위해서 전산팀(소프트웨어 개발 부서)외의 현업 부서에서 해야할 사전 준비 항목 서술
|
||||
- 신규 프로젝트 시 ,
|
||||
- DXF, PDF GRAPH화를 위한 캐드 도면 작성 규칙
|
||||
- 문서 작성 규칙
|
||||
- 기존 프로젝트 RAG화 방안
|
||||
- 좋은 방법 제안해줘
|
||||
- 현실적인 OCR 등의 문제(기존 도면 및 문서의 표준 PDF, DXF 화 불가능시)를 감안
|
||||
|
||||
- 다른 관점에서도 제안
|
||||
3) 현재 ExperionCrawler 소개
|
||||
4) 위의 AX화를 위한 문제점을 ExperionCrawler에서 어떻게 보완하고 있는가 설명
|
||||
|
||||
5) 너의 의견 및 제안 환영함!!!
|
||||
BIN
plans/AX_도입_프레젠테이션.pptx
Normal file
BIN
plans/AX_도입_프레젠테이션.pptx
Normal file
Binary file not shown.
BIN
plans/Field Instruments Draft 20260514.xlsx
Normal file
BIN
plans/Field Instruments Draft 20260514.xlsx
Normal file
Binary file not shown.
591
plans/PID-추출이후-이용방안플랜.md
Normal file
591
plans/PID-추출이후-이용방안플랜.md
Normal file
@@ -0,0 +1,591 @@
|
||||
# P&ID 추출 데이터 이용방안 플랜
|
||||
|
||||
작성일: 2026-05-14
|
||||
배경: `pid_equipment` 테이블에 No-10_Plant_PID.dxf로부터 460건의 구조화 데이터(태그·라인번호·fluid·종류·area) 적재 완료. Experion realtime_table과의 자동 매핑(exact + prefix only, false positive 제거)도 동작 중. 다음 단계로 이 데이터를 무엇에 쓸지 결정.
|
||||
|
||||
## 현재 보유 데이터
|
||||
|
||||
| 컬럼 | 의미 | 예시 |
|
||||
|------|------|------|
|
||||
| `tag_no` | 추출된 원본 태그/라인번호 | `P-10138-600A-F2A-H100`, `PSV-10217`, `FCV-6113` |
|
||||
| `instrument_type` | prefix 분류 | `FCV`, `PSV`, `P`, `T`, `BT`, `LIA` |
|
||||
| `line_number` | LineNo 식별번호 (pipe만) | `10138`, `6203` |
|
||||
| `confidence` | 추출 신뢰도 | 0.95~0.99 |
|
||||
| `experion_tag_id` | realtime_table FK (매핑 시) | NULL or int |
|
||||
| `is_active` | 활성 여부 | true/false |
|
||||
|
||||
서버 내부 사전(코드)으로 `_PID_FLUID_DICT`(P=PROCESS, CWS=COOLING WATER SUPPLY 등 19종), `_PID_EQUIPMENT_PREFIX`(P=Pump, T=Tank, F=Filter 등 14종), `_PID_INSTRUMENT_FIRST`/`_PID_INSTRUMENT_MODIFIER`(ISA letter 코드)도 함께 보유.
|
||||
|
||||
---
|
||||
|
||||
## 갈래 1 — 채팅 컨텍스트 보강 (최우선 추천)
|
||||
|
||||
**가치**: 운전원이 즉시 체감. 작업량 최소.
|
||||
**작업량**: 0.5~1일.
|
||||
|
||||
### 구현
|
||||
MCP 도구 2개 추가 (mcp-server/server.py):
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
async def find_pid_equipment(query: str, kind: str | None = None,
|
||||
area: str | None = None, limit: int = 20) -> str:
|
||||
"""P&ID 장비/계기 검색. tag_no ILIKE 매칭 + kind/area 필터.
|
||||
|
||||
kind: 'pipe'|'equipment'|'instrument' (없으면 전체)
|
||||
area: 6-1 → tag_no가 P-61XX/T-61XX 등 6+1로 시작
|
||||
"""
|
||||
|
||||
@mcp.tool()
|
||||
async def get_pid_equipment_info(tag_no: str) -> str:
|
||||
"""단일 태그 상세. 매핑된 Experion 태그도 함께 반환.
|
||||
|
||||
응답: {tag_no, type(fluid 또는 장비종류), instrument_type, line_number,
|
||||
experion_tag, latest_value (있으면)}
|
||||
"""
|
||||
```
|
||||
|
||||
`ToolGuideKo`(OllamaController.cs)에 새 도구 설명 추가 → LLM이 자동 활용.
|
||||
|
||||
### 효과 예시
|
||||
- "PSV-10217이 뭐야?" → "Pressure Safety Valve (mechanical, OPC 신호 없음). 10차 area"
|
||||
- "10차에 펌프 몇 대?" → `find_pid_equipment(kind='equipment', area='10')` → 카운트 + 리스트
|
||||
- "P-10138 어떤 fluid?" → "PROCESS FLUID, 600A, F2A spec, H100 단열"
|
||||
- "FCV-6113 지금 값은?" → 매핑된 experion 태그로 자동 join → 실시간값
|
||||
|
||||
---
|
||||
|
||||
## 갈래 2 — 알람·이벤트 컨텍스트 자동 주입
|
||||
|
||||
**가치**: 알람 보고서가 풍부해짐. 운전원이 태그 코드만 보고 의미 파악 가능.
|
||||
**작업량**: 0.5일. 기존 `summarize_events`/`generate_status_report`에 join 추가.
|
||||
|
||||
### 구현
|
||||
- `query_events` 결과를 `pid_equipment`와 LEFT JOIN해서 각 이벤트에 PID 컨텍스트 첨부
|
||||
- 예: ALARM event `tagname=fcv-6113.pv` → 컨텍스트 "FCV-6113, Flow Control Valve, 6-1 area, P-10138 라인의 PROCESS 흐름 제어"
|
||||
- `generate_status_report` 마크다운 출력에 "## 영향 장비" 섹션 추가 (해당 area의 PID 인벤토리 요약)
|
||||
|
||||
### 효과
|
||||
- 알람 요약 LLM이 "단순 태그 나열" → "공정 맥락 포함된 운전 보고서"로 격상
|
||||
- KB 문서에서 같은 area의 절차서 자동 인용 (search_kb area 필터 + PID 데이터 연동)
|
||||
|
||||
---
|
||||
|
||||
## 갈래 3 — P&ID 다이어그램 시각화 (UI)
|
||||
|
||||
**가치**: 시각적 자산 파악. 도면 없이도 area별 장비 구조 한눈에 보기.
|
||||
**작업량**: UI 작업 중심. 그래프 보기까지 가려면 from-to 추출 추가 필요.
|
||||
|
||||
### Phase 3a (즉시 가능)
|
||||
현재 데이터만으로 — area별·종류별 트리뷰/리스트뷰:
|
||||
```
|
||||
└─ 10차 area
|
||||
├─ 펌프 (15) P-10101, P-10116, …
|
||||
├─ 탱크 (19) T-10101, T-10102, …
|
||||
├─ 필터 (4) F-10101A, F-10101B, …
|
||||
├─ 안전밸브 (18) PSV-10101, PSV-10103, …
|
||||
└─ 라인 (172) P-10138, CWS-10612, …
|
||||
```
|
||||
14번 P&ID 탭 또는 신규 탭에서 구현.
|
||||
|
||||
### Phase 3b (확장, 작업 큼)
|
||||
DXF에서 라인 끝점 좌표 + 장비 좌표 추출 후 KD-tree로 from-to 추정 → cytoscape/d3로 노드 그래프. 사용자 이미 결정한 "위치좌표 계산 없이" 방향과 상충 — **추후 필요해질 때만 착수**.
|
||||
|
||||
---
|
||||
|
||||
## 갈래 4 — 자산 마스터 + KB 통합 (장기 그림)
|
||||
|
||||
**가치**: 단순 검색을 넘어 정비·운영 데이터 통합 플랫폼.
|
||||
**작업량**: 큼. 다른 데이터 소스 통합 필요.
|
||||
|
||||
### 구성요소
|
||||
1. **equipment_registry.yaml** (이전 논의) — Experion htm에서 추출한 좌표·area + PID 추출 데이터 머지
|
||||
2. **정비/점검 일정 테이블** — 종류별 주기 정의 (PSV 1년, FCV 6개월, …) → 다음 점검일 자동 계산
|
||||
3. **누락 검출 리포트** — `pid_equipment`(P&ID 기준) vs `realtime_table`(Experion 기준) 양방향 차집합
|
||||
- P&ID에만 있는 것 → 측정 누락 (or mechanical/지표 없는 장비)
|
||||
- Experion에만 있는 것 → 도면 누락 (or 시스템 신호)
|
||||
4. **KB 문서 자동 인용** — `search_kb`에 PID 태그 정보 자동 추가해서 검색 품질 향상
|
||||
|
||||
### 의존성
|
||||
- equipment_registry yaml 구축이 선결 (이전 결정: htm 파서 추가)
|
||||
- 정비 일정은 별도 사용자 입력 또는 기존 정비 시스템 연동 필요
|
||||
|
||||
---
|
||||
|
||||
## 갈래 5 — 기존 `infer_field_instruments`와의 통합 (보완 관계, 죽이지 않음)
|
||||
|
||||
**배경**: RAG 관리 → Field Instrument 탭의 기존 기능(`infer_field_instruments` → Excel 초안 다운로드)이 `parse_pid_dxf` 등장 후 의미를 잃었는지 의문 제기됨. **결론: 의미 없어지지 않음, 오히려 통합 가치가 생김**.
|
||||
|
||||
### 두 기능의 본질적 차이
|
||||
|
||||
| 구분 | `infer_field_instruments` (기존) | `parse_pid_dxf` (신규) |
|
||||
|------|--------------------------------|-----------------------|
|
||||
| **입력 소스** | Experion DB의 OPC 태그 (`v_tag_summary`) | DXF 도면 파일 |
|
||||
| **추론 방향** | "실제 측정점이 있다 → 현장에 계기 있을 것" | "도면에 그려져 있다 → 자산으로 존재" |
|
||||
| **`ficq-6101` 처리** | base 보고 FT-6101 / FIC-6101 / FCV-6101 **3개로 분해** | 도면에 그려진 그대로 (보통 FCV-6101, FIC-6101 등) 인식 |
|
||||
| **PSV-10217** (mechanical, 신호 없음) | ❌ 못 만듦 | ✅ 도면에 있으면 추출 |
|
||||
| **추가된 임시 계기** (OPC 신호만 존재) | ✅ 추론 가능 | ❌ 도면에 없으면 못 잡음 |
|
||||
| **출력** | Excel 초안 (운전원 수동 검수 후 KB 업로드) | `pid_equipment` 테이블 |
|
||||
| **동력기기** | `power_equipment` role로 별도 분류 | Pump prefix(`P`) 등으로 분류 |
|
||||
|
||||
### 잡는 누락이 정반대 — 단독으로는 불완전
|
||||
|
||||
- **OPC 신호 있지만 도면 미반영**: `infer_field_instruments`만 잡음 (도면 업데이트 누락 케이스)
|
||||
- **도면엔 있지만 OPC 신호 없음**: `parse_pid_dxf`만 잡음 (PSV/수동밸브/게이지/파이프 등 mechanical 자산)
|
||||
- **둘 다 존재**: `match_pid_tags`가 자동 연결 (exact + prefix)
|
||||
|
||||
### 통합으로 새로 가능해지는 작업
|
||||
|
||||
1. **`infer_field_instruments` 출력 자동 검증**
|
||||
- infer가 추론한 FT-6101이 실제 도면(`pid_equipment`)에 있는지 즉시 대조
|
||||
- 추론 정확도 자동 측정 → 추론 룰 개선 피드백 루프 가능
|
||||
|
||||
2. **Excel 초안 자동 라벨링 보강**
|
||||
- 도면에는 PSV-10217이 있는데 infer는 못 만들었음 → "OPC 신호 없는 mechanical" 라벨로 Excel에 자동 행 추가
|
||||
- 도면 LineNo 정보(`P-10138-600A-F2A-H100`)로 fluid/size/material 컬럼 자동 채움
|
||||
- infer가 만든 row와 P&ID에서만 보이는 row를 한 Excel에 합쳐서 운전원이 한 번에 검수
|
||||
|
||||
3. **양방향 차집합 리포트** (갈래 4의 핵심 산출물)
|
||||
|
||||
| 차집합 | 의미 | 운영 액션 |
|
||||
|--------|------|-----------|
|
||||
| `pid_equipment` − `realtime_table` (도면 ⊖ 실제) | 도면엔 있는데 측정 없는 자산 | (a) mechanical → 정상, "OPC 신호 없음" 라벨로 마스터에 등록<br>(b) 측정 누락 → 신호 추가 검토 |
|
||||
| `realtime_table` − `pid_equipment` (실제 ⊖ 도면) | 측정은 있는데 도면 없는 신호 | 도면 업데이트 누락 → P&ID 도면 보완 요청 |
|
||||
| 양쪽 모두 | 정상 매핑 | UI에서 ✅ 표시 (현재 상태) |
|
||||
|
||||
### 구현 (작업량 적음)
|
||||
|
||||
- `infer_field_instruments`의 dedup 단계 뒤에 `pid_equipment` LEFT JOIN 한 번
|
||||
- infer 결과 row마다 `in_pid_drawing: bool`, `pid_line_number`, `pid_drawing_no` 컬럼 추가
|
||||
- infer가 만들지 못한 `pid_equipment` row를 별도 시트(`Pid_Only_Mechanical`)로 Excel에 추가
|
||||
- `parse_pid_dxf`는 손대지 않음. infer 쪽에만 join 추가
|
||||
- 운전원이 다운받는 Excel이 그대로 더 풍부해짐 — 추가 UI 변경 불필요
|
||||
|
||||
### 의존성
|
||||
|
||||
- 갈래 1·2 없어도 가능. 단독 진행 가능
|
||||
- `infer_field_instruments`의 출력 Excel 포맷 일부 변경 (시트 추가) — 기존 운영 흐름에 영향 적음
|
||||
|
||||
---
|
||||
|
||||
## 갈래 6 — PID 추출 → 옵시디언 노트 부트스트랩 → 하이브리드 RAG
|
||||
|
||||
**배경**: `plans/옵시디언-구조적용-플랜.md`는 마크다운 노트 + `[[wikilinks]]` + frontmatter 기반 그래프 RAG를 제안하지만 **아직 미구현** (`notes/` 디렉토리·DB 테이블 없음, 위키링크가 들어있는 .md는 계획 문서 자체뿐). 옵시디언 노트 시스템을 0에서 시작하면 "초기 비어있음" 문제가 큰데, PID 추출 결과를 활용하면 **첫날부터 460개 anchor 노트**로 부트스트랩 가능.
|
||||
|
||||
### 역할 분담
|
||||
|
||||
| 영역 | PID 추출 (`pid_equipment`) | 옵시디언 노트 (planned) |
|
||||
|------|---------------------------|------------------------|
|
||||
| 채우는 것 | **뼈대** — 모든 태그/라인/장비 자동 인벤토리 | **살** — 절차, 루프 의미, 트러블슈팅, 벤더 정보, 운전 노하우 |
|
||||
| 입력 | 도면 1번 + 자동 | 운전원/엔지니어 수기 + 채팅 저장 |
|
||||
| 갱신 | 도면 바뀌면 재추출 | 사람이 알게 될 때마다 |
|
||||
| 핵심 식별자 | `tag_no` (FCV-6113, P-10101) | 노트 ID `tag/fcv-6113`, `loop/compression-a` |
|
||||
|
||||
**공통 anchor가 tag ID** — PID가 만든 모든 태그는 곧 노트 ID가 되고, 사람이 쓴 노트는 `[[tag/fcv-6113]]` 위키링크로 PID 인벤토리에 자동 연결.
|
||||
|
||||
### 구현 단계
|
||||
|
||||
#### Step 1 — PID stub 노트 자동 생성
|
||||
|
||||
`pid_equipment` 460건을 각각 frontmatter-only `.md` 파일로 발행:
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: tag/fcv-6113
|
||||
kind: tag
|
||||
prefix: FCV
|
||||
type: Flow Control Valve
|
||||
area: 6-1
|
||||
line_number: 6113
|
||||
experion_tag: fcv-6113.pv
|
||||
source_drawing: No-10_Plant_PID.dxf
|
||||
source: pid_equipment.id=42
|
||||
generated_at: 2026-05-14
|
||||
---
|
||||
<!-- 본문은 비어둠 — 운전원이 채움 -->
|
||||
```
|
||||
|
||||
- 디렉토리: `notes/tag/`, `notes/area/`, `notes/drawing/` 등
|
||||
- 한 파일 = 한 노트 = `pid_equipment` 1행
|
||||
- 본문 비어있어도 anchor 역할 → 위키링크 타깃으로 즉시 사용 가능
|
||||
|
||||
#### Step 2 — 사람 노트는 위에 자라남
|
||||
|
||||
운전원/엔지니어가 만드는 손 노트는 별도 kind:
|
||||
- `notes/loop/compression-a.md` — "이 루프는 [[tag/fcv-6113]] [[tag/pt-6111]] [[tag/ft-6113]]로 구성됨"
|
||||
- `notes/procedure/start-up-a.md` — "기동 시 [[loop/compression-a]] 먼저 안정화"
|
||||
- `notes/event-pattern/surge-recovery.md` — "[[tag/fcv-6113]] surge 발생 시 [[procedure/anti-surge]] 적용"
|
||||
|
||||
**원칙**: 사람은 신규 마크다운만 쓰고 PID stub은 안 건드림. 재추출 시 stub 안전하게 덮어쓰기 가능.
|
||||
|
||||
#### Step 3 — Stub 갱신 정책
|
||||
|
||||
PID 재추출 시:
|
||||
- frontmatter는 항상 최신 `pid_equipment` 값으로 덮어씀
|
||||
- 본문(`<!-- comment -->` 아래)에 운전원이 추가한 텍스트가 있으면 보존 (merge 로직)
|
||||
- 새로 등장한 tag → 신규 stub 생성
|
||||
- 사라진 tag → `archived: true` frontmatter 추가 (파일 삭제 안 함, 위키링크 깨짐 방지)
|
||||
|
||||
#### Step 4 — KB 인덱싱 + 그래프 엣지 추출
|
||||
|
||||
`notes/` 디렉토리를 KbIngestWorker가 별도 컬렉션(예: `kb_notes`)으로 처리:
|
||||
- 노트 1개 = 1 청크 (헤딩으로 자르지 않음 — 작은 단위 유지)
|
||||
- frontmatter → Qdrant payload (area, kind, prefix 필터링 가능)
|
||||
- 본문에서 `[[note-id]]` 정규식 추출 → 신규 테이블 `kb_note_edges (source_id, target_id, link_type)`에 적재
|
||||
|
||||
#### Step 5 — 채팅 RAG에 그래프 1-hop 결합
|
||||
|
||||
`search_kb`가 top-k 청크 검색 → 각 청크가 노트면 `kb_note_edges`에서 1-hop 위키링크 타깃 조회 → 그 타깃 노트들의 frontmatter+첫 200자를 컨텍스트에 함께 주입.
|
||||
|
||||
```python
|
||||
# 의사 코드
|
||||
chunks = search_kb(query, top_k=5)
|
||||
extra_context = []
|
||||
for chunk in chunks:
|
||||
if chunk.note_id:
|
||||
targets = db.fetch_edges(chunk.note_id, depth=1)
|
||||
extra_context.extend(targets) # 5개 청크 + N개 1-hop 노트
|
||||
return chunks + extra_context # LLM에 함께 전달
|
||||
```
|
||||
|
||||
LLM은 청크 5개만이 아니라 "이 청크와 명시적으로 연결된 절차/루프/이벤트 패턴"까지 함께 보고 답변.
|
||||
|
||||
### 트레이드오프
|
||||
|
||||
- (+) 옵시디언 시스템의 "초기 비어있음" 문제 해결 — 첫날부터 460개 anchor 보유
|
||||
- (+) 운전원이 채팅에서 받은 답변을 `loop/surge-recovery.md` 한 줄로 영구화 가능 → 지식 자산이 자라남
|
||||
- (+) 마크다운이라 외부 옵시디언 앱으로 열어 편집해도 호환 (부수효과)
|
||||
- (−) stub 갱신 시 본문 보존 머지 로직 필요 (text diff 충돌 검출)
|
||||
- (−) 운전원이 위키링크 작성 습관 안 들이면 anchor만 잔뜩에 살이 없는 상태 → **저진입 UX 필수**:
|
||||
- 채팅에서 "이 답변을 [[loop/compression-a]]에 저장" 같은 한 클릭 액션
|
||||
- 노트 작성 시 `[[` 입력하면 자동완성 드롭다운으로 PID stub 추천
|
||||
|
||||
### 의존성
|
||||
|
||||
- 갈래 1 권장 선결 — PID 데이터 조회 인프라가 있어야 stub 생성 스크립트 작성이 쉬움
|
||||
- `옵시디언-구조적용-플랜.md`의 데이터 모델 결정사항 따름 (note ID 규칙, kind 9종, frontmatter 스키마)
|
||||
- KbIngestWorker에 notes 디렉토리 전용 처리 분기 추가
|
||||
|
||||
### 작업 분할
|
||||
|
||||
| 단계 | 산출물 | 작업량 |
|
||||
|------|--------|--------|
|
||||
| Step 1 | `mcp-server/notes_sync.py` — pid_equipment → notes/tag/*.md stub 생성기 | 0.5일 |
|
||||
| Step 3 | 머지 보존 로직 + archived 처리 | 0.5일 |
|
||||
| Step 4 | KbIngestWorker notes 분기 + `kb_note_edges` 테이블 + 위키링크 파서 | 1일 |
|
||||
| Step 5 | `search_kb` 그래프 1-hop 보강 + 채팅 통합 | 0.5일 |
|
||||
| 사람 노트 저진입 UX | "답변 → 노트 저장" 버튼, `[[` 자동완성 | 1일 (UI) |
|
||||
| **합계** | | **3.5일** |
|
||||
|
||||
---
|
||||
|
||||
## 갈래 7 — Excel 라운드트립으로 service/role 메타데이터 보강 (마지막 연결고리)
|
||||
|
||||
**배경**: DXF 파서는 tag·LineNo·fluid(파이프 한정)·prefix 분류는 자동 추출하지만, **장비의 실질 정보(service, role, contents, from-to)는 도면에 텍스트로 없음**. 예: T-201이 원료탱크인지 폐액탱크인지, 무슨 fluid를 담는지, 어디로 보내는지 — LLM이 답할 컨텍스트가 없음. 운전원이 가장 익숙한 도구(Excel)로 한 번에 채우는 round-trip이 가장 실용적인 해결책.
|
||||
|
||||
**갈래 5와의 차이**: 갈래 5는 `infer_field_instruments` Excel(현장 계기 초안, **다운로드 전용**)에 PID 정보를 합치는 것. **갈래 7은 PID 메타데이터를 사람이 채워 다시 시스템에 반영하는 round-trip — 편집 가능한 마스터 시트**.
|
||||
|
||||
### 시트 구성 (장비 종류별 분리)
|
||||
|
||||
| 시트 | 자동 채워진 컬럼 | 사용자가 채울 컬럼 |
|
||||
|------|----------------|------------------|
|
||||
| `Tanks` (T-XXX) | tag_no, area, source_drawing | service, role(feed/buffer/product/waste), contents(PGMEA/HBM/...), capacity, notes |
|
||||
| `Drums` (D-XXX) | 위와 동일 | service, role, contents |
|
||||
| `Filters` (F-XXX) | tag_no, area | service, medium, from_tag, to_tag |
|
||||
| `Heat Exchangers` (E-XXX) | tag_no, area | service, hot_side, cold_side, duty |
|
||||
| `Columns` (C-XXX) | tag_no, area | service, product, overhead, bottom |
|
||||
| `Pumps` (P-XXX) | tag_no, area, mapped_experion_tag | service, driver(motor/turbine), from_tag, to_tag, power_kw |
|
||||
| `Compressors / Fans` | tag_no, area | service, driver, power_kw |
|
||||
| `Cooling Towers` | tag_no, area | service, capacity |
|
||||
| `Manual Valves` (BV/GV/XV) | tag_no, area | service, normal_position(open/closed), purpose |
|
||||
| `Pipes` (LineNo) | service, fluid, size, spec, insul (이미 파싱됨) | from_tag, to_tag (오직 이거만) |
|
||||
| `Legend` | dropdown 후보 (role 종류, fluid 코드, position 등) | — |
|
||||
| `Index` | 시트별 완성도 (채워진 비율) | — |
|
||||
|
||||
### Round-trip 흐름
|
||||
|
||||
```
|
||||
[다운로드]
|
||||
pid_equipment → 장비 종류별 그룹핑 → 시트별 분리 → Excel 생성
|
||||
(자동 채운 컬럼 + 빈 컬럼 + dropdown legend)
|
||||
↓
|
||||
[운전원이 채움 (며칠~몇 주, 영역별 분할 가능)]
|
||||
↓
|
||||
[업로드]
|
||||
Excel 파싱 → 검증 (dropdown 값, 필수 필드, 참조 tag_no 존재 여부)
|
||||
→ pid_equipment에 신규 컬럼들(service, role, contents, from_tag, to_tag, …) UPDATE
|
||||
→ 변경 diff 리포트 (몇 건 채워졌는지, 무효 값 몇 건)
|
||||
→ (갈래 6 도입 후) tag stub 노트 frontmatter 자동 갱신
|
||||
```
|
||||
|
||||
### 데이터 모델 확장
|
||||
|
||||
`pid_equipment` 테이블에 컬럼 추가:
|
||||
```sql
|
||||
ALTER TABLE pid_equipment
|
||||
ADD COLUMN service TEXT, -- 운전원 입력 fluid/용도 (P=PROCESS와 별개로 자유 텍스트)
|
||||
ADD COLUMN role TEXT, -- feed_tank, buffer_tank, waste_tank, transfer_pump, ...
|
||||
ADD COLUMN contents TEXT, -- PGMEA, HBM, CITY_WATER, ...
|
||||
ADD COLUMN from_tag TEXT, -- 흐름 상류 tag (수동 입력)
|
||||
ADD COLUMN to_tag TEXT, -- 흐름 하류 tag
|
||||
ADD COLUMN driver TEXT, -- motor / turbine / electric / steam
|
||||
ADD COLUMN capacity TEXT, -- 자유 텍스트 (단위 포함)
|
||||
ADD COLUMN power_kw NUMERIC,
|
||||
ADD COLUMN user_notes TEXT, -- 운전원 자유 메모
|
||||
ADD COLUMN metadata_filled_at TIMESTAMPTZ; -- 마지막 사용자 갱신 시각
|
||||
```
|
||||
|
||||
### 갈래 5/6과의 관계 — 같은 그림의 다른 단계
|
||||
|
||||
| 단계 | 역할 | 갈래 |
|
||||
|------|------|------|
|
||||
| 자동 추출 | 뼈대 (모든 태그 자동 인벤토리) | PID 파서 (완료) |
|
||||
| 자동 검증 Excel | mechanical/누락 자산 검출 (read-only) | 갈래 5 |
|
||||
| **수기 보강 round-trip** | **구조화 슬롯 일괄 채우기 (service/role/from-to)** | **갈래 7 (이 항목)** |
|
||||
| 노트 자동 생성 | tag stub frontmatter에 갈래 7 데이터 자동 반영 | 갈래 6 Step 1 |
|
||||
| 채팅 RAG 강화 | 위 모두 통합 검색 | 갈래 1+2+6 |
|
||||
|
||||
→ **갈래 7이 진짜 마지막 연결고리**. 이게 없으면 갈래 6 stub의 frontmatter가 빈약하고, 채팅도 "T-201이 뭐 담는지" 답을 못 함.
|
||||
|
||||
### 트레이드오프
|
||||
|
||||
- (+) Excel은 운전원이 가장 자연스럽게 다루는 도구 — 마크다운 학습 비용 0
|
||||
- (+) 한 번 다운로드받아 오프라인에서도 채울 수 있음
|
||||
- (+) 시트별 분리로 "오늘은 펌프만, 내일은 탱크만" 같은 분할 작업 가능
|
||||
- (+) dropdown legend로 데이터 일관성 자동 확보
|
||||
- (−) Excel은 discrete batch — 동시에 여러 사람이 채우면 머지 충돌 위험. 한 번에 한 사람 권장
|
||||
- (−) 다운로드 받은 파일이 오래되면 사이에 PID 재추출로 신규 tag 생긴 경우 머지 필요 — 업로드 시 "신규 tag는 이번 Excel에 없음" 경고
|
||||
- (−) 한 번에 460건 다 채우라 하면 부담 → 영역별/종류별 분할 가이드 필요 ("이번 주는 6-1 area Pump만")
|
||||
|
||||
### 구현 (작업량)
|
||||
|
||||
| 단계 | 산출물 | 작업량 |
|
||||
|------|--------|--------|
|
||||
| DB 마이그레이션 | `pid_equipment` 신규 컬럼 ALTER + InitializeAsync DDL 갱신 | 0.3일 |
|
||||
| 다운로드 엔드포인트 | `GET /api/pid/metadata/excel` — 시트별 분리 Excel 생성 (openpyxl) | 0.5일 |
|
||||
| 업로드 엔드포인트 | `POST /api/pid/metadata/excel` — 파싱+검증+UPDATE+diff 리포트 | 1일 |
|
||||
| UI (RAG 관리 또는 P&ID 탭) | 다운로드/업로드 버튼 + 진행률 + diff 표시 | 0.5일 |
|
||||
| Legend dropdown 사전 | role/contents/driver 등 유효값 정의 + 운영 후 확장 가능하게 | 0.2일 |
|
||||
| **합계** | | **2.5일** |
|
||||
|
||||
### 의존성
|
||||
|
||||
- 갈래 1·5와 독립적으로 진행 가능
|
||||
- 갈래 6보다 먼저 끝나야 stub frontmatter가 풍부해짐 → **Phase B 또는 Phase C 초반에 진행 권장**
|
||||
|
||||
---
|
||||
|
||||
## 갈래 8 — LineNo 파생 정보 컬럼 추가 (작지만 모든 갈래에 가치 전파)
|
||||
|
||||
**배경**: 현업 통찰 — 운영 부서는 LineNo(`P-10138`, `16456` 등 시공·단관 제작용 번호)로 파이프를 부르지 않음. 가치 있는 건 거기서 파생되는 **{service, fluid, size, material, flange_rating, insulation}**. LineNo 자체는 시공·정비 참조용으로 보존하되, 파생 정보를 별도 컬럼으로 저장해야 운영 검색·통계·일관성 검증이 가능해짐.
|
||||
|
||||
**현재 상태**: `_parse_pid_lineno`가 MCP 응답에 7개 필드를 다 담고 있지만, C# 측이 `line_number` 하나만 DB에 저장하고 나머지는 버리고 있음. 추출은 되는데 활용이 안 됨.
|
||||
|
||||
### 구현
|
||||
|
||||
```sql
|
||||
-- pid_equipment 컬럼 추가 (line_number는 그대로 유지)
|
||||
ALTER TABLE pid_equipment
|
||||
ADD COLUMN service_code TEXT, -- 'P', 'CWS', 'ST', 'VG', ...
|
||||
ADD COLUMN fluid_name TEXT, -- 'PROCESS FLUID', 'STEAM' (legend 변환)
|
||||
ADD COLUMN pipe_size TEXT, -- '25A', '600A'
|
||||
ADD COLUMN material_spec TEXT, -- 'F', 'S'
|
||||
ADD COLUMN flange_rating INT, -- 1=150#, 2=300#
|
||||
ADD COLUMN insul_code TEXT, -- 'A', 'B'
|
||||
ADD COLUMN insul_thickness TEXT; -- 'H50', 'H100', 'E50', 'n'
|
||||
```
|
||||
|
||||
장비/계기 행은 이 컬럼들이 NULL — 정상. 한 테이블 유지, 정규화 분리 안 함.
|
||||
|
||||
### 변경 파일
|
||||
|
||||
| 파일 | 변경 |
|
||||
|------|------|
|
||||
| `ExperionDbContext.InitializeAsync` | ALTER TABLE 7개 컬럼 추가 (또는 새 설치용 CREATE TABLE 갱신) |
|
||||
| `PidEquipment` 엔티티 | 7개 속성 + `[Column("snake_case")]` 매핑 |
|
||||
| `ExtractedItem` DTO | 7개 필드 추가 (PipeService/FluidName/PipeSize/...) |
|
||||
| `PidExtractorService.ParseJson` | MCP 응답의 pipe 필드를 ExtractedItem에 매핑 |
|
||||
| `PidExtractorService.ExtractFromStreamAsync` | save 시 7개 컬럼 채움 (pipe 행만, instrument/equipment는 null) |
|
||||
| MCP `extract_pid_tags` / `parse_pid_dxf` | 이미 7개 필드 응답 중 — 변경 불필요 |
|
||||
|
||||
### 활용은 자동으로 따라옴
|
||||
|
||||
컬럼만 채워지면 기존 갈래들이 즉시 더 풍부해짐:
|
||||
|
||||
- **갈래 1 채팅 검색**: "CWS 25A 이상 라인", "스팀 H100 단열 라인" 같은 속성 조합 검색 즉시 가능
|
||||
- **갈래 6 stub frontmatter**: pipe 노트에 `service: P`, `fluid: PROCESS FLUID`, `pipe_size: 25A`, `insul_thickness: H50` 자동 반영 → 노트가 빈약하지 않음
|
||||
- **갈래 7 Pipes 시트**: 자동 채워진 컬럼이 7개로 늘어남, 사용자는 from_tag/to_tag/서비스별칭만 채우면 됨
|
||||
- **(부수효과)** 사양 분포 리포트, 일관성 검증 같은 분석이 가능해짐 — 필요할 때 별도 도구로 노출하면 됨 (지금은 컬럼만 추가)
|
||||
|
||||
### 작업량
|
||||
|
||||
| 단계 | 산출물 | 작업량 |
|
||||
|------|--------|--------|
|
||||
| DB 컬럼 추가 | ALTER TABLE + InitializeAsync DDL 갱신 | 0.2일 |
|
||||
| `PidEquipment` 엔티티 + `[Column]` 매핑 | 7개 속성 추가 | 0.1일 |
|
||||
| `ExtractedItem` 7개 필드 + ParseJson 매핑 | DTO 확장 | 0.2일 |
|
||||
| `PidExtractorService` save 경로 | 7개 컬럼 INSERT | 0.2일 |
|
||||
| 기존 460건 재추출 (또는 in-place UPDATE 스크립트) | truncate 후 재추출이 간단 | 0.1일 |
|
||||
| **합계** | | **0.8일** |
|
||||
|
||||
### 의존성
|
||||
|
||||
- 없음. 단독 진행 가능
|
||||
- **갈래 1·6·7의 가치를 증폭시키므로 가장 먼저 배치 권장**
|
||||
|
||||
---
|
||||
|
||||
## 갈래 9 — 추출 결과 저장·폐기 선택 UX (실수 방지)
|
||||
|
||||
**배경**: 현재 P&ID 추출 UI는 "추출 시작" 클릭 시 MCP 추출 → `pid_equipment` 즉시 INSERT가 자동으로 이뤄짐. 운전원이 실수로 다른 파일(다른 플랜트, 구버전 도면 등)을 추출해도 되돌릴 방법이 없음. TRUNCATE는 정상 데이터까지 날아가서 사용 불가. 추출 전/후에 사용자가 검토·취사선택할 수 있어야 함.
|
||||
|
||||
**결정 보류**: 아래 3개 옵션 중 어느 방향으로 갈지 사용자 결정 필요.
|
||||
|
||||
### 옵션 비교
|
||||
|
||||
#### 옵션 A — Dry-run + 저장 버튼 (사전 검토)
|
||||
|
||||
```
|
||||
[추출 시작] → MCP 추출만 (DB 저장 X) → "미리보기 460건" 표시
|
||||
├── [✅ DB에 저장] → INSERT 수행
|
||||
└── [🗑️ 폐기] → 결과 버림
|
||||
```
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| 장점 | 가장 안전 — DB 들어가기 전 검토 가능. 운전원 의도 명확 ("이걸 저장할까?") |
|
||||
| 단점 | UI 흐름 2단계로 늘어남. 결과를 서버 임시 캐시 또는 클라이언트 메모리에 보관 필요. 사용자가 검토 중 페이지 떠나면 데이터 잃음 |
|
||||
| 작업량 | 약 1일 |
|
||||
| 구현 | `PidExtractorService.ExtractWithoutSaveAsync` 추가, `IMemoryCache` 결과 보관, UI에 미리보기 + 저장/폐기 버튼 |
|
||||
|
||||
#### 옵션 B — 자동 저장 + 즉시 취소 버튼 (사후 롤백)
|
||||
|
||||
```
|
||||
[추출 시작] → MCP 추출 → DB 저장 (extraction_batch_id 부여)
|
||||
→ "✅ 추출 완료: 460건" + [🗑️ 이번 추출 취소] 버튼
|
||||
↓
|
||||
batch_id WHERE 절로 일괄 삭제
|
||||
```
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| 장점 | 기존 UX 거의 그대로. 추출 이력 자체가 자동으로 남아 추후 분석 가능 |
|
||||
| 단점 | 저장 후 후속 작업(confidence 수정 등)이 일어났으면 일괄 삭제가 그 작업까지 날림 → "최근 N분만 취소 가능" 같은 가드 필요. 다른 사용자가 잠깐 잘못된 데이터 볼 수 있음 |
|
||||
| 작업량 | 약 0.5일 |
|
||||
| 구현 | `pid_equipment.extraction_batch_id` (UUID) 컬럼 추가, 추출 시 부여, UI에 "이번 추출 취소" 버튼 + 시간 가드 |
|
||||
|
||||
#### 옵션 C — A + 영구 batch_id 보존 (절충)
|
||||
|
||||
```
|
||||
[추출 시작] → MCP 추출 (DB 저장 X, staging 보관)
|
||||
├── [저장] → batch_id와 함께 INSERT
|
||||
│ → 이후에도 "추출 이력" 페이지에서 batch_id로 일괄 삭제 가능
|
||||
└── [폐기] → staging만 버림
|
||||
```
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| 장점 | A의 안전성 + B의 사후 롤백 모두 확보. "추출 이력" 페이지에서 과거 batch 단위 관리 가능 |
|
||||
| 단점 | 작업량 가장 큼. UI도 가장 복잡 (미리보기 + 저장 + 이력 관리) |
|
||||
| 작업량 | 약 1.5~2일 |
|
||||
| 구현 | 옵션 A + 옵션 B 합친 형태. staging 캐시 + batch_id 컬럼 + 이력 페이지 |
|
||||
|
||||
### 추천 (결정 보류 상태)
|
||||
|
||||
| 우선 고려 | 추천 옵션 |
|
||||
|----------|----------|
|
||||
| 안전성 우선 | **옵션 A** |
|
||||
| 작업량 최소 | 옵션 B |
|
||||
| 장기 운영 (감사·이력) | 옵션 C |
|
||||
|
||||
운전원이 자주 추출하는 운영 환경이면 옵션 A의 사전 검토가 정신적 부담 적고 안전 — **기본 추천은 옵션 A**. 단, "추출 이력 관리" 같은 요구가 미래에 나올 가능성이 크면 옵션 C가 미리 대응됨.
|
||||
|
||||
### 의존성
|
||||
|
||||
- 옵션 A: `IMemoryCache` 또는 클라이언트 세션 — 표준 ASP.NET Core 기능
|
||||
- 옵션 B/C: `pid_equipment.extraction_batch_id` 컬럼 (UUID) — 갈래 8 컬럼 추가와 함께 ALTER 묶어 처리하면 효율적
|
||||
- 어느 옵션이든 갈래 8과 같은 시점에 진행하면 마이그레이션 1번으로 끝낼 수 있음
|
||||
|
||||
### 결정 시점
|
||||
|
||||
- 갈래 1~5 진행 중 사용 패턴 보면서 결정 가능 (당장 결정 안 해도 됨)
|
||||
- 단, 옵션 B/C를 선택한다면 갈래 8 마이그레이션 시 `extraction_batch_id` 컬럼을 함께 추가하는 게 효율적 → **갈래 8 착수 전에 한 번 가볍게 결정 권장**
|
||||
|
||||
---
|
||||
|
||||
## 추천 진행 순서
|
||||
|
||||
| 우선순위 | 항목 | 작업량 | 가치 | 의존성 |
|
||||
|----------|------|--------|------|--------|
|
||||
| **0** | **갈래 8 (LineNo 파생 정보 컬럼 추가)** | 0.8일 | **모든 갈래에 가치 전파** | 없음 |
|
||||
| **0+** | **갈래 9 (추출 결과 저장/폐기 UX)** | 0.5~2일 (옵션별) | 높음 (실수 방지) | 갈래 8과 함께 마이그레이션 묶기 권장 |
|
||||
| **1** | 갈래 1 (MCP 도구 2개) | 0.5~1일 | 높음 | 갈래 8 권장 |
|
||||
| **2** | 갈래 5 (infer ↔ pid_equipment 통합 Excel) | 0.5일 | 높음 | 없음 |
|
||||
| **3** | 갈래 2 (이벤트 컨텍스트) | 0.5일 | 높음 | 갈래 1 |
|
||||
| **4** | 갈래 7 (Excel round-trip으로 service/role 보강) | 2.5일 | 매우 높음 | 갈래 8 권장 |
|
||||
| **5** | 갈래 6 (옵시디언 노트 부트스트랩 + 하이브리드 RAG) | 3.5일 | 매우 높음 (장기) | 갈래 1·7·8 권장 |
|
||||
| 6 | 갈래 3a (트리뷰 UI) | 1~2일 | 중 | 없음 |
|
||||
| 7 | 갈래 4 일부 (누락 검출 리포트 단독) | 0.5일 | 중 | 갈래 5와 중복 — 갈래 5에 통합 권장 |
|
||||
| 보류 | 갈래 3b (그래프 시각화) | 큼 | 사용 패턴 보고 결정 | from-to 추출 |
|
||||
| 보류 | 갈래 4 전체 (정비 일정·KB 통합) | 큼 | 큼 | equipment_registry, 정비 시스템 |
|
||||
|
||||
### 추천 흐름
|
||||
|
||||
**Phase 0 (반나절, 0.8일)** — 모든 갈래의 가치 증폭
|
||||
0. **갈래 8**: `pid_equipment`에 service/fluid/size/material/flange/insul 7개 컬럼 추가 + 460건 재추출
|
||||
- 이후 모든 갈래가 더 풍부한 데이터로 동작
|
||||
|
||||
**Phase A (이번 주, 1~2일)** — 즉시 효과
|
||||
1. **갈래 1**: `find_pid_equipment` / `get_pid_equipment_info` 추가 → 갈래 8로 채워진 7개 필드까지 검색·표시
|
||||
2. **갈래 5** 병행: `infer_field_instruments`에 `pid_equipment` join 추가 → Excel 초안에 mechanical/누락 자산 자동 포함
|
||||
|
||||
**Phase B (Phase A 사용 패턴 본 뒤, 0.5일)** — 정착 단계
|
||||
3. **갈래 2**: 알람·이벤트 컨텍스트에 PID 정보 자동 첨부 → 보고서 품질 향상
|
||||
|
||||
**Phase C (2.5일 + 운전원 채움 기간)** — 마지막 연결고리
|
||||
4. **갈래 7**: Excel round-trip 인프라 구축 → 운전원이 service/role/contents/from-to 채움
|
||||
- 자동 컬럼이 갈래 8 덕분에 7개 더 풍부 → 사용자 부담 줄어듦
|
||||
- 구축 자체는 2.5일이지만 **운전원이 채우는 데 며칠~몇 주** 걸림 (영역별 분할)
|
||||
- 일부라도 채워지면 즉시 갈래 1·2의 답변 품질이 올라감 (T-201 → "PGMEA 원료 탱크" 답변 가능)
|
||||
|
||||
**Phase D (Phase C 진행 중 병행, 3.5일)** — 지식 자산화로 진화
|
||||
5. **갈래 6**: PID stub 자동 생성 → 옵시디언 노트 시스템 부트스트랩 → 하이브리드 RAG
|
||||
- 갈래 8·7의 결과가 stub frontmatter로 자동 반영되어 빈약하지 않음
|
||||
- 운전원이 채팅에서 노트 작성 습관 들이면 시간이 갈수록 그래프가 자라남
|
||||
- **장기적으로는 이 갈래가 시스템의 핵심 자산** — PID/Experion은 자동 입력 소스, 노트는 사람의 운영 지식 저장소
|
||||
|
||||
**Phase E (필요해질 때)** — 확장
|
||||
6. **갈래 3a**: 시각적 인벤토리 필요해지면 트리뷰 UI 작업
|
||||
7. **갈래 4 전체** 또는 **갈래 3b**: 정비 시스템 연동/그래프 시각화 등은 실제 운영 요구가 명확해진 시점에 착수
|
||||
|
||||
### 우선순위 배치 근거
|
||||
|
||||
- **갈래 8 최우선**: 0.8일 작업으로 갈래 1·6·7 모두의 데이터 가치를 증폭. ROI 극단적으로 높음. 다른 모든 작업 전에 끝내는 게 합리적
|
||||
- **갈래 5 > 갈래 4**: 갈래 4의 핵심 산출물(양방향 차집합 리포트)이 갈래 5의 Excel 시트로 자연스럽게 포함됨. 운전원이 이미 익숙한 UX 위에 얹는 거라 학습 비용 0
|
||||
- **갈래 7 > 갈래 6**: 갈래 6 stub의 frontmatter는 갈래 7의 결과(service/role/contents)로 채워질 때 비로소 가치가 있음. 갈래 7 없이 갈래 6만 가면 빈 anchor만 잔뜩
|
||||
- **갈래 7 > 갈래 3a**: service/role이 채워지지 않은 상태에서 시각화는 "이름만 있는 그림" — 우선 의미를 채워야 함
|
||||
- **갈래 6은 갈래 1·2·7·8 이후가 적기**: 채팅에서 PID 데이터 활용 패턴이 나오고 메타데이터가 채워진 뒤라야 노트 시스템이 의미 있음
|
||||
|
||||
---
|
||||
|
||||
## 결정 필요 항목
|
||||
|
||||
- [ ] **갈래 8** 즉시 착수 여부 — `pid_equipment` 7개 컬럼 추가 + 460건 재추출 (0.8일, ROI 최고)
|
||||
- [ ] **갈래 9** 옵션 선택 — A(dry-run, 1일) / B(자동저장+취소, 0.5일) / C(절충, 1.5~2일) 중 하나 선택. **갈래 8 착수 전 결정 권장** (B/C면 `extraction_batch_id` 컬럼을 갈래 8 마이그레이션에 함께 묶음)
|
||||
- [ ] **갈래 1** 즉시 착수 여부 — `find_pid_equipment` / `get_pid_equipment_info` MCP 도구 추가
|
||||
- [ ] **갈래 5** 즉시 착수 여부 — `infer_field_instruments`에 `pid_equipment` LEFT JOIN + 별도 시트 추가
|
||||
- [ ] 갈래 2 진행 여부 — Phase A 정착 후
|
||||
- [ ] **갈래 7** 데이터 모델 확정 — `pid_equipment` 컬럼 추가 항목(service/role/contents/from_tag/to_tag/driver/capacity/power_kw/user_notes) 확인
|
||||
- [ ] **갈래 7** legend dropdown 사전 시드값 — role/contents/driver 유효값 누가 정의
|
||||
- [ ] **갈래 7** 운전원 채우기 가이드 — 영역별 분할 방식(어떤 area부터?)
|
||||
- [ ] **갈래 6 Step 1·2** 일정 — PID stub 생성기 + 머지 보존 로직 (갈래 8·7 컬럼이 stub frontmatter로 흐르도록 같이 설계)
|
||||
- [ ] 갈래 6 Step 5 (그래프 1-hop RAG) — `search_kb` 수정 시점 확정 필요
|
||||
- [ ] 갈래 6 저진입 UX (채팅 답변 → 노트 저장 버튼) — UI 작업 일정
|
||||
- [ ] 갈래 3a 트리뷰 UI 작업 일정 — 갈래 7 보강 데이터가 채워진 뒤가 효과적
|
||||
- [ ] 갈래 4 단독 누락 검출 리포트 — 갈래 5에 흡수할지, 별도 페이지로 만들지
|
||||
|
||||
## 잔여 데이터 정합성 이슈
|
||||
|
||||
- **중복 추출**: 현재 460건 중 동일 tag_no가 2번씩 들어간 행 다수 발견 (예: PSV-10101이 2회). DXF에서 같은 텍스트가 여러 위치에 그려져서 발생. unique index 또는 (tag_no, pid_drawing_no) 복합키로 해결 필요.
|
||||
- **pid_drawing_no NULL**: 현재 모든 행 NULL. 도면 파일명을 자동 채우거나 사용자 입력 받도록 보강 필요 — 갈래 4 누락 검출에 도면 단위 추적 필요할 때.
|
||||
- **prefix 오분류**: PFD, SP, SC, TR 등 도면 라벨/약어가 instrument로 잘못 분류되는 케이스 — `_PID_TAG_RE` 또는 `_classify_pid_tag`에 예외 사전 추가 필요.
|
||||
1651
plans/RAG-지식증강-보완플랜-실행코딩.md
Normal file
1651
plans/RAG-지식증강-보완플랜-실행코딩.md
Normal file
File diff suppressed because it is too large
Load Diff
407
plans/RAG-지식증강-보완플랜.md
Normal file
407
plans/RAG-지식증강-보완플랜.md
Normal file
@@ -0,0 +1,407 @@
|
||||
# RAG 지식 증강 보완 플랜 — Field-Instrument 자동 유추 + 관계 그래프
|
||||
|
||||
> 옵시디언 vault 풀구현(`plans/옵시디언-구조적용-플랜.md`) 이전에, **신규 인프라 0** + **휴먼-인-더-루프**로 RAG 품질을 빠르게 끌어올리는 단계.
|
||||
>
|
||||
> DCS 로직 태그(`ficq-6101.pv` 등) → 글자/loop 룰로 현장 계기 유추 → role/from/to까지 채운 Excel 초안 → 운영자가 검토·수정 → 기존 KB 업로드 흐름으로 인덱싱.
|
||||
|
||||
작성: 2026-05-14 | 위치: 14번 탭(RAG 관리) 확장 | 의존: 기존 `xlsx_parser`, `KbIngestWorker`, Qdrant `system_instrument` 컬렉션
|
||||
|
||||
---
|
||||
|
||||
## 1. 목적과 이유
|
||||
|
||||
### 1.1 해결하려는 문제
|
||||
|
||||
- `node_map_master`/`v_tag_summary`에는 **DCS 로직 블록 태그**만 있다 (`ficq-6101`, `xv-6124` ...).
|
||||
- 운전원 현장 언어는 **계기 단위**다 ("FT-6101 막혔어", "FCV-6101 포지셔너 점검").
|
||||
- 사이의 **번역 갭**을 RAG가 메우려면 명시적 매핑이 필요한데, 현재 KB에는 그 매핑이 없다.
|
||||
- 게다가 도면/PDF만으로는 "이 송신기 → 이 컨트롤러 → 저 밸브" 같은 **신호 흐름**이 LLM 컨텍스트로 들어오지 않는다.
|
||||
|
||||
### 1.2 왜 지금 이 단계인가
|
||||
|
||||
- 옵시디언 vault 풀구현은 데이터 모델 5개 테이블 + 워커 + UI 확장 + 그래프 검색까지 1~2주 작업.
|
||||
- 이 단계는 **0.5~1일** 작업으로 RAG 품질이 즉시 좋아지고, 만들어진 Excel은 vault 마이그레이션 시 **그대로 시드**가 된다 → 손해보는 작업이 아니다.
|
||||
- LLM은 지루한 초안만, 사람은 도메인 검증만 → 가장 비용 효율적인 분업.
|
||||
|
||||
### 1.3 비채택 옵션
|
||||
|
||||
- LLM 100% 추론 (옵션 C) — 2000행에 27B 호출 비용 큼, 환각 위험. 채택 안 함.
|
||||
- 자동 인덱싱 (운영자 검토 없이 바로 KB) — 부정확한 매핑이 RAG 답변에 그대로 섞이면 신뢰도 손상. 채택 안 함.
|
||||
|
||||
---
|
||||
|
||||
## 2. 채택: 규칙 + 부분 LLM (옵션 B)
|
||||
|
||||
| 부분 | 방법 | 처리량 |
|
||||
|------|------|--------|
|
||||
| 계기 구조 추론 (FT/FCV/Totalizer) | **결정론적 룰** | 2000행 < 5초 |
|
||||
| role/from/to 기본값 | **결정론적 룰** | 위와 함께 |
|
||||
| description 한국어 초안 (없을 때) | **LLM 배치** (옵션 토글) | 2000행 ≈ 수십 분 |
|
||||
| confidence / needs_review | 룰 적중도 계산 | 즉시 |
|
||||
|
||||
`use_llm=false`로도 운영자가 즉시 작업 시작 가능. LLM은 옵션.
|
||||
|
||||
---
|
||||
|
||||
## 3. Excel 스키마 — Long Format (1행 = 1계기)
|
||||
|
||||
> 이전 안의 "1행 = 1 base_tag, 컬럼에 FT/FCV/..." 와이드 포맷을 버리고 **롱 포맷** 채택. role/from/to를 1열에 자연스럽게 표현할 수 있고, 향후 vault note 1개 ↔ 행 1개 대응이 깔끔.
|
||||
|
||||
### 3.1 시트 1: `instruments`
|
||||
|
||||
| 열 | 타입 | 설명 | 예 |
|
||||
|----|------|------|----|
|
||||
| `instrument_id` | text (PK) | 정규화 ID, 소문자 kebab | `ft-6101` |
|
||||
| `display_name` | text | 운전원이 부르는 이름 | `FT-6101` |
|
||||
| `parent_base_tag` | text | 이 계기가 속한 DCS 태그 | `ficq-6101` |
|
||||
| `role` | enum | §4의 역할 표 | `transmitter` |
|
||||
| `loop` | text | loop 번호 | `6101` |
|
||||
| `area` | text | unit/area | `A` |
|
||||
| `measures` | enum/text | 측정량 (transmitter류) | `flow` |
|
||||
| `data_points` | text(csv) | DCS 데이터포인트 | `.pv` 또는 `.qv,.qv.value` |
|
||||
| `from` | text | 신호/물질 출처 | `process/compressor-a-suction` |
|
||||
| `to` | text | 신호/물질 목적지 | `tag/ficq-6101` |
|
||||
| `description` | text | 한국어 설명 | `압축기 A열 입구 유량 송신기` |
|
||||
| `confidence` | enum | `high` / `medium` / `low` | `high` |
|
||||
| `needs_review` | bool | TRUE면 운영자 검토 필수 | `FALSE` |
|
||||
| `inference_basis` | text | 어떤 룰로 유추됐는지 | `F+T@modifier rule` |
|
||||
| `operator_notes` | text | 운영자 자유 입력 | `2024년 교체. P&ID 105` |
|
||||
| `delete` | bool | TRUE면 재업로드 시 제외 | `FALSE` |
|
||||
|
||||
### 3.2 시트 2: `naming_convention` (참조용, read-only)
|
||||
|
||||
운영자가 시트 1을 수정할 때 참조하는 룰 표. §6 YAML을 그대로 시트로 풀어둠.
|
||||
|
||||
### 3.3 시트 3: `unmatched_tags` (운영자 보충용)
|
||||
|
||||
룰이 전혀 매칭 안 된 base_tag 목록 (예: 비표준 prefix, 한글 prefix). 운영자가 직접 시트 1에 행 추가해야 함.
|
||||
|
||||
### 3.4 예시 5행
|
||||
|
||||
| instrument_id | display_name | parent_base_tag | role | loop | area | measures | data_points | from | to | description | confidence | needs_review | inference_basis |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| ft-6101 | FT-6101 | ficq-6101 | transmitter | 6101 | A | flow | .pv | process/inlet-a | tag/ficq-6101 | 입구 유량 송신기 | high | FALSE | F+(implied T) |
|
||||
| fic-6101 | FIC-6101 | ficq-6101 | controller | 6101 | A | flow | .sp,.op | tag/ft-6101 | tag/fcv-6101 | 입구 유량 제어기 | high | FALSE | F+I+C |
|
||||
| fcv-6101 | FCV-6101 | ficq-6101 | control-valve | 6101 | A | — | .op | tag/fic-6101 | process/compressor-a-suction | 입구 제어밸브 | high | FALSE | C → CV |
|
||||
| fq-6101 | Totalizer-6101 | ficq-6101 | totalizer | 6101 | A | flow | .qv,.qv.value | tag/ficq-6101 | (none) | 입구 유량 적산기 | medium | FALSE | Q → totalizer |
|
||||
| xv-6124 | XV-6124 | xv-6124 | shutdown-valve | 6124 | A | — | .instate0..7 | safety/esd-loop-1 | process/header-a | ESD 차단밸브 | low | **TRUE** | digital-only |
|
||||
|
||||
---
|
||||
|
||||
## 4. Role 정의 (시드 표)
|
||||
|
||||
> 옵시디언 vault의 `kind: instrument` 노트 frontmatter `role` 필드와 1:1 매칭되도록 설계.
|
||||
|
||||
| role | 의미 | 룰 글자 | 일반 data_point |
|
||||
|------|------|---------|-----------------|
|
||||
| `transmitter` | 측정 송신기 (FT/PT/TT/LT/AT) | 수식어 T 또는 측정 letter 단독 | `.pv` |
|
||||
| `indicator` | 표시기 (계기 없이 DCS 화면) | I | (가상) |
|
||||
| `controller` | 제어기 (FIC/PIC/TIC/...) | C | `.sp`, `.op`, `.mode` |
|
||||
| `recorder` | 기록계 | R | (가상) |
|
||||
| `totalizer` | 적산기 | Q | `.qv`, `.qv.value` |
|
||||
| `switch` | 스위치 (FS/PS/LS/...) | S | `.instate0..n` |
|
||||
| `alarm` | 알람 단독 | A | (이벤트 채널) |
|
||||
| `control-valve` | 제어밸브 (FCV/PCV/TCV) | 제어기 C 동반 시 자동 추론 | `.op` 참조 |
|
||||
| `shutdown-valve` | 차단/ESD 밸브 (XV/SDV) | X/SDV prefix | `.instate*` |
|
||||
| `check-valve` | 체크밸브 | (운영자 수동) | — |
|
||||
| `positioner` | 포지셔너 (FZ) | Z | — |
|
||||
| `interlock-relay` | 인터록 릴레이 (FY) | Y | — |
|
||||
| `motor` | 모터 (M, KM) | 별도 prefix | `.run`, `.fault` |
|
||||
| `pump` | 펌프 (P) | 별도 prefix | `.run`, `.flow` |
|
||||
| `compressor` | 압축기 (K, C) | 별도 prefix | (다중) |
|
||||
| `analyzer` | 분석기 (AT, AIT) | A 단독+T | `.pv` |
|
||||
| `damper` | 댐퍼 | (운영자 수동) | — |
|
||||
| `equipment` | 그 외 정적 장치 | (운영자 수동) | — |
|
||||
|
||||
→ 운영자가 신규 role을 만들어도 그대로 보존. role 자체가 frontmatter라 시스템 enum 위반은 경고만(차단 X).
|
||||
|
||||
---
|
||||
|
||||
## 5. From / To 의미론
|
||||
|
||||
> "**신호 또는 물질의 흐름**"을 표현. 운영자 직관과 맞아야 함.
|
||||
|
||||
### 5.1 기본 규칙 (role별 자동 채움)
|
||||
|
||||
| role | from 기본값 | to 기본값 |
|
||||
|------|------------|----------|
|
||||
| `transmitter` | `process/<area>-<loop-측정점>` (placeholder) | `tag/<parent>` |
|
||||
| `controller` | `tag/<measurement-transmitter>` | `tag/<paired-valve>` |
|
||||
| `control-valve` | `tag/<paired-controller>` | `process/<area>-<loop-downstream>` (placeholder) |
|
||||
| `totalizer` | `tag/<parent>` | `(none)` |
|
||||
| `switch` | `process/<area>-<loop-측정점>` | `safety/<interlock-id>` (운영자 보충) |
|
||||
| `shutdown-valve` | `safety/<esd-trigger>` (운영자 보충) | `process/<area>-<downstream>` |
|
||||
| 그 외 | `(none)` | `(none)` |
|
||||
|
||||
→ `process/...`, `safety/...` 경로는 **placeholder**. 운영자가 실제 ID(있다면)로 바꾸거나 그대로 둬도 됨. vault 마이그레이션 시 자동으로 `process` kind 노트가 빨간 링크로 잡힘 (= 채워야 할 노트 목록 = §10 옵시디언 플랜의 `vault_unresolved` 기능).
|
||||
|
||||
### 5.2 작성 형식
|
||||
|
||||
운영자는 4가지 중 하나로 적을 수 있음:
|
||||
1. **vault note id**: `tag/ficq-6101`, `instrument/ft-6101`, `process/header-a` — 향후 vault 그래프로 직변환
|
||||
2. **base_tag 그대로**: `ficq-6101` — 자동 정규화로 `tag/ficq-6101`
|
||||
3. **자유 텍스트**: `압축기 1열 입구 헤더` — 보존만, 그래프 엣지로는 안 잡힘
|
||||
4. **`(none)`**: 명시적 없음
|
||||
|
||||
룰 엔진은 4번을 디폴트로 두지 않고, 가능한 한 1번 형식의 placeholder를 채움.
|
||||
|
||||
### 5.3 검증
|
||||
|
||||
업로드 시:
|
||||
- `from` / `to`가 형식 1·2면 자동 엣지로 인식
|
||||
- 형식 3은 그대로 보존하되 "그래프 엣지 미생성" 정보 행 카운트 표시
|
||||
- 형식 4는 명시적 제외
|
||||
|
||||
---
|
||||
|
||||
## 6. 룰 테이블 (`prompts/instrument_inference.yaml`)
|
||||
|
||||
> 코드 외부에 두는 이유: 운영자/엔지니어가 plant_context.md처럼 직접 편집 가능.
|
||||
|
||||
```yaml
|
||||
# 첫 글자 = 측정량
|
||||
measurement:
|
||||
F: flow
|
||||
P: pressure
|
||||
T: temperature
|
||||
L: level
|
||||
A: analysis
|
||||
S: speed
|
||||
W: weight
|
||||
D: density
|
||||
J: power
|
||||
M: moisture
|
||||
|
||||
# 두 번째 이후 = 기능 수식어 (순서대로 별도 계기 생성)
|
||||
modifiers:
|
||||
I: { role: indicator, virtual: true }
|
||||
R: { role: recorder, virtual: true }
|
||||
T: { role: transmitter, data_points: [.pv] }
|
||||
C: { role: controller, data_points: [.sp, .op] }
|
||||
Q: { role: totalizer, data_points: [.qv, "qv.value"] }
|
||||
S: { role: switch, data_points: [.instate0, .instate1] }
|
||||
A: { role: alarm }
|
||||
Y: { role: interlock-relay }
|
||||
Z: { role: positioner }
|
||||
|
||||
# 컨트롤러가 있으면 자동으로 제어밸브 1개 생성
|
||||
auto_pair:
|
||||
- if_role: controller
|
||||
create:
|
||||
role: control-valve
|
||||
id_pattern: "{meas}cv-{loop}"
|
||||
display_pattern: "{MEAS}CV-{loop}"
|
||||
|
||||
# 특수 prefix (글자 분해 룰 미적용)
|
||||
special_prefixes:
|
||||
xv: { role: shutdown-valve, measures: null }
|
||||
sdv: { role: shutdown-valve, measures: null }
|
||||
fy: { role: interlock-relay, measures: flow }
|
||||
fz: { role: positioner, measures: flow }
|
||||
km: { role: motor }
|
||||
p: { role: pump }
|
||||
k: { role: compressor }
|
||||
|
||||
# data_point 패턴별 보정 (룰 추론 결과 검증)
|
||||
data_point_validation:
|
||||
has_qv_when: role == "totalizer"
|
||||
has_sp_op_when: role == "controller"
|
||||
has_instate_when: role in ["switch", "shutdown-valve"]
|
||||
|
||||
# 신뢰도 계산
|
||||
confidence:
|
||||
high_when:
|
||||
- prefix matches measurement OR special_prefixes
|
||||
- all modifiers resolved
|
||||
- data_points present and consistent
|
||||
medium_when:
|
||||
- prefix matches but some data_points missing
|
||||
- or modifiers contain unknown letter
|
||||
low_when:
|
||||
- special_prefixes only AND data_points unusual
|
||||
- or first letter not in measurement table
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 유추 알고리즘 (Python 의사코드)
|
||||
|
||||
```python
|
||||
def infer_instruments_for_base_tag(base_tag: str, data_points: list[str], area: str) -> list[dict]:
|
||||
# 1. 분해
|
||||
head, loop = split_letters_and_number(base_tag) # "ficq", "6101"
|
||||
|
||||
# 2. 특수 prefix 우선
|
||||
if head in rules["special_prefixes"]:
|
||||
return [build_special_instrument(head, loop, data_points, area)]
|
||||
|
||||
# 3. 첫 글자 = 측정량
|
||||
if head[0] not in rules["measurement"]:
|
||||
return [build_unmatched(base_tag, area)] # confidence=low, needs_review=TRUE
|
||||
|
||||
meas = rules["measurement"][head[0]]
|
||||
instruments = []
|
||||
|
||||
# 4. 수식어 글자별로 계기 생성
|
||||
has_transmitter = False
|
||||
for letter in head[1:]:
|
||||
mod = rules["modifiers"].get(letter)
|
||||
if not mod or mod.get("virtual"):
|
||||
continue # I, R은 가상
|
||||
inst = build_instrument(meas, mod["role"], loop, area, data_points)
|
||||
instruments.append(inst)
|
||||
if mod["role"] == "transmitter":
|
||||
has_transmitter = True
|
||||
|
||||
# 5. T 글자가 명시 안 됐어도 측정 letter만으로 송신기 암시 (FI-6101 → FT-6101 묵시)
|
||||
if not has_transmitter and any(m["role"] == "controller" for m in instruments):
|
||||
instruments.insert(0, build_implicit_transmitter(meas, loop, area))
|
||||
|
||||
# 6. 컨트롤러 → 제어밸브 자동 생성 (auto_pair)
|
||||
for inst in list(instruments):
|
||||
if inst["role"] == "controller":
|
||||
instruments.append(build_paired_valve(meas, loop, area))
|
||||
|
||||
# 7. from/to 채우기 (§5.1)
|
||||
link_signal_flow(instruments)
|
||||
|
||||
# 8. data_point 검증 → confidence 계산
|
||||
for inst in instruments:
|
||||
inst["confidence"] = score_confidence(inst, data_points)
|
||||
inst["needs_review"] = inst["confidence"] == "low"
|
||||
|
||||
return instruments
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. LLM 보강 (옵션, `use_llm=true`)
|
||||
|
||||
- **대상**: `description` 열이 비어 있거나 confidence=low인 행만.
|
||||
- **호출**: 한 번에 최대 50행 묶어서 vLLM에 한국어 요약 요청. 시스템 프롬프트는 `prompts/plant_context.md` + role 표.
|
||||
- **출력**: description 한 줄(≤80자). 환각 방지 위해 "확실치 않으면 비워두라" 명시.
|
||||
- **캐시**: 같은 (parent_base_tag, role) 조합 결과는 재실행 시 재사용.
|
||||
- **토큰 예산**: 50행 묶음 × ~40 묶음 = 호출 40회, 각 ≤ 2k 토큰.
|
||||
|
||||
---
|
||||
|
||||
## 9. 워크플로
|
||||
|
||||
```
|
||||
[14번 탭 > Field Instrument 모드]
|
||||
|
||||
1. ▼ 초안 생성 (use_llm 토글)
|
||||
↓
|
||||
POST /api/kb/instruments/infer → 백그라운드 잡 → 진행률 폴링
|
||||
↓
|
||||
2. ▼ Excel 다운로드 (instruments_draft_YYYYMMDD.xlsx)
|
||||
↓
|
||||
3. 운영자가 로컬에서 Excel 편집
|
||||
- confidence=low / needs_review=TRUE 행 우선
|
||||
- from/to 보충 (process/, safety/ placeholder 채우기)
|
||||
- delete=TRUE로 잘못된 추론 제외
|
||||
- 시트 3(unmatched_tags)에서 행 추가
|
||||
↓
|
||||
4. ▼ 같은 탭에 업로드 (collection = system_instrument)
|
||||
↓
|
||||
5. 기존 KbIngestWorker → 시트별/행별 청킹 → Qdrant 인덱싱
|
||||
↓
|
||||
6. 채팅에서 즉시 search_kb로 활용
|
||||
- "FT-6101 점검 이력" → instrument_id, parent_base_tag, description 청크 hit
|
||||
- "ficq-6101 관련 계기" → parent_base_tag 매칭으로 4행(FT/FIC/FCV/Q) 회수
|
||||
```
|
||||
|
||||
재실행 시: 운영자가 수정한 Excel을 시드로 받아 (`POST /api/kb/instruments/infer?seed_doc_id=...`) **사용자 수정사항 보존 + 신규 base_tag만 추가** 모드.
|
||||
|
||||
---
|
||||
|
||||
## 10. 구현 단계
|
||||
|
||||
### Phase A — 룰 엔진 + Excel 생성 (0.5일)
|
||||
- `prompts/instrument_inference.yaml` 작성 (§6)
|
||||
- `mcp-server/instrument_inference/` 신규 모듈
|
||||
- `rules.py` — YAML 로더
|
||||
- `infer.py` — `infer_instruments_for_base_tag()` (§7)
|
||||
- `excel.py` — openpyxl 기반 3시트 생성기
|
||||
- `@mcp.tool() infer_field_instruments(use_llm=False, seed_doc_id=None)` 신규 도구
|
||||
- 단위 테스트 10건 (ficq, xv, pic, tic, ts, lt, fy, km, 비표준 prefix, area 누락)
|
||||
|
||||
### Phase B — API + UI (0.3일)
|
||||
- `GET /api/kb/instruments/infer/start` (admin) — 백그라운드 잡 ID 반환
|
||||
- `GET /api/kb/instruments/infer/status/{jobId}` — 진행률
|
||||
- `GET /api/kb/instruments/infer/download/{jobId}` — xlsx 스트림
|
||||
- 14번 탭: "Field Instrument 초안" 버튼 + 진행률 카드 + 다운로드 링크
|
||||
- 업로드는 **기존 14번 탭 업로드** 그대로 (system_instrument 컬렉션 선택)
|
||||
|
||||
### Phase C — LLM 보강 (0.2일, 옵션)
|
||||
- `infer.py`에 `enrich_with_llm(rows, batch=50)` 추가
|
||||
- vLLM 호출, 캐시 dict
|
||||
- 토글로 on/off
|
||||
|
||||
### Phase D — 검증/소비 강화 (0.2일)
|
||||
- `search_kb`가 `system_instrument` 컬렉션 우선 가중 (이미 가능, 가중치만)
|
||||
- `ToolGuideKo`에 "FT/FCV 등 현장 계기명 질의는 system_instrument 컬렉션 우선" 한 줄 추가
|
||||
- 채팅 답변에 instrument hit 시 `parent_base_tag` 동시 노출
|
||||
|
||||
### Phase E (선택) — diff 도구
|
||||
- 운영자가 수정한 Excel ↔ 룰 출력 비교 → 어떤 룰을 추가/수정하면 되는지 리포트
|
||||
- 룰 진화 피드백 루프
|
||||
|
||||
총 1.2일 (옵션 제외 0.8일).
|
||||
|
||||
---
|
||||
|
||||
## 11. vault 마이그레이션 시 변환 (미래)
|
||||
|
||||
> 옵시디언 vault 구현이 시작되면 이 Excel이 **그대로 시드**.
|
||||
|
||||
| Excel 열 | vault 변환 |
|
||||
|---------|----------|
|
||||
| `instrument_id` | `instrument/{id}` 노트 ID |
|
||||
| `display_name` | `title` |
|
||||
| `role` | frontmatter `role` |
|
||||
| `parent_base_tag` | 본문에 `[[tag/{parent}]]` 위키링크 |
|
||||
| `loop`, `area` | frontmatter |
|
||||
| `measures`, `data_points` | frontmatter |
|
||||
| `from` (vault id 형식) | 본문 `## 입력` 섹션에 `[[from]]` |
|
||||
| `to` (vault id 형식) | 본문 `## 출력` 섹션에 `[[to]]` |
|
||||
| `description` | 본문 첫 단락 |
|
||||
| `operator_notes` | 본문 `## 운영자 메모` 섹션 |
|
||||
|
||||
→ 변환기 1개로 노트 N개 + `vault_links` M개 자동 생성. Excel 수정 이력이 그대로 그래프가 됨.
|
||||
|
||||
---
|
||||
|
||||
## 12. 산출물 미리보기 (작업 후 KB가 답할 수 있는 질문)
|
||||
|
||||
| 질의 | 응답 가능성 (전) | 응답 가능성 (후) |
|
||||
|------|----------------|----------------|
|
||||
| "FT-6101 어디 붙어있어?" | ❌ (계기 매핑 없음) | ✅ parent_base_tag + area + from |
|
||||
| "FCV-6101은 뭐가 제어해?" | ❌ | ✅ `to: tag/ficq-6101` 백트래킹 |
|
||||
| "ficq-6101 관련 현장 계기 다 보여줘" | △ description ILIKE만 | ✅ parent_base_tag로 4행 일괄 |
|
||||
| "ESD 차단밸브 목록" | ❌ | ✅ role=shutdown-valve 필터 |
|
||||
| "FT-6101 점검 이력" | △ (운영자 노트가 KB 어딘가에 있다면) | ✅ 동일 + operator_notes 청크 매칭 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 잔여 결정 (사용자 확인 필요)
|
||||
|
||||
| 항목 | 옵션 | 추천 |
|
||||
|------|------|------|
|
||||
| 첫 실행 LLM 사용 | (A) off (B) description만 on | **A** — 빠른 1차 산출, 운영자 작업 시작 |
|
||||
| Excel 컬렉션 | `system_instrument` | 기존 시드 그대로 |
|
||||
| ID 정규화 정책 | 무조건 소문자 kebab | 운영자가 대문자 입력해도 자동 변환 |
|
||||
| 운영자 신규 role 허용 | 허용(경고만) / 차단 | **허용** — frontmatter 자유도 유지 |
|
||||
| `from`/`to` 미입력 시 | placeholder 자동 / 공란 | **placeholder** — vault 시 빨간 링크로 가시화 |
|
||||
| 재실행 정책 | 매번 새 Excel / seed_doc_id 기반 머지 | **머지** — 운영자 수정 보존 |
|
||||
| unmatched_tags 시트 알림 | UI에 카운트 | 운영자 행동 유도 |
|
||||
|
||||
---
|
||||
|
||||
## 14. 핵심 메시지
|
||||
|
||||
- **신규 인프라 0** — 기존 xlsx 인덱싱·KB 업로드·Qdrant 컬렉션 그대로 활용.
|
||||
- **롱 포맷 + role/from/to** — 단순 매핑 표가 아니라 **계기 그래프의 노드+엣지**. vault로 그대로 이전.
|
||||
- **휴먼-인-더-루프** — LLM은 초안만, 운영자는 검증만. 백지에서 작성하는 것 대비 100배 빠름.
|
||||
- **재실행 안전** — 룰 개선 시 운영자 수정 보존하고 신규만 추가.
|
||||
- **즉시 효과** — 업로드 직후 채팅이 현장 계기명으로 답할 수 있음. 옵시디언 vault 풀구현을 기다리지 않아도 됨.
|
||||
14
plans/RAG-지식증강-보완플랜2.md
Normal file
14
plans/RAG-지식증강-보완플랜2.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 현재 운엉상의 문제점
|
||||
개선안 1) RAG관리 -> 초안생성시 : 계기 유추 전혀 안됨, FICQ-6101 로 부터 FT-6101, FCV-6101를 유추해서 초안에 만들어 줘야 함
|
||||
예) 1. FICQ-XXXXX, FIC-XXXXX -> FT-6101, FCV-6101 유추, PICX-> PT & PCV, LICX->LT & LCV 등등 ( 태그명 XXCXX- 'C'가 있으면 CONTROL VALVE가 있다고 생각)
|
||||
2. FI-XXXXX, FIA-XXXXX -> FT-XXXXX
|
||||
3. TI-XXXXX, TIA-XXXXX, TIS-XXXXX -> TE-XXXXX (Temperature Element)
|
||||
4. LI-XXXXX, LIA-XXXXX, LIS-XXXXX -> LT-XXXXX
|
||||
5. PI-XXXXX, PIA-XXXXX, PIS-XXXXX -> PT-XXXXX
|
||||
수정완료[x]
|
||||
|
||||
개선안 2) :
|
||||
1. display_name : FT-10114a --> 모두 대문자로
|
||||
2. 규칙에서 어긋난 태그명 : unmatched_tags로 분류할 것, fica-3102_op -> ft-3102_op, fcv-3102_op
|
||||
3. lt-9113-lo-esd, lcv-9113-lo-esd 등등 %-esd% 인 것들 --> 시스템 포인트 임 계기 아님
|
||||
|
||||
680
plans/no10-pid-extraction.md
Normal file
680
plans/no10-pid-extraction.md
Normal file
@@ -0,0 +1,680 @@
|
||||
# No-10 플랜트 P&ID 추출 데이터
|
||||
|
||||
**도면**: `No-10_Plant_PID.dxf` (DXF)
|
||||
**추출 일자**: 2026-05-14
|
||||
**데이터 소스**: PostgreSQL `pid_equipment` 테이블 (460건, is_active=true)
|
||||
**용도**: RAG 검색 보조용 — 운전원 질의 시 P&ID 컨텍스트 자동 인용
|
||||
|
||||
---
|
||||
|
||||
## 통계
|
||||
|
||||
| 구분 | 수량 |
|
||||
|---|---|
|
||||
| **파이프 (LineNo)** | 247 |
|
||||
| **장비** | 78 |
|
||||
| **계기** | 110 |
|
||||
| **미분류** | 25 |
|
||||
| **Experion 매핑 완료** | 25 |
|
||||
|
||||
### 영역(area)별 분포
|
||||
|
||||
| area 추정 | 항목 수 | 비고 |
|
||||
|---|---|---|
|
||||
| `10` | 318 | 태그 prefix 숫자 기반 추정 |
|
||||
| `91` | 19 | 태그 prefix 숫자 기반 추정 |
|
||||
| `61` | 17 | 태그 prefix 숫자 기반 추정 |
|
||||
| `94` | 16 | 태그 prefix 숫자 기반 추정 |
|
||||
| `1` | 13 | 태그 prefix 숫자 기반 추정 |
|
||||
| `21` | 13 | 태그 prefix 숫자 기반 추정 |
|
||||
| `62` | 12 | 태그 prefix 숫자 기반 추정 |
|
||||
| `92` | 9 | 태그 prefix 숫자 기반 추정 |
|
||||
| `51` | 8 | 태그 prefix 숫자 기반 추정 |
|
||||
| `9` | 7 | 태그 prefix 숫자 기반 추정 |
|
||||
| `29` | 5 | 태그 prefix 숫자 기반 추정 |
|
||||
| `2` | 4 | 태그 prefix 숫자 기반 추정 |
|
||||
| `32` | 3 | 태그 prefix 숫자 기반 추정 |
|
||||
| `34` | 3 | 태그 prefix 숫자 기반 추정 |
|
||||
| `0` | 2 | 태그 prefix 숫자 기반 추정 |
|
||||
| `27` | 2 | 태그 prefix 숫자 기반 추정 |
|
||||
| `31` | 2 | 태그 prefix 숫자 기반 추정 |
|
||||
| `81` | 2 | 태그 prefix 숫자 기반 추정 |
|
||||
| `53` | 1 | 태그 prefix 숫자 기반 추정 |
|
||||
| `66` | 1 | 태그 prefix 숫자 기반 추정 |
|
||||
| `7` | 1 | 태그 prefix 숫자 기반 추정 |
|
||||
| `90` | 1 | 태그 prefix 숫자 기반 추정 |
|
||||
| `99` | 1 | 태그 prefix 숫자 기반 추정 |
|
||||
|
||||
### 파이프 서비스(fluid) 분포
|
||||
|
||||
| service 코드 | 의미 | 수량 |
|
||||
|---|---|---|
|
||||
| `P` | PROCESS FLUID | 90 |
|
||||
| `VG` | VENT GAS | 46 |
|
||||
| `CWS` | COOLING WATER SUPPLY | 24 |
|
||||
| `CWR` | COOLING WATER RETURN | 22 |
|
||||
| `IA` | INSTRUMENT AIR | 20 |
|
||||
| `SW` | SOFT WATER | 12 |
|
||||
| `SAM` | SAMPLE LINE | 9 |
|
||||
| `CD` | STEAM CONDENSATE | 6 |
|
||||
| `NBD` | NITROGEN BLOW DOWN | 5 |
|
||||
| `ST` | STEAM | 3 |
|
||||
| `WW` | WASTE WATER | 3 |
|
||||
| `CHR` | CHILLED WATER RETURN | 2 |
|
||||
| `CHS` | CHILLED WATER SUPPLY | 2 |
|
||||
| `SC` | VENT GAS | 2 |
|
||||
| `PW` | PW | 1 |
|
||||
|
||||
### 장비 종류 분포
|
||||
|
||||
| prefix | 의미 | 수량 |
|
||||
|---|---|---|
|
||||
| `T` | Tank | 20 |
|
||||
| `E` | Heat Exchanger | 18 |
|
||||
| `P` | Pump | 15 |
|
||||
| `D` | Drum | 5 |
|
||||
| `C` | Column | 4 |
|
||||
| `F` | Filter | 4 |
|
||||
| `K` | Compressor | 4 |
|
||||
| `DP` | Drainage Point | 3 |
|
||||
| `VP` | Vacuum Pump | 2 |
|
||||
| `BT` | Buffer Tank | 1 |
|
||||
| `CH` | Chiller | 1 |
|
||||
| `CT` | Cooling Tower | 1 |
|
||||
|
||||
### 계기 종류 분포
|
||||
|
||||
| prefix | 의미 | 수량 |
|
||||
|---|---|---|
|
||||
| `FCV` | Flow Control Valve | 46 |
|
||||
| `PSV` | Pressure Switch Valve | 27 |
|
||||
| `PCV` | Pressure Control Valve | 11 |
|
||||
| `LCV` | Level Control Valve | 10 |
|
||||
| `TCV` | Temperature Control Valve | 9 |
|
||||
| `TR` | Temperature Recorder | 3 |
|
||||
| `PFD` | Pressure | 2 |
|
||||
| `SP` | Speed | 2 |
|
||||
|
||||
---
|
||||
|
||||
## 장비 목록
|
||||
|
||||
### BT — Buffer Tank (1대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `BT-6200` | 62 | `—` | 0.950 |
|
||||
|
||||
### C — Column (4대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `C-10111` | 10 | `—` | 0.950 |
|
||||
| `C-10211` | 10 | `—` | 0.950 |
|
||||
| `C-9111` | 91 | `—` | 0.950 |
|
||||
| `C-9128` | 91 | `—` | 0.950 |
|
||||
|
||||
### CH — Chiller (1대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `CH-6601` | 66 | `—` | 0.950 |
|
||||
|
||||
### CT — Cooling Tower (1대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `CT-10601` | 10 | `—` | 0.950 |
|
||||
|
||||
### D — Drum (5대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `D-10113` | 10 | `—` | 0.950 |
|
||||
| `D-10213` | 10 | `—` | 0.950 |
|
||||
| `D-10901` | 10 | `—` | 0.950 |
|
||||
| `D-2901` | 29 | `—` | 0.950 |
|
||||
| `D-901` | 9 | `—` | 0.950 |
|
||||
|
||||
### DP — Drainage Point (3대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `DP-10101` | 10 | `—` | 0.950 |
|
||||
| `DP-10201` | 10 | `—` | 0.950 |
|
||||
| `DP-3210` | 32 | `—` | 0.950 |
|
||||
|
||||
### E — Heat Exchanger (18대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `E-10103` | 10 | `—` | 0.950 |
|
||||
| `E-10112` | 10 | `—` | 0.950 |
|
||||
| `E-10115` | 10 | `—` | 0.950 |
|
||||
| `E-10115A` | 10 | `—` | 0.950 |
|
||||
| `E-10115B` | 10 | `—` | 0.950 |
|
||||
| `E-10117` | 10 | `—` | 0.950 |
|
||||
| `E-10119` | 10 | `—` | 0.950 |
|
||||
| `E-10203` | 10 | `—` | 0.950 |
|
||||
| `E-10212` | 10 | `—` | 0.950 |
|
||||
| `E-10215` | 10 | `—` | 0.950 |
|
||||
| `E-10217` | 10 | `—` | 0.950 |
|
||||
| `E-10219` | 10 | `—` | 0.950 |
|
||||
| `E-8115` | 81 | `—` | 0.950 |
|
||||
| `E-9103` | 91 | `—` | 0.950 |
|
||||
| `E-9112` | 91 | `—` | 0.950 |
|
||||
| `E-9115` | 91 | `—` | 0.950 |
|
||||
| `E-9203` | 92 | `—` | 0.950 |
|
||||
| `E-9215` | 92 | `—` | 0.950 |
|
||||
|
||||
### F — Filter (4대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `F-10900` | 10 | `—` | 0.950 |
|
||||
| `F-10952` | 10 | `—` | 0.950 |
|
||||
| `F-2952` | 29 | `—` | 0.950 |
|
||||
| `F-952` | 9 | `—` | 0.950 |
|
||||
|
||||
### K — Compressor (4대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `K-10901` | 10 | `—` | 0.500 |
|
||||
| `K-2901` | 29 | `—` | 0.500 |
|
||||
| `K-901A` | 9 | `—` | 0.500 |
|
||||
| `K-901B` | 9 | `—` | 0.500 |
|
||||
|
||||
### P — Pump (15대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `P-10101` | 10 | `p-10101.pv` | 0.950 |
|
||||
| `P-10114` | 10 | `p-10114.pv` | 0.950 |
|
||||
| `P-10116` | 10 | `p-10116.pv` | 0.950 |
|
||||
| `P-10118` | 10 | `p-10118.pv` | 0.950 |
|
||||
| `P-10201` | 10 | `p-10201.pv` | 0.950 |
|
||||
| `P-10214` | 10 | `p-10214.pv` | 0.950 |
|
||||
| `P-10216` | 10 | `p-10216.pv` | 0.950 |
|
||||
| `P-10218` | 10 | `p-10218.pv` | 0.950 |
|
||||
| `P-10221` | 10 | `p-10221.pv` | 0.950 |
|
||||
| `P-201` | 2 | `p-201.pv` | 0.950 |
|
||||
| `P-202` | 2 | `p-202.pv` | 0.950 |
|
||||
| `P-3101` | 31 | `—` | 0.950 |
|
||||
| `P-5101` | 51 | `—` | 0.950 |
|
||||
| `P-6101` | 61 | `p-6101.pv` | 0.950 |
|
||||
| `P-9102` | 91 | `p-9102.pv` | 0.950 |
|
||||
|
||||
### T — Tank (20대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `T-10100` | 10 | `—` | 0.950 |
|
||||
| `T-10101` | 10 | `—` | 0.950 |
|
||||
| `T-10200` | 10 | `—` | 0.950 |
|
||||
| `T-10201` | 10 | `—` | 0.950 |
|
||||
| `T-10221` | 10 | `—` | 0.950 |
|
||||
| `T-10800` | 10 | `—` | 0.950 |
|
||||
| `T-201` | 2 | `—` | 0.950 |
|
||||
| `T-202` | 2 | `—` | 0.950 |
|
||||
| `T-2704` | 27 | `—` | 0.950 |
|
||||
| `T-3101` | 31 | `—` | 0.950 |
|
||||
| `T-3210` | 32 | `—` | 0.950 |
|
||||
| `T-6121` | 61 | `—` | 0.950 |
|
||||
| `T-6122` | 61 | `—` | 0.950 |
|
||||
| `T-6125` | 61 | `—` | 0.950 |
|
||||
| `T-6126` | 61 | `—` | 0.950 |
|
||||
| `T-6222` | 62 | `—` | 0.950 |
|
||||
| `T-8121` | 81 | `—` | 0.950 |
|
||||
| `T-9123` | 91 | `—` | 0.950 |
|
||||
| `T-9124` | 91 | `—` | 0.950 |
|
||||
| `T-9125` | 91 | `—` | 0.950 |
|
||||
|
||||
### VP — Vacuum Pump (2대)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `VP-10117` | 10 | `vp-10117.pv` | 0.950 |
|
||||
| `VP-10217` | 10 | `vp-10217.pv` | 0.950 |
|
||||
|
||||
---
|
||||
|
||||
## 계기 목록
|
||||
|
||||
### FCV — Flow Control Valve (46개)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `FCV-10101` | 10 | `—` | 0.950 |
|
||||
| `FCV-10113` | 10 | `—` | 0.950 |
|
||||
| `FCV-10114A` | 10 | `—` | 0.950 |
|
||||
| `FCV-10116` | 10 | `—` | 0.950 |
|
||||
| `FCV-10118` | 10 | `—` | 0.950 |
|
||||
| `FCV-10201` | 10 | `—` | 0.950 |
|
||||
| `FCV-10213` | 10 | `—` | 0.950 |
|
||||
| `FCV-10214` | 10 | `—` | 0.950 |
|
||||
| `FCV-10216` | 10 | `—` | 0.950 |
|
||||
| `FCV-10218` | 10 | `—` | 0.950 |
|
||||
| `FCV-111` | 1 | `—` | 0.950 |
|
||||
| `FCV-113` | 1 | `—` | 0.950 |
|
||||
| `FCV-122` | 1 | `—` | 0.950 |
|
||||
| `FCV-123` | 1 | `—` | 0.950 |
|
||||
| `FCV-124` | 1 | `—` | 0.950 |
|
||||
| `FCV-131` | 1 | `—` | 0.950 |
|
||||
| `FCV-2111` | 21 | `—` | 0.950 |
|
||||
| `FCV-2122` | 21 | `—` | 0.950 |
|
||||
| `FCV-2123` | 21 | `—` | 0.950 |
|
||||
| `FCV-2124` | 21 | `—` | 0.950 |
|
||||
| `FCV-2131` | 21 | `—` | 0.950 |
|
||||
| `FCV-5101` | 51 | `—` | 0.950 |
|
||||
| `FCV-5113` | 51 | `—` | 0.950 |
|
||||
| `FCV-5114` | 51 | `—` | 0.950 |
|
||||
| `FCV-5116` | 51 | `—` | 0.950 |
|
||||
| `FCV-5118` | 51 | `—` | 0.950 |
|
||||
| `FCV-6101` | 61 | `—` | 0.950 |
|
||||
| `FCV-6113` | 61 | `—` | 0.950 |
|
||||
| `FCV-6114` | 61 | `—` | 0.950 |
|
||||
| `FCV-6116` | 61 | `—` | 0.950 |
|
||||
| `FCV-6118` | 61 | `—` | 0.950 |
|
||||
| `FCV-6201` | 62 | `—` | 0.950 |
|
||||
| `FCV-6213` | 62 | `—` | 0.950 |
|
||||
| `FCV-6214` | 62 | `—` | 0.950 |
|
||||
| `FCV-6216` | 62 | `—` | 0.950 |
|
||||
| `FCV-6218` | 62 | `—` | 0.950 |
|
||||
| `FCV-9101` | 91 | `—` | 0.950 |
|
||||
| `FCV-9113` | 91 | `—` | 0.950 |
|
||||
| `FCV-9114` | 91 | `—` | 0.950 |
|
||||
| `FCV-9116` | 91 | `—` | 0.950 |
|
||||
| `FCV-9118` | 91 | `—` | 0.950 |
|
||||
| `FCV-9201` | 92 | `—` | 0.950 |
|
||||
| `FCV-9213` | 92 | `—` | 0.950 |
|
||||
| `FCV-9214` | 92 | `—` | 0.950 |
|
||||
| `FCV-9216` | 92 | `—` | 0.950 |
|
||||
| `FCV-9218` | 92 | `—` | 0.950 |
|
||||
|
||||
### LCV — Level Control Valve (10개)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `LCV-111` | 1 | `—` | 0.950 |
|
||||
| `LCV-113` | 1 | `—` | 0.950 |
|
||||
| `LCV-121` | 1 | `—` | 0.950 |
|
||||
| `LCV-131` | 1 | `—` | 0.950 |
|
||||
| `LCV-2111` | 21 | `—` | 0.950 |
|
||||
| `LCV-2113` | 21 | `—` | 0.950 |
|
||||
| `LCV-2121` | 21 | `—` | 0.950 |
|
||||
| `LCV-2131` | 21 | `—` | 0.950 |
|
||||
| `LCV-2705` | 27 | `—` | 0.950 |
|
||||
| `LCV-3402` | 34 | `—` | 0.950 |
|
||||
|
||||
### PCV — Pressure Control Valve (11개)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `PCV-10111` | 10 | `—` | 0.950 |
|
||||
| `PCV-10211` | 10 | `—` | 0.950 |
|
||||
| `PCV-111` | 1 | `—` | 0.950 |
|
||||
| `PCV-121` | 1 | `—` | 0.950 |
|
||||
| `PCV-2111` | 21 | `—` | 0.950 |
|
||||
| `PCV-2121` | 21 | `—` | 0.950 |
|
||||
| `PCV-5111` | 51 | `—` | 0.950 |
|
||||
| `PCV-6111` | 61 | `—` | 0.950 |
|
||||
| `PCV-6211` | 62 | `—` | 0.950 |
|
||||
| `PCV-9111` | 91 | `—` | 0.950 |
|
||||
| `PCV-9211` | 92 | `—` | 0.950 |
|
||||
|
||||
### PFD — Pressure (2개)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `PFD-001` | 0 | `—` | 0.950 |
|
||||
| `PFD-002` | 0 | `—` | 0.950 |
|
||||
|
||||
### PSV — Pressure Switch Valve (27개)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `PSV-10101` | 10 | `—` | 0.950 |
|
||||
| `PSV-10103` | 10 | `—` | 0.950 |
|
||||
| `PSV-10111` | 10 | `—` | 0.950 |
|
||||
| `PSV-10112` | 10 | `—` | 0.950 |
|
||||
| `PSV-10115` | 10 | `—` | 0.950 |
|
||||
| `PSV-10117` | 10 | `—` | 0.950 |
|
||||
| `PSV-10119A` | 10 | `—` | 0.950 |
|
||||
| `PSV-10119B` | 10 | `—` | 0.950 |
|
||||
| `PSV-10201` | 10 | `—` | 0.950 |
|
||||
| `PSV-10203` | 10 | `—` | 0.950 |
|
||||
| `PSV-10211` | 10 | `—` | 0.950 |
|
||||
| `PSV-10212` | 10 | `—` | 0.950 |
|
||||
| `PSV-10215` | 10 | `—` | 0.950 |
|
||||
| `PSV-10217` | 10 | `—` | 0.950 |
|
||||
| `PSV-10219A` | 10 | `—` | 0.950 |
|
||||
| `PSV-10219B` | 10 | `—` | 0.950 |
|
||||
| `PSV-10900` | 10 | `—` | 0.950 |
|
||||
| `PSV-10900A` | 10 | `—` | 0.950 |
|
||||
| `PSV-10900B` | 10 | `—` | 0.950 |
|
||||
| `PSV-10901` | 10 | `—` | 0.950 |
|
||||
| `PSV-10902` | 10 | `—` | 0.950 |
|
||||
| `PSV-2900` | 29 | `—` | 0.950 |
|
||||
| `PSV-2901` | 29 | `—` | 0.950 |
|
||||
| `PSV-6203` | 62 | `—` | 0.950 |
|
||||
| `PSV-900A` | 9 | `—` | 0.950 |
|
||||
| `PSV-900B` | 9 | `—` | 0.950 |
|
||||
| `PSV-901` | 9 | `—` | 0.950 |
|
||||
|
||||
### SP — Speed (2개)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `SP-10601` | 10 | `—` | 0.950 |
|
||||
| `SP-10602` | 10 | `—` | 0.950 |
|
||||
|
||||
### TCV — Temperature Control Valve (9개)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `TCV-10111` | 10 | `—` | 0.950 |
|
||||
| `TCV-10211` | 10 | `—` | 0.950 |
|
||||
| `TCV-111` | 1 | `—` | 0.950 |
|
||||
| `TCV-2111` | 21 | `—` | 0.950 |
|
||||
| `TCV-5111` | 51 | `—` | 0.950 |
|
||||
| `TCV-6111` | 61 | `—` | 0.950 |
|
||||
| `TCV-6211` | 62 | `—` | 0.950 |
|
||||
| `TCV-9111` | 91 | `—` | 0.950 |
|
||||
| `TCV-9211` | 92 | `—` | 0.950 |
|
||||
|
||||
### TR — Temperature Recorder (3개)
|
||||
|
||||
| tag_no | area 추정 | Experion 매핑 | confidence |
|
||||
|---|---|---|---|
|
||||
| `TR-10115A` | 10 | `—` | 0.950 |
|
||||
| `TR-10115B` | 10 | `—` | 0.950 |
|
||||
| `TR-10215` | 10 | `—` | 0.950 |
|
||||
|
||||
---
|
||||
|
||||
## 파이프(LineNo) 목록
|
||||
|
||||
총 247개 파이프 라인. 각 라인은 `service-line_no-size-spec-insul` 형식으로 명명됨.
|
||||
|
||||
| tag_no | service (fluid) | line_no | size | material | flange | insul |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `CD-10513-40A-S1A-H50` | CD (STEAM CONDENSATE) | 10513 | 40A | S | 1 | AH50 |
|
||||
| `CD-10514-40A-S1A-H50` | CD (STEAM CONDENSATE) | 10514 | 40A | S | 1 | AH50 |
|
||||
| `CD-10515-65A-S1A-H50` | CD (STEAM CONDENSATE) | 10515 | 65A | S | 1 | AH50 |
|
||||
| `CD-10516-65A-S1A-H50` | CD (STEAM CONDENSATE) | 10516 | 65A | S | 1 | AH50 |
|
||||
| `CD-10522-40A-S1A-H50` | CD (STEAM CONDENSATE) | 10522 | 40A | S | 1 | AH50 |
|
||||
| `CD-10523-40A-S1A-H50` | CD (STEAM CONDENSATE) | 10523 | 40A | S | 1 | AH50 |
|
||||
| `CHR-10641-65A-F1A-C50` | CHR (CHILLED WATER RETURN) | 10641 | 65A | F | 1 | AC50 |
|
||||
| `CHR-10642-50A-F1A-C50` | CHR (CHILLED WATER RETURN) | 10642 | 50A | F | 1 | AC50 |
|
||||
| `CHS-10631-65A-F1A-C50` | CHS (CHILLED WATER SUPPLY) | 10631 | 65A | F | 1 | AC50 |
|
||||
| `CHS-10632-50A-F1A-C50` | CHS (CHILLED WATER SUPPLY) | 10632 | 50A | F | 1 | AC50 |
|
||||
| `CWR-10620-300A-S2A-N` | CWR (COOLING WATER RETURN) | 10620 | 300A | S | 2 | AN |
|
||||
| `CWR-10621-80A-S2A-N` | CWR (COOLING WATER RETURN) | 10621 | 80A | S | 2 | AN |
|
||||
| `CWR-10621-80A-S2A-n` | CWR (COOLING WATER RETURN) | 10621 | 80A | S | 2 | An |
|
||||
| `CWR-10622-200A-S2A-N` | CWR (COOLING WATER RETURN) | 10622 | 200A | S | 2 | AN |
|
||||
| `CWR-10622-200A-S2A-n` | CWR (COOLING WATER RETURN) | 10622 | 200A | S | 2 | An |
|
||||
| `CWR-10623-50A-S2A-N` | CWR (COOLING WATER RETURN) | 10623 | 50A | S | 2 | AN |
|
||||
| `CWR-10623-50A-S2A-n` | CWR (COOLING WATER RETURN) | 10623 | 50A | S | 2 | An |
|
||||
| `CWR-10624-150A-S2A-N` | CWR (COOLING WATER RETURN) | 10624 | 150A | S | 2 | AN |
|
||||
| `CWR-10624-150A-S2A-n` | CWR (COOLING WATER RETURN) | 10624 | 150A | S | 2 | An |
|
||||
| `CWR-10625-50A-F1A-N` | CWR (COOLING WATER RETURN) | 10625 | 50A | F | 1 | AN |
|
||||
| `CWR-10625-50A-S2A-N` | CWR (COOLING WATER RETURN) | 10625 | 50A | S | 2 | AN |
|
||||
| `CWR-10625-50A-S2A-n` | CWR (COOLING WATER RETURN) | 10625 | 50A | S | 2 | An |
|
||||
| `CWR-10626-25A-F1A-N` | CWR (COOLING WATER RETURN) | 10626 | 25A | F | 1 | AN |
|
||||
| `CWR-10626-25A-F1A-n` | CWR (COOLING WATER RETURN) | 10626 | 25A | F | 1 | An |
|
||||
| `CWR-10626-25A-S2A-N` | CWR (COOLING WATER RETURN) | 10626 | 25A | S | 2 | AN |
|
||||
| `CWR-10627-25A-F1A-N` | CWR (COOLING WATER RETURN) | 10627 | 25A | F | 1 | AN |
|
||||
| `CWR-10627-25A-F1A-n` | CWR (COOLING WATER RETURN) | 10627 | 25A | F | 1 | An |
|
||||
| `CWR-10627-25A-S2A-N` | CWR (COOLING WATER RETURN) | 10627 | 25A | S | 2 | AN |
|
||||
| `CWR-10628-15A-F1A-N` | CWR (COOLING WATER RETURN) | 10628 | 15A | F | 1 | AN |
|
||||
| `CWR-10628-25A-S2A-N` | CWR (COOLING WATER RETURN) | 10628 | 25A | S | 2 | AN |
|
||||
| `CWR-10629-15A-F1A-N` | CWR (COOLING WATER RETURN) | 10629 | 15A | F | 1 | AN |
|
||||
| `CWR-10629-25A-S2A-N` | CWR (COOLING WATER RETURN) | 10629 | 25A | S | 2 | AN |
|
||||
| `CWS-10600-300A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10600 | 300A | S | 2 | AN |
|
||||
| `CWS-10601-300A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10601 | 300A | S | 2 | AN |
|
||||
| `CWS-10611-80A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10611 | 80A | S | 2 | AN |
|
||||
| `CWS-10611-80A-S2A-n` | CWS (COOLING WATER SUPPLY) | 10611 | 80A | S | 2 | An |
|
||||
| `CWS-10612-200A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10612 | 200A | S | 2 | AN |
|
||||
| `CWS-10612-200A-S2A-n` | CWS (COOLING WATER SUPPLY) | 10612 | 200A | S | 2 | An |
|
||||
| `CWS-10613-50A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10613 | 50A | S | 2 | AN |
|
||||
| `CWS-10613-50A-S2A-n` | CWS (COOLING WATER SUPPLY) | 10613 | 50A | S | 2 | An |
|
||||
| `CWS-10614-150A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10614 | 150A | S | 2 | AN |
|
||||
| `CWS-10614-150A-S2A-n` | CWS (COOLING WATER SUPPLY) | 10614 | 150A | S | 2 | An |
|
||||
| `CWS-10615-50A-F1A-N` | CWS (COOLING WATER SUPPLY) | 10615 | 50A | F | 1 | AN |
|
||||
| `CWS-10615-50A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10615 | 50A | S | 2 | AN |
|
||||
| `CWS-10615-50A-S2A-n` | CWS (COOLING WATER SUPPLY) | 10615 | 50A | S | 2 | An |
|
||||
| `CWS-10616-25A-F1A-N` | CWS (COOLING WATER SUPPLY) | 10616 | 25A | F | 1 | AN |
|
||||
| `CWS-10616-25A-F1A-n` | CWS (COOLING WATER SUPPLY) | 10616 | 25A | F | 1 | An |
|
||||
| `CWS-10616-25A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10616 | 25A | S | 2 | AN |
|
||||
| `CWS-10617-25A-F1A-N` | CWS (COOLING WATER SUPPLY) | 10617 | 25A | F | 1 | AN |
|
||||
| `CWS-10617-25A-F1A-n` | CWS (COOLING WATER SUPPLY) | 10617 | 25A | F | 1 | An |
|
||||
| `CWS-10617-25A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10617 | 25A | S | 2 | AN |
|
||||
| `CWS-10618-15A-F1A-N` | CWS (COOLING WATER SUPPLY) | 10618 | 15A | F | 1 | AN |
|
||||
| `CWS-10618-25A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10618 | 25A | S | 2 | AN |
|
||||
| `CWS-10619-15A-F1A-N` | CWS (COOLING WATER SUPPLY) | 10619 | 15A | F | 1 | AN |
|
||||
| `CWS-10619-25A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10619 | 25A | S | 2 | AN |
|
||||
| `CWS-10620-300A-S2A-N` | CWS (COOLING WATER SUPPLY) | 10620 | 300A | S | 2 | AN |
|
||||
| `IA-10900-25A-F1A-n` | IA (INSTRUMENT AIR) | 10900 | 25A | F | 1 | An |
|
||||
| `IA-10901-15A-F1A-n` | IA (INSTRUMENT AIR) | 10901 | 15A | F | 1 | An |
|
||||
| `IA-10902-15A-F1A-n` | IA (INSTRUMENT AIR) | 10902 | 15A | F | 1 | An |
|
||||
| `IA-10903-15A-F1A-n` | IA (INSTRUMENT AIR) | 10903 | 15A | F | 1 | An |
|
||||
| `IA-10904-15A-F1A-n` | IA (INSTRUMENT AIR) | 10904 | 15A | F | 1 | An |
|
||||
| `IA-10905-15A-F1A-n` | IA (INSTRUMENT AIR) | 10905 | 15A | F | 1 | An |
|
||||
| `IA-10906-15A-F1A-n` | IA (INSTRUMENT AIR) | 10906 | 15A | F | 1 | An |
|
||||
| `IA-10907-15A-F1A-n` | IA (INSTRUMENT AIR) | 10907 | 15A | F | 1 | An |
|
||||
| `IA-10908-15A-F1A-n` | IA (INSTRUMENT AIR) | 10908 | 15A | F | 1 | An |
|
||||
| `IA-10909-15A-F1A-n` | IA (INSTRUMENT AIR) | 10909 | 15A | F | 1 | An |
|
||||
| `IA-10910-15A-F1A-n` | IA (INSTRUMENT AIR) | 10910 | 15A | F | 1 | An |
|
||||
| `IA-10912-15A-F1A-n` | IA (INSTRUMENT AIR) | 10912 | 15A | F | 1 | An |
|
||||
| `IA-10913-25A-F1A-n` | IA (INSTRUMENT AIR) | 10913 | 25A | F | 1 | An |
|
||||
| `IA-10914-25A-F1A-n` | IA (INSTRUMENT AIR) | 10914 | 25A | F | 1 | An |
|
||||
| `IA-10915-15A-F1A-n` | IA (INSTRUMENT AIR) | 10915 | 15A | F | 1 | An |
|
||||
| `IA-10918-15A-F1A-n` | IA (INSTRUMENT AIR) | 10918 | 15A | F | 1 | An |
|
||||
| `IA-10919-15A-F1A-n` | IA (INSTRUMENT AIR) | 10919 | 15A | F | 1 | An |
|
||||
| `IA-10920-15A-F1A-n` | IA (INSTRUMENT AIR) | 10920 | 15A | F | 1 | An |
|
||||
| `IA-10921-15A-F1A-n` | IA (INSTRUMENT AIR) | 10921 | 15A | F | 1 | An |
|
||||
| `IA-10922-25A-F1A-n` | IA (INSTRUMENT AIR) | 10922 | 25A | F | 1 | An |
|
||||
| `NBD-10100` | NBD (NITROGEN BLOW DOWN) | 10100 | — | — | — | — |
|
||||
| `NBD-10101` | NBD (NITROGEN BLOW DOWN) | 10101 | — | — | — | — |
|
||||
| `NBD-10200` | NBD (NITROGEN BLOW DOWN) | 10200 | — | — | — | — |
|
||||
| `NBD-10201` | NBD (NITROGEN BLOW DOWN) | 10201 | — | — | — | — |
|
||||
| `NBD-10221` | NBD (NITROGEN BLOW DOWN) | 10221 | — | — | — | — |
|
||||
| `P-10101-25A-F1A-n` | P (PROCESS FLUID) | 10101 | 25A | F | 1 | An |
|
||||
| `P-10102-25A-F1A-n` | P (PROCESS FLUID) | 10102 | 25A | F | 1 | An |
|
||||
| `P-10103-25A-F1A-n` | P (PROCESS FLUID) | 10103 | 25A | F | 1 | An |
|
||||
| `P-10104-25A-F1A-n` | P (PROCESS FLUID) | 10104 | 25A | F | 1 | An |
|
||||
| `P-10106-25A-F1A-n` | P (PROCESS FLUID) | 10106 | 25A | F | 1 | An |
|
||||
| `P-10107-40A-F1A-n` | P (PROCESS FLUID) | 10107 | 40A | F | 1 | An |
|
||||
| `P-10109-40A-F1A-n` | P (PROCESS FLUID) | 10109 | 40A | F | 1 | An |
|
||||
| `P-10110-40A-F1A-n` | P (PROCESS FLUID) | 10110 | 40A | F | 1 | An |
|
||||
| `P-10111-25A-F1A-n` | P (PROCESS FLUID) | 10111 | 25A | F | 1 | An |
|
||||
| `P-10112-25A-F1A-n` | P (PROCESS FLUID) | 10112 | 25A | F | 1 | An |
|
||||
| `P-10113-25A-F1A-H50` | P (PROCESS FLUID) | 10113 | 25A | F | 1 | AH50 |
|
||||
| `P-10114-250A-F2A-H50` | P (PROCESS FLUID) | 10114 | 250A | F | 2 | AH50 |
|
||||
| `P-10115-200A-F2A-H50` | P (PROCESS FLUID) | 10115 | 200A | F | 2 | AH50 |
|
||||
| `P-10116-200A-F2A-H50` | P (PROCESS FLUID) | 10116 | 200A | F | 2 | AH50 |
|
||||
| `P-10117-300A-F2A-H50` | P (PROCESS FLUID) | 10117 | 300A | F | 2 | AH50 |
|
||||
| `P-10117-500A-F2A-H50` | P (PROCESS FLUID) | 10117 | 500A | F | 2 | AH50 |
|
||||
| `P-10118-300A-F2A-H50` | P (PROCESS FLUID) | 10118 | 300A | F | 2 | AH50 |
|
||||
| `P-10119-32A-F1A-H50` | P (PROCESS FLUID) | 10119 | 32A | F | 1 | AH50 |
|
||||
| `P-10120-25A-F1A-H50` | P (PROCESS FLUID) | 10120 | 25A | F | 1 | AH50 |
|
||||
| `P-10121-25A-F1A-H50` | P (PROCESS FLUID) | 10121 | 25A | F | 1 | AH50 |
|
||||
| `P-10122-20A-F1A-n` | P (PROCESS FLUID) | 10122 | 20A | F | 1 | An |
|
||||
| `P-10122-25A-F1A-N` | P (PROCESS FLUID) | 10122 | 25A | F | 1 | AN |
|
||||
| `P-10123-20A-F1A-n` | P (PROCESS FLUID) | 10123 | 20A | F | 1 | An |
|
||||
| `P-10124-20A-F1A-n` | P (PROCESS FLUID) | 10124 | 20A | F | 1 | An |
|
||||
| `P-10125-20A-F1A-n` | P (PROCESS FLUID) | 10125 | 20A | F | 1 | An |
|
||||
| `P-10126-20A-F1A-n` | P (PROCESS FLUID) | 10126 | 20A | F | 1 | An |
|
||||
| `P-10127-65A-F2A-n` | P (PROCESS FLUID) | 10127 | 65A | F | 2 | An |
|
||||
| `P-10128-50A-F2A-n` | P (PROCESS FLUID) | 10128 | 50A | F | 2 | An |
|
||||
| `P-10129-25A-F2A-n` | P (PROCESS FLUID) | 10129 | 25A | F | 2 | An |
|
||||
| `P-10130-25A-F2A-n` | P (PROCESS FLUID) | 10130 | 25A | F | 2 | An |
|
||||
| `P-10132-25A-F2A-n` | P (PROCESS FLUID) | 10132 | 25A | F | 2 | An |
|
||||
| `P-10133-25A-F2A-n` | P (PROCESS FLUID) | 10133 | 25A | F | 2 | An |
|
||||
| `P-10134-25A-F2A-n` | P (PROCESS FLUID) | 10134 | 25A | F | 2 | An |
|
||||
| `P-10135-25A-F2A-n` | P (PROCESS FLUID) | 10135 | 25A | F | 2 | An |
|
||||
| `P-10136-25A-F2A-n` | P (PROCESS FLUID) | 10136 | 25A | F | 2 | An |
|
||||
| `P-10137-25A-F1A-n` | P (PROCESS FLUID) | 10137 | 25A | F | 1 | An |
|
||||
| `P-10138-600A-F2A-H100` | P (PROCESS FLUID) | 10138 | 600A | F | 2 | AH100 |
|
||||
| `P-10139-600A-F2A-n` | P (PROCESS FLUID) | 10139 | 600A | F | 2 | An |
|
||||
| `P-10140-40A-F2A-n` | P (PROCESS FLUID) | 10140 | 40A | F | 2 | An |
|
||||
| `P-10141-32A-F2A-n` | P (PROCESS FLUID) | 10141 | 32A | F | 2 | An |
|
||||
| `P-10142-25A-F2A-n` | P (PROCESS FLUID) | 10142 | 25A | F | 2 | An |
|
||||
| `P-10143-32A-F2A-n` | P (PROCESS FLUID) | 10143 | 32A | F | 2 | An |
|
||||
| `P-10144-25A-F1A-n` | P (PROCESS FLUID) | 10144 | 25A | F | 1 | An |
|
||||
| `P-10146-20A-F1A-n` | P (PROCESS FLUID) | 10146 | 20A | F | 1 | An |
|
||||
| `P-10147-25A-F1A-n` | P (PROCESS FLUID) | 10147 | 25A | F | 1 | An |
|
||||
| `P-10148-25A-F1A-n` | P (PROCESS FLUID) | 10148 | 25A | F | 1 | An |
|
||||
| `P-10149-40A-F1A-n` | P (PROCESS FLUID) | 10149 | 40A | F | 1 | An |
|
||||
| `P-10150-25A-F1A-n` | P (PROCESS FLUID) | 10150 | 25A | F | 1 | An |
|
||||
| `P-10151-15A-F1A-n` | P (PROCESS FLUID) | 10151 | 15A | F | 1 | An |
|
||||
| `P-10201-25A-F1A-n` | P (PROCESS FLUID) | 10201 | 25A | F | 1 | An |
|
||||
| `P-10202-40A-F1A-n` | P (PROCESS FLUID) | 10202 | 40A | F | 1 | An |
|
||||
| `P-10203-40A-F1A-n` | P (PROCESS FLUID) | 10203 | 40A | F | 1 | An |
|
||||
| `P-10204-25A-F1A-n` | P (PROCESS FLUID) | 10204 | 25A | F | 1 | An |
|
||||
| `P-10205-25A-F1A-n` | P (PROCESS FLUID) | 10205 | 25A | F | 1 | An |
|
||||
| `P-10207-25A-F1A-H50` | P (PROCESS FLUID) | 10207 | 25A | F | 1 | AH50 |
|
||||
| `P-10208-400A-F2A-H50` | P (PROCESS FLUID) | 10208 | 400A | F | 2 | AH50 |
|
||||
| `P-10209-200A-F2A-H50` | P (PROCESS FLUID) | 10209 | 200A | F | 2 | AH50 |
|
||||
| `P-10210-25A-F1A-H50` | P (PROCESS FLUID) | 10210 | 25A | F | 1 | AH50 |
|
||||
| `P-10211-20A-F1A-H50` | P (PROCESS FLUID) | 10211 | 20A | F | 1 | AH50 |
|
||||
| `P-10212-20A-F1A-H50` | P (PROCESS FLUID) | 10212 | 20A | F | 1 | AH50 |
|
||||
| `P-10213-20A-F1A-n` | P (PROCESS FLUID) | 10213 | 20A | F | 1 | An |
|
||||
| `P-10214-20A-F1A-n` | P (PROCESS FLUID) | 10214 | 20A | F | 1 | An |
|
||||
| `P-10215-20A-F1A-n` | P (PROCESS FLUID) | 10215 | 20A | F | 1 | An |
|
||||
| `P-10216-20A-F1A-n` | P (PROCESS FLUID) | 10216 | 20A | F | 1 | An |
|
||||
| `P-10217-65A-F2A-n` | P (PROCESS FLUID) | 10217 | 65A | F | 2 | An |
|
||||
| `P-10218-40A-F2A-n` | P (PROCESS FLUID) | 10218 | 40A | F | 2 | An |
|
||||
| `P-10219-25A-F2A-n` | P (PROCESS FLUID) | 10219 | 25A | F | 2 | An |
|
||||
| `P-10220-25A-F1A-n` | P (PROCESS FLUID) | 10220 | 25A | F | 1 | An |
|
||||
| `P-10221-25A-F2A-n` | P (PROCESS FLUID) | 10221 | 25A | F | 2 | An |
|
||||
| `P-10222-25A-F2A-n` | P (PROCESS FLUID) | 10222 | 25A | F | 2 | An |
|
||||
| `P-10223-25A-F2A-n` | P (PROCESS FLUID) | 10223 | 25A | F | 2 | An |
|
||||
| `P-10224-40A-F2A-n` | P (PROCESS FLUID) | 10224 | 40A | F | 2 | An |
|
||||
| `P-10225-25A-F2A-n` | P (PROCESS FLUID) | 10225 | 25A | F | 2 | An |
|
||||
| `P-10226-25A-F2A-n` | P (PROCESS FLUID) | 10226 | 25A | F | 2 | An |
|
||||
| `P-10227-25A-F2A-n` | P (PROCESS FLUID) | 10227 | 25A | F | 2 | An |
|
||||
| `P-10228-500A-F2A-H100` | P (PROCESS FLUID) | 10228 | 500A | F | 2 | AH100 |
|
||||
| `P-10228-500A-F2A-n` | P (PROCESS FLUID) | 10228 | 500A | F | 2 | An |
|
||||
| `P-10229-40A-F2A-n` | P (PROCESS FLUID) | 10229 | 40A | F | 2 | An |
|
||||
| `P-10230-25A-F2A-n` | P (PROCESS FLUID) | 10230 | 25A | F | 2 | An |
|
||||
| `P-10231-15A-F2A-n` | P (PROCESS FLUID) | 10231 | 15A | F | 2 | An |
|
||||
| `P-10232-25A-F1A-n` | P (PROCESS FLUID) | 10232 | 25A | F | 1 | An |
|
||||
| `P-10233-25A-F2A-n` | P (PROCESS FLUID) | 10233 | 25A | F | 2 | An |
|
||||
| `P-10234-15A-F1A-n` | P (PROCESS FLUID) | 10234 | 15A | F | 1 | An |
|
||||
| `P-10235-15A-F1A-n` | P (PROCESS FLUID) | 10235 | 15A | F | 1 | An |
|
||||
| `P-10236-40A-F1A-n` | P (PROCESS FLUID) | 10236 | 40A | F | 1 | An |
|
||||
| `P-10237-40A-F1A-n` | P (PROCESS FLUID) | 10237 | 40A | F | 1 | An |
|
||||
| `P-10311-125A-F1A-n` | P (PROCESS FLUID) | 10311 | 125A | F | 1 | An |
|
||||
| `P-10312-100A-F1A-n` | P (PROCESS FLUID) | 10312 | 100A | F | 1 | An |
|
||||
| `P-10313-100A-F1A-n` | P (PROCESS FLUID) | 10313 | 100A | F | 1 | An |
|
||||
| `P-10314-80A-F1A-n` | P (PROCESS FLUID) | 10314 | 80A | F | 1 | An |
|
||||
| `PW-10903-25A-F1A-E40` | PW (PW) | 10903 | 25A | F | 1 | AE40 |
|
||||
| `SAM-10951-10A-F2A-n` | SAM (SAMPLE LINE) | 10951 | 10A | F | 2 | An |
|
||||
| `SAM-10952-10A-F2A-n` | SAM (SAMPLE LINE) | 10952 | 10A | F | 2 | An |
|
||||
| `SAM-10953-10A-F2A-n` | SAM (SAMPLE LINE) | 10953 | 10A | F | 2 | An |
|
||||
| `SAM-10954-10A-F2A-n` | SAM (SAMPLE LINE) | 10954 | 10A | F | 2 | An |
|
||||
| `SAM-10955-10A-F2A-n` | SAM (SAMPLE LINE) | 10955 | 10A | F | 2 | An |
|
||||
| `SAM-10956-10A-F2A-n` | SAM (SAMPLE LINE) | 10956 | 10A | F | 2 | An |
|
||||
| `SAM-10957-10A-F2A-n` | SAM (SAMPLE LINE) | 10957 | 10A | F | 2 | An |
|
||||
| `SAM-10958-10A-F2A-n` | SAM (SAMPLE LINE) | 10958 | 10A | F | 2 | An |
|
||||
| `SAM-9954-10A-F2A-n` | SAM (SAMPLE LINE) | 9954 | 10A | F | 2 | An |
|
||||
| `SC-10128` | SC (VENT GAS) | 10128 | — | — | — | — |
|
||||
| `SC-9128` | SC (VENT GAS) | 9128 | — | — | — | — |
|
||||
| `ST-10511-100A-S1A-H50` | ST (STEAM) | 10511 | 100A | S | 1 | AH50 |
|
||||
| `ST-10512-100A-S1A-H50` | ST (STEAM) | 10512 | 100A | S | 1 | AH50 |
|
||||
| `ST-10521-65A-S1A-H50` | ST (STEAM) | 10521 | 65A | S | 1 | AH50 |
|
||||
| `SW-10801-50A-F1A-E50` | SW (SOFT WATER) | 10801 | 50A | F | 1 | AE50 |
|
||||
| `SW-10802-25A-F1A-E50` | SW (SOFT WATER) | 10802 | 25A | F | 1 | AE50 |
|
||||
| `SW-10803-20A-F1A-E50` | SW (SOFT WATER) | 10803 | 20A | F | 1 | AE50 |
|
||||
| `SW-10804-25A-F1A-E50` | SW (SOFT WATER) | 10804 | 25A | F | 1 | AE50 |
|
||||
| `SW-10805-25A-F1A-E50` | SW (SOFT WATER) | 10805 | 25A | F | 1 | AE50 |
|
||||
| `SW-10806-25A-F1A-E50` | SW (SOFT WATER) | 10806 | 25A | F | 1 | AE50 |
|
||||
| `SW-10807-25A-F1A-E50` | SW (SOFT WATER) | 10807 | 25A | F | 1 | AE50 |
|
||||
| `SW-10808-25A-F1A-E50` | SW (SOFT WATER) | 10808 | 25A | F | 1 | AE50 |
|
||||
| `SW-10809-15A-F1A-E50` | SW (SOFT WATER) | 10809 | 15A | F | 1 | AE50 |
|
||||
| `SW-10810-25A-F1A-E50` | SW (SOFT WATER) | 10810 | 25A | F | 1 | AE50 |
|
||||
| `SW-10821-50A-F1A-E50` | SW (SOFT WATER) | 10821 | 50A | F | 1 | AE50 |
|
||||
| `SW-10822-32A-F1A-E50` | SW (SOFT WATER) | 10822 | 32A | F | 1 | AE50 |
|
||||
| `VG-10401-150A-F1A-N` | VG (VENT GAS) | 10401 | 150A | F | 1 | AN |
|
||||
| `VG-10411-65A-F1A-N` | VG (VENT GAS) | 10411 | 65A | F | 1 | AN |
|
||||
| `VG-10411-65A-F1A-n` | VG (VENT GAS) | 10411 | 65A | F | 1 | An |
|
||||
| `VG-10412-50A-F1A-N` | VG (VENT GAS) | 10412 | 50A | F | 1 | AN |
|
||||
| `VG-10412-50A-F1A-n` | VG (VENT GAS) | 10412 | 50A | F | 1 | An |
|
||||
| `VG-10421-50A-F1A-N` | VG (VENT GAS) | 10421 | 50A | F | 1 | AN |
|
||||
| `VG-10421-50A-F1A-n` | VG (VENT GAS) | 10421 | 50A | F | 1 | An |
|
||||
| `VG-10422-50A-F1A-N` | VG (VENT GAS) | 10422 | 50A | F | 1 | AN |
|
||||
| `VG-10422-50A-F1A-n` | VG (VENT GAS) | 10422 | 50A | F | 1 | An |
|
||||
| `VG-10423-50A-F1A-N` | VG (VENT GAS) | 10423 | 50A | F | 1 | AN |
|
||||
| `VG-10424-50A-F1A-N` | VG (VENT GAS) | 10424 | 50A | F | 1 | AN |
|
||||
| `VG-10425-50A-F1A-N` | VG (VENT GAS) | 10425 | 50A | F | 1 | AN |
|
||||
| `VG-10426-50A-F1A-N` | VG (VENT GAS) | 10426 | 50A | F | 1 | AN |
|
||||
| `VG-10431-50A-F1A-N` | VG (VENT GAS) | 10431 | 50A | F | 1 | AN |
|
||||
| `VG-10431-50A-F1A-n` | VG (VENT GAS) | 10431 | 50A | F | 1 | An |
|
||||
| `VG-10432-50A-F1A-N` | VG (VENT GAS) | 10432 | 50A | F | 1 | AN |
|
||||
| `VG-10432-50A-F1A-n` | VG (VENT GAS) | 10432 | 50A | F | 1 | An |
|
||||
| `VG-10433-50A-F1A-N` | VG (VENT GAS) | 10433 | 50A | F | 1 | AN |
|
||||
| `VG-10433-50A-F1A-n` | VG (VENT GAS) | 10433 | 50A | F | 1 | An |
|
||||
| `VG-10440-300A-F1A-N` | VG (VENT GAS) | 10440 | 300A | F | 1 | AN |
|
||||
| `VG-10441-125A-F1A-N` | VG (VENT GAS) | 10441 | 125A | F | 1 | AN |
|
||||
| `VG-10441-200A-F1A-N` | VG (VENT GAS) | 10441 | 200A | F | 1 | AN |
|
||||
| `VG-10441-200A-F1A-n` | VG (VENT GAS) | 10441 | 200A | F | 1 | An |
|
||||
| `VG-10442-100A-F1A-N` | VG (VENT GAS) | 10442 | 100A | F | 1 | AN |
|
||||
| `VG-10442-150A-F1A-N` | VG (VENT GAS) | 10442 | 150A | F | 1 | AN |
|
||||
| `VG-10443-25A-F1A-N` | VG (VENT GAS) | 10443 | 25A | F | 1 | AN |
|
||||
| `VG-10443-25A-F1A-n` | VG (VENT GAS) | 10443 | 25A | F | 1 | An |
|
||||
| `VG-10444-25A-F1A-N` | VG (VENT GAS) | 10444 | 25A | F | 1 | AN |
|
||||
| `VG-10444-25A-F1A-n` | VG (VENT GAS) | 10444 | 25A | F | 1 | An |
|
||||
| `VG-6203-15A-F1A-n` | VG (VENT GAS) | 6203 | 15A | F | 1 | An |
|
||||
| `VG-9400-150A-F1A-N` | VG (VENT GAS) | 9400 | 150A | F | 1 | AN |
|
||||
| `VG-9411-50A-F1A-N` | VG (VENT GAS) | 9411 | 50A | F | 1 | AN |
|
||||
| `VG-9412-50A-F1A-N` | VG (VENT GAS) | 9412 | 50A | F | 1 | AN |
|
||||
| `VG-9421-50A-F1A-N` | VG (VENT GAS) | 9421 | 50A | F | 1 | AN |
|
||||
| `VG-9423-50A-F1A-N` | VG (VENT GAS) | 9423 | 50A | F | 1 | AN |
|
||||
| `VG-9424-50A-F1A-N` | VG (VENT GAS) | 9424 | 50A | F | 1 | AN |
|
||||
| `VG-9425-50A-F1A-N` | VG (VENT GAS) | 9425 | 50A | F | 1 | AN |
|
||||
| `VG-9426-50A-F1A-N` | VG (VENT GAS) | 9426 | 50A | F | 1 | AN |
|
||||
| `VG-9431-50A-F1A-N` | VG (VENT GAS) | 9431 | 50A | F | 1 | AN |
|
||||
| `VG-9432-50A-F1A-N` | VG (VENT GAS) | 9432 | 50A | F | 1 | AN |
|
||||
| `VG-9433-50A-F1A-N` | VG (VENT GAS) | 9433 | 50A | F | 1 | AN |
|
||||
| `VG-9434-50A-F1A-N` | VG (VENT GAS) | 9434 | 50A | F | 1 | AN |
|
||||
| `VG-9440-300A-F1A-N` | VG (VENT GAS) | 9440 | 300A | F | 1 | AN |
|
||||
| `VG-9441-125A-F1A-N` | VG (VENT GAS) | 9441 | 125A | F | 1 | AN |
|
||||
| `VG-9441-150A-F1A-n` | VG (VENT GAS) | 9441 | 150A | F | 1 | An |
|
||||
| `VG-9442-65A-F1A-N` | VG (VENT GAS) | 9442 | 65A | F | 1 | AN |
|
||||
| `WW-10191-25A-F1A-E50` | WW (WASTE WATER) | 10191 | 25A | F | 1 | AE50 |
|
||||
| `WW-10193-25A-F1A-N` | WW (WASTE WATER) | 10193 | 25A | F | 1 | AN |
|
||||
| `WW-9193-25A-F1A-N` | WW (WASTE WATER) | 9193 | 25A | F | 1 | AN |
|
||||
|
||||
---
|
||||
|
||||
## 미분류 항목 (25건)
|
||||
|
||||
| tag_no | instrument_type | line_number | confidence |
|
||||
|---|---|---|---|
|
||||
| `BV-10100` | BV | — | 0.500 |
|
||||
| `BV-10101` | BV | — | 0.500 |
|
||||
| `BV-10200` | BV | — | 0.500 |
|
||||
| `BV-10201` | BV | — | 0.500 |
|
||||
| `BV-10221` | BV | — | 0.500 |
|
||||
| `KA-10901` | KA | — | 0.500 |
|
||||
| `KD-10901` | KD | — | 0.500 |
|
||||
| `KF-10901A` | KF | — | 0.500 |
|
||||
| `KF-10901B` | KF | — | 0.500 |
|
||||
| `UFD-9005` | UFD | — | 0.500 |
|
||||
| `XV-10111` | XV | — | 0.500 |
|
||||
| `XV-10211` | XV | — | 0.500 |
|
||||
| `XV-2136` | XV | — | 0.500 |
|
||||
| `XV-3208` | XV | — | 0.500 |
|
||||
| `XV-3402` | XV | — | 0.500 |
|
||||
| `XV-3470` | XV | — | 0.500 |
|
||||
| `XV-5320` | XV | — | 0.500 |
|
||||
| `XV-6120` | XV | — | 0.500 |
|
||||
| `XV-6121` | XV | — | 0.500 |
|
||||
| `XV-6122` | XV | — | 0.500 |
|
||||
| `XV-6125` | XV | — | 0.500 |
|
||||
| `XV-6126` | XV | — | 0.500 |
|
||||
| `XV-6220` | XV | — | 0.500 |
|
||||
| `XV-705` | XV | — | 0.500 |
|
||||
| `XV-9125` | XV | — | 0.500 |
|
||||
145
plans/결정보류2건-사용자액션.md
Normal file
145
plans/결정보류2건-사용자액션.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# 결정 보류 2건 — 사용자 액션 가이드
|
||||
|
||||
> 작성일: 2026-05-14
|
||||
> 대상: `plans/빅피클-잔여작업-코딩계획.md` 7번(현장 재고), 8번(BGE-M3)
|
||||
> 상태: 코드 작업 불필요. **사용자 의사 결정과 운영 액션이 필요**.
|
||||
|
||||
---
|
||||
|
||||
## 1. 현장 재고 데이터 출처 결정
|
||||
|
||||
### 현황
|
||||
Phase 0 설계서(G3)에서 "현장 재고 데이터 자체가 시스템에 없음"으로 식별됨.
|
||||
운전원이 "P-6201 펌프 예비품 재고", "교체용 PT100 센서 수량" 같은 질문을 했을 때
|
||||
참조할 데이터 소스가 없어 답변 불가.
|
||||
|
||||
### 결정해야 할 것
|
||||
|
||||
| 옵션 | 설명 | 사용자 액션 |
|
||||
|------|------|------------|
|
||||
| **A. KB 업로드 (권장)** | 엑셀/CSV/PDF 재고 대장을 `plant_operation` 또는 `report` 컬렉션에 업로드 → 즉시 RAG 검색 가능 | 1) 재고 대장 파일 확보 (관리부/구매팀 협조)<br>2) 컬럼 구조 정리 (품목·규격·수량·위치·재발주점)<br>3) 14번 탭 "RAG 관리" → 업로드 |
|
||||
| **B. ERP/MES API 연동** | 외부 시스템에서 실시간 재고 조회 | 1) ERP 운영팀과 API 협의<br>2) 인증/네트워크 방화벽 정책<br>3) 별도 개발 필요 (Phase 8) |
|
||||
| **C. 수동 입력 테이블** | PostgreSQL에 `inventory_table` 신설, 관리자 UI에서 직접 입력 | 1) 누가 어떻게 최신화할지 거버넌스 결정<br>2) 별도 개발 필요 |
|
||||
| **D. 보류 유지** | 운전원 질문 시 "재고 정보는 별도 시스템 참조" 안내 | 운전원 교육 |
|
||||
|
||||
### 권장: 옵션 A
|
||||
|
||||
**근거**:
|
||||
- 현재 KB 시스템(Qdrant 5종 컬렉션)이 이미 운영 중
|
||||
- xlsx/pdf 자동 파싱 + 임베딩 + 한국어 검색 모두 동작
|
||||
- 코드 추가 0줄, 즉시 시도 가능
|
||||
- 부족하다면 그때 B 또는 C로 확장
|
||||
|
||||
### 사용자가 해야 할 일 (옵션 A 채택 시)
|
||||
|
||||
1. **재고 대장 파일 확보**
|
||||
- [ ] 관리부/구매팀에서 최신 재고 엑셀 입수
|
||||
- [ ] 민감정보(가격·공급사 단가 등) 제외 또는 가공 결정
|
||||
2. **업로드**
|
||||
- [ ] 앱 접속 → 14번 "RAG 관리" 탭
|
||||
- [ ] 관리자 비밀번호 로그인
|
||||
- [ ] 컬렉션 = `plant_operation` (또는 신규 `inventory` 컬렉션을 원하면 DB 시드 확장 요청)
|
||||
- [ ] 태그 = `inventory`, `예비품` 등 일관된 태그 부착
|
||||
3. **검증**
|
||||
- [ ] 채팅(#13)에서 "PT100 센서 재고 알려줘" 같은 질문으로 검색 결과 확인
|
||||
- [ ] 결과가 부정확하면 청크 미리보기(🔍 버튼)로 인덱싱 품질 확인
|
||||
4. **운영 룰**
|
||||
- [ ] 월 1회 or 분기 1회 최신 파일 재업로드 (이전 버전은 "🚫 비활성화" 후 "🗑 정리")
|
||||
- [ ] 갱신 주체 1명 지정
|
||||
|
||||
### 사용자가 해야 할 일 (옵션 B/C로 갈 경우)
|
||||
**이 문서 범위 밖**. 별도 요건 정의서가 필요. 본 항목은 옵션 결정만 요청.
|
||||
|
||||
---
|
||||
|
||||
## 2. 임베딩 모델 BGE-M3 마이그레이션 검토
|
||||
|
||||
### 현황
|
||||
- 현재 사용 중: **`nomic-embed-text`** (768-dim, Ollama 호스트)
|
||||
- 후보: **`bge-m3`** (1024-dim, 다국어 SOTA, 특히 한국어 성능 우수)
|
||||
- 5개 Qdrant 컬렉션이 768-dim으로 생성된 상태
|
||||
|
||||
### 결정해야 할 것
|
||||
|
||||
전환 시 **이득**과 **비용**을 비교 후 GO/NO-GO/POSTPONE 결정.
|
||||
|
||||
| 이득 | 비용/위험 |
|
||||
|------|----------|
|
||||
| 한국어 검색 품질 향상 (대시기관 평가 기준 +5~15%) | Qdrant 컬렉션 5개 전부 재생성 → 기존 인덱스 일시 소실 |
|
||||
| 멀티링구얼(영문 매뉴얼+한국어 SOP 혼합)에 강함 | 1024-dim → Qdrant 디스크 사용량 +33% |
|
||||
| Dense + Sparse + ColBERT 통합 모델 | 임베딩 속도 ~30% 느려짐 (GPU 점유율 ↑) |
|
||||
| 청크 크기 8K 토큰까지 지원 (nomic은 2K) | Ollama가 bge-m3 미지원 시 다른 호스트 필요 (HF transformers 등) |
|
||||
|
||||
### 마이그레이션 절차 (실행 시)
|
||||
|
||||
1. **사전 평가** ⚠️ (사용자 액션)
|
||||
- [ ] BGE-M3가 Ollama Library에 있는지 확인 (`ollama pull bge-m3`)
|
||||
- [ ] 없으면 대안 결정: HF transformers, sentence-transformers 또는 별도 임베딩 서버
|
||||
- [ ] 현재 KB 검색 품질에 실질 문제가 있는지 — 운전원 피드백 수집 (1~2주)
|
||||
2. **샘플 비교 (선택)** (사용자 액션)
|
||||
- [ ] 동일 한국어 쿼리 10건을 두 모델로 비교
|
||||
- [ ] 한국어 정밀도에서 명백한 개선이 보일 때만 GO
|
||||
3. **백업** (사용자 액션)
|
||||
- [ ] `storage/kb/` 전체 백업
|
||||
- [ ] `pg_dump` 로 `kb_*` 테이블 백업
|
||||
4. **코드 작업** (개발자 액션 — 본 문서 범위 아님)
|
||||
- `appsettings.json` 에 `Kb:EmbeddingModel`, `Kb:VectorSize` 설정 추가
|
||||
- `KbEmbeddingClient.cs` 에서 모델명/차원 환경변수 참조
|
||||
- `KbStartupService.cs` 에서 컬렉션 차원 mismatch 감지 시 재생성
|
||||
5. **컷오버** (사용자 액션)
|
||||
- [ ] 운영 외 시간대 선택 (KB 검색 일시 중단)
|
||||
- [ ] Qdrant 컬렉션 5개 삭제 → 새 차원으로 재생성
|
||||
- [ ] 모든 KB 문서 일괄 `재인덱싱(↻)` 트리거
|
||||
- [ ] 완료까지 모니터링 (문서 수 × 청크 수 × 임베딩 시간)
|
||||
|
||||
### 권장: POSTPONE (당분간 보류)
|
||||
|
||||
**근거**:
|
||||
- 현재 nomic-embed-text 검색 품질이 운영에 임계 미달이라는 정량 증거가 없음
|
||||
- BGE-M3 전환은 비가역적 비용(재인덱싱 시간, 디스크 +33%)이 큰 결정
|
||||
- Phase 7 신규 기능(채팅 통합·청크 미리보기·요약 등)이 운영에서 안정화된 후
|
||||
사용자 피드백으로 검색 품질 이슈가 누적되면 그때 검토
|
||||
|
||||
### 사용자가 해야 할 일
|
||||
|
||||
- [ ] 1~2개월간 운영하면서 **KB 검색 결과가 부정확했던 사례를 메모**
|
||||
- 질문 / 기대 답변 / 실제 검색된 문서 / 점수
|
||||
- 메모 위치: 별도 텍스트 파일 또는 본 문서에 추가
|
||||
- [ ] 사례가 **5건 이상 누적**되고 한국어 매칭 실패가 패턴화되면 마이그레이션 재검토 회의
|
||||
- [ ] 회의 시점에 다시 본 문서를 갱신 (GO/NO-GO 결정 기록)
|
||||
|
||||
### 즉시 가능한 대안 (개발자 액션 불필요)
|
||||
|
||||
검색 품질이 부족할 때 BGE-M3 전환 없이 시도해 볼 수 있는 것:
|
||||
|
||||
| 시도 | 방법 |
|
||||
|------|------|
|
||||
| 더 좋은 청킹 | 업로드 시 `chunking_policy` 옵션 조정 (현재는 자동) |
|
||||
| 태그 보강 | KB 업로드 시 태그를 풍부하게 부착 → `search_kb`의 tags 필터로 정확도 ↑ |
|
||||
| 컬렉션 분리 | doc_type별 5개 컬렉션을 더 세분화 |
|
||||
| `top_k` 조정 | 채팅에서 검색 결과 수를 늘려 LLM이 더 많은 단서로 답하게 |
|
||||
|
||||
---
|
||||
|
||||
## 체크리스트 요약 (사용자가 해야 할 일)
|
||||
|
||||
### 단기 (이번 주 ~ 2주 내)
|
||||
- [ ] **재고 데이터**: 옵션 A(KB 업로드) 채택 여부 결정 → 채택 시 관리부에 파일 요청
|
||||
- [ ] **BGE-M3**: 마이그레이션은 보류, 검색 품질 모니터링 시작
|
||||
|
||||
### 중기 (1~3개월)
|
||||
- [ ] 재고 KB 파일 1차 업로드 + 검증
|
||||
- [ ] KB 검색 부정확 사례 누적 (5건 임계)
|
||||
|
||||
### 장기 (3개월+)
|
||||
- [ ] BGE-M3 마이그레이션 재검토 회의 (사례 누적 시)
|
||||
- [ ] 재고 데이터를 ERP 연동으로 격상할지 검토 (옵션 A로 한계 도달 시)
|
||||
|
||||
---
|
||||
|
||||
## 본 문서 갱신 규칙
|
||||
|
||||
이 문서는 결정이 진행됨에 따라 사용자가 직접 갱신합니다.
|
||||
- 결정 완료 시: 해당 섹션에 "결정 결과", "결정자", "결정일" 추가
|
||||
- 보류 유지: "재검토 예정일" 추가
|
||||
- 옵션 변경: 새 옵션 행 추가 + 이전 결정 사유 보존
|
||||
14
plans/막쓰는노트.md
Normal file
14
plans/막쓰는노트.md
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
qwen 역할
|
||||
1) todo.md 읽고,
|
||||
2) diagnosis-checklist.md 규칙에 따라서 스텝별 실행계획 만들기
|
||||
3) 스텝 미션을 gemma4 에게 전달
|
||||
5) 끝났다고 보고 받으면, 검증 시작
|
||||
6) 검증완료면 다음 스텝 미션 전달
|
||||
|
||||
gemma4는
|
||||
1) 스텝 미션 받아서,
|
||||
2) 코딩하고,
|
||||
3) diagnosis-checklist.md에 따라서 자기 검증
|
||||
4) 문제있으면 loop 돌림
|
||||
5) 끝나면 끝났다고 보고
|
||||
301
plans/배포테스트-qwen2.5-모델전환.md
Normal file
301
plans/배포테스트-qwen2.5-모델전환.md
Normal file
@@ -0,0 +1,301 @@
|
||||
# 배포 테스트 — Qwen2.5-7B-Instruct-FP8 모델 전환 검토
|
||||
|
||||
**날짜**: 2026-05-15
|
||||
**환경**: NVIDIA GB10 (DGX Spark) / 통합 메모리 121 GiB / vLLM in Docker `vllm_node`
|
||||
**결론**: **7B 운영 부적합. 27B 유지 결정.**
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
- 동기: 27B Qwen3.6-FP8이 48 tok/s로 느리게 느껴져, 더 가벼운 7B FP8로 교체 시 속도/메모리 이득을 볼 수 있는지 검토.
|
||||
- 실측: 7B FP8(`RedHatAI/Qwen2.5-7B-Instruct-FP8-dynamic`)이 29.4 tok/s로 오히려 **느림** (27B는 `qwen3_next_mtp` speculative decoding 효과).
|
||||
- 결정타: 7B는 **도구 호출을 회피하고 가짜 데이터를 만들어냄** — 운전원 안전 위험. 도구 토글 ON·에이전트 모드 ON에도 동작 안 함.
|
||||
- 조치: 27B로 즉시 복귀. 검증 중 만든 시스템 프롬프트 강화는 그대로 유지(27B에도 도움).
|
||||
|
||||
---
|
||||
|
||||
## 1. 환경 정보
|
||||
|
||||
### 하드웨어
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| GPU/CPU | NVIDIA GB10 (Grace Blackwell, unified memory) |
|
||||
| 통합 메모리 | 121 GiB (시스템·GPU 공유) |
|
||||
| Swap | 15 GiB |
|
||||
|
||||
### 27B 운영 구성 (baseline)
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| 모델 | `Qwen/Qwen3.6-27B-FP8` |
|
||||
| 메모리 점유 | ~97 GiB |
|
||||
| `max-model-len` | 262,144 (256K) |
|
||||
| `gpu-memory-utilization` | 0.80 |
|
||||
| `speculative-config` | `qwen3_next_mtp`, 2 tokens |
|
||||
| `tool-call-parser` | `qwen3_coder` |
|
||||
| 측정 속도 | **48 tok/s** |
|
||||
| GB10 튜닝 mod | `mods/vllm-tune-qwen--qwen3.6-27b-fp8-tp1` 적용 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 7B FP8 띄우기 레시피 (재현용)
|
||||
|
||||
### 모델 선택
|
||||
HF에서 `neuralmagic/Qwen2.5-7B-Instruct-FP8`은 **존재하지 않음** (404). neuralmagic이 RedHatAI로 흡수됨.
|
||||
|
||||
올바른 repo:
|
||||
- ✅ `RedHatAI/Qwen2.5-7B-Instruct-FP8-dynamic` (다운로드 2378회, 공식 후속)
|
||||
- 그 외: `CalamitousFelicitousness/...`, `llmcompressor-quants/...` 등 community 변형
|
||||
|
||||
### 27B 종료 + 7B 기동 절차
|
||||
|
||||
```bash
|
||||
# 1. 현재 27B vllm 프로세스만 종료 (컨테이너는 sleep infinity로 유지)
|
||||
docker exec vllm_node pkill -f "vllm serve"
|
||||
sleep 5
|
||||
|
||||
# 2. 7B FP8 기동 (launch-cluster.sh exec 패턴)
|
||||
cd /home/windpacer/ai-models/spark-vllm-docker
|
||||
./launch-cluster.sh -t vllm-node-tf5 --solo -d \
|
||||
exec vllm serve RedHatAI/Qwen2.5-7B-Instruct-FP8-dynamic \
|
||||
--served-model-name Qwen2.5-7B-Instruct-FP8 \
|
||||
--max-model-len 32768 \
|
||||
--max-num-seqs 4 \
|
||||
--enable-prefix-caching \
|
||||
--gpu-memory-utilization 0.30 \
|
||||
--port 8000 --host 0.0.0.0 \
|
||||
--enable-chunked-prefill \
|
||||
--enable-auto-tool-choice \
|
||||
--tool-call-parser hermes \
|
||||
--trust-remote-code \
|
||||
-tp 1
|
||||
```
|
||||
|
||||
### 27B 명령에서 제거한 인자 (Qwen3 전용)
|
||||
|
||||
| 인자 | 이유 |
|
||||
|---|---|
|
||||
| `--apply-mod mods/vllm-tune-qwen--qwen3.6-27b-fp8-tp1` | Qwen3.6-27B 전용 GEMM kernel config |
|
||||
| `--load-format instanttensor` | Qwen3 instant tensor 최적화 |
|
||||
| `--reasoning-parser qwen3` | Qwen3 reasoning trace (Qwen2.5 미지원) |
|
||||
| `--default-chat-template-kwargs '{"preserve_thinking":true}'` | Qwen3 thinking block |
|
||||
| `--speculative-config '{"method":"qwen3_next_mtp",...}'` | Qwen3 MTP — Qwen2.5에 적용 불가 |
|
||||
| `--tool-call-parser qwen3_coder` | → `hermes`로 교체 (Qwen2.5는 hermes 포맷) |
|
||||
| `--max-model-len 262144` | → 32768로 축소 (KV 캐시 절약) |
|
||||
| `--gpu-memory-utilization 0.80` | → 0.30 (7B는 메모리 적게 필요) |
|
||||
| `--max-num-batched-tokens 32768` | 32K로 충분 |
|
||||
| `--override-generation-config` | 7B 기본값 사용 |
|
||||
|
||||
### 동기화 작업
|
||||
|
||||
```bash
|
||||
# vllm_model 표시명을 UI에 맞게 변경
|
||||
echo '{"vllm_model":"Qwen2.5-7B-Instruct-FP8"}' > mcp-server/llm-model.json
|
||||
```
|
||||
|
||||
`appsettings.json`의 vLLM endpoint, `OllamaController.cs`, `KbEmbeddingClient.cs` 등은 **변경 불필요** — 포트 8000 동일.
|
||||
|
||||
### 기동 시간
|
||||
- 가중치 다운로드: 152초 (~8 GiB, 첫 회만)
|
||||
- 가중치 로드: 45초
|
||||
- torch.compile 캐시: ~30초
|
||||
- **합계 약 4분** (캐시 후 재기동은 ~75초)
|
||||
|
||||
---
|
||||
|
||||
## 3. 검증 결과
|
||||
|
||||
### 3.1 메모리 점유
|
||||
|
||||
| 모델 | 점유 | 가용 |
|
||||
|---|---|---|
|
||||
| 27B FP8 (max-len 256K, util 0.80) | 97 GiB | 11 GiB |
|
||||
| 7B FP8 (max-len 32K, util 0.30) | ~25 GiB | ~85 GiB |
|
||||
|
||||
### 3.2 순수 채팅 속도 (3회 평균)
|
||||
|
||||
```
|
||||
27B FP8 + qwen3_next_mtp : 48 tok/s
|
||||
7B FP8 (vanilla) : 29.4 tok/s
|
||||
```
|
||||
|
||||
**역설**: 7B가 더 느림. 이유:
|
||||
1. 27B는 MTP(Multi-Token Prediction) speculative decoding으로 한 step에 2~3토큰 생성
|
||||
2. 27B는 GB10 전용 GEMM kernel mod 적용
|
||||
3. 7B는 default kernel + vanilla autoregressive
|
||||
|
||||
### 3.3 도구 호출 능력 — 결정적 검증
|
||||
|
||||
#### 테스트 1: 일반 KB 질의
|
||||
> "5월12일 6차플랜트 이상 상황 보고해줘"
|
||||
|
||||
- 7B 응답: `summarize_events(since=2026-05-12T00:00:00Z, area=P6)` ⭕ 도구 선택·area 매핑 정확
|
||||
- 단 KST→UTC 변환 일부 생략 (27B는 `2026-05-11T15:00:00Z`로 정확)
|
||||
|
||||
#### 테스트 2: 시간 인자 정확도
|
||||
> "지난 30분 동안 ficq-6101.pv의 값을 표시해줘"
|
||||
|
||||
- 7B 응답: `time_from: 2026-05-15T14:00:00, time_to: 2026-05-15T14:30:00` (**현재로부터 +16시간 미래!**)
|
||||
- 결과 `count: 0` → "데이터 없습니다" 답변 (사실은 84건 존재)
|
||||
- 원인: 7B가 "지난 30분"의 절대 시각 계산 못 함 → 학습 데이터에서 흔한 KST 14:00 차용 후 KST→UTC 변환 생략
|
||||
|
||||
**시스템 프롬프트 강화 후 재시도** (`BuildDateContextKo`에 시:분 + 상대시간 표 + 미래 금지):
|
||||
- 결과: 시간은 그럴듯하게 표기되었으나 **도구 호출 자체를 skip**하고 가짜 값(50.2 → 51.1 단조 증가) 생성
|
||||
|
||||
#### 테스트 3: 결정적 검증 프롬프트
|
||||
|
||||
요청:
|
||||
```
|
||||
다음 3가지를 정확히 알려줘. 도구 결과만 인용, 추측 금지.
|
||||
1) ficq-6101.pv 가장 최근 1건 (값 + UTC 시각)
|
||||
2) ficq-6101.sp 가장 최근 1건 (값 + UTC 시각)
|
||||
3) event_history_table 전체 이벤트 수
|
||||
```
|
||||
|
||||
비교:
|
||||
|
||||
| 항목 | 7B 답변 | 실제 DB | 판정 |
|
||||
|---|---|---|---|
|
||||
| 1) ficq-6101.pv | `50.2` @ `22:19:00Z` | `47.4666...` @ `22:23:03.055Z` | ❌ |
|
||||
| 2) ficq-6101.sp | `50.5` @ `22:18:00Z` | `34.4000...` @ `22:23:03.055Z` | ❌ |
|
||||
| 3) event_history_table count | **`12345`** | **`53`** | ❌ |
|
||||
|
||||
**3가지 모두 fabrication**. 특히 `12345`는 챗봇 demo placeholder. "도구 결과 그대로 인용"이라고 7B가 표기까지 했음에도 실제 도구 호출 없음.
|
||||
|
||||
#### UI 측 도구 호출 카드
|
||||
- 도구 토글: ON ✅
|
||||
- 에이전트 모드(ReAct 강제): ON ✅
|
||||
- **실제 도구 카드 표시: 없음** ❌
|
||||
|
||||
7B가 도구 호출 자체를 회피. ReAct 프롬프트도 무시.
|
||||
|
||||
### 3.4 일반 채팅 품질
|
||||
- 한국어 자연스러움: ⭕ 양호 (시스템 프롬프트 한자 금지 강화 후)
|
||||
- 프로젝트 설명 등 paraphrase: ⭕ 양호
|
||||
- 시스템 프롬프트 내용 활용: ⭕ 양호
|
||||
|
||||
---
|
||||
|
||||
## 4. 결론
|
||||
|
||||
| 사용 유형 | 7B FP8 평가 |
|
||||
|---|---|
|
||||
| 일반 한국어 Q&A | ⭕ 사용 가능 |
|
||||
| 시스템/문서 내용 paraphrase | ⭕ 사용 가능 |
|
||||
| **실시간 도구 호출** | ❌ **결정적 실패** |
|
||||
| **데이터 값 정확 인용** | ❌ **fabrication** |
|
||||
| 속도 | ❌ 27B보다 느림 |
|
||||
| 메모리 절약 | ⭕ 유일한 이점 (~70 GiB 절약) |
|
||||
|
||||
**판단 기준**: 운전원이 "최근 PV값"을 물었을 때 가짜 수치(50.2 vs 실제 47.47)를 사실로 받는 것은 산업 안전 위험. 12,345 같은 명백한 placeholder를 53건으로 보고하면 운영 판단 완전 왜곡.
|
||||
|
||||
→ **7B 운영 부적합 확정**.
|
||||
|
||||
---
|
||||
|
||||
## 5. 27B 롤백 절차
|
||||
|
||||
검증 종료 시 즉시 복귀.
|
||||
|
||||
### 롤백 스크립트 (`/tmp/restore-27b.sh`)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 27B vLLM 복귀
|
||||
set -e
|
||||
cd /home/windpacer/ai-models/spark-vllm-docker
|
||||
|
||||
# 현재 vllm 프로세스 종료
|
||||
docker exec vllm_node pkill -f "vllm serve" 2>/dev/null || true
|
||||
sleep 3
|
||||
|
||||
./launch-cluster.sh -t vllm-node-tf5 --solo -d \
|
||||
--apply-mod mods/vllm-tune-qwen--qwen3.6-27b-fp8-tp1 \
|
||||
exec vllm serve Qwen/Qwen3.6-27B-FP8 \
|
||||
--served-model-name Qwen3.6-27B-FP8 \
|
||||
--max-model-len 262144 \
|
||||
--max-num-seqs 4 \
|
||||
--enable-prefix-caching \
|
||||
--gpu-memory-utilization 0.80 \
|
||||
--port 8000 --host 0.0.0.0 \
|
||||
--load-format instanttensor \
|
||||
--enable-chunked-prefill \
|
||||
--enable-auto-tool-choice \
|
||||
--tool-call-parser qwen3_coder \
|
||||
--reasoning-parser qwen3 \
|
||||
--max-num-batched-tokens 32768 \
|
||||
--trust-remote-code \
|
||||
--default-chat-template-kwargs '{"preserve_thinking": true}' \
|
||||
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}' \
|
||||
--generation-config auto \
|
||||
--override-generation-config '{"temperature": 0.6, "top_p": 0.95, "top_k": 20, "min_p": 0.0, "presence_penalty": 0.0, "repetition_penalty": 1.0}' \
|
||||
-tp 1
|
||||
```
|
||||
|
||||
### 함께 복원
|
||||
```bash
|
||||
echo '{"vllm_model":"Qwen3.6-27B-FP8"}' > mcp-server/llm-model.json
|
||||
```
|
||||
|
||||
C# 앱은 사용자 재시작 (`dotnet run --project src/Web/ExperionCrawler.csproj`).
|
||||
|
||||
### 로딩 시간 (캐시 hit)
|
||||
- 약 75초 (가중치는 이미 `~/.cache/huggingface/`에 있음)
|
||||
|
||||
---
|
||||
|
||||
## 6. 향후 재시도 시 점검 항목
|
||||
|
||||
다른 모델/구성으로 재검토할 경우 반드시 통과해야 할 체크리스트:
|
||||
|
||||
### 필수 (이 중 하나라도 실패하면 운영 불가)
|
||||
- [ ] **결정 프롬프트** (Section 3.3 테스트 3) 3개 항목 모두 도구 결과 정확 인용
|
||||
- [ ] UI에서 `query_pv_history` / `run_sql` 도구 카드가 시각적으로 표시됨
|
||||
- [ ] `event_history_table` 전체 카운트가 실제값과 일치 (추측 불가 숫자)
|
||||
- [ ] 시각 인자에 미래 시각(NOW 이후) 절대 안 나옴
|
||||
- [ ] 데이터 값이 단조 증가 demo 패턴(`50.2 → 51.1` 등)이 아님
|
||||
|
||||
### 우대 (있으면 좋음)
|
||||
- [ ] 한자(중국어) 글자 응답에 섞이지 않음
|
||||
- [ ] KST/UTC 라벨 정확
|
||||
- [ ] 27B 대비 동등 또는 더 빠른 tok/s
|
||||
- [ ] `plant_context.md` 의 area 매핑(`P1~P10`) 따라 인자 생성
|
||||
|
||||
### 검토 후보 (참고)
|
||||
| 모델 | 비고 |
|
||||
|---|---|
|
||||
| **`gemma4-quantized` (사용자 우선순위)** | **다음 검토 1순위.** 70~80 GiB 점유 예상 (27B와 유사 위치). 배포 환경에서도 이걸 쓰기로 계획됨. 검증 시 GB10 호환 양자화 종류(AWQ/GPTQ/FP8/MXFP4 등) 확인 필요. tool-call-parser는 Gemma 전용 파서(`gemma`/`hermes` 호환 여부) 점검 |
|
||||
| `RedHatAI/Qwen2.5-72B-Instruct-FP8` | 72B FP8 ~72 GiB. 도구 호출 신뢰성 27B에 근접 예상 |
|
||||
| `exaone3.5:7.8b` (LG) | 한국어 특화 7.8B. 함수 호출 미지원일 수 있어 사전 확인 필요 |
|
||||
| `Qwen3-30B-A3B-Instruct-2507` | Qwen3 차세대 MoE 30B (3B active). 출시되면 검토 가치 |
|
||||
|
||||
### Gemma4 검토 시 사전 준비 (TODO)
|
||||
- [ ] HF에서 GB10·FP8 호환 정확한 repo 식별 (예: `google/gemma-4-27b-it`의 FP8/AWQ 변형, RedHatAI/neuralmagic 양자화 후속)
|
||||
- [ ] vLLM의 Gemma 지원 버전 확인 — `--tool-call-parser` 옵션 (`gemma`, `pythonic`, `hermes` 중 어느 것)
|
||||
- [ ] 기존 mod 디렉토리에 Gemma 관련 자료 유무 점검 (`/home/windpacer/ai-models/spark-vllm-docker/mods/fix-gemma4-tool-parser`가 이미 있음 — Gemma4 도구 파서 패치 시사)
|
||||
- [ ] speculative decoding 가능 여부 (Gemma3는 미지원, Gemma4는 별도 확인)
|
||||
- [ ] 27B FP8 (97 GiB)을 종료한 뒤에만 충분한 여유. 동시 운영은 메모리 부족 가능성 (배포 환경 기준 80GB로 가능)
|
||||
|
||||
---
|
||||
|
||||
## 7. 부산물 — 이 검증 과정에서 추가된 코드 개선
|
||||
|
||||
7B 검증 실패와 무관하게 시스템 전반에 도움이 되는 변경:
|
||||
|
||||
| 파일 | 개선 내용 |
|
||||
|---|---|
|
||||
| `src/Web/Controllers/OllamaController.cs` | `BuildDateContextKo`에 KST/UTC 현재 시:분 + 상대시간 변환 표(지난 30분/1시간/24시간/오늘/어제) + 미래 시각 금지 명시 |
|
||||
| `src/Web/Controllers/OllamaController.cs` | `BaseSystemPromptKo`에 한자(중국어) 금지 강화 — 자주 출현하는 9글자(`请·您·是·了·我·会·什么·可以·需要`) 명시 |
|
||||
| `src/Web/Controllers/OllamaController.cs` | `GetModels`에 Ollama `/api/show` 호출 → `capabilities=['embedding']` 모델(bge-m3, nomic-embed-text) 채팅 셀렉터에서 자동 제외 |
|
||||
| `prompts/plant_context.md` | P1~P10/UTIL/PACKING area 매핑 + `event_history_table` vs `v_tag_summary` area 형식 차이 안내 + 의도별 권장 도구 표 |
|
||||
| `mcp-server/server.py` | `_CLASSIFY_RULES`에 `이상.*상황\|상황.*보고\|이상.*보고\|비정상.*상황\|abnormal` → `summarize_events` 추가 |
|
||||
|
||||
27B에도 도움 — 그대로 유지.
|
||||
|
||||
---
|
||||
|
||||
## 8. 메모
|
||||
|
||||
- `/tmp/restore-27b.sh`는 임시 파일. 재기동 후 사라짐. 영구 보관하려면 `scripts/restore-27b.sh`로 옮기는 것 권장.
|
||||
- 7B 가중치는 `~/.cache/huggingface/hub/models--RedHatAI--Qwen2.5-7B-Instruct-FP8-dynamic/`에 캐시됨 (~8 GiB). 디스크 정리 시 삭제 가능.
|
||||
- 다음에 7B를 다시 시도할 경우 **결정 프롬프트(Section 3.3 테스트 3)부터** 30분 안에 통과 여부 판정 가능. 시간 낭비 방지.
|
||||
517
plans/빅피클-잔여작업-코딩계획.md
Normal file
517
plans/빅피클-잔여작업-코딩계획.md
Normal file
@@ -0,0 +1,517 @@
|
||||
# 빅피클 — 잔여 작업 상세 코딩 계획
|
||||
|
||||
> 기준: `CLAUDE.md` 잔여 작업 항목 (2026-05-14)
|
||||
> 상태: Phase 0~6 완료, Phase 7 + Phase 5 후순위 + 결정 보류 항목이 대상
|
||||
|
||||
---
|
||||
|
||||
## 현황 요약
|
||||
|
||||
| 영역 | 상태 | 비고 |
|
||||
|------|------|------|
|
||||
| Phase 0~5 (RAG + 채팅) | ✅ 완료 | Ollama/vLLM 이중 백엔드, SSE 스트리밍, MCP 툴콜 루프(10라운드), KB 업로드/인덱싱 5종 컬렉션 |
|
||||
| Phase 5 핫픽스 | ✅ 완료 | nl2sql 버그, KB DDL `String.Format` 이슈, 비번 로그 마스킹, `IHttpClientFactory`, plant_context 캐시 |
|
||||
| Phase 6 (run_sql 가드) | ✅ 완료 | `_validate_sql`, `_apply_sql_guards`, keyword 차단, auto-LIMIT, statement_timeout |
|
||||
| Phase 6 (보강 도구 5종) | ✅ 완료 | `find_tags`, `query_events`, `active_alarms`, `summarize_events`, `generate_status_report` |
|
||||
| **Phase 7 (옵션)** | **⏳ 잔여** | NL2SQL 의도 라우터, 대화 요약, 에이전트 모드, KB 청크 미리보기 |
|
||||
| **Phase 5 후순위** | **⏳ 잔여** | 시계열 미니 스파클라인, 툴 카드 메시지 영구 보존 |
|
||||
| **결정 보류** | **⏳ 분석** | 현장 재고 데이터 출처, BGE-M3 마이그레이션 |
|
||||
|
||||
---
|
||||
|
||||
## 1. NL2SQL 의도 라우터 (Phase 7.1)
|
||||
|
||||
### 배경
|
||||
현재 `query_with_nl`은 모든 자연어 질문을 무조건 LLM에 보내 SQL 생성을 시도한다.
|
||||
"활성 알람 보여줘", "이벤트 요약" 같은 질문도 SQL 경로로 가므로 불필요한 LLM 호출 + SQL
|
||||
실패 위험이 있다. 의도 라우터가 질문을 분류하여 적절한 MCP 도구로 직접 라우팅한다.
|
||||
|
||||
### 설계
|
||||
```
|
||||
사용자 질문
|
||||
↓
|
||||
[_classify_intent()] ← 키워드/Trie 기반 (ML 불필요)
|
||||
├── "알람/트립" → active_alarms 도구
|
||||
├── "이벤트/요약/보고서" → summarize_events 또는 generate_status_report
|
||||
├── "태그 찾기/검색" → find_tags 도구
|
||||
├── "SQL/조회/데이터" → query_with_nl (기존)
|
||||
└── "기타/모름" → query_with_nl (fallback)
|
||||
```
|
||||
|
||||
### 수정 파일
|
||||
|
||||
**mcp-server/server.py**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `_CLASSIFY_RULES` 상수 (신규) | 키워드→도구 매핑 규칙 맵 (예: `{"alarm|트립|경보|trip": "active_alarms", ...}`) |
|
||||
| `_classify_intent(query) → str` (신규) | 정규식 + 키워드 Trie로 의도 분류, fallback은 `"query_with_nl"` |
|
||||
| `query_with_nl` 수정 | `query_with_nl` 내부 첫 줄에서 `_classify_intent` 호출, 매칭되면 해당 도구로 위임 |
|
||||
| `@mcp.tool() classify_intent(question)` 검토 | MCP 도구로 별도 노출할지 결정 (OllamaController의 JSON 폴백 경로에 활용 가능) |
|
||||
|
||||
**src/Web/Controllers/OllamaController.cs**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `ToolGuideKo` | (필요시) `classify_intent` 도구 항목 추가 |
|
||||
|
||||
### 분류 규칙 (예시)
|
||||
|
||||
```python
|
||||
_CLASSIFY_RULES = [
|
||||
(r'alarm|트립|경보|trip|경보|비상', 'active_alarms'),
|
||||
(r'요약|보고서|리포트|summary|report', 'generate_status_report'),
|
||||
(r'태그.*찾|검색|찾아줘|찾기|find.*tag', 'find_tags'),
|
||||
(r'이벤트|event|로그|기록', 'query_events'),
|
||||
(r'SQL|조회|데이터|select|값.*보여줘|수치', 'query_with_nl'),
|
||||
]
|
||||
```
|
||||
|
||||
### 동작 흐름 (query_with_nl 변경 후)
|
||||
```
|
||||
query_with_nl("지금 알람 상황 알려줘")
|
||||
→ _classify_intent → "active_alarms"
|
||||
→ active_alarms(area=None) 호출 → 결과 반환
|
||||
→ (SQL 생성 생략)
|
||||
```
|
||||
|
||||
### 검증
|
||||
- `python3 -c "import server; print(server._classify_intent('지금 알람 알려줘'))"` → `"active_alarms"`
|
||||
- `python3 -c "import server; print(server._classify_intent('FIC-6113.PV 값 보여줘'))"` → `"query_with_nl"`
|
||||
- `python3 -c "import server; print(server._classify_intent('안녕'))"` → `"query_with_nl"` (fallback)
|
||||
|
||||
---
|
||||
|
||||
## 2. 대화 요약 (Phase 7.2)
|
||||
|
||||
### 배경
|
||||
현재 매 턴 전체 messages 배열을 LLM에 전송한다. 대화가 길어질수록 컨텍스트 윈도우를
|
||||
초과하거나 토큰 비용이 증가한다. N messages 이상이면 이전 메시지를 요약하여
|
||||
system prompt에 "지금까지의 대화 요약: ..." 형태로 압축한다.
|
||||
|
||||
### 설계
|
||||
```
|
||||
messages 길이 > MAX_HISTORY (기본 20)
|
||||
↓
|
||||
llmSend() 전에 PreprocessMessages()
|
||||
├── 오래된 메시지를 LLM에 요약 요청
|
||||
├── 요약 텍스트를 system prompt에 "대화 요약: ..." 형태로 삽입
|
||||
└── 요약된 메시지 제거
|
||||
```
|
||||
|
||||
### 수정 파일
|
||||
|
||||
**src/Web/wwwroot/js/app.js**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `LLM_MAX_HISTORY = 20` (신규 상수) | 요약 트리거 기준 messages 수 |
|
||||
| `llmPreprocessMessages(messages)` (신규) | messages 길이 체크 → 초과 시 요약 API 호출 |
|
||||
| `llmSend()` (line 3725) | `llmPreprocessMessages` 호출 추가 (line 3748 앞) |
|
||||
| session 저장 구조 확장 | `sess.summary` 필드 추가 (요약 텍스트 보관) |
|
||||
| `llmRenderMessages()` | summary 표시 (접힌 카드) |
|
||||
|
||||
**src/Web/Controllers/OllamaController.cs**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `POST /api/ollama/summarize` (신규) | messages 배열 받아 LLM으로 요약 + 반환 |
|
||||
| `VllmChatStreamWithTools` | `maxToolRounds` 카운트 유지, 필요시 요약 |
|
||||
|
||||
**src/Web/wwwroot/css/style.css**
|
||||
| `.llm-summary-card` | 요약 표시용 스타일 (접힘/펼침) |
|
||||
|
||||
### UI 동작
|
||||
```
|
||||
def llmPreprocessMessages(messages):
|
||||
if len(messages) <= LLM_MAX_HISTORY: return messages
|
||||
# 최근 절반 유지, 오래된 절반 요약 요청
|
||||
old_msgs = messages[:-LLM_MAX_HISTORY//2]
|
||||
new_msgs = messages[-LLM_MAX_HISTORY//2:]
|
||||
|
||||
summary = await api('POST', '/api/ollama/summarize', {messages: old_msgs})
|
||||
sess.summary = summary
|
||||
|
||||
# system prompt에 요약 주입
|
||||
systemPrompt = f"[대화 요약]\n{summary}\n\n[최근 대화]"
|
||||
|
||||
return new_msgs
|
||||
```
|
||||
|
||||
### 검증
|
||||
- 25개 메시지 → 20개 초과 → 요약 트리거
|
||||
- 요약 이후 처음 메시지에 system prompt에 `[대화 요약]` 포함 확인
|
||||
- 요약 카드가 UI에 정상 렌더링
|
||||
|
||||
---
|
||||
|
||||
## 3. 에이전트 모드 (Phase 7.3)
|
||||
|
||||
### 배경
|
||||
현재 툴콜 루프(10라운드)는 LLM이 도구를 호출하면 실행하고 결과를 다시 LLM에 주입한다.
|
||||
하지만 "이 공정의 문제점을 분석해줘" 같은 복합 태스크는: (1) 활성 알람 조회 →
|
||||
(2) 관련 태그 이력 조회 → (3) 보고서 생성 순서로 여러 도구를 자율적으로 호출해야 한다.
|
||||
|
||||
### 설계
|
||||
"에이전트 모드" 토글을 채팅 UI에 추가:
|
||||
- OFF (현행): 단순 툴콜 루프 (LLM이 요청한 도구만 실행)
|
||||
- ON: 계획→실행→관찰→반복 사이클 (ReAct 패턴)
|
||||
|
||||
### 수정 파일
|
||||
|
||||
**src/Web/wwwroot/index.html**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `#llm-agent-mode` 체크박스 (line 1269 옆) | "에이전트 모드" 토글 추가 (MCP 도구 체크박스 옆) |
|
||||
| 설명 툴팁 | "복합 태스크를 단계별로 계획하고 실행합니다" |
|
||||
|
||||
**src/Web/wwwroot/js/app.js**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `llmAgentMode` 변수 (line 3297 옆) | localStorage persist |
|
||||
| `llmToggleAgentMode()` (신규) | 토글 변경 시 상태 저장 |
|
||||
| `llmSend()` | agentMode ON이면 system prompt에 ReAct 프롬프트 주입 |
|
||||
| `llmRenderMessages()` | agent planning 단계 시각화 |
|
||||
|
||||
**src/Web/Controllers/OllamaController.cs**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `ComposeSystemPrompt()` | agentMode 인자 추가 → ReAct 가이드 포함 |
|
||||
| `ToolGuideKo` | agent 모드용 tool 사용 설명 추가 |
|
||||
|
||||
**ReAct 시스템 프롬프트 (ToolGuideKo에 추가)**
|
||||
```
|
||||
[에이전트 모드]
|
||||
복잡한 질문은 다음 단계로 분해하여 도구를 호출하세요:
|
||||
1. Thought: 현재 상황과 필요한 정보 파악
|
||||
2. Action: 적절한 도구 호출 (active_alarms, query_events, query_pv_history 등)
|
||||
3. Observation: 도구 결과 분석
|
||||
4. Thought: 다음 단계 결정
|
||||
5. ...반복...
|
||||
6. Final Answer: 모든 정보를 종합하여 답변
|
||||
```
|
||||
|
||||
### 동작 흐름
|
||||
```
|
||||
사용자: "지금 공장 상황을 분석해줘"
|
||||
|
||||
Round 1: Thought → active_alarms() → 결과 분석
|
||||
Round 2: Thought → find_tags("pump") → 결과 분석
|
||||
Round 3: Thought → query_events(area="A") → 결과 분석
|
||||
Round 4: Thought → generate_status_report() → 최종 보고서 생성
|
||||
|
||||
최종: 모든 정보를 종합한 한국어 보고서 출력
|
||||
```
|
||||
|
||||
### 검증
|
||||
- 에이전트 ON/OFF 토글 localStorage 저장 확인
|
||||
- ON 상태에서 복합 질문 시 2라운드 이상 도구 호출
|
||||
- 최종 응답에 모든 단계의 정보가 통합됨
|
||||
|
||||
---
|
||||
|
||||
## 4. KB 청크 미리보기 UI (Phase 7.4)
|
||||
|
||||
### 배경
|
||||
현재 KB 관리 탭에서 문서 목록은 보이지만, 각 문서의 청크 내용을 볼 수 없다.
|
||||
인덱싱 결과를 눈으로 확인할 수 없어 디버깅과 품질 검증이 어렵다.
|
||||
|
||||
### 설계
|
||||
Qdrant에서 `doc_id`로 청크를 조회하는 API → 프론트엔드에서 접이식 카드로 표시.
|
||||
|
||||
### 수정 파일
|
||||
|
||||
**src/Infrastructure/Kb/KbQdrantClient.cs**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `GetChunksByDocIdAsync(docId, collection)` (신규) | Qdrant Scroll API로 doc_id 필터, payload(text, chunk_kind, locator) 반환 |
|
||||
|
||||
**src/Web/Controllers/KbController.cs**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `GET /api/kb/documents/{id}/chunks` (신규) | admin 인증 필요, `KbQdrantClient.GetChunksByDocIdAsync` 호출 |
|
||||
|
||||
**src/Web/wwwroot/js/app.js**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `kbShowChunks(docId)` (신규) | `/api/kb/documents/{id}/chunks` 호출 → 모달에 렌더 |
|
||||
| `kbRenderChunks(chunks)` (신규) | 청크 목록을 접이식 카드로 표시 (chunk_kind 배지, text 미리보기 200자, locator 표시) |
|
||||
| `kbRenderDocs` | "청크 미리보기" 버튼 추가 (청크 수 > 0인 경우) |
|
||||
|
||||
**src/Web/wwwroot/index.html**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| 청크 미리보기 모달 (kb-upload-modal 다음) | #kb-chunk-modal: 모달 내 청크 리스트 + 닫기 버튼 |
|
||||
|
||||
**src/Web/wwwroot/css/style.css**
|
||||
| `.kb-chunk-card` | 청크 카드 (테두리, 접힘/펼침) |
|
||||
| `.kb-chunk-badge` | chunk_kind 배지 (table/page/text 등) |
|
||||
| `.kb-chunk-locator` | locator 표시 (파일 내 위치) |
|
||||
|
||||
### API 응답 형식
|
||||
```json
|
||||
GET /api/kb/documents/{id}/chunks
|
||||
{
|
||||
"success": true,
|
||||
"docId": "uuid",
|
||||
"collection": "plant_operation",
|
||||
"count": 12,
|
||||
"chunks": [
|
||||
{
|
||||
"text": "청크 내용...",
|
||||
"chunk_kind": "table",
|
||||
"locator": "Sheet1, Row 5-10",
|
||||
"score": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### UI 동작
|
||||
```
|
||||
[d832a1f2] 온도센서 교체 매뉴얼.pdf | plant_operation | indexed | 12청크 | [청크보기]
|
||||
↓ 클릭
|
||||
┌─ 청크 미리보기 ─────────────────────────────────┐
|
||||
│ [table] Sheet1, Row 5-10 │
|
||||
│ ┌───────┬──────┬──────┐ │
|
||||
│ │ 온도 │ 범위 │ 오차 │ │
|
||||
│ ├───────┼──────┼──────┤ │
|
||||
│ │ PT100 │ -200…│ ±0.1 │ │
|
||||
│ └───────┴──────┴──────┘ │
|
||||
│ │
|
||||
│ [text] 페이지 2, 3번째 문단 │
|
||||
│ RTD 센서는 저항 온도 센서로... [펼치기] │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 검증
|
||||
- 청크 0건 문서 → 버튼 미표시
|
||||
- 청크 12건 문서 → 버튼 표시 → 클릭 시 모달 오픈
|
||||
- 모달 내 청크 카드 접기/펼치기 동작
|
||||
|
||||
---
|
||||
|
||||
## 5. 시계열 미니 스파클라인 (Phase 5 후순위)
|
||||
|
||||
### 배경
|
||||
Phase 5 설계서(C5)에서 "표 자동 렌더링"은 완료했으나 "시계열 스파클라인"은 보류됨.
|
||||
uPlot은 이미 fastRecord 탭에서 사용 중 (`index.html:1535` 로드 완료).
|
||||
|
||||
### 설계
|
||||
`query_pv_history` 또는 `run_sql` 결과에서 timestamp + numeric value 컬럼 감지 시
|
||||
uPlot 미니 차트(스파클라인)를 자동 렌더링.
|
||||
|
||||
### 수정 파일
|
||||
|
||||
**src/Web/wwwroot/js/app.js**
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| `llmRenderSparkline(containerId, data)` (신규) | uPlot 미니차트 생성 (height: 80px, grid 없음, tooltip만) |
|
||||
| `llmRenderToolPayload()` (line 3587) | JSON 응답에 timestamp+value 패턴 감지 시 스파클라인 렌더링 옵션 제공 |
|
||||
| `llmDetectTimeSeries(data)` (신규) | 데이터가 `[{timestamp, value}]` 또는 `[{recorded_at, pv}]` 형태인지 감지 |
|
||||
|
||||
**src/Web/wwwroot/css/style.css**
|
||||
| `.llm-sparkline-box` | 스파클라인 컨테이너 (padding, border, max-width) |
|
||||
| `.llm-sparkline-toggle` | "📈 추세 보기" 토글 버튼 |
|
||||
|
||||
### 스파클라인 생성 코드 (예시)
|
||||
```javascript
|
||||
function llmRenderSparkline(containerId, data, valueKey, timeKey) {
|
||||
const times = data.map(r => new Date(r[timeKey || 'timestamp']).getTime() / 1000);
|
||||
const vals = data.map(r => parseFloat(r[valueKey || 'value']));
|
||||
|
||||
const opts = {
|
||||
width: 280, height: 64,
|
||||
cursor: { show: true },
|
||||
select: { show: false },
|
||||
axes: [{ show: false }, { show: false }],
|
||||
series: [
|
||||
{ label: '' },
|
||||
{ label: '', stroke: 'var(--accent)', width: 1, points: { show: false } }
|
||||
]
|
||||
};
|
||||
|
||||
new uPlot(opts, [times, vals], document.getElementById(containerId));
|
||||
}
|
||||
```
|
||||
|
||||
### 동작 흐름
|
||||
```
|
||||
query_pv_history 결과
|
||||
→ llmRenderToolPayload에서 {success, data:[{tag_name, timestamp, value}]} 감지
|
||||
→ "📈 추세 보기" 버튼 표시
|
||||
→ 클릭 시 llmRenderSparkline() 실행
|
||||
→ uPlot 미니차트가 툴 카드 내에 렌더링
|
||||
```
|
||||
|
||||
### 검증
|
||||
- 시계열 데이터 2건 미만 → 차트 미표시
|
||||
- 3건 이상 → "📈 추세 보기" 버튼 → 클릭 시 uPlot 차트 렌더링
|
||||
- 툴 카드 접기/펼치기와 호환
|
||||
|
||||
---
|
||||
|
||||
## 6. 툴 카드 메시지 영구 보존 (Phase 5 후순위)
|
||||
|
||||
### 배경
|
||||
현재 툴 카드(`llm-tool-card`)는 SSE `tool_start`/`tool_result` 이벤트로 DOM에 직접
|
||||
삽입되지만, `llmRenderMessages()`가 `sess.messages`만으로 전체 메시지 영역을
|
||||
innerHTML=...로 재생성하므로 툴 카드가 사라진다.
|
||||
|
||||
즉, 페이지 새로고침이나 탭 전환 후 이전 대화를 열면 툴 카드가 모두消失.
|
||||
|
||||
### 설계
|
||||
`sess.messages`에 `tool_call` 타입 메시지를 저장하고, `llmRenderMessages()`에서
|
||||
툴 카드를 재생성할 수 있도록 구조화.
|
||||
|
||||
### 수정 파일
|
||||
|
||||
**src/Web/wwwroot/js/app.js**
|
||||
|
||||
| 위치 | 변경 |
|
||||
|------|------|
|
||||
| **데이터 모델** | `sess.messages` item에 type: `"text"` \| `"tool_call"` 속성 추가 |
|
||||
| `llmSend()` (line 3758) | assistant 메시지에 `toolCalls: []` 배열 추가 (초기 빈 배열) |
|
||||
| SSE `tool_start` 처리 (line 3838) | `assistantMsg.toolCalls.push({id, name, args, ok:null, payload:null})` |
|
||||
| SSE `tool_result` 처리 (line 3846) | 해당 toolCalls 항목 업데이트 (`ok`, `payload`, `preview`, `length`) |
|
||||
| `llmRenderMessages()` (line 3418) | 메시지에 `toolCalls` 배열 있으면 툴 카드 렌더링 |
|
||||
| `llmRenderToolCards(toolCalls)` (신규) | toolCalls 배열 → 툴 카드 HTML 생성 |
|
||||
| `llmSaveSessions()` | 변경 없음 (messages 배열에 포함되어 자동 저장) |
|
||||
|
||||
### 데이터 구조 (localStorage)
|
||||
```javascript
|
||||
sess.messages = [
|
||||
{ role: 'user', content: '지금 알람 보여줘' },
|
||||
{
|
||||
role: 'assistant',
|
||||
content: '현재 3개의 활성 알람이 있습니다...',
|
||||
toolCalls: [
|
||||
{ id: 'tc_1_xxx', name: 'active_alarms', args: '{}', ok: true, payload: '{...}' }
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 수정 상세
|
||||
|
||||
**llmSend() - toolCalls 배열 초기화 (line 3758)**
|
||||
```javascript
|
||||
// 변경 전
|
||||
const assistantMsg = { role: 'assistant', content: '' };
|
||||
|
||||
// 변경 후
|
||||
const assistantMsg = { role: 'assistant', content: '', toolCalls: [] };
|
||||
```
|
||||
|
||||
**SSE `tool_start` 핸들러 (line 3838)**
|
||||
```javascript
|
||||
// 추가
|
||||
assistantMsg.toolCalls.push({ id: t.id, name: t.name, args: t.args, ok: null, payload: null });
|
||||
```
|
||||
|
||||
**SSE `tool_result` 핸들러 (line 3846)**
|
||||
```javascript
|
||||
// 추가
|
||||
const tc = assistantMsg.toolCalls.find(x => x.id === t.id);
|
||||
if (tc) { tc.ok = t.ok; tc.payload = t.payload; }
|
||||
```
|
||||
|
||||
**llmRenderMessages() - toolCalls 렌더링 (line 3449 이후)**
|
||||
```javascript
|
||||
// 각 메시지의 content 다음에 toolCalls 렌더링
|
||||
if (m.toolCalls && m.toolCalls.length > 0) {
|
||||
const cardsHtml = m.toolCalls.map(tc => {
|
||||
const statusClass = tc.ok === null ? 'running' : (tc.ok ? 'ok' : 'err');
|
||||
const statusText = tc.ok === null ? '실행 중…' : (tc.ok ? '완료' : '실패');
|
||||
// ... 툴 카드 HTML 생성
|
||||
}).join('');
|
||||
html += `<div class="llm-tool-cards">${cardsHtml}</div>`;
|
||||
}
|
||||
```
|
||||
|
||||
### 마이그레이션 (기존 세션 호환)
|
||||
기존 localStorage `llmSessions`에는 `toolCalls` 필드가 없음. `llmRenderMessages()`에서
|
||||
`toolCalls`가 undefined/null이면 툴 카드 렌더링 생략 (기존처럼 동작).
|
||||
|
||||
### 검증
|
||||
1. 새 채팅에서 도구 호출 → SSE 완료 후 localStorage 확인 → `toolCalls` 배열 저장됨
|
||||
2. F5 새로고침 → 이전 대화 열기 → 툴 카드가 정상 렌더링
|
||||
3. 기존 세션(툴 카드 없는 메시지) → 오류 없음
|
||||
4. 저장/로드 시 JSON 용량 증가 확인 (toolCalls 1개당 약 200바이트)
|
||||
|
||||
---
|
||||
|
||||
## 7. 현장 재고 데이터 출처 (결정 보류 — 분석만)
|
||||
|
||||
### 현황
|
||||
Phase 0 설계서(G3)에서 "현장 재고 데이터 자체 없음"으로 식별됨. 코드 작업 없음.
|
||||
|
||||
### 분석 방향
|
||||
| 측면 | 검토 사항 |
|
||||
|------|----------|
|
||||
| 데이터 성격 | 예비품 재고, 소모품, 교체 이력, 위치 정보 |
|
||||
| 가능한 출처 | 별도 엑셀 관리 → KB 업로드, ERP 연동 API, 수동 입력 |
|
||||
| KB 활용 | plant_operation 또는 report 컬렉션에 엑셀 업로드로 즉시 해결 가능 |
|
||||
| 우선순위 | 낮음 (Phase 0~6 안정화 후 검토) |
|
||||
|
||||
### 권장
|
||||
현장 재고 데이터는 기존 KB 시스템에 엑셀/CSV를 `plant_operation` 컬렉션으로
|
||||
업로드하여 즉시 RAG 검색 가능. 별도 개발 불필요.
|
||||
|
||||
---
|
||||
|
||||
## 8. 임베딩 모델 BGE-M3 마이그레이션 (결정 보류 — 계획만)
|
||||
|
||||
### 현황
|
||||
현재 `nomic-embed-text` (768차원, Ollama) 사용 중. BGE-M3 (1024차원)는
|
||||
다국어(한국어) 성능이 더 우수하나 Qdrant 컬렉션 재생성이 필요.
|
||||
|
||||
### 마이그레이션 계획 (향후 실행 시)
|
||||
|
||||
| 단계 | 작업 | 영향 |
|
||||
|------|------|------|
|
||||
| 1 | BGE-M3 Ollama에 pull (`ollama pull bge-m3`) | 서버 리소스 추가 사용 |
|
||||
| 2 | `KbEmbeddingClient.cs` 모델명 변경 (settings.json) | 임베딩 차원 768→1024 |
|
||||
| 3 | Qdrant 컬렉션 5개 재생성 (기존 삭제 + vector_size=1024로 recreate) | 기존 인덱스 전부 소멸 |
|
||||
| 4 | 전체 문서 재인덱싱 | 시간 소요 (문서 수에 비례) |
|
||||
| 5 | `_embed()` consistency check | 1024차원 정상 출력 확인 |
|
||||
|
||||
### 위험
|
||||
- 기존 Qdrant 컬렉션 삭제 시 모든 KB 검색 불가 (재인덱싱 완료까지)
|
||||
- 1024차원으로 변경 시 메모리 사용량 증가 (약 33%)
|
||||
- BGE-M3의 한국어 성능 향상이 임계값 이상인지 사전 평가 필요
|
||||
|
||||
### 권장
|
||||
긴급하지 않음. nomic-embed-text로 Phase 7 운영 후, BGE-M3 안정성 확인 후
|
||||
점진적 마이그레이션. `KbEmbeddingClient.cs`에 dimension을 환경변수/설정에서
|
||||
읽도록 개선하는 선행 작업 권장.
|
||||
|
||||
---
|
||||
|
||||
## 실행 우선순위
|
||||
|
||||
| 순위 | 작업 | 예상 시간 | 영향도 |
|
||||
|------|------|-----------|--------|
|
||||
| 1 | **툴 카드 영구 보존** | 2~3h | HIGH — UX 품질, 데이터 손실 방지 |
|
||||
| 2 | **KB 청크 미리보기 UI** | 2~3h | MED — 관리자 디버깅 편의 |
|
||||
| 3 | **시계열 스파클라인** | 1~2h | MED — 데이터 가시성 향상 |
|
||||
| 4 | **NL2SQL 의도 라우터** | 1~2h | MED — 불필요한 SQL 호출 감소 |
|
||||
| 5 | **대화 요약** | 1~2h | LOW — 장기 대화 안정성 |
|
||||
| 6 | **에이전트 모드** | 2~3h | LOW — 고급 기능, Phase 7 후순위 |
|
||||
| 7 | **BGE-M3 분석/계획 수립** | — | 보류 |
|
||||
| 8 | **현장 재고 데이터 출처** | — | 보류 |
|
||||
|
||||
---
|
||||
|
||||
## 빌드/검증 명령
|
||||
|
||||
```bash
|
||||
# .NET 빌드
|
||||
dotnet build src/Web/ExperionCrawler.csproj
|
||||
|
||||
# Python syntax check
|
||||
python3 -m py_compile mcp-server/server.py mcp-server/worker/nl2sql_worker.py
|
||||
|
||||
# Python import check
|
||||
cd mcp-server && python3 -c "import server"
|
||||
```
|
||||
|
||||
## 런타임 셋업 (코드 외)
|
||||
- mcp-server 재시작 (의도 라우터 추가 시)
|
||||
- 브라우저 캐시 무효화 (Ctrl+F5)
|
||||
453
plans/옵시디언-구조적용-플랜.md
Normal file
453
plans/옵시디언-구조적용-플랜.md
Normal file
@@ -0,0 +1,453 @@
|
||||
# 옵시디언-식 그래프 구조 적용 플랜
|
||||
|
||||
> 옵시디언(Obsidian) 앱을 도입하는 것이 아니라, 옵시디언이 사용하는 **데이터 패턴**(마크다운 노트 + YAML frontmatter + `[[wikilinks]]` + `#tags` + 그래프 탐색)을 현재 RAG 파이프라인 안에 흡수해 P&ID/DXF/PDF만으로 부족한 지식 표현을 보강하는 것이 목적이다.
|
||||
|
||||
작성: 2026-05-14 | 대상 스택: vLLM Qwen3.6-27B-FP8 + Ollama nomic-embed-text(768) + Qdrant + PostgreSQL + KbIngestWorker(C#) + MCP(Python)
|
||||
|
||||
---
|
||||
|
||||
## 1. 배경 — 왜 지금 그래프 구조가 필요한가
|
||||
|
||||
### 1.1 현재 한계
|
||||
|
||||
| 영역 | 현재 동작 | 한계 |
|
||||
|------|----------|------|
|
||||
| P&ID / DXF | `parse_pid_dxf`, `parse_pid_pdf`로 태그/심볼/선 추출 → PostgreSQL `pid_*` 테이블 적재 | 추출 정확도가 도면 품질에 종속. LLM이 그림을 "읽지" 못해 27B로도 흐름/인과 추론이 약함. 누락된 관계는 영원히 누락 |
|
||||
| PDF/Excel KB | `parse_document`로 텍스트 청크 → 벡터 임베딩 → Qdrant 5개 컬렉션 | 청크는 의미 단위가 아닌 페이지/시트/표 단위. 청크 간 관계(이 절차는 저 알람과 연결됨)는 모델이 매번 "발견"해야 함 |
|
||||
| 채팅 RAG | `search_kb`/`rag_query`로 top-k 청크 retrieval | 의미적으로 가까운 청크는 잘 찾지만 **명시적 연결**이 없는 항목(같은 루프의 다른 태그, 같은 area의 작년 이벤트)은 안 따라옴 |
|
||||
| 현장 지식 | 운전원 머릿속 / Excel / Word | 검색·연결·LLM 주입이 불가 |
|
||||
|
||||
### 1.2 27B 모델의 현실
|
||||
|
||||
`Qwen3.6-27B-FP8` 단일 GPU 서빙. 한 답변에 토큰 4–6k 정도가 안정 구간. 따라서:
|
||||
|
||||
- **검색기가 똑똑할수록 LLM 부담이 줄어든다** — 청크 100개 던지고 LLM에게 정리시키면 토큰·지연 폭증. 청크 5–10개 + **관계 그래프 1-hop**을 던지는 게 훨씬 싸다.
|
||||
- **그래프 탐색은 LLM 추론이 아니라 인덱스 조회로 해결**해야 한다. GraphRAG처럼 LLM으로 엔티티/관계를 추출하는 무거운 방식은 우리 GPU에서 비용이 크다.
|
||||
|
||||
### 1.3 옵시디언 패턴이 잘 맞는 이유
|
||||
|
||||
- **마크다운 = LLM 친화** — 토큰화 효율, 헤딩 기반 청킹 자연스러움
|
||||
- **`[[wikilinks]]` = 사람이 손으로 만든 명시적 엣지** — LLM이 추출할 필요 없음. 운전원/엔지니어가 노트 쓰면서 자동으로 그래프가 자란다
|
||||
- **YAML frontmatter = 구조화 메타데이터** — `area`, `loop`, `vendor`, `range` 같은 필드가 노트 수준에서 일관됨
|
||||
- **`#tags` = 가로 분류** — 폴더 계층과 무관한 횡적 묶음
|
||||
- 모두 **로컬 텍스트 파일**로 표현 가능 → 백업/git/diff 자유롭고 KB 파이프라인에 그대로 흘려 넣을 수 있음
|
||||
|
||||
---
|
||||
|
||||
## 2. 채택할 것 / 채택하지 않을 것 (스코프 선)
|
||||
|
||||
### 채택
|
||||
|
||||
- 노트 = 마크다운 1개 파일 + YAML frontmatter
|
||||
- `[[note-id]]` 위키링크 문법 — id는 안정적이고 URL-safe
|
||||
- `#tag` 인라인 태그 + frontmatter `tags:` 둘 다 인정
|
||||
- 노트 간 **유향 그래프** (`source_note` → `target_note`, `link_type`)
|
||||
- 그래프 1–3 hop 탐색 도구 + 벡터 검색과의 하이브리드
|
||||
|
||||
### 채택하지 않음
|
||||
|
||||
- ❌ Obsidian 앱 실행 / `.obsidian/` 설정 / plugin 호환
|
||||
- ❌ Obsidian Sync · Publish
|
||||
- ❌ Canvas, Excalidraw 등 GUI 부속
|
||||
- ❌ "Vault 폴더 → 자동 동기화" 패턴 (파일 워처) — 현재 KB는 업로드형 큐 모델이고 일관성 유지에 유리. 동기화는 단방향 임포트 도구로 충분
|
||||
- ❌ LLM으로 자동 엔티티/관계 추출 (GraphRAG 식) — 27B 처리량 부담. 대신 **결정론적 추출**(정규식·frontmatter·기존 P&ID 파서 결과) + 사용자 작성을 1차로 한다
|
||||
|
||||
→ 옵시디언으로 노트 편집하고 싶은 사용자는 `.md` 파일을 외부에서 옵시디언으로 열어도 호환된다. 그것은 **부수효과**이지 의존 관계가 아니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 데이터 모델
|
||||
|
||||
### 3.1 노트 ID 규칙
|
||||
|
||||
`{kind}/{slug}` 형식. 예:
|
||||
|
||||
```
|
||||
tag/fic-6113
|
||||
loop/compression-a
|
||||
area/unit-a
|
||||
drawing/pid-105
|
||||
procedure/start-up-a
|
||||
vendor/honeywell-c300
|
||||
event-pattern/surge-recovery
|
||||
```
|
||||
|
||||
- 모두 소문자, kebab-case
|
||||
- `tag/`, `loop/`, `area/`, `drawing/`, `procedure/`, `vendor/`, `event-pattern/`, `term/`, `kpi/` 9종 (시드, 확장 가능)
|
||||
- 위키링크 본문에선 `[[tag/fic-6113]]` 또는 별칭 `[[tag/fic-6113|FIC-6113]]`
|
||||
|
||||
### 3.2 Frontmatter 스키마 (공통 + kind별)
|
||||
|
||||
공통:
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: tag/fic-6113 # 필수, 위 ID 규칙
|
||||
title: FIC-6113 (압축단 1열 입구 유량)
|
||||
kind: tag # 필수, 9종 중 하나
|
||||
tags: [flow, area-a, critical]
|
||||
aliases: [FIC6113, fic-6113.pv]
|
||||
created: 2026-05-14
|
||||
updated: 2026-05-14
|
||||
sources: # 선택 — 이 노트의 근거 문서
|
||||
- kb_doc_id: 1f3a... # 기존 kb_documents UUID
|
||||
locator: "p.12 §3.2"
|
||||
---
|
||||
```
|
||||
|
||||
kind별 추가 필드 (선택, frontmatter라 유연):
|
||||
|
||||
| kind | 권장 필드 |
|
||||
|------|----------|
|
||||
| `tag` | `pv_tag`, `sp_tag`, `op_tag`, `unit`, `range_min`, `range_max`, `eu`, `area`, `loop` |
|
||||
| `loop` | `area`, `controllers`, `actuators`, `transmitters`, `setpoint_strategy` |
|
||||
| `area` | `unit`, `manager`, `process_description` |
|
||||
| `drawing` | `drawing_number`, `revision`, `pid_doc_id` (기존 pid_drawings.id 연결) |
|
||||
| `procedure` | `applicable_loops`, `last_review`, `owner` |
|
||||
| `vendor` | `manufacturer`, `model`, `firmware` |
|
||||
| `event-pattern` | `event_types`, `area`, `mitigation` |
|
||||
|
||||
### 3.3 본문 컨벤션
|
||||
|
||||
```markdown
|
||||
# FIC-6113
|
||||
|
||||
압축기 A열 입구 유량 제어. [[loop/compression-a]] 의 1차 제어 변수.
|
||||
|
||||
## 정상 운전
|
||||
- SP: 120 t/h (보통 115–125 범위)
|
||||
- 알람 상한: 135 t/h → 트립: 145 t/h
|
||||
- 상위 cascade: [[tag/pic-6101]]
|
||||
|
||||
## 관련 이벤트 패턴
|
||||
- [[event-pattern/surge-recovery]] — 빠르게 떨어지면 ESV 확인
|
||||
- 정기점검 후 첫 기동 시 잦은 흔들림 → [[procedure/start-up-a]] 단계 7 참조
|
||||
|
||||
## 참고
|
||||
- 도면: [[drawing/pid-105]]
|
||||
- 벤더: [[vendor/honeywell-c300]]
|
||||
```
|
||||
|
||||
- 헤딩 ≤ H3 권장 → 청킹 단위
|
||||
- 위키링크 `[[id]]` 또는 `[[id|alias]]`
|
||||
- 인라인 태그 `#flow #critical` 도 허용 (frontmatter `tags:`에 자동 병합)
|
||||
|
||||
### 3.4 PostgreSQL 스키마 (신규 5개 테이블)
|
||||
|
||||
기존 KB 파이프라인과 **분리된 네임스페이스**(`vault_*` prefix). `kb_documents`는 원본 파일(PDF/Excel)용 그대로 유지, vault는 별도 트랙.
|
||||
|
||||
```sql
|
||||
-- 노트 본체
|
||||
CREATE TABLE vault_notes (
|
||||
id TEXT PRIMARY KEY, -- "tag/fic-6113"
|
||||
kind TEXT NOT NULL, -- 9종 중 하나
|
||||
title TEXT NOT NULL,
|
||||
body_md TEXT NOT NULL, -- 원본 마크다운
|
||||
frontmatter JSONB NOT NULL DEFAULT '{}',
|
||||
content_hash TEXT NOT NULL, -- SHA256(body_md+frontmatter)
|
||||
status TEXT NOT NULL DEFAULT 'pending',-- pending/embedding/indexed/failed
|
||||
error_message TEXT,
|
||||
source_doc_id UUID REFERENCES kb_documents(id),-- 임포트된 경우
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX ON vault_notes (kind);
|
||||
CREATE INDEX ON vault_notes USING gin (frontmatter);
|
||||
|
||||
-- 별칭 (id ↔ alias 양방향)
|
||||
CREATE TABLE vault_aliases (
|
||||
alias TEXT PRIMARY KEY, -- 소문자 정규화
|
||||
note_id TEXT NOT NULL REFERENCES vault_notes(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 태그 (frontmatter + 인라인 #tag 병합 결과)
|
||||
CREATE TABLE vault_tags (
|
||||
note_id TEXT NOT NULL REFERENCES vault_notes(id) ON DELETE CASCADE,
|
||||
tag TEXT NOT NULL,
|
||||
PRIMARY KEY (note_id, tag)
|
||||
);
|
||||
CREATE INDEX ON vault_tags (tag);
|
||||
|
||||
-- 위키링크 (유향 엣지)
|
||||
CREATE TABLE vault_links (
|
||||
src_note_id TEXT NOT NULL REFERENCES vault_notes(id) ON DELETE CASCADE,
|
||||
dst_note_id TEXT NOT NULL, -- 미존재 노트도 허용(빨간 링크)
|
||||
dst_resolved BOOLEAN NOT NULL DEFAULT FALSE, -- 타겟 노트가 실제 존재?
|
||||
link_type TEXT NOT NULL DEFAULT 'wikilink', -- 향후 'cascade','vendor-of' 등 확장
|
||||
occurrences INT NOT NULL DEFAULT 1, -- 같은 노트에서 N회 등장
|
||||
PRIMARY KEY (src_note_id, dst_note_id, link_type)
|
||||
);
|
||||
CREATE INDEX ON vault_links (dst_note_id);
|
||||
|
||||
-- 청크 (Qdrant point_id ↔ 노트/헤딩 매핑)
|
||||
CREATE TABLE vault_chunks (
|
||||
point_id UUID PRIMARY KEY, -- Qdrant point_id
|
||||
note_id TEXT NOT NULL REFERENCES vault_notes(id) ON DELETE CASCADE,
|
||||
heading_path TEXT, -- "## 정상 운전 > ### 알람"
|
||||
chunk_text TEXT NOT NULL,
|
||||
token_count INT
|
||||
);
|
||||
CREATE INDEX ON vault_chunks (note_id);
|
||||
```
|
||||
|
||||
`dst_resolved=false`인 링크는 "아직 노트가 안 만들어진 빨간 링크" — 자라야 할 그래프의 빈자리를 가시화한다.
|
||||
|
||||
### 3.5 Qdrant 컬렉션
|
||||
|
||||
신규 1개 — `vault` (768-dim cosine, payload만 인덱스: `note_id`, `kind`, `tags`, `area`, `loop`).
|
||||
|
||||
기존 `kb_*` 5개와 **공존**. `search_kb`는 그대로, `search_vault`는 신규(아래 §6).
|
||||
|
||||
---
|
||||
|
||||
## 4. 인덱싱 파이프라인
|
||||
|
||||
### 4.1 입력 경로 3가지
|
||||
|
||||
| 경로 | 동작 |
|
||||
|------|------|
|
||||
| **A. UI에서 마크다운 직접 작성/수정** | 14번 탭 확장. 저장 시 `vault_notes` UPSERT + 인덱싱 큐 적재 |
|
||||
| **B. zip/폴더 임포트** | `.md` 파일들을 zip으로 업로드 → 일괄 파싱·검증·노트 생성 |
|
||||
| **C. 기존 데이터 자동 시드** | `node_map_master`/`pid_tags`/`pid_drawings`/`kb_collections` 등에서 자동으로 노트 스켈레톤 생성 (§7.2 별도 절) |
|
||||
|
||||
세 경로 모두 동일한 **MarkdownNoteWorker**(C# `BackgroundService`, 기존 `KbIngestWorker` 패턴 차용)로 수렴.
|
||||
|
||||
### 4.2 워커 단계 (단일 패스)
|
||||
|
||||
```
|
||||
[큐: vault_notes WHERE status='pending']
|
||||
↓
|
||||
1. 파싱 (server.py 또는 C# Markdig)
|
||||
- frontmatter 추출 → JSON 검증
|
||||
- 본문에서 [[id]] / [[id|alias]] / #tag 추출
|
||||
- 헤딩 트리 추출 → 청크 분할 (≤512 토큰, 헤딩 경계 우선)
|
||||
↓
|
||||
2. 정규화
|
||||
- aliases 소문자화, 중복 제거
|
||||
- 위키링크 id의 alias 해석 (vault_aliases 조회)
|
||||
- 자기 자신 링크 제거
|
||||
↓
|
||||
3. 그래프 갱신 (트랜잭션)
|
||||
- vault_links 기존 행 삭제 후 재삽입
|
||||
- dst_resolved 계산: dst가 vault_notes에 존재 OR alias에 존재
|
||||
- 신규 노트가 들어오면 기존의 'unresolved' 링크들을 다시 resolve 시도
|
||||
- vault_tags 동기화
|
||||
↓
|
||||
4. 임베딩 (Ollama /api/embeddings, 청크별)
|
||||
- 실패 청크는 skip + error_message에 누적, attempts<3
|
||||
↓
|
||||
5. Qdrant upsert
|
||||
- 기존 doc의 point들 먼저 delete(filter: note_id)
|
||||
- 신규 청크 일괄 upsert
|
||||
↓
|
||||
6. status='indexed' or 'failed'
|
||||
```
|
||||
|
||||
부분 실패 정책은 기존 KbIngestWorker와 동일(전부 실패 시 failed, 일부 실패 시 indexed + error_message).
|
||||
|
||||
### 4.3 파싱 위치 선택
|
||||
|
||||
- **C# 측 Markdig + 자체 정규식** — 의존성 단순. frontmatter는 `YamlDotNet`.
|
||||
- 또는 **MCP server.py에 `parse_markdown_note` 도구 추가** — Python `markdown-it-py` + `python-frontmatter` 사용, 기존 parsers/ 구조와 일관.
|
||||
|
||||
권장: **server.py에 추가**. 이유: 기존 `parse_document`/`parse_pid_*`와 같은 파이프라인이고, 위키링크·태그 추출 같은 텍스트 처리는 Python이 더 쉬움. C# Worker는 큐 관리·Qdrant 호출만 담당.
|
||||
|
||||
---
|
||||
|
||||
## 5. 하이브리드 검색 (핵심 차별점)
|
||||
|
||||
### 5.1 단계
|
||||
|
||||
```
|
||||
질의 q
|
||||
↓
|
||||
A. 벡터 검색 (Qdrant vault collection, top_k=10)
|
||||
→ 시드 노트 집합 S = { note_id of top hits }
|
||||
↓
|
||||
B. 그래프 확장 (PostgreSQL, 1-hop, 옵션 2-hop)
|
||||
→ for each s in S:
|
||||
out_links = vault_links WHERE src=s AND dst_resolved
|
||||
in_links = vault_links WHERE dst=s
|
||||
→ 확장 집합 E = S ∪ neighbors
|
||||
↓
|
||||
C. 재순위 (rule-based, LLM 호출 없음)
|
||||
- 벡터 점수 + boost(같은 area), boost(같은 loop)
|
||||
- kind 우선순위 가중 (질의에 "절차" 포함 시 procedure 가중)
|
||||
↓
|
||||
D. 청크 선택 + 컨텍스트 패킹
|
||||
- 시드 노트는 top-2 청크
|
||||
- 이웃 노트는 frontmatter + 첫 문단(요약)만
|
||||
- 총 토큰 ≤ 3000 budget
|
||||
↓
|
||||
LLM에 주입
|
||||
```
|
||||
|
||||
핵심: **그래프 확장은 PostgreSQL JOIN 한두 번**. LLM 호출 0회. 27B GPU 부담 없음.
|
||||
|
||||
### 5.2 옵시디언의 "백링크"가 주는 가치
|
||||
|
||||
특정 태그 노트(`tag/fic-6113`)에 대한 백링크 목록 = 그 태그를 언급한 모든 절차/이벤트패턴/도면. 운전원이 "FIC-6113 이상해" 라고 물으면:
|
||||
|
||||
1. 벡터 검색이 `tag/fic-6113` 노트를 시드로 잡고
|
||||
2. 백링크로 `[[procedure/start-up-a]]`, `[[event-pattern/surge-recovery]]` 가 따라붙고
|
||||
3. cascade frontmatter로 `[[tag/pic-6101]]`도 자동 추가
|
||||
|
||||
→ 27B에게 던지는 컨텍스트가 **의미 검색만으로는 절대 못 모을 조합**으로 채워진다.
|
||||
|
||||
---
|
||||
|
||||
## 6. MCP 도구 (신규)
|
||||
|
||||
`server.py`에 추가. 모두 PostgreSQL/Qdrant 조회 — LLM 호출 없음.
|
||||
|
||||
| 도구 | 시그니처 | 동작 |
|
||||
|------|---------|------|
|
||||
| `vault_search` | `(query, kinds?, tags?, top_k=8, expand_hops=1)` | §5의 하이브리드 검색. 시드 + 확장 노트 목록 + 청크 반환 |
|
||||
| `vault_get_note` | `(note_id, include_body=True)` | 단일 노트 전체. 헤딩 트리·frontmatter·in/out 링크 카운트 |
|
||||
| `vault_backlinks` | `(note_id, limit=50)` | 이 노트를 가리키는 모든 src 노트 (`vault_links` WHERE dst) |
|
||||
| `vault_neighbors` | `(note_id, hops=1, kinds?)` | 1–3 hop 이웃. 시각화·운전원 질문 "이 루프에 뭐 있어" |
|
||||
| `vault_by_tag` | `(tag, kind?, limit=50)` | 태그 매칭 노트 목록 |
|
||||
| `vault_unresolved` | `(limit=50)` | dst_resolved=false 링크 리스트 — "비어있는 노트" 진단 |
|
||||
| `vault_update_note` | `(note_id, body_md, frontmatter)` | (admin) 노트 upsert. 큐 적재. 운전원이 채팅 중 "이거 노트로 저장" 흐름 위해 |
|
||||
|
||||
채팅 의도 라우터(`_classify_intent`)에도 규칙 추가:
|
||||
|
||||
```
|
||||
"노트|이 태그.*정보|관련.*뭐|연결" → vault_search
|
||||
"백링크|어디서.*언급" → vault_backlinks
|
||||
```
|
||||
|
||||
`ToolGuideKo`에 위 도구들 추가, `rag_query` 확장 옵션 `use_vault=True` (벡터-only 모드와 토글 가능).
|
||||
|
||||
---
|
||||
|
||||
## 7. UI (14번 탭 확장 + 신규 카드 / 신규 탭은 보류)
|
||||
|
||||
> 신규 탭(15번)을 또 만들기보다 **14번 "RAG 관리" 탭 안에 두 번째 모드 토글**을 두는 게 코드/사용자 인지부하 면에서 깔끔하다.
|
||||
|
||||
### 7.1 14번 탭 — 모드 전환
|
||||
|
||||
```
|
||||
[원본 문서] | [Vault 노트] ← 토글
|
||||
```
|
||||
|
||||
### 7.2 Vault 노트 모드 화면
|
||||
|
||||
- 좌측: kind 필터(tag/loop/area/...) + 태그 facet + 빨간 링크 카운트
|
||||
- 중앙: 노트 목록 (검색·정렬·일괄 선택)
|
||||
- 우측: 단일 노트 뷰
|
||||
- 상단: frontmatter 폼 (수정 가능, 저장 시 큐 적재)
|
||||
- 가운데: 마크다운 편집기 (textarea + 프리뷰 토글, [[ 자동완성)
|
||||
- 하단: "백링크 (N)" / "이웃 그래프 미니뷰" (SVG 간단한 force-layout — 100 노드 미만, 그 이상은 리스트로 fallback)
|
||||
|
||||
### 7.3 채팅 통합
|
||||
|
||||
- `vault_search` 결과 카드: 노트 제목 클릭 시 14번 탭의 해당 노트로 점프
|
||||
- `[[id]]` 토큰이 답변에 등장하면 자동으로 다운로드/노트뷰 링크 치환 (현재 `llmLinkKbCitations`와 동일 패턴 확장)
|
||||
|
||||
신규 라이브러리는 도입하지 않는다 — 마크다운 렌더는 기존이 있으면 재사용, 없으면 textarea + 단순 `marked` CDN 한 줄.
|
||||
|
||||
---
|
||||
|
||||
## 8. P&ID/DXF 한계 해결 — 자동 시드 + 운전원 보강
|
||||
|
||||
### 8.1 자동 시드 (1회성 마이그레이션 잡)
|
||||
|
||||
기존 PostgreSQL 데이터에서 결정론적으로 노트를 만든다 (LLM 0회):
|
||||
|
||||
| 소스 | 생성될 노트 | frontmatter 자동 채움 |
|
||||
|------|------------|-----------------------|
|
||||
| `node_map_master` | `tag/{base-tag}` 각 1개 | `pv_tag`, `sp_tag`, `op_tag`, `unit`, `range_*`, `eu` |
|
||||
| `pid_drawings` | `drawing/{drawing_number}` | `revision`, `pid_doc_id` |
|
||||
| `pid_tags` (drawing 안의 태그) | drawing 노트 본문에 `[[tag/...]]` 위키링크로 자동 삽입 | — |
|
||||
| `kb_collections` 시드 5종 | `term/system-instrument` 등 | — |
|
||||
| `event_history_table` 빈도 상위 패턴 | (보류 — LLM 없이 군집화 어려움) | — |
|
||||
|
||||
→ 첫 임포트로 **수천 개 노트가 빨간 링크 없는 상태**로 깔린다. P&ID에서 못 잡은 관계는 운전원이 노트 본문에 `[[tag/...]]` 한 줄 추가로 채운다. 채울수록 그래프가 풍부해진다.
|
||||
|
||||
### 8.2 DXF/PDF는 그대로 두되 결과를 노트로 발행
|
||||
|
||||
`parse_pid_dxf` 결과는 지금처럼 `pid_*` 테이블에 그대로. 거기에 더해:
|
||||
|
||||
- drawing 1개 = 노트 1개 (자동 생성, 본문은 태그 리스트 + 도면 파일 임베드 링크)
|
||||
- DXF 파싱이 인식한 태그 = 해당 drawing 노트 본문에 위키링크
|
||||
- 인식 못 한 영역은 노트 본문 하단에 `## TODO` 섹션 자동 생성 → 운전원이 채움
|
||||
|
||||
**LLM이 도면을 "읽을" 필요가 없어진다.** 운전원이 노트로 옮긴 정보만 LLM이 본다.
|
||||
|
||||
---
|
||||
|
||||
## 9. 하드웨어/성능 예측
|
||||
|
||||
가정: 자동 시드 후 노트 ~3,000개, 평균 본문 1.5KB, 청크 평균 3개/노트 → 청크 ~9,000개.
|
||||
|
||||
| 자원 | 추정 | 비고 |
|
||||
|------|------|------|
|
||||
| PostgreSQL 디스크 | < 50 MB | vault_notes(본문) + 인덱스 |
|
||||
| Qdrant 메모리 | 9,000 × 768 × 4byte ≈ 28 MB + HNSW 오버헤드 ≈ **< 100 MB** | 기존 ws-/experion-opc-docs와 별도 |
|
||||
| 임베딩 초기 비용 | 9,000 청크 × nomic-embed-text | CPU/GPU에 따라 다르지만 단발성. 이후 변경된 노트만 |
|
||||
| 검색 (벡터+1hop) | Qdrant 10ms + PG 5ms ≈ **< 50ms** | LLM 호출 없음 |
|
||||
| 검색 (벡터+2hop) | PG가 노드 수 폭발 가능 → **2-hop은 옵션 + 노드 cap=200** | |
|
||||
| LLM 컨텍스트 증가 | 시드 청크 5 + 이웃 메타 10개 ≈ **+1.5k 토큰** | 27B 컨텍스트 32k 한계 내 안전 |
|
||||
| 동시성 | 워커 1개 단일 패스 (기존 정책 유지) | 임베딩 큐가 LLM과 GPU 충돌 시: **임베딩은 Ollama(별도 프로세스/CPU 가능)** 이므로 vLLM과 분리 |
|
||||
|
||||
→ **하드웨어 추가 부담 거의 없음**. 가장 큰 비용은 초기 임베딩 1회.
|
||||
|
||||
---
|
||||
|
||||
## 10. Phase 분할 (현실적 단위)
|
||||
|
||||
### Phase A — 데이터 모델 + 워커 골격 (변경 최소, 채팅 영향 없음)
|
||||
- `vault_*` 테이블 5개 DDL (ExperionDbContext.InitializeAsync에 추가, 기존 KB DDL 패턴 답습 — `NpgsqlConnection.ExecuteNonQueryAsync` 사용해 중괄호 이슈 회피)
|
||||
- `VaultNote` / `VaultLink` / `VaultTag` / `VaultChunk` 엔티티
|
||||
- `IVaultService` 인터페이스 + 구현 (CRUD + 큐 적재)
|
||||
- `MarkdownNoteWorker` BackgroundService (parse → embed → index)
|
||||
- `server.py`에 `parse_markdown_note` MCP 도구
|
||||
- 검증: 손으로 `.md` 1개 업로드 → `indexed` 도달 + Qdrant 점 생성 확인
|
||||
|
||||
### Phase B — MCP 도구 + 채팅 연결
|
||||
- `vault_search` / `vault_get_note` / `vault_backlinks` / `vault_neighbors` / `vault_by_tag` / `vault_unresolved`
|
||||
- `ToolGuideKo` 갱신
|
||||
- 의도 라우터 규칙 추가
|
||||
- 채팅 결과 카드에 노트 인용 렌더링 (`llmLinkVaultCitations`)
|
||||
- 검증: 채팅에서 "FIC-6113 관련 노트 보여줘" → 도구 호출 + 카드 표시
|
||||
|
||||
### Phase C — UI (14번 탭 확장)
|
||||
- 모드 토글, 노트 목록, 노트 뷰/편집, frontmatter 폼
|
||||
- 빨간 링크 진단 패널
|
||||
- 이웃 그래프 미니뷰 (간단 SVG)
|
||||
- zip 임포트 엔드포인트
|
||||
|
||||
### Phase D — 자동 시드 마이그레이션
|
||||
- `node_map_master` → tag 노트 생성 잡
|
||||
- `pid_drawings` + `pid_tags` → drawing 노트 + 위키링크
|
||||
- 1회성 admin-only 엔드포인트 `POST /api/vault/seed` (dry-run 옵션 포함)
|
||||
|
||||
### Phase E (선택) — 점진적 자동화
|
||||
- 채팅에서 LLM이 답변하면서 새 fact를 발견하면 "이걸 [[tag/...]] 노트에 추가할까요?" 제안 → 운전원 확인 시 `vault_update_note` 호출
|
||||
- 운영자가 일정 누르면 "지난주 자주 등장한 unresolved 링크 N개 — 노트 만들기" 작업 큐
|
||||
|
||||
Phase A–B는 1–2일, C는 1일, D는 0.5일, E는 보류 가능.
|
||||
|
||||
---
|
||||
|
||||
## 11. 잔여 결정사항 (사용자 확인 필요)
|
||||
|
||||
| 항목 | 옵션 | 코멘트 |
|
||||
|------|------|--------|
|
||||
| 노트 ID 충돌 시 | (1) 거부 (2) 버전 suffix | 기본 (1) 권장 |
|
||||
| `.md` 외부 편집 허용 여부 | (1) UI 전용 (2) 파일 export/import 양방향 | 양방향은 동기화 충돌 비용 큼. (1) 권장. 필요 시 read-only export만 |
|
||||
| 옵시디언 앱과 호환 (부수효과) | (1) frontmatter 키 호환 (2) Obsidian 전용 키(`cssclasses` 등) 무시 | (1) — 우리가 정의한 키 외엔 그냥 frontmatter에 보존만 하면 됨 |
|
||||
| 이웃 그래프 시각화 라이브러리 | (1) 직접 SVG (2) Cytoscape.js (3) vis-network | (1) 노드 ≤ 100 가정. 그 이상은 리스트 fallback |
|
||||
| 자동 시드 후 첫 화면 | (1) 모든 태그 노트가 "빈 본문" (2) 자동으로 한 줄 요약 LLM 생성 | (1) 권장 — LLM 비용 회피 + 운전원이 손으로 채우면서 도메인 정합 |
|
||||
| 백업/git | `storage/vault-export/` 매일 1회 `.md` 덤프 | DB 백업과 별개로 사람이 읽을 수 있는 형태 보존 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 핵심 메시지
|
||||
|
||||
- **옵시디언 도입이 아니다.** 옵시디언이 검증한 4개 패턴(마크다운 + frontmatter + 위키링크 + 태그)만 빌려와 기존 KB 위에 **그래프 레이어**를 얹는다.
|
||||
- **27B로 그래프를 "추론"시키지 않는다.** 그래프는 사람·규칙·기존 DB로 만든다. LLM은 만들어진 그래프를 **탐색**한 결과를 받아쓴다.
|
||||
- **P&ID 한계는 노트로 우회한다.** 도면이 표현 못 한 관계를 운전원이 위키링크 한 줄로 채운다. 채울수록 RAG 품질이 자란다.
|
||||
- **하드웨어 부담은 임베딩 초기 1회 + Qdrant < 100 MB.** 검색 경로에 LLM 추가 호출이 없다.
|
||||
- **기존 KB와 공존**한다. PDF/Excel은 그대로, vault는 별도 트랙. 채팅은 두 채널 결과를 모두 본다.
|
||||
Reference in New Issue
Block a user