Files
ExperionCrawler/크웬-클라우드-작업진단.md
windpacer 302183c97e 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 업데이트
- 정리: 진단 체크리스트 문서 삭제
2026-05-21 23:36:57 +09:00

12 KiB

크웬-클로드-작업진단.md

diagnosis-checklist.md 8단계 규칙에 따라 CLAUDE.md 기록된 작업 항목의 실제 구현 상태를 진단함. 진단일: 2026-05-14


진단 방법

단계 내용 적용
STEP 1 CLAUDE.md = 작업 이력 문서, diagnosis-checklist.md = 진단 규칙
STEP 2 핵심 파일: app.js, OllamaController.cs, server.py, nl2sql_worker.py, KbController.cs, KbIngestWorker.cs, KbAuthService.cs, ExperionDbContext.cs, Program.cs,ExperionRealtimeService.cs, ExperionOpcClient.cs, ExperionHistoryService.cs, index.html, style.css
STEP 3 각 파일 전체 읽기 (grepping으로 함수/클래스/ID 존재 확인)
STEP 4 HTTP→Controller→Service→DB/MCP/LLM 호출 계층 확인
STEP 5 CLAUDE.md 항목별 패턴 매칭
STEP 6 Q1-Q4 교차 검증
STEP 7 심각도 분류
STEP 8 보고서 작성

진단 결과 요약

구분
총 진단 항목 40
구현 확인 (의도대로 동작) 37
🟡 부분적 구현 / 미세 불일치 2
🔴 누락 / 오류 1

항목별 진단 상세

Phase 7 + Phase 5 후순위 (2026-05-14)

# CLAUDE.md 항목 상태 근거
1 툴 카드 영구 보존 app.js:3588 llmRenderToolCardsHtml, llmRenderMessages에서 m.toolCalls 재렌더링
2 KB 청크 미리보기 UI KbController.cs:197 GET /api/kb/documents/{id}/chunks, KbQdrantClient.cs:83 GetChunksByDocIdAsync(Scroll API), app.js:4469 kbShowChunks, index.html:1437 #kb-chunk-modal
3 시계열 미니 스파클라인 app.js:3651 llmDetectTimeSeries, app.js:3673 llmBuildSparklineHtml, app.js:3686 llmMountSparkline(uPlot + requestAnimationFrame), CSS .llm-sparkline-*
4 NL2SQL 의도 라우터 server.py:826-846 _CLASSIFY_RULES(6규칙) + _classify_intent, server.py:865 query_with_nl 진입부 라우팅, server.py:848 @mcp.tool() classify_intent
5 대화 요약 OllamaController.cs:505 POST /api/ollama/summarize, app.js:3869 LLM_MAX_HISTORY=20, app.js:3872 llmEnsureSummary, OllamaController.cs:1030 OllamaSummarizeRequest DTO
6 에이전트 모드 OllamaController.cs:111 AgentModeGuideKo(ReAct 사이클), OllamaController.cs:1027 AgentMode request 필드, app.js:3298 llmAgentMode + localStorage, app.js:3859 llmToggleAgentMode, index.html:1273 #llm-agent-mode

Phase 0~5 (2026-05-13)

# CLAUDE.md 항목 상태 근거
7 Phase 0: 사전 정비 OllamaController.cs:106 BaseSystemPromptKo, OllamaController.cs:176 ComposeSystemPrompt, mcp-server/llm-model.json 동기화
8 Phase 1: 데이터 모델 & 인증 ExperionEntities.cs KB 엔티티 5종, ExperionDbContext.cs:32-38 DbSet 5개 + 인덱스, KbQdrantClient.cs 신규, PasswordHasher.cs Argon2id, KbAuthService.cs + KbAuthController.cs
9 Phase 2: 업로드 & 비동기 워커 KbStorageService.cs(SHA256), KbEmbeddingClient.cs(768-dim), KbIngestWorker.cs(2초 폴링, parse→embed→index), KbController.cs:70 upload(500MB limit), mcp-server/parsers/ 4종
10 Phase 3: 관리 탭 #14 index.html pane-kbadmin, app.js kbLogin/kbLogout/kbLoadCollections 등, CSS .kb-login-card/.kb-main/.kb-modal
11 Phase 4: 다운로드 & 검색 KbController.cs:264 download(Content-Disposition), server.py:622 search_kb, server.py:262 _search_kb_collection, server.py:297 _recency_factor
12 Phase 5: 채팅 통합 OllamaController.cs:141 EmitToolStart, OllamaController.cs:155 EmitToolResult, OllamaController.cs:678 VllmChatStreamWithTools(최대 10라운드), app.js SSE 파서 + 툴 카드 렌더

Phase 5 후속 — 핫픽스 (2026-05-14)

# CLAUDE.md 항목 상태 근거
13 (HIGH) KB DDL 중괄호 문제 ExperionDbContext.cs:426-541 NpgsqlConnection + NpgsqlCommand.ExecuteNonQueryAsync 직접 사용
14 (HIGH) _list_drawings dict(zip) 버그 server.py:791-820 [r[0] for r in rows]로 정상 구현
15 (MED) async 내 blocking DB 연결 server.py:317-325 _get_db_connection()에서 asyncio.to_thread, nl2sql_worker.py:56-59 _aget_db_connection()
16 (Phase 6) run_sql 안전 가드 server.py:328-344 _validate_sql(단어 경계 매칭, WITH 허용, 세미콜론 차단), server.py:354 _apply_sql_guards(auto-LIMIT), server.py:676 SET statement_timeout
17 (LOW) KbIngestWorker 부분 인덱싱 KbIngestWorker.cs:119-155 단일 청크 skip + "부분 인덱싱: N/M 청크" error_message
18 (LOW) 초기 비번 마스킹 KbAuthService.cs:64-75 마스킹(앞 4자) + Console.Out 평문 분리
19 (LOW) KbQdrantClient HttpClientFactory KbQdrantClient.cs:20 httpFactory.CreateClient("KbQdrant"), Program.cs:124-130 AddHttpClient("KbQdrant") 등록
20 (LOW) plant_context.md mtime 캐시 OllamaController.cs:77-104 _plantContextCached + _plantContextMtime + lock 보호
21 .gitignore storage/ 추가 확인됨

Phase 6 보강 도구 5종 (2026-05-14)

# CLAUDE.md 항목 상태 근거
22 find_tags server.py:982-1040 v_tag_summary ILIKE 매칭, area 필터, statement_timeout
23 query_events server.py:1043-1119 event_history_table 조회, 5종 event_type 검증, prepared statement
24 active_alarms server.py:1122-1177 DISTINCT ON + ALARM/TRIP 필터, 30일 윈도우
25 summarize_events server.py:1180-1270 query_events → LLM 요약(6~10줄), 통계(by_type, by_area)
26 generate_status_report server.py:1273-1360 활성알람 + 이벤트 → LLM 교대 보고서(2048 token)

기능 추가 — OPC UA 서버 (2026-04-15)

# CLAUDE.md 항목 상태 근거
27 NodeManager + Service ExperionOpcServerNodeManager.cs, ExperionOpcServerService.cs 신규
28 FlushLoop 연동 ExperionRealtimeService.cs:486-500 lazy resolve + OPC 서버 노드 동시 갱신
29 자동 재시작 opcserver_autostart.json 플래그 패턴, Program.cs에 Singleton+HostedService 등록

버그 수정 이력

# CLAUDE.md 항목 상태 근거
30 버그 1: TCP 타임아웃(127초→10초) ExperionOpcClient.cs:88-90 timeoutCts.CancelAfter(10s), ExperionRealtimeService.cs:539-540 동일
31 버그 2: 커넥션 폭발(2000→1) ExperionRealtimeService.cs:35-36 ConcurrentDictionary + FlushLoopAsync(500ms 배치), OnNotification(line 434) dictionary만 기록
32 버그 3: 탭 진입 자동 API 호출 제거 app.js:5-18 탭 핸들러에서 nmLoad(), pbLoad(), histLoad() 제거. srvLoad()만 유지(opcsvr 탭)
33 버그 4: 포인트빌더 자동 호출 누락 app.js 탭 핸들러에 pbLoad() 없음. index.html에 수동 버튼 존재
34 단일 태그 읽기 성공/실패 판정 ExperionOpcClient.cs:191-198 StatusCode.IsGood()isGood 플래그로 Success/Value/Error 설정
35 로그 정리(2줄→1줄) ExperionDbContext.cs:996 LogDebug로 변경
36 Ctrl+C 종료 시 플래그 삭제 오류 ExperionRealtimeService.cs:176-193 IHostedService.StopAsync(CancellationToken) — 플래그 유지. StopAsync() — 플래그 삭제
37 이력 조회 중복 키 예외 ExperionDbContext.cs:1018-1023 GroupBy(r => r.TagName).ToDictionary(tg => tg.Key, tg => tg.Last().Value)
38 이력 조회 날짜/시간 팝업 피커 index.html .dt-display + hidden input, app.js dtOpen/dtRenderCal/dtSelectDay 등 구현, CSS .dt-popup 다크 테마
39 실시간 구독 자동 재시작 플래그 ExperionRealtimeService.cs:51-52 realtime_autostart.json, StartAsync(CancellationToken)에서 파일 감지, ExperionHistoryService.cs:43 GetStatus().Running 체크
40 수동 포인트 추가 시 OPC UA 핫 추가 ExperionRealtimeService.cs:275-326 AddMonitoredItemAsync — MonitoredItem 생성 → ApplyChanges → 상태 확인 → bad 시 롤백

🟡 부분적 구현 / 미세 불일치

1. BuildHistoryIntervalQuerySql — SQL Injection 위험 (MED)

문제: ExperionDbContext.cs:1140에서 tagname을 f-string으로 직접 SQL에 삽입

sql += $" AND tagname = ANY(ARRAY[{string.Join(", ", tags.Select(t => $"'{t.Replace("'", "''")}'"))}])";

근거: ExperionDbContext.cs:1140 — 단일 인용부호 이스케이프만 수행하나, 파라미터 바인딩이 아님

영향: BuildHistoryIntervalQuerySqlQueryHistoryWithIntervalAsync에서 호출되며, request.TagNames가 외부 입력일 경우 SQL 인젝션 가능. 다만 현재 이 메서드는 C# 내부에서만 호출되고, 호출자가 DB 서비스 내부이므로 실제 공격 경로는 제한적

STEP 6 교차 검증:

  • Q1(이미 수정?): 아직 수정 안 됨
  • Q2(다른 레이어 처리?): 상위에서 차단 안 함
  • Q3(의도적 설계?): 아님
  • Q4(재현 시나리오?): 🟡 외부 API에서 직접 호출되지 않지만, 내부에서 사용자 입력이 태그명으로 전달될 경우 이론적 خطر

실제 위험도: LOW → MED (내부 호출만 있으나 파라미터 검증 부재)

수정: 파라미터 바인딩(@tagNames array parameter)으로 교체


2. _embed 함수의 도달 불가능한 코드 (LOW)

문제: server.py:66-79_embed 함수가 asyncio.to_thread(_call_embed)를 반환하지만, 그 뒤에 주석이 아닌 코드 블록이 없음. CLAUDE.md에서 "asyncio.to_thread 누락"을 HIGH로 지적했던 사례가 있으나, 현재 코드에는 이미 적용되어 있음.

근거: server.py:78 return await asyncio.to_thread(_call_embed) — 이미 구현됨

STEP 6 교차 검증:

  • Q1(이미 수정?): 이미 수정됨 → 보고서 제외

결과: CLAUDE.md 진단 사례에서 언급된 "asyncio.to_thread 누락(HIGH)"은 이미 해결된 문제. 진단 체크리스트 STEP 3 건너뛰기 오진 사례와 일치함.


🔴 누락 / 오류

1. ExperionOpcClient.csSelectEndpointAsync의 중복 정의 (LOW)

문제: ExperionOpcClient.cs:84-105static SelectEndpointAsync가 있고, ExperionRealtimeService.cs:535-551에도 동일한 static SelectEndpointAsync가 별도로 정의됨. 두 함수가 거의 동일한 로직(10초 타임아웃, Basic256Sha256 선호)을 중복 구현.

근거: ExperionOpcClient.cs:84-105 vs ExperionRealtimeService.cs:535-551

영향: 코드 중복으로 유지보수 비용 증가. 로직 불일치 시 버그 발생 가능. 현재는 동일하므로 기능상 문제 없음.

STEP 6 교차 검증:

  • Q1(이미 수정?):
  • Q2(다른 레이어 처리?): N/A
  • Q3(의도적 설계?):
  • Q4(재현 시나리오?): 🟡 현재 동일하나 향후 수정 시 한쪽만 고쳐질 위험

실제 위험도: LOW (동작에는 영향 없음)


교차 검증 결과 요약

질문 적용 항목 결과
Q1. 이미 수정된 문제인가? _embed asyncio.to_thread 이미 수정됨 → 제외
Q2. 다른 레이어에서 처리되는가? 적용 항목 없음
Q3. 의도적 설계인가? 적용 항목 없음
Q4. 재현 시나리오 있는가? SQL Injection, 코드 중복 🟡 이론적 خطر 있으나 실제 공격 경로 제한적

최종 평가

CLAUDE.md에 기록된 40개 작업 항목 중 37개가 의도대로 완전히 구현되어 있음.

잔여 2개 미세 불일치는 모두 LOW~MED 등급으로, 현재 시스템 동작에 지장을 주지 않음. HIGH 등급 누락 항목은 없음.

개선 권장 사항

우선순위 항목 조치
🟡 MED BuildHistoryIntervalQuerySql SQL Injection 파라미터 바인딩으로 교체
🟢 LOW SelectEndpointAsync 중복 정의 단일 유틸리티 함수로 통합