Files
ExperionCrawler/CLAUDE.md
windpacer 9cc359b803 feat: 문서 탐색기 (Tab 16) — 프로젝트 폴더 트리 탐색 + txt/md/pdf 뷰어
프로젝트 폴더 전체를 안전하게 탐색하고 문서를 보고 편집하는 웹 UI.

- DocBrowserService: 루트 자동탐색(.git/.sln), 경로이탈·심볼릭·제외디렉토리
  ·민감파일 가드, 목록/읽기/원본/쓰기/이름변경/삭제/폴더/업로드
- DocsController(/api/docs): config·tree·text·raw(공개) / 변경계열은 KB admin 토큰
- 뷰어: md(marked+DOMPurify+hljs+KaTeX+mermaid) / pdf(원본 iframe) / txt(pre) / 그 외 다운로드
- 인라인 편집(md 실시간 분할 미리보기) + 관리(새폴더/업로드/이름변경/삭제)
- 사이드바 nav 스크롤 수정(.nav overflow), 헤더 컴팩트화로 문서 영역 확보

주의: 프론트 라이브러리(marked/mermaid/katex 등)는 wwwroot/lib/(.gitignore)라
미추적 — 별도 처리 필요.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 06:32:19 +09:00

10 KiB

ExperionCrawler — 작업 이력

작업 규칙

  • 복잡한 작업은 항상 todo 목록 먼저 생성
  • 각 단계 시작 전 todo 목록 확인
  • 단계 완료 후 즉시 completed 표시

완료된 작업

문서 탐색기 (Tab 16) 구현 (2026-05-24)

배경

프로젝트 폴더의 문서를 Web UI에서 직접 보고 관리. 트리는 프로젝트 폴더 전체를 탐색, 뷰어는 txt · md · pdf 3종(그 외 다운로드), 보기 + 관리 + 인라인 편집 범위. 사용자 결정: PDF=원본 그대로(iframe), Excel=제외, md 렌더=marked+코드강조+KaTeX+mermaid, 뷰어 바탕=흰색.

구현 내역

# 항목 핵심
1 안전 파일트리 루트 자동탐색(.git/.sln) → 상대경로만 취급, Path.GetFullPath 정규화 후 루트이탈·심볼릭이탈 차단. 제외 디렉토리(.git/bin/obj/node_modules/storage…)·민감파일(.pfx/.env/appsettings*.json…) 숨김+차단
2 뷰어 디스패치 md=marked→DOMPurify→hljs→KaTeX→mermaid / pdf=원본 iframe(Content-Type: application/pdf) / txt=pre / 그 외=다운로드. md 라이브러리는 첫 진입 시 지연로딩
3 인라인 편집 admin이면 ✎ 편집 → textarea(md는 실시간 분할 미리보기) → PUT /api/docs/text 저장
4 관리 새 폴더·업로드·이름변경·재귀삭제 — KB admin 토큰(X-Kb-Token) 재사용
5 UI 다크 트리(지연확장·필터·노드별 hover 액션) + 흰색 종이 뷰어, 상대링크 클릭 시 탐색기 내 이동, 토스트

수정 파일

파일 변경 요약
src/Infrastructure/Docs/DocBrowserService.cs (신규) 루트결정·SafeResolve(이탈/심볼릭/제외/민감 가드)·List/ReadText(바이너리·BOM·크기상한)/OpenRaw(MIME)/WriteText/Rename/Delete/MakeDir/SaveUploadAsync
src/Web/Controllers/DocsController.cs (신규) /api/docs — config·tree·text·raw(공개) / PUT text·rename·mkdir·upload·DELETE(admin, IKbAuthService.ValidateAsync)
src/Web/Program.cs DocBrowserService 싱글톤 등록
src/Web/appsettings.json DocBrowser 섹션(Root="", MaxTextBytes=2MB, MaxUploadBytes=50MB)
src/Web/wwwroot/index.html nav-item(16 문서 탐색기), #pane-docs(트리+뷰어), docs.css 링크, docs.js 스크립트
src/Web/wwwroot/js/app.js 탭 전환부 if (tab === 'docs') docsInit()
src/Web/wwwroot/js/docs.js (신규) 트리(지연확장·필터·관리액션 위임), 뷰어 디스패치, md 렌더 파이프라인, 지연 라이브러리 로더, 인라인 에디터, 관리(mkdir/upload/rename/delete), KB 토큰 재사용 잠금해제, 토스트
src/Web/wwwroot/css/docs.css (신규) 다크 트리 + 흰색 종이 뷰어, GitHub-라이트 마크다운 타이포, 편집 분할
src/Web/wwwroot/lib/ (신규) marked@12, dompurify@3.1.6, highlight.js@11.10(+github 테마), katex@0.16.11(js/css/auto-render + woff2 20종), mermaid@10.9.1 — 전부 로컬 번들

설계 결정

항목 결정
적용 환경 소스 트리가 있는 개발 환경 전용 (배포본 /opt/ExperionCrawler엔 소스 없음). 루트는 DocBrowser:Root로 재설정 가능
인증 조회=공개, 변경계열=KB admin 토큰 재사용(별도 비번 없음). 프론트는 sessionStorage.kbToken 공유
md 라이브러리 로딩 페이지 로드 시점이 아닌 md 첫 열람 시 지연로딩(mermaid 3.3MB 등 무게 회피)
XSS marked 출력은 DOMPurify 살균. pdf/원본은 iframe·MIME 한정
download 파라미터 ASP.NET bool 바인딩은 true/false만 허용 — 프론트 download=true 사용(초기 download=1은 400, 수정함)
뷰어 대상 우선 txt/md/pdf만. DOCS_TEXT_EXT/DOCS_MD_EXT 상수로 추후 확장 용이

빌드/검증 (라이브 :5000)

  • dotnet build 경고 0 / 에러 0, node -c docs.js/app.js OK, 정적자산 11종 200 서빙
  • 루트 자동탐색 → 프로젝트 루트, 트리 .git/bin/obj 제외, 경로이탈·민감파일(appsettings/.env)·.git 진입 차단
  • md/txt 읽기·하위트리·PDF inline(application/pdf)·다운로드 첨부 정상
  • 미인증 PUT/DELETE → 401, admin 로그인 후 mkdir/쓰기/읽기/이름변경/재귀삭제 정상, .env 쓰기 차단, 정리 확인

잔여

  • 브라우저 실물 렌더(트리 클릭·md/mermaid/KaTeX·편집 UI)는 미확인 — 사용자 Ctrl+F5 후 탭 진입으로 확인 필요
  • 검증 위해 기존 dotnet run(터미널)을 백그라운드 새 빌드로 교체 기동

Phase 7 + Phase 5 후순위 일괄 구현 (2026-05-14)

배경

plans/빅피클-잔여작업-코딩계획.md의 1~6번 항목을 일괄 구현. Phase 7 옵션 4종 + Phase 5 후순위 2종으로 채팅 UX·관리 편의·운영자 분석 능력을 강화.

구현 내역

# 항목 핵심
1 툴 카드 영구 보존 sess.messages[*].toolCalls[] 저장 → llmRenderMessages에서 재렌더링. F5 새로고침 후에도 툴 카드 유지
2 KB 청크 미리보기 UI Qdrant Scroll API → /api/kb/documents/{id}/chunks → 모달에 청크 카드 (접기/펼침)
3 시계열 미니 스파클라인 llmDetectTimeSeries (timestamp+value 키 자동 감지) → uPlot 90px 차트가 표 위에 자동 렌더링
4 NL2SQL 의도 라우터 _classify_intent 정규식 6규칙 → query_with_nl 진입 시 알람/요약/태그검색/이벤트로 위임. classify_intent MCP 도구로도 노출
5 대화 요약 sess.summary + summarizedUpTo 인덱스, LLM_MAX_HISTORY=20 초과 시 /api/ollama/summarize 호출 → systemPrompt에 누적 요약 prepend
6 에이전트 모드 #llm-agent-mode 토글 → AgentMode 요청 필드 → ComposeSystemPromptAgentModeGuideKo (ReAct 사이클) 주입

수정 파일

파일 변경 요약
src/Web/wwwroot/js/app.js llmRenderToolCardsHtml(영구 렌더), llmDetectTimeSeries+llmBuildSparklineHtml+llmMountSparkline(uPlot), kbShowChunks+kbRenderChunks(모달), LLM_MAX_HISTORY+llmEnsureSummary+sess.summary 표시, llmAgentMode+llmToggleAgentMode, 툴 카드 표시 + 요약 prepend 로직
src/Web/wwwroot/index.html #kb-chunk-modal 모달, #llm-agent-row/#llm-agent-mode 토글
src/Web/wwwroot/css/style.css .kb-chunk-*(청크 카드/뱃지/locator), .llm-sparkline-box/-chart, .llm-summary-card(접기/펼침)
src/Web/Controllers/OllamaController.cs AgentMode request 필드, ComposeSystemPrompt(agentMode), AgentModeGuideKo ReAct 가이드, POST /api/ollama/summarize 엔드포인트, OllamaSummarizeRequest DTO, ToolGuideKoclassify_intent 추가
src/Web/Controllers/KbController.cs GET /api/kb/documents/{id}/chunks (admin) — KbQdrantClient Scroll 호출
src/Infrastructure/Kb/KbQdrantClient.cs GetChunksByDocIdAsync(collection, docId, limit) — payload-only Scroll API
mcp-server/server.py _CLASSIFY_RULES+_classify_intent+@mcp.tool() classify_intent, query_with_nl 진입부에서 라우팅 후 5개 전용 도구로 위임 (실패 시 SQL fallback)

의도 라우터 규칙

정규식 라우팅 대상
활성.*알람|현재.*알람|지금.*알람|active.*alarm active_alarms
트립|trip active_alarms
상태\s*보고서|교대.*보고|status.*report|운전.*보고 generate_status_report
요약|보고서|리포트|summary|summarize|report summarize_events
태그.*찾|tag.*찾|찾아\s*줘|find.*tag|어떤.*태그 find_tags
이벤트.*조회|이벤트.*목록|event.*list|event.*query|로그.*조회 query_events
(그 외) query_with_nl (기본 SQL 경로)

스모크 테스트 10건 모두 통과 — "지금 알람 알려줘"→active_alarms / "FIC-6113.PV 값 보여줘"→query_with_nl / "안녕"→query_with_nl 등.

설계 결정

항목 결정
툴 카드 영구화 assistantMsg.toolCalls{id,name,args,ok,preview,length,payload} 누적 저장. 기존 세션에 toolCalls 없으면 렌더링 생략(역호환)
AbortError 시 content 또는 toolCalls가 비어있지 않으면 메시지 유지 (도구만 호출하고 중단된 경우도 보존)
시계열 감지 timestamp/recorded_at/ts/time/datetime + value/pv/val/fieldvalue/sp/op 페어. 3건 이상이어야 차트 생성. uPlot 미로딩 시 렌더 생략
스파클라인 부착 innerHTML로 컨테이너만 만든 후 requestAnimationFrame에서 uPlot 생성 (DOM 마운트 후 실행 보장)
요약 임계 LLM_MAX_HISTORY=20 초과 시 오래된 절반을 요약. 누적 요약은 이전 요약을 system 메시지로 함께 전송
요약 송신 sess.summary가 있으면 systemPrompt 맨 앞에 [이전 대화 요약] 블록 prepend. 사용자에게는 접이식 카드로 표시
의도 라우터 fallback 라우팅 시도 실패(예외) 시 조용히 SQL 경로로 fallback
에이전트 모드 조건부 표시 llmType==='vllm' && llmUseTools 일 때만 토글 노출. localStorage 영속
청크 미리보기 권한 admin 토큰 필요. payload만 조회(vector 제외), 최대 500개/문서

빌드/검증

  • dotnet build src/Web/ExperionCrawler.csproj — 경고 0건, 에러 0건
  • python3 -m py_compile mcp-server/server.py mcp-server/worker/nl2sql_worker.py — OK
  • python3 -c "import server" — 9개 도구 모두 attribute로 노출 확인
  • node -c src/Web/wwwroot/js/app.js — syntax OK
  • 의도 분류기 10/10 통과

런타임 셋업

  • mcp-server 재시작 — classify_intent 신규 도구 인식
  • 브라우저 캐시 무효화 (Ctrl+F5) — 신규 JS/CSS 적용
  • 사용자 첫 진입 시 에이전트 모드 토글은 OFF (옵트인)

잔여

  • 결정 보류: 현장 재고 데이터 출처 (KB 엑셀 업로드로 즉시 가능, 별도 개발 불필요), 임베딩 모델 BGE-M3 마이그레이션 (위험 대비 임계값 평가 필요)
  • 모두 코드 작업 아닌 분석/결정 항목