Files
tk-factory-services/system3-nonconformance/web/ai-assistant.html
Hyungi Ahn b3012b8320 feat: AI 서비스 및 AI 어시스턴트 전용 페이지 추가
- ai-service: Ollama 기반 AI 서비스 (분류, 시맨틱 검색, RAG Q&A, 패턴 분석)
- AI 어시스턴트 페이지: 채팅형 Q&A, 시맨틱 검색, 패턴 분석, 분류 테스트
- 권한 시스템에 ai_assistant 페이지 등록 (기본 비활성)
- 기존 페이지에 AI 기능 통합 (대시보드, 수신함, 관리함)
- docker-compose, gateway, nginx 설정 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:38:30 +09:00

285 lines
17 KiB
HTML

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI 어시스턴트</title>
<link rel="preload" href="https://cdn.tailwindcss.com" as="script">
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="/static/css/tkqc-common.css?v=20260306">
<link rel="stylesheet" href="/static/css/ai-assistant.css?v=20260306">
</head>
<body>
<!-- 로딩 스크린 -->
<div id="loadingScreen" class="fixed inset-0 bg-white z-50 flex items-center justify-center">
<div class="text-center">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-600 mx-auto mb-4"></div>
<p class="text-gray-600">AI 어시스턴트를 불러오는 중...</p>
</div>
</div>
<!-- 메인 콘텐츠 -->
<div id="mainContent" class="min-h-screen">
<!-- 공통 헤더 -->
<div id="commonHeader"></div>
<main class="container mx-auto px-4 py-8 content-fade-in" style="padding-top: 72px;">
<!-- 페이지 헤더 -->
<div class="bg-white rounded-xl shadow-sm p-6 mb-6">
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900 flex items-center">
<i class="fas fa-robot text-purple-500 mr-3"></i>
AI 어시스턴트
</h1>
<p class="text-gray-600 mt-1">AI 기반 부적합 분석, 검색, 질의응답을 한곳에서 사용하세요</p>
</div>
</div>
</div>
<!-- 1. 상태 카드 (3열 그리드) -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="status-card bg-white rounded-xl shadow-sm p-5 border-l-4 border-purple-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500">AI 서비스</p>
<p class="text-lg font-bold mt-1" id="aiStatusText">확인 중...</p>
</div>
<div id="aiStatusIcon" class="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center">
<i class="fas fa-spinner fa-spin text-gray-400"></i>
</div>
</div>
</div>
<div class="status-card bg-white rounded-xl shadow-sm p-5 border-l-4 border-indigo-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500">임베딩 데이터</p>
<p class="text-lg font-bold mt-1" id="aiEmbeddingCount">-</p>
</div>
<div class="w-10 h-10 rounded-full bg-indigo-50 flex items-center justify-center">
<i class="fas fa-database text-indigo-500"></i>
</div>
</div>
</div>
<div class="status-card bg-white rounded-xl shadow-sm p-5 border-l-4 border-blue-500">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500">AI 모델</p>
<p class="text-lg font-bold mt-1" id="aiModelName">-</p>
</div>
<div class="w-10 h-10 rounded-full bg-blue-50 flex items-center justify-center">
<i class="fas fa-brain text-blue-500"></i>
</div>
</div>
</div>
</div>
<!-- 2. AI Q&A (메인 — 채팅형) -->
<div class="section-card bg-white rounded-xl shadow-sm p-6 mb-6">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<i class="fas fa-comments text-purple-500 mr-2"></i>
<h2 class="text-lg font-semibold text-gray-800">AI Q&A</h2>
<span class="ml-2 text-xs bg-purple-100 text-purple-600 px-2 py-0.5 rounded-full">과거 사례 기반</span>
</div>
<button onclick="clearChat()" class="text-sm text-gray-400 hover:text-gray-600 transition-colors">
<i class="fas fa-trash-alt mr-1"></i>대화 초기화
</button>
</div>
<!-- 채팅 히스토리 -->
<div id="chatContainer" class="chat-container bg-gray-50 rounded-lg p-4 mb-4 min-h-[200px]">
<div class="text-center text-gray-400 text-sm py-8" id="chatPlaceholder">
<i class="fas fa-robot text-4xl mb-3 text-gray-300"></i>
<p>부적합 관련 질문을 입력하세요.</p>
<p class="text-xs mt-1">과거 사례를 분석하여 답변합니다.</p>
</div>
</div>
<!-- 빠른 질문 템플릿 -->
<div class="flex flex-wrap gap-2 mb-3">
<button onclick="setQuickQuestion('최근 가장 많이 발생하는 부적합 유형은?')"
class="quick-question-btn text-xs px-3 py-1.5 rounded-full bg-white text-gray-600">
<i class="fas fa-chart-pie mr-1 text-purple-400"></i>많이 발생하는 유형
</button>
<button onclick="setQuickQuestion('용접 불량의 주요 원인과 해결방법은?')"
class="quick-question-btn text-xs px-3 py-1.5 rounded-full bg-white text-gray-600">
<i class="fas fa-fire mr-1 text-orange-400"></i>용접 불량 원인
</button>
<button onclick="setQuickQuestion('자재 관련 부적합을 줄이려면 어떻게 해야 하나요?')"
class="quick-question-btn text-xs px-3 py-1.5 rounded-full bg-white text-gray-600">
<i class="fas fa-box mr-1 text-blue-400"></i>자재 부적합 개선
</button>
<button onclick="setQuickQuestion('반복적으로 발생하는 부적합 패턴이 있나요?')"
class="quick-question-btn text-xs px-3 py-1.5 rounded-full bg-white text-gray-600">
<i class="fas fa-redo mr-1 text-red-400"></i>반복 패턴 분석
</button>
</div>
<!-- 입력 영역 -->
<div class="flex flex-col md:flex-row gap-2">
<div class="flex-1">
<textarea id="qaQuestion" rows="3"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 resize-none text-sm"
placeholder="질문을 입력하세요... (Ctrl+Enter로 전송)"
onkeydown="if(event.ctrlKey && event.key==='Enter') submitQuestion()"></textarea>
</div>
<div class="flex flex-col gap-2 md:w-48">
<select id="qaProjectFilter" class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500">
<option value="">전체 프로젝트</option>
</select>
<button onclick="submitQuestion()"
class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors text-sm font-medium">
<i class="fas fa-paper-plane mr-1"></i>질문하기
</button>
</div>
</div>
</div>
<!-- 3. 시맨틱 검색 -->
<div class="section-card bg-white rounded-xl shadow-sm p-6 mb-6">
<div class="flex items-center mb-4">
<i class="fas fa-search text-indigo-500 mr-2"></i>
<h2 class="text-lg font-semibold text-gray-800">시맨틱 검색</h2>
<span class="ml-2 text-xs bg-indigo-100 text-indigo-600 px-2 py-0.5 rounded-full">유사 부적합 찾기</span>
</div>
<div class="flex flex-col md:flex-row gap-2 mb-4">
<input type="text" id="searchQuery"
class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm"
placeholder="부적합 내용을 자연어로 검색하세요... (예: 볼트 누락, 용접 불량)"
onkeydown="if(event.key==='Enter') executeSemanticSearch()">
<select id="searchProjectFilter" class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-indigo-500 md:w-40">
<option value="">전체 프로젝트</option>
</select>
<select id="searchCategoryFilter" class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-indigo-500 md:w-40">
<option value="">전체 카테고리</option>
<option value="civil">토목</option>
<option value="architecture">건축</option>
<option value="mechanical">기계</option>
<option value="electrical">전기</option>
<option value="piping">배관</option>
<option value="instrument">계장</option>
<option value="painting">도장</option>
<option value="insulation">보온</option>
<option value="fireproof">내화</option>
<option value="other">기타</option>
</select>
<select id="searchResultCount" class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-indigo-500 md:w-28">
<option value="5">5건</option>
<option value="10" selected>10건</option>
<option value="20">20건</option>
</select>
<button onclick="executeSemanticSearch()"
class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors text-sm whitespace-nowrap">
<i class="fas fa-search mr-1"></i>검색
</button>
</div>
<div id="searchLoading" class="hidden text-center py-4">
<i class="fas fa-spinner fa-spin text-indigo-500 mr-1"></i>
<span class="text-sm text-gray-500">AI 검색 중...</span>
</div>
<div id="searchResults" class="space-y-2">
<!-- 검색 결과 -->
</div>
</div>
<!-- 4. 패턴 분석 -->
<div class="section-card bg-white rounded-xl shadow-sm p-6 mb-6">
<div class="flex items-center mb-4">
<i class="fas fa-chart-bar text-green-500 mr-2"></i>
<h2 class="text-lg font-semibold text-gray-800">패턴 분석</h2>
<span class="ml-2 text-xs bg-green-100 text-green-600 px-2 py-0.5 rounded-full">부적합 패턴 파악</span>
</div>
<div class="flex flex-col md:flex-row gap-2 mb-4">
<textarea id="patternInput" rows="2"
class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 resize-none text-sm"
placeholder="분석할 부적합 내용을 입력하세요... (예: 배관 용접부 결함)"></textarea>
<button onclick="executePatternAnalysis()"
class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors text-sm whitespace-nowrap self-end">
<i class="fas fa-chart-bar mr-1"></i>패턴 분석
</button>
</div>
<div id="patternLoading" class="hidden text-center py-4">
<i class="fas fa-spinner fa-spin text-green-500 mr-1"></i>
<span class="text-sm text-gray-500">패턴 분석 중...</span>
</div>
<div id="patternResults" class="hidden">
<!-- 패턴 분석 결과 -->
</div>
</div>
<!-- 5. AI 분류 테스트 -->
<div class="section-card bg-white rounded-xl shadow-sm p-6 mb-6">
<div class="flex items-center mb-4">
<i class="fas fa-tags text-amber-500 mr-2"></i>
<h2 class="text-lg font-semibold text-gray-800">AI 분류 테스트</h2>
<span class="ml-2 text-xs bg-amber-100 text-amber-600 px-2 py-0.5 rounded-full">기본 vs RAG 비교</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">부적합 설명</label>
<textarea id="classifyDescription" rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-amber-500 resize-none text-sm"
placeholder="부적합 설명을 입력하세요..."></textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">상세 내용 (선택)</label>
<textarea id="classifyDetail" rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-amber-500 resize-none text-sm"
placeholder="상세 내용을 입력하세요..."></textarea>
</div>
</div>
<div class="flex gap-2 mb-4">
<button onclick="executeClassification(false)"
class="px-4 py-2 bg-amber-500 text-white rounded-lg hover:bg-amber-600 transition-colors text-sm">
<i class="fas fa-tag mr-1"></i>기본 분류
</button>
<button onclick="executeClassification(true)"
class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors text-sm">
<i class="fas fa-tags mr-1"></i>RAG 분류
</button>
</div>
<div id="classifyLoading" class="hidden text-center py-4">
<i class="fas fa-spinner fa-spin text-amber-500 mr-1"></i>
<span class="text-sm text-gray-500">AI 분류 중...</span>
</div>
<div id="classifyResults" class="hidden">
<!-- 분류 결과 -->
</div>
</div>
</main>
</div>
<!-- AI 이슈 상세 모달 -->
<div id="aiIssueModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center" onclick="if(event.target===this)this.classList.add('hidden')">
<div class="bg-white rounded-xl shadow-2xl w-full max-w-lg mx-4 max-h-[80vh] overflow-y-auto">
<div class="sticky top-0 bg-white border-b px-5 py-3 flex justify-between items-center rounded-t-xl">
<h3 id="aiIssueModalTitle" class="font-bold text-gray-800"></h3>
<button onclick="document.getElementById('aiIssueModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i>
</button>
</div>
<div id="aiIssueModalBody" class="p-5 text-sm text-gray-700 space-y-3"></div>
</div>
</div>
<!-- 스크립트 -->
<script src="/static/js/core/permissions.js?v=20260306"></script>
<script src="/static/js/components/common-header.js?v=20260306"></script>
<script src="/static/js/core/page-manager.js?v=20260306"></script>
<script src="/static/js/core/auth-manager.js?v=20260306"></script>
<script src="/static/js/utils/issue-helpers.js?v=20260306"></script>
<script src="/static/js/utils/toast.js?v=20260306"></script>
<script src="/static/js/components/mobile-bottom-nav.js?v=20260306"></script>
<script src="/static/js/api.js?v=20260306"></script>
<script src="/static/js/pages/ai-assistant.js?v=20260306"></script>
</body>
</html>