diff --git a/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/ExplanationController.class b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/ExplanationController.class new file mode 100644 index 0000000..cca7ffe Binary files /dev/null and b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/ExplanationController.class differ diff --git a/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/RelationController.class b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/RelationController.class new file mode 100644 index 0000000..6da54e2 Binary files /dev/null and b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/RelationController.class differ diff --git a/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/SectionController.class b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/SectionController.class new file mode 100644 index 0000000..fef6784 Binary files /dev/null and b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/SectionController.class differ diff --git a/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/SuggestionController.class b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/SuggestionController.class new file mode 100644 index 0000000..adb9073 Binary files /dev/null and b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/SuggestionController.class differ diff --git a/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TermController.class b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TermController.class new file mode 100644 index 0000000..09790ad Binary files /dev/null and b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TermController.class differ diff --git a/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TodoController.class b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TodoController.class new file mode 100644 index 0000000..c5bcf13 Binary files /dev/null and b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TodoController.class differ diff --git a/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TranscriptController.class b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TranscriptController.class new file mode 100644 index 0000000..1ce5622 Binary files /dev/null and b/ai/bin/main/com/unicorn/hgzero/ai/infra/controller/TranscriptController.class differ diff --git a/design/구현방안-관련자료.md b/design/구현방안-관련자료.md new file mode 100644 index 0000000..bd165e4 --- /dev/null +++ b/design/구현방안-관련자료.md @@ -0,0 +1,1480 @@ +# 회의주제 관련자료 구현 방안 + +**문서 버전**: 1.0 +**작성일**: 2025-10-27 +**작성자**: AI Service 팀 +**관련 유저스토리**: UFR-AI-040 (관련회의록연결) + +--- + +## 목차 +1. [개요](#개요) +2. [최종 선정 방안](#최종-선정-방안) +3. [데이터 수집 및 처리](#데이터-수집-및-처리) +4. [Claude AI 연동 구조](#claude-ai-연동-구조) +5. [Architectural Decisions](#architectural-decisions) +6. [구현 로드맵](#구현-로드맵) +7. [성능 및 품질 기준](#성능-및-품질-기준) + +--- + +## 개요 + +### 목표 +회의 참석자가 이전 회의 내용을 쉽게 참조할 수 있도록, **AI가 자동으로 관련 회의록을 찾아 유사한 내용을 요약**하여 제공합니다. + +### 핵심 차별화 포인트 +- **단순 키워드 검색 ❌** → **벡터 유사도 + 맥락 기반 추천 ✅** +- 전체 회의록을 열지 않고도 **유사 내용 요약(3문장)**으로 핵심 파악 +- 같은 폴더 내 회의록 우선 추천 +- Claude 3.5 Sonnet으로 정확한 유사 내용 추출 + +### 용어집과의 차이점 + +| 구분 | 용어집 | 관련회의록 | +|-----|--------|-----------| +| **데이터 규모** | 소규모 (수백 건) | 대규모 (수만 건) | +| **Vector DB** | Shared Azure AI Search | 별도 Azure AI Search 인덱스 | +| **청크 크기** | 500-1000 tokens | 2000-2500 tokens | +| **검색 방식** | 키워드 우선 | Vector 우선 + Semantic Ranking | +| **Claude 역할** | 맥락 설명 생성 | 유사 내용 요약 생성 | +| **업데이트 주기** | 수동 | 야간 배치 자동 | + +--- + +## 최종 선정 방안 + +**10회 반복 평가 결과, 아래 방안을 최종 선정했습니다:** + +### ✅ **Azure AI Search + Semantic Ranking + Claude 3.5 Sonnet** + +``` +┌─────────────────────────────────────────────────────────┐ +│ 사용자: 회의 종료 또는 회의록 수정 │ +└────────────────┬────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ Step 1: 현재 회의 분석 (AI Service) │ +│ - 회의 제목, 안건, 키워드, 참석자 추출 │ +│ - 벡터 임베딩 생성 (Azure OpenAI Embedding) │ +└────────────────┬────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ Step 2: Hybrid Search (Azure AI Search) │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ • Keyword Search (제목, 안건) │ │ +│ │ • Vector Search (임베딩 유사도) │ │ +│ │ • Folder Filter (같은 폴더 우선, 가중치 +20%) │ │ +│ │ • Semantic Ranking (Top 50 → Top 10) │ │ +│ │ • RRF (Reciprocal Rank Fusion) 통합 │ │ +│ └──────────────────────────────────────────────────┘ │ +│ 결과: Top 5 후보 회의록 │ +└────────────────┬────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ Step 3: 관련도 필터링 (AI Service) │ +│ - 관련도 < 70% 제외 │ +│ - 최대 3개 선정 (관련도 높은 순) │ +└────────────────┬────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ Step 4: 유사 내용 요약 생성 (Claude API) │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ 각 회의록에 대해: │ │ +│ │ 1. 현재 회의와 과거 회의록 비교 │ │ +│ │ 2. 유사한 안건/결정사항 추출 │ │ +│ │ 3. 3문장으로 요약 (Claude 3.5 Sonnet) │ │ +│ └──────────────────────────────────────────────────┘ │ +└────────────────┬────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ Step 5: 캐싱 및 결과 반환 │ +│ - Redis L1: 회의별 추천 결과 (1시간) │ +│ - PostgreSQL L2: 회의별 매핑 테이블 │ +│ - Frontend: Progressive Loading (제목 → 요약) │ +└────────────────────────────────────────────────────────┘ +``` + +### 선정 이유 + +| 평가 기준 | 점수 | 이유 | +|----------|------|------| +| **정확도** | 9/10 | Semantic Ranking + RRF로 높은 정확도 | +| **성능** | 9/10 | 캐싱 + 배치 처리로 빠른 응답 | +| **비용** | 8/10 | Azure 통합으로 비용 효율적 | +| **확장성** | 9/10 | 수만 건 문서 처리 가능 | +| **MVP 적합** | 10/10 | 회의록만 처리, 점진적 확장 | +| **총점** | **45/50** | | + +--- + +## 데이터 수집 및 처리 + +### 1. 데이터 소스 및 우선순위 + +#### Phase 1: MVP (회의록만) +- **이전 회의록**: 같은 폴더 내 과거 회의록 + - Meeting 서비스 DB에서 확정된 회의록 추출 + - 제목, 안건, 상세 요약, 결정사항, 참석자 포함 + +#### Phase 2: 확장 (프로젝트 문서) +- **프로젝트 회의록**: + - 요구사항 정의서 (Requirements Document) + - 설계 문서 (Design Document) + - 프로젝트 수행 결과서 (Project Report) + - 분기별 진행 보고서 + +#### Phase 3: 전체 확장 (업무/운영 문서) +- **업무 관련 회의록**: + - 업무 매뉴얼 (User Manual) + - 정책 및 규정 (Policy & Regulations) + - 표준화 문서 (Standards Document) + +- **운영 관련 회의록**: + - 장애 보고서 (Incident Report) + - 고객 응대 문서 (Customer Support Log) + - 유지보수 기록 (Maintenance Log) + +### 2. 데이터 수집 파이프라인 + +```java +/** + * 회의록 수집 배치 서비스 + * 매일 새벽 3시 실행 + */ +@Service +public class MeetingDocumentCollectionService { + + @Scheduled(cron = "0 0 3 * * *") + public void collectDailyDocuments() { + log.info("회의록 수집 배치 시작"); + + // Step 1: 어제 확정된 회의록 조회 + LocalDate yesterday = LocalDate.now().minusDays(1); + List confirmedMeetings = meetingRepository + .findByStatusAndUpdatedDateBetween( + MeetingStatus.CONFIRMED, + yesterday.atStartOfDay(), + yesterday.plusDays(1).atStartOfDay() + ); + + log.info("수집 대상 회의록 {}건", confirmedMeetings.size()); + + // Step 2: 각 회의록 처리 + for (Meeting meeting : confirmedMeetings) { + try { + processDocument(meeting); + } catch (Exception e) { + log.error("회의록 처리 실패: {}", meeting.getId(), e); + // 실패한 문서는 재처리 큐에 추가 + failedQueue.add(meeting.getId()); + } + } + + log.info("회의록 수집 배치 완료"); + } + + private void processDocument(Meeting meeting) { + // 데이터 추출 → 정제 → 벡터화 + DocumentMetadata metadata = extractMetadata(meeting); + List chunks = chunkDocument(meeting); + vectorizeAndIndex(metadata, chunks); + } +} +``` + +### 3. 데이터 추출 (Extraction) + +```java +/** + * 회의록에서 메타데이터와 본문 추출 + */ +public DocumentMetadata extractMetadata(Meeting meeting) { + return DocumentMetadata.builder() + .documentId(meeting.getId().toString()) + .documentType(DocumentType.MEETING_MINUTES) + .title(meeting.getTitle()) + .createdDate(meeting.getCreatedAt()) + .folder(meeting.getFolder()) + .participants(meeting.getParticipants().stream() + .map(User::getName) + .collect(Collectors.toList())) + .keywords(extractKeywords(meeting)) + .build(); +} + +/** + * 회의록에서 키워드 추출 + */ +private List extractKeywords(Meeting meeting) { + List keywords = new ArrayList<>(); + + // 안건에서 명사 추출 + for (Agenda agenda : meeting.getAgendas()) { + keywords.addAll(nlpService.extractNouns(agenda.getTitle())); + keywords.addAll(nlpService.extractNouns(agenda.getSummary())); + } + + // 중복 제거 및 빈도순 정렬 + return keywords.stream() + .distinct() + .limit(20) + .collect(Collectors.toList()); +} +``` + +### 4. 데이터 정제 (Refinement) + +```java +/** + * 회의록 데이터 정제 + */ +public String refineContent(Meeting meeting) { + StringBuilder refinedContent = new StringBuilder(); + + // 제목 + refinedContent.append("# ").append(meeting.getTitle()).append("\n\n"); + + // 기본 정보 + refinedContent.append("**일시**: ").append(meeting.getMeetingDate()).append("\n"); + refinedContent.append("**참석자**: ") + .append(meeting.getParticipants().stream() + .map(User::getName) + .collect(Collectors.joining(", "))) + .append("\n\n"); + + // 안건별 내용 + for (Agenda agenda : meeting.getAgendas()) { + refinedContent.append("## ").append(agenda.getTitle()).append("\n\n"); + + // AI 한줄 요약 + if (agenda.getOneLinerSummary() != null) { + refinedContent.append("**요약**: ") + .append(agenda.getOneLinerSummary()).append("\n\n"); + } + + // 상세 내용 + refinedContent.append(agenda.getDetailedSummary()).append("\n\n"); + + // 결정사항 + if (!agenda.getDecisions().isEmpty()) { + refinedContent.append("**결정사항**:\n"); + for (String decision : agenda.getDecisions()) { + refinedContent.append("- ").append(decision).append("\n"); + } + refinedContent.append("\n"); + } + } + + // 불필요한 문자 제거 + String cleaned = refinedContent.toString() + .replaceAll("\\s+", " ") // 연속 공백 제거 + .replaceAll("\\n{3,}", "\n\n") // 연속 줄바꿈 제거 + .trim(); + + return cleaned; +} +``` + +### 5. 청킹 (Chunking) + +**전략**: **Semantic-based Chunking** (의미 기반 분할) + +```java +/** + * 회의록을 의미 단위로 청킹 + */ +public List chunkDocument(Meeting meeting) { + List chunks = new ArrayList<>(); + String refinedContent = refineContent(meeting); + + // 안건 단위로 청킹 (Primary Strategy) + int chunkIndex = 0; + for (Agenda agenda : meeting.getAgendas()) { + String agendaContent = buildAgendaContent(agenda); + + // 안건이 너무 크면 추가 분할 + if (TokenCounter.count(agendaContent) > 2500) { + List subChunks = splitLargeAgenda(agendaContent, 2000); + for (String subChunk : subChunks) { + chunks.add(createChunk(meeting, agenda, subChunk, chunkIndex++)); + } + } else { + chunks.add(createChunk(meeting, agenda, agendaContent, chunkIndex++)); + } + } + + // 전체 요약 청크 추가 (Overview Chunk) + String overviewChunk = buildOverviewChunk(meeting); + chunks.add(createChunk(meeting, null, overviewChunk, chunkIndex)); + + return chunks; +} + +/** + * 큰 안건을 추가 분할 + */ +private List splitLargeAgenda(String content, int maxTokens) { + List chunks = new ArrayList<>(); + + // 문단 단위로 분할 + String[] paragraphs = content.split("\n\n"); + StringBuilder currentChunk = new StringBuilder(); + + for (String paragraph : paragraphs) { + if (TokenCounter.count(currentChunk.toString() + paragraph) > maxTokens) { + chunks.add(currentChunk.toString()); + currentChunk = new StringBuilder(paragraph); + } else { + currentChunk.append("\n\n").append(paragraph); + } + } + + if (currentChunk.length() > 0) { + chunks.add(currentChunk.toString()); + } + + return chunks; +} + +/** + * DocumentChunk 생성 + */ +private DocumentChunk createChunk(Meeting meeting, Agenda agenda, + String content, int index) { + return DocumentChunk.builder() + .documentId(meeting.getId().toString()) + .chunkIndex(index) + .content(content) + .agendaId(agenda != null ? agenda.getId().toString() : null) + .agendaTitle(agenda != null ? agenda.getTitle() : "전체 요약") + .tokenCount(TokenCounter.count(content)) + .build(); +} +``` + +### 6. 벡터화 (Vectorization) + +```java +/** + * 청크를 벡터화하고 Azure AI Search에 인덱싱 + */ +public void vectorizeAndIndex(DocumentMetadata metadata, + List chunks) { + + // Step 1: Azure OpenAI Embedding API 호출 + List requests = chunks.stream() + .map(chunk -> new EmbeddingRequest(chunk.getContent())) + .collect(Collectors.toList()); + + List embeddings = azureOpenAIClient.createEmbeddings( + "text-embedding-ada-002", + requests + ); + + // Step 2: Azure AI Search Document 생성 + List searchDocuments = new ArrayList<>(); + for (int i = 0; i < chunks.size(); i++) { + DocumentChunk chunk = chunks.get(i); + float[] embedding = embeddings.get(i); + + SearchDocument doc = new SearchDocument(); + doc.put("id", metadata.getDocumentId() + "_chunk_" + i); + doc.put("documentId", metadata.getDocumentId()); + doc.put("documentType", metadata.getDocumentType().name()); + doc.put("title", metadata.getTitle()); + doc.put("folder", metadata.getFolder()); + doc.put("createdDate", metadata.getCreatedDate()); + doc.put("participants", metadata.getParticipants()); + doc.put("keywords", metadata.getKeywords()); + doc.put("agendaId", chunk.getAgendaId()); + doc.put("agendaTitle", chunk.getAgendaTitle()); + doc.put("chunkIndex", chunk.getChunkIndex()); + doc.put("content", chunk.getContent()); + doc.put("contentVector", embedding); + doc.put("tokenCount", chunk.getTokenCount()); + + searchDocuments.add(doc); + } + + // Step 3: Batch Indexing (50개씩) + for (int i = 0; i < searchDocuments.size(); i += 50) { + List batch = searchDocuments.subList( + i, + Math.min(i + 50, searchDocuments.size()) + ); + + searchIndexClient.uploadDocuments(batch); + } + + log.info("문서 벡터화 완료: {} ({}개 청크)", + metadata.getDocumentId(), chunks.size()); +} +``` + +### 7. 증분 업데이트 (Incremental Update) + +```java +/** + * 증분 업데이트: 변경된 문서만 재처리 + */ +@Service +public class IncrementalUpdateService { + + public void updateModifiedDocuments() { + // 마지막 업데이트 이후 수정된 회의록 조회 + LocalDateTime lastUpdate = getLastUpdateTime(); + List modifiedMeetings = meetingRepository + .findByUpdatedAtAfterAndStatus(lastUpdate, MeetingStatus.CONFIRMED); + + for (Meeting meeting : modifiedMeetings) { + // 기존 문서 삭제 + deleteDocumentFromIndex(meeting.getId()); + + // 새로 인덱싱 + processDocument(meeting); + } + + updateLastUpdateTime(LocalDateTime.now()); + } + + private void deleteDocumentFromIndex(UUID meetingId) { + // Azure AI Search에서 해당 문서의 모든 청크 삭제 + String filter = String.format("documentId eq '%s'", meetingId); + searchIndexClient.deleteDocumentsByFilter(filter); + } +} +``` + +--- + +## Claude AI 연동 구조 + +### 1. 관련 회의록 검색 프로세스 + +```java +/** + * 관련 회의록 검색 및 요약 생성 + */ +@Service +public class RelatedMeetingService { + + public List findRelatedMeetings(UUID currentMeetingId) { + // Step 1: 캐시 확인 + String cacheKey = "related_meetings:" + currentMeetingId; + List cached = redisTemplate.opsForValue().get(cacheKey); + if (cached != null) { + return cached; + } + + // Step 2: 현재 회의 분석 + Meeting currentMeeting = meetingRepository.findById(currentMeetingId) + .orElseThrow(() -> new EntityNotFoundException("Meeting not found")); + + MeetingContext context = analyzeMeeting(currentMeeting); + + // Step 3: Hybrid Search (Azure AI Search) + List searchResults = performHybridSearch(context); + + // Step 4: 관련도 필터링 (상위 3개, 70% 이상) + List topResults = searchResults.stream() + .filter(r -> r.getScore() >= 0.70) + .limit(3) + .collect(Collectors.toList()); + + // Step 5: Claude API로 유사 내용 요약 생성 + List relatedMeetings = generateSummaries( + currentMeeting, + topResults + ); + + // Step 6: 캐싱 (1시간) + redisTemplate.opsForValue().set(cacheKey, relatedMeetings, + Duration.ofHours(1)); + + return relatedMeetings; + } +} +``` + +### 2. 현재 회의 분석 + +```java +/** + * 현재 회의 맥락 분석 + */ +public MeetingContext analyzeMeeting(Meeting meeting) { + // 제목, 안건, 키워드 추출 + List keywords = extractKeywords(meeting); + + // 전체 내용 임베딩 생성 + String fullContent = refineContent(meeting); + float[] embedding = azureOpenAIClient.createEmbedding( + "text-embedding-ada-002", + fullContent + ); + + return MeetingContext.builder() + .meetingId(meeting.getId()) + .title(meeting.getTitle()) + .folder(meeting.getFolder()) + .keywords(keywords) + .participants(meeting.getParticipants()) + .embedding(embedding) + .build(); +} +``` + +### 3. Hybrid Search with Semantic Ranking + +```java +/** + * 하이브리드 검색: Keyword + Vector + Semantic Ranking + */ +public List performHybridSearch(MeetingContext context) { + SearchOptions options = new SearchOptions(); + + // Keyword Search Query + String keywordQuery = String.join(" ", context.getKeywords()); + options.setSearchText(keywordQuery); + + // Vector Search + VectorSearchOptions vectorOptions = new VectorSearchOptions(); + vectorOptions.setVectorQueries(Arrays.asList( + new VectorQuery(context.getEmbedding()) + .setKNearestNeighborsCount(50) + .setFields("contentVector") + )); + options.setVectorSearchOptions(vectorOptions); + + // Folder Filter (같은 폴더 우선, 가중치 +20%) + String filter = String.format("folder eq '%s'", context.getFolder()); + options.setFilter(filter); + + // Semantic Ranking (Top 50 → Top 10) + options.setSemanticSearchOptions(new SemanticSearchOptions() + .setSemanticConfigurationName("meeting-semantic-config") + .setQueryType(QueryType.SEMANTIC) + .setQueryAnswerCount(10) + ); + + // Select Fields + options.setSelect("documentId", "title", "createdDate", "content", + "agendaTitle", "folder"); + options.setTop(50); + + // Execute Search + SearchPagedIterable results = searchIndexClient.search( + keywordQuery, + options, + Context.NONE + ); + + // RRF (Reciprocal Rank Fusion) 적용 + return applyRRF(results); +} + +/** + * RRF (Reciprocal Rank Fusion) 통합 + */ +private List applyRRF(SearchPagedIterable results) { + Map scores = new HashMap<>(); + int rank = 1; + + for (SearchResult result : results) { + String docId = result.getDocument(SearchDocument.class) + .get("documentId").toString(); + + // RRF Score: 1 / (rank + 60) + double rrfScore = 1.0 / (rank + 60); + scores.merge(docId, rrfScore, Double::sum); + rank++; + } + + // 점수 기준 정렬 + return scores.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(10) + .map(entry -> findSearchResult(results, entry.getKey())) + .collect(Collectors.toList()); +} +``` + +### 4. Claude API 요약 생성 + +#### 요청 JSON 구조 + +```json +{ + "model": "claude-3-5-sonnet-20241022", + "max_tokens": 500, + "temperature": 0.3, + "system": "당신은 회의록 분석 전문가입니다. 두 회의록을 비교하여 유사한 내용을 정확하게 추출하고 간결하게 요약합니다.", + "messages": [ + { + "role": "user", + "content": "아래 두 회의록을 비교하여 유사한 내용을 정확히 3문장으로 요약해주세요.\n\n## 현재 회의\n제목: {currentTitle}\n날짜: {currentDate}\n안건:\n{currentAgendas}\n\n## 과거 회의\n제목: {pastTitle}\n날짜: {pastDate}\n안건:\n{pastAgendas}\n\n## 요구사항\n1. 두 회의에서 공통적으로 논의된 주제나 결정사항을 찾아주세요\n2. 정확히 3문장으로 요약해주세요 (각 문장은 한 문단)\n3. 구체적인 내용을 포함해주세요 (예: 날짜, 수치, 결정사항)\n4. 과거 회의에서 실제로 다뤄진 내용만 포함해주세요 (환각 금지)" + } + ] +} +``` + +#### 응답 JSON 구조 + +```json +{ + "id": "msg_01XYZ...", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "두 회의 모두 신제품 개발 일정을 중점적으로 논의했으며, 과거 회의에서는 1차 프로토타입을 11월 15일까지 완성하기로 결정했습니다.\n\n타겟 고객층 설정에서 20-30대 모바일 사용자를 우선 대상으로 하는 전략이 양쪽 회의에서 공통적으로 언급되었습니다.\n\n마케팅 예산 배분은 과거 회의에서 SNS 광고 60%, 인플루언서 마케팅 40%로 결정되었으며, 현재 회의에서도 유사한 비율로 논의되고 있습니다." + } + ], + "model": "claude-3-5-sonnet-20241022", + "stop_reason": "end_turn", + "usage": { + "input_tokens": 1250, + "output_tokens": 180 + } +} +``` + +### 5. Java 서비스 구현 + +```java +/** + * Claude API를 통한 유사 내용 요약 생성 + */ +public List generateSummaries( + Meeting currentMeeting, + List topResults +) { + List relatedMeetings = new ArrayList<>(); + + for (SearchResult result : topResults) { + try { + // Step 1: 과거 회의록 조회 + UUID pastMeetingId = UUID.fromString( + result.getDocument(SearchDocument.class) + .get("documentId").toString() + ); + Meeting pastMeeting = meetingRepository.findById(pastMeetingId) + .orElseThrow(); + + // Step 2: Claude API 요청 생성 + ClaudeRequest request = buildClaudeRequest( + currentMeeting, + pastMeeting + ); + + // Step 3: Claude API 호출 + ClaudeResponse response = claudeClient.sendMessage(request); + + // Step 4: 요약 추출 + String summary = response.getContent().get(0).getText(); + + // Step 5: RelatedMeeting 객체 생성 + relatedMeetings.add(RelatedMeeting.builder() + .meetingId(pastMeeting.getId()) + .title(pastMeeting.getTitle()) + .meetingDate(pastMeeting.getMeetingDate()) + .relevanceScore(result.getScore()) + .similarContentSummary(summary) + .build()); + + } catch (Exception e) { + log.error("Claude API 요약 생성 실패: {}", + result.getDocumentId(), e); + // Fallback: 요약 없이 기본 정보만 제공 + relatedMeetings.add(createFallbackRelatedMeeting(result)); + } + } + + return relatedMeetings; +} + +/** + * Claude API 요청 생성 + */ +private ClaudeRequest buildClaudeRequest(Meeting current, Meeting past) { + String prompt = String.format(""" + 아래 두 회의록을 비교하여 유사한 내용을 정확히 3문장으로 요약해주세요. + + ## 현재 회의 + 제목: %s + 날짜: %s + 안건: + %s + + ## 과거 회의 + 제목: %s + 날짜: %s + 안건: + %s + + ## 요구사항 + 1. 두 회의에서 공통적으로 논의된 주제나 결정사항을 찾아주세요 + 2. 정확히 3문장으로 요약해주세요 (각 문장은 한 문단) + 3. 구체적인 내용을 포함해주세요 (예: 날짜, 수치, 결정사항) + 4. 과거 회의에서 실제로 다뤄진 내용만 포함해주세요 (환각 금지) + """, + current.getTitle(), + current.getMeetingDate(), + formatAgendas(current.getAgendas()), + past.getTitle(), + past.getMeetingDate(), + formatAgendas(past.getAgendas()) + ); + + return ClaudeRequest.builder() + .model("claude-3-5-sonnet-20241022") + .maxTokens(500) + .temperature(0.3) + .system("당신은 회의록 분석 전문가입니다. 두 회의록을 비교하여 유사한 내용을 정확하게 추출하고 간결하게 요약합니다.") + .messages(List.of( + new Message("user", prompt) + )) + .build(); +} + +/** + * 안건 포맷팅 + */ +private String formatAgendas(List agendas) { + return agendas.stream() + .map(a -> String.format("- %s: %s", + a.getTitle(), + a.getOneLinerSummary())) + .collect(Collectors.joining("\n")); +} + +/** + * Fallback: Claude API 실패 시 + */ +private RelatedMeeting createFallbackRelatedMeeting(SearchResult result) { + SearchDocument doc = result.getDocument(SearchDocument.class); + + return RelatedMeeting.builder() + .meetingId(UUID.fromString(doc.get("documentId").toString())) + .title(doc.get("title").toString()) + .meetingDate(LocalDateTime.parse(doc.get("createdDate").toString())) + .relevanceScore(result.getScore()) + .similarContentSummary(null) // 요약 없음 + .build(); +} +``` + +### 6. API 응답 구조 + +#### GET `/api/ai/meetings/{meetingId}/related` + +**응답**: +```json +{ + "meetingId": "550e8400-e29b-41d4-a716-446655440000", + "relatedMeetings": [ + { + "meetingId": "660e8400-e29b-41d4-a716-446655440001", + "title": "Q4 회의록 작성 가이드 배포", + "meetingDate": "2024-10-01T14:00:00", + "relevanceScore": 95.5, + "relevanceLevel": "HIGH", + "similarContentSummary": "두 회의 모두 회의록 자동 작성 기능 개발을 중점적으로 논의했으며, 과거 회의에서는 AI 정확도 목표를 85% 이상으로 설정했습니다.\n\n요구사항 정의 단계에서 사용자가 수동으로 검증할 수 있는 기능을 포함하기로 결정했으며, 현재 회의에서도 유사한 검증 프로세스를 논의하고 있습니다.\n\n개발 일정은 과거 회의에서 1차 프로토타입을 11월 15일까지 완성하기로 결정했으며, 현재 회의에서도 유사한 일정으로 진행 중입니다.", + "url": "/meetings/660e8400-e29b-41d4-a716-446655440001" + }, + { + "meetingId": "770e8400-e29b-41d4-a716-446655440002", + "title": "신제품 개발 킥오프", + "meetingDate": "2024-09-15T10:00:00", + "relevanceScore": 82.3, + "relevanceLevel": "MEDIUM", + "similarContentSummary": "양쪽 회의에서 협업 도구 시장 분석을 수행했으며, 과거 회의에서는 경쟁사 대비 차별화 전략을 수립했습니다.\n\n1분기 신제품 개발 방향성이 논의되었고, 과거 회의에서는 AI 기반 자동화 기능을 핵심 차별화 포인트로 선정했습니다.\n\n타겟 시장은 중소기업 및 스타트업으로 설정했으며, 현재 회의에서도 동일한 시장을 대상으로 하고 있습니다.", + "url": "/meetings/770e8400-e29b-41d4-a716-446655440002" + }, + { + "meetingId": "880e8400-e29b-41d4-a716-446655440003", + "title": "API 설계 리뷰", + "meetingDate": "2024-09-28T15:00:00", + "relevanceScore": 78.1, + "relevanceLevel": "MEDIUM", + "similarContentSummary": "두 회의에서 RESTful API 설계 원칙을 논의했으며, 과거 회의에서는 보안 정책으로 JWT 토큰 기반 인증을 채택했습니다.\n\n마이크로서비스 아키텍처 구성이 공통 주제였으며, 과거 회의에서는 API Gateway로 AWS API Gateway를 선택했습니다.\n\n담당자별 역할 분담이 이루어졌고, 과거 회의에서는 백엔드 개발자 2명, 프론트엔드 개발자 2명으로 팀을 구성했습니다.", + "url": "/meetings/880e8400-e29b-41d4-a716-446655440003" + } + ], + "totalCount": 3, + "generatedAt": "2025-10-27T10:30:00" +} +``` + +--- + +## Architectural Decisions + +### AD-000: 통합 플랫폼 전략 (용어집 + 관련회의록) + +**결정**: 단일 Azure AI Search 계정에 두 개의 별도 인덱스 운영 + +**통합 아키텍처**: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Azure AI Search (단일 계정) │ +│ ┌──────────────────────┐ ┌────────────────────────────┐ │ +│ │ terms-index │ │ meetings-index │ │ +│ │ (용어집 전용) │ │ (관련회의록 전용) │ │ +│ │ - 소규모 (수백 건) │ │ - 대규모 (수만 건) │ │ +│ │ - 작은 청크(400 tok) │ │ - 큰 청크(2000-2500 tok) │ │ +│ │ - Keyword 우선 │ │ - Vector 우선 │ │ +│ └──────────────────────┘ └────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + ▲ + │ 공통 사용 +┌──────────────────────────┼────────────────────────────────┐ +│ Azure OpenAI Embedding │ Claude 3.5 Sonnet │ +│ text-embedding-ada-002 │ 맥락 설명 / 요약 생성 │ +└──────────────────────────┴────────────────────────────────┘ + ▲ + │ 공통 사용 +┌──────────────────────────┼────────────────────────────────┐ +│ Redis L1 Cache (1시간 TTL) │ +│ - 용어 설명 캐싱 │ +│ - 관련 회의록 추천 캐싱 │ +└─────────────────────────────────────────────────────────────┘ + ▲ ▲ + │ │ +┌──────────┴──────────┐ ┌───────────┴────────────┐ +│ CDN L2 Cache │ │ PostgreSQL L2 Cache │ +│ (용어 설명) │ │ (관련 회의록 매핑) │ +│ - 정적 콘텐츠 │ │ - 동적 관계 데이터 │ +└─────────────────────┘ └────────────────────────┘ +``` + +**대안 비교**: + +| 대안 | 장점 | 단점 | 점수 | +|-----|------|------|------| +| **단일 계정 + 별도 인덱스** ✅ | • 비용 절감 (단일 Standard tier)
• 독립적 최적화
• 통합 관리
• 확장성 | • 인덱스 수 제한 고려 필요 | **10/10** | +| 별도 Azure AI Search 계정 | • 완전 독립성
• 리소스 격리 | • 비용 2배 ($500/월)
• 관리 복잡도 증가 | 6/10 | +| 단일 인덱스 공유 | • 가장 저렴
• 관리 단순 | • 스키마 충돌
• 성능 저하
• 최적화 불가 | 3/10 | +| 별도 Vector DB (Pinecone 등) | • 특화 기능 | • 비용 증가
• 통합 복잡
• 일관성 부족 | 5/10 | + +**결정 이유**: +1. **비용 효율성**: Standard tier $250/월 공유 (별도 계정 대비 50% 절감) +2. **독립 최적화**: 각 기능별 최적 스키마 및 설정 +3. **운영 단순성**: 단일 플랫폼 통합 모니터링 +4. **확장성**: 각 인덱스 독립 스케일링 + +**공통 컴포넌트**: +- **Embedding**: Azure OpenAI text-embedding-ada-002 (일관된 벡터 공간) +- **LLM**: Claude 3.5 Sonnet (동일한 품질 기준) +- **L1 Cache**: Redis (통합 캐싱 플랫폼) + +**차별화 전략**: +- **용어집**: JSON 우선 + RAG fallback (하이브리드) +- **관련회의록**: Hybrid Search + Semantic Ranking (순수 벡터) +- **캐싱 L2**: CDN (정적) vs PostgreSQL (동적) + +**비용 분석** (월간): +``` +Azure AI Search Standard: $250 +Azure OpenAI Embedding: $55 (용어집 $25 + 관련자료 $30) +Claude API: $366 (용어집 $150 + 관련자료 $216) +Redis Enterprise: $50 +CDN: $10 +PostgreSQL Storage: $10 +───────────────────────────── +통합 총계: $741/월 + +개별 계정 대비 절감: $250/월 (25% 절감) +``` + +--- + +### AD-001: Vector Database 선정 - Azure AI Search (meetings-index) + +**결정**: Azure AI Search의 `meetings-index` 사용 (관련회의록 전용) + +**대안 비교**: + +| 대안 | 장점 | 단점 | 평가 | +|-----|------|------|------| +| **Azure AI Search** ✅ | • Semantic Ranking 내장
• Hybrid Search 지원
• Azure 생태계 통합
• 한글 형태소 분석 우수 | • 비용 높음 (Standard $250/월)
• 벡터 차원 제한 (최대 2048) | **9/10** | +| Pinecone | • 빠른 벡터 검색
• Serverless 옵션
• 간단한 API | • Keyword Search 약함
• 한글 지원 부족
• 별도 통합 필요 | 7/10 | +| Weaviate | • 오픈소스
• Hybrid Search 지원
• 비용 절감 | • 운영 부담
• 성능 튜닝 필요
• Azure 통합 약함 | 6/10 | +| Elasticsearch | • 검색 기능 강력
• 한글 형태소 분석
• 오픈소스 | • Vector Search 약함
• 직접 구축 필요
• Semantic Ranking 없음 | 7/10 | + +**선정 이유**: +1. **Semantic Ranking**: 키워드+벡터 결과를 의미적으로 재순위화 +2. **Hybrid Search**: Keyword + Vector를 RRF로 통합 +3. **Azure 통합**: Azure OpenAI Embedding과 원활한 연동 +4. **한글 지원**: Lucene Korean Analyzer 기본 제공 +5. **확장성**: 10만 건 이상 문서 처리 가능 + +**비용**: Standard tier $250/월 (10,000 documents 기준) + +--- + +### AD-002: Embedding Model 선정 - Azure OpenAI text-embedding-ada-002 + +**결정**: Azure OpenAI text-embedding-ada-002 사용 + +**대안 비교**: + +| 대안 | 장점 | 단점 | 평가 | +|-----|------|------|------| +| **Azure OpenAI Embedding** ✅ | • 높은 정확도
• 1536 dimensions
• Azure 통합
• 한글 지원 우수 | • 비용 ($0.0001/1K tokens)
• API 호출 필요 | **9/10** | +| OpenAI Embedding (Direct) | • 최신 모델
• 높은 정확도 | • Azure 통합 없음
• 별도 인증
• 비용 동일 | 7/10 | +| Sentence Transformers | • 무료
• 로컬 실행
• 빠른 속도 | • 정확도 낮음
• 한글 성능 부족
• GPU 필요 | 5/10 | + +**선정 이유**: +1. **Azure AI Search 최적화**: Azure OpenAI와 네이티브 통합 +2. **한글 성능**: 다국어 지원 우수 +3. **일관성**: 용어집과 동일한 모델 사용 (운영 단순화) + +**비용**: 월 10,000 문서 기준 약 $30 + +--- + +### AD-003: 청킹 전략 - Semantic-based Chunking + +**결정**: 안건 단위 의미 기반 청킹 (2000-2500 tokens) + +**대안 비교**: + +| 대안 | 장점 | 단점 | 평가 | +|-----|------|------|------| +| **Semantic-based** ✅ | • 맥락 보존
• 안건별 분리
• 검색 정확도 높음 | • 청크 크기 불균등
• 복잡한 로직 | **9/10** | +| Fixed-size (1000 tokens) | • 구현 간단
• 균등한 크기 | • 맥락 단절
• 안건 분리 안됨 | 5/10 | +| Fixed-size (2000 tokens) | • 큰 맥락
• 구현 간단 | • 여전히 맥락 단절
• 안건 혼재 | 6/10 | +| Recursive Chunking | • 계층적 분할
• 유연성 | • 복잡도 높음
• 오버헤드 | 7/10 | + +**선정 이유**: +1. **안건 단위 검색**: 특정 안건과 관련된 과거 논의 찾기 +2. **맥락 보존**: 안건 전체 내용을 하나의 청크로 유지 +3. **검색 정확도**: 의미 단위로 분할하여 정확도 향상 + +**청크 크기**: +- 기본: 안건 1개 = 1 청크 +- 큰 안건: 2000 tokens 기준 추가 분할 +- 전체 요약: Overview Chunk 별도 생성 + +--- + +### AD-004: LLM 선정 - Claude 3.5 Sonnet + +**결정**: Claude 3.5 Sonnet 사용 (유사 내용 요약 생성) + +**대안 비교**: + +| 대안 | 장점 | 단점 | 평가 | +|-----|------|------|------| +| **Claude 3.5 Sonnet** ✅ | • 높은 정확도
• 환각 낮음
• 긴 컨텍스트 (200K)
• 한글 성능 우수 | • 비용 높음 ($3/MTok input)
• API 레이턴시 | **10/10** | +| GPT-4 Turbo | • 높은 정확도
• Azure 통합
• 빠른 속도 | • 환각 비율 높음
• 비용 높음 ($10/MTok) | 8/10 | +| GPT-3.5 Turbo | • 빠른 속도
• 저렴 ($0.5/MTok)
• Azure 통합 | • 정확도 낮음
• 한글 성능 부족
• 요약 품질 낮음 | 5/10 | +| LLaMA 3 (Self-hosted) | • 무료
• 데이터 프라이버시 | • 정확도 낮음
• GPU 필요
• 운영 부담 | 4/10 | + +**선정 이유**: +1. **낮은 환각율**: 과거 회의록에서 실제 내용만 추출 (정확성 중요) +2. **긴 컨텍스트**: 두 회의록 전체를 한 번에 비교 가능 +3. **한글 성능**: 한글 요약 품질 우수 +4. **일관성**: 용어집과 동일한 모델 사용 + +**비용**: 월 5만 건 요약 기준 약 $150-200 + +--- + +### AD-005: 캐싱 전략 - L1 (Redis) + L2 (PostgreSQL) + +**결정**: 2단계 캐싱 (L1: Redis, L2: PostgreSQL) + +**대안 비교**: + +| 대안 | 장점 | 단점 | 평가 | +|-----|------|------|------| +| **L1 + L2 캐싱** ✅ | • 빠른 응답
• 비용 절감
• 부하 분산 | • 캐시 무효화 복잡
• 저장 공간 필요 | **9/10** | +| Redis만 | • 단순함
• 빠름 | • 휘발성
• 재시작 시 손실 | 6/10 | +| PostgreSQL만 | • 영구 저장
• 트랜잭션 | • 느림
• 부하 증가 | 5/10 | +| 캐싱 없음 | • 구현 단순
• 항상 최신 | • 높은 비용
• 느린 응답 | 3/10 | + +**캐싱 전략**: + +**L1 (Redis)**: +- 키: `related_meetings:{meetingId}` +- TTL: 1시간 +- 값: RelatedMeeting 객체 리스트 (JSON) +- 용도: 실시간 조회 성능 + +**L2 (PostgreSQL)**: +- 테이블: `meeting_relationships` +- 영구 저장 +- 용도: Redis 캐시 미스 시 fallback, 통계 분석 + +```sql +CREATE TABLE meeting_relationships ( + id UUID PRIMARY KEY, + current_meeting_id UUID NOT NULL, + related_meeting_id UUID NOT NULL, + relevance_score DECIMAL(5,2) NOT NULL, + similar_content_summary TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE (current_meeting_id, related_meeting_id) +); + +CREATE INDEX idx_current_meeting +ON meeting_relationships(current_meeting_id); +``` + +**선정 이유**: +1. **비용 절감**: Claude API 호출 70% 감소 (캐시 히트율 70% 가정) +2. **응답 속도**: Redis에서 < 10ms 응답 +3. **데이터 영속성**: PostgreSQL에 영구 저장 +4. **통계 분석**: 어떤 회의록이 자주 연결되는지 분석 가능 + +--- + +## 구현 로드맵 + +### Phase 1: MVP (회의록 전용) - 3주 + +#### Week 1: 인프라 및 데이터 파이프라인 +- **Task 1.1**: Azure AI Search 인덱스 생성 및 스키마 설계 + - 회의록 전용 인덱스 생성 + - Semantic Configuration 설정 + - Vector Search 활성화 + +- **Task 1.2**: 데이터 수집 배치 서비스 구현 + - MeetingDocumentCollectionService 개발 + - 매일 새벽 3시 자동 실행 (Cron) + - 실패 재처리 로직 + +- **Task 1.3**: 데이터 추출 및 정제 로직 구현 + - extractMetadata() 함수 + - refineContent() 함수 + - 키워드 추출 (NLP) + +#### Week 2: 검색 및 Claude 연동 +- **Task 2.1**: Semantic-based Chunking 구현 + - 안건 단위 청킹 + - 큰 안건 분할 로직 + - Overview Chunk 생성 + +- **Task 2.2**: 벡터화 및 인덱싱 + - Azure OpenAI Embedding API 연동 + - Batch Indexing (50개씩) + - 증분 업데이트 로직 + +- **Task 2.3**: Hybrid Search 구현 + - Keyword + Vector Search + - Folder Filter (가중치 +20%) + - Semantic Ranking + RRF + +- **Task 2.4**: Claude API 연동 + - ClaudeClient 구현 + - 유사 내용 요약 생성 + - 에러 핸들링 및 Fallback + +#### Week 3: 캐싱 및 API 개발 +- **Task 3.1**: L1 캐싱 (Redis) + - RelatedMeeting 캐싱 로직 + - TTL 1시간 설정 + +- **Task 3.2**: L2 캐싱 (PostgreSQL) + - meeting_relationships 테이블 생성 + - 영구 저장 로직 + +- **Task 3.3**: REST API 개발 + - `GET /api/ai/meetings/{meetingId}/related` + - Progressive Loading 지원 + +- **Task 3.4**: 테스트 및 튜닝 + - 100개 회의록 샘플 테스트 + - Precision@3 측정 (목표 80%) + - 성능 튜닝 + +### Phase 2: 확장 (프로젝트 문서) - 2주 + +#### Week 4: 프로젝트 문서 수집 +- **Task 4.1**: 문서 저장소 연동 + - SharePoint/Confluence API 연동 + - 문서 타입 분류 (요구사항/설계/수행결과) + +- **Task 4.2**: 멀티포맷 파싱 + - PDF 파서 + - DOCX 파서 + - HWP 파서 (한글 문서) + +- **Task 4.3**: 메타데이터 확장 + - 프로젝트 ID + - 문서 버전 + - 작성자 + +#### Week 5: 통합 및 최적화 +- **Task 5.1**: 통합 검색 로직 + - 회의록 + 프로젝트 문서 통합 검색 + - 문서 타입별 가중치 적용 + +- **Task 5.2**: 성능 최적화 + - 인덱스 튜닝 + - 쿼리 최적화 + +- **Task 5.3**: 모니터링 대시보드 + - Grafana 대시보드 구축 + - 검색 품질 지표 추적 + +### Phase 3: 전체 확장 (업무/운영 문서) - 2주 + +#### Week 6-7: 업무/운영 문서 추가 +- **Task 6.1**: 업무 문서 수집 + - 업무 매뉴얼 + - 정책 및 규정 + - 표준화 문서 + +- **Task 6.2**: 운영 문서 수집 + - 장애 보고서 + - 고객 응대 문서 + - 유지보수 기록 + +- **Task 6.3**: 보안 강화 + - 문서 접근 권한 필터링 + - 민감 정보 마스킹 + +- **Task 6.4**: 최종 테스트 + - 1000개 문서 대상 성능 테스트 + - 사용자 피드백 수집 + - 품질 개선 + +--- + +## 성능 및 품질 기준 + +### 1. 성능 SLA + +| 항목 | 목표 | 측정 방법 | +|-----|------|----------| +| **검색 응답 시간** | P50 < 1.5초
P95 < 3초
P99 < 5초 | Prometheus + Grafana | +| **Claude 요약 생성** | 평균 1.5초/건
최대 3초/건 | API 레이턴시 모니터링 | +| **캐시 히트율** | > 70% | Redis 통계 | +| **배치 처리 시간** | 1000건 < 30분 | Batch Job 로그 | +| **동시 처리** | 50명 동시 조회 가능 | 부하 테스트 | + +### 2. 품질 SLA + +| 항목 | 목표 | 측정 방법 | +|-----|------|----------| +| **Precision@3** | > 80% | 수동 라벨링 100건 | +| **요약 정확도** | 환각 < 5% | 수동 검증 50건 | +| **관련도 정확성** | 70% 이상 = 실제 관련
95% 이상 = 매우 관련 | 사용자 클릭률 분석 | +| **다운타임** | < 0.1% (연 8.7시간) | Uptime 모니터링 | + +### 3. 비용 추정 (월간) + +| 항목 | 수량 | 단가 | 월 비용 | +|-----|------|------|--------| +| **Azure AI Search** | Standard tier | - | $250 | +| **Azure OpenAI Embedding** | 10,000 documents
25M tokens | $0.0001/1K | $25 | +| **Claude API** | 50,000 summaries
3M input tokens
0.5M output tokens | $3/MTok input
$15/MTok output | $9 + $7.5 = $16.5 | +| **Redis** | Enterprise tier | - | $50 | +| **Storage** | 100GB | $0.10/GB | $10 | +| **총계** | | | **$351.5** | + +**비용 절감 전략**: +- 캐싱으로 Claude API 호출 70% 감소 → $351.5 → **$246/월** +- MVP (회의록만): $200/월 + +### 4. 모니터링 지표 + +#### 검색 성능 +``` +# Prometheus Metrics + +# 검색 응답 시간 +hybrid_search_duration_seconds{quantile="0.5"} 0.8 +hybrid_search_duration_seconds{quantile="0.95"} 1.5 +hybrid_search_duration_seconds{quantile="0.99"} 2.5 + +# Claude API 응답 시간 +claude_summarization_duration_seconds{quantile="0.5"} 1.2 +claude_summarization_duration_seconds{quantile="0.95"} 2.3 + +# 캐시 히트율 +related_meetings_cache_hit_ratio 0.72 + +# 배치 처리 +document_batch_processed_total 1000 +document_batch_failed_total 5 +``` + +#### Grafana 대시보드 + +**Panel 1: 검색 성능** +- 응답 시간 분포 (P50, P95, P99) +- 캐시 히트율 추이 +- Claude API 호출 횟수 + +**Panel 2: 품질 지표** +- Precision@3 트렌드 +- 사용자 클릭률 (관련도별) +- 환각 발생률 + +**Panel 3: 비용 모니터링** +- Azure AI Search 쿼리 수 +- Claude API 토큰 사용량 +- 월간 예상 비용 + +**Panel 4: 데이터 현황** +- 총 인덱싱된 문서 수 +- 문서 타입별 분포 +- 일별 신규 문서 수 + +### 5. 테스트 계획 + +#### 단위 테스트 +- DocumentChunking 로직 +- MetadataExtraction 로직 +- ClaudeRequestBuilder 로직 + +#### 통합 테스트 +- Azure AI Search 연동 +- Claude API 연동 +- Redis 캐싱 + +#### 성능 테스트 +- 동시 50명 조회 (목표: P95 < 3초) +- 1000건 배치 처리 (목표: < 30분) +- 캐시 히트율 측정 (목표: > 70%) + +#### 품질 테스트 +- 수동 라벨링 100건 (Precision@3) +- 요약 정확도 검증 50건 (환각률 < 5%) +- Edge Case 테스트 (관련 회의록 없음, 권한 없음) + +--- + +## 부록 + +### A. Azure AI Search 인덱스 스키마 + +```json +{ + "name": "meeting-minutes-index", + "fields": [ + { + "name": "id", + "type": "Edm.String", + "key": true, + "searchable": false, + "filterable": false, + "sortable": false, + "facetable": false + }, + { + "name": "documentId", + "type": "Edm.String", + "searchable": false, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "documentType", + "type": "Edm.String", + "searchable": false, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "title", + "type": "Edm.String", + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": false, + "analyzer": "ko.lucene" + }, + { + "name": "folder", + "type": "Edm.String", + "searchable": false, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "createdDate", + "type": "Edm.DateTimeOffset", + "searchable": false, + "filterable": true, + "sortable": true, + "facetable": false + }, + { + "name": "participants", + "type": "Collection(Edm.String)", + "searchable": true, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "keywords", + "type": "Collection(Edm.String)", + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": true + }, + { + "name": "agendaId", + "type": "Edm.String", + "searchable": false, + "filterable": true, + "sortable": false, + "facetable": false + }, + { + "name": "agendaTitle", + "type": "Edm.String", + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": false, + "analyzer": "ko.lucene" + }, + { + "name": "chunkIndex", + "type": "Edm.Int32", + "searchable": false, + "filterable": true, + "sortable": true, + "facetable": false + }, + { + "name": "content", + "type": "Edm.String", + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": false, + "analyzer": "ko.lucene" + }, + { + "name": "contentVector", + "type": "Collection(Edm.Single)", + "searchable": true, + "dimensions": 1536, + "vectorSearchProfile": "meeting-vector-profile" + }, + { + "name": "tokenCount", + "type": "Edm.Int32", + "searchable": false, + "filterable": true, + "sortable": false, + "facetable": false + } + ], + "vectorSearch": { + "profiles": [ + { + "name": "meeting-vector-profile", + "algorithm": "meeting-hnsw", + "vectorizer": null + } + ], + "algorithms": [ + { + "name": "meeting-hnsw", + "kind": "hnsw", + "hnswParameters": { + "metric": "cosine", + "m": 4, + "efConstruction": 400, + "efSearch": 500 + } + } + ] + }, + "semantic": { + "configurations": [ + { + "name": "meeting-semantic-config", + "prioritizedFields": { + "titleField": { + "fieldName": "title" + }, + "prioritizedContentFields": [ + { + "fieldName": "content" + } + ], + "prioritizedKeywordsFields": [ + { + "fieldName": "keywords" + } + ] + } + } + ] + } +} +``` + +### B. 프롬프트 템플릿 + +```java +public class ClaudePromptTemplates { + + public static final String SUMMARY_SYSTEM_PROMPT = """ + 당신은 회의록 분석 전문가입니다. + 두 회의록을 비교하여 유사한 내용을 정확하게 추출하고 간결하게 요약합니다. + + 중요한 원칙: + 1. 과거 회의록에서 실제로 다뤄진 내용만 포함하세요 + 2. 환각(Hallucination)을 절대 생성하지 마세요 + 3. 구체적인 날짜, 수치, 결정사항을 포함하세요 + 4. 정확히 3문장으로 요약하세요 + """; + + public static final String SUMMARY_USER_PROMPT_TEMPLATE = """ + 아래 두 회의록을 비교하여 유사한 내용을 정확히 3문장으로 요약해주세요. + + ## 현재 회의 + 제목: %s + 날짜: %s + 안건: + %s + + ## 과거 회의 + 제목: %s + 날짜: %s + 안건: + %s + + ## 요구사항 + 1. 두 회의에서 공통적으로 논의된 주제나 결정사항을 찾아주세요 + 2. 정확히 3문장으로 요약해주세요 (각 문장은 한 문단) + 3. 구체적인 내용을 포함해주세요 (예: 날짜, 수치, 결정사항) + 4. 과거 회의에서 실제로 다뤄진 내용만 포함해주세요 (환각 금지) + """; +} +``` + +### C. 참조 문서 + +- **유저스토리**: `design/userstory.md` - UFR-AI-040 (관련회의록연결) +- **프로토타입**: `design/uiux/prototype/05-회의진행.html` - 관련회의록 탭 +- **AI Service API**: `design/backend/api/ai-service-api.yaml` +- **용어집 구현방안**: `design/구현방안-용어집.md` + +--- + +**문서 히스토리**: +- v1.0 (2025-10-27): 초안 작성 (AI Service 팀) diff --git a/design/구현방안-용어집.md b/design/구현방안-용어집.md new file mode 100644 index 0000000..7ddc5b7 --- /dev/null +++ b/design/구현방안-용어집.md @@ -0,0 +1,1816 @@ +# 맥락기반 용어설명 데이터 확보 및 Claude AI 연동 아키텍처 결정 문서 + +## 1단계: 각자의 생각 + +### 민준 (Product Owner) +비즈니스 관점에서 용어 데이터는 두 가지 소스가 핵심입니다. +1. **조직 문서**: 내부 업무 매뉴얼, 정책, 프로젝트 문서가 차별화의 핵심 +2. **외부 기술 문서**: 범용 기술 용어는 보조적으로 활용 + +데이터 품질이 서비스 차별화의 핵심이므로, 초기에는 작은 규모로 시작해도 정확도가 높아야 합니다. MVP에서는 핵심 조직 문서 100개 정도로 시작하고, 점진적으로 확장하는 전략이 필요합니다. + +### 서연 (AI Specialist) +**전문용어 처리 파이프라인**을 4단계로 제안합니다: + +1. **수집 (Collection)** + - 조직문서: SharePoint, Confluence, Google Drive API 연동 + - 외부문서: 공개 API 문서, GitHub 기술문서 + +2. **추출 (Extraction)** + - NER(Named Entity Recognition) 기반 기술용어 추출 + - TF-IDF + 도메인 사전 기반 조직 특화 용어 추출 + - Claude API를 활용한 맥락 기반 용어 추출 + +3. **정제 (Refinement)** + - 중복 제거: Fuzzy matching으로 유사 용어 통합 + - 품질 검증: 인간 피드백 루프(HITL) 구축 + - 메타데이터 태깅: 카테고리, 출처, 신뢰도 점수 + +4. **벡터라이징 (Vectorization)** + - **임베딩 모델**: OpenAI text-embedding-3-large 또는 Cohere embed-multilingual-v3.0 + - **벡터 DB**: Pinecone 또는 Qdrant (관리형 vs 자체 호스팅) + - **청크 전략**: + - 용어 정의: 단일 청크 (256 토큰) + - 문맥 정보: 슬라이딩 윈도우 (512 토큰, 128 오버랩) + +**Claude API 연동 구조**: +```json +{ + "request": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "system": "당신은 회의록 작성 보조 AI입니다. 전문용어를 회의 맥락에 맞춰 설명해주세요.", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "회의 내용: {meeting_transcript}\n\n발견된 용어: {term}\n\n관련 문서:\n{rag_context}\n\n위 용어를 회의 맥락에 맞춰 2-3문장으로 설명해주세요." + } + ] + } + ] + }, + "response": { + "id": "msg_...", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "API Gateway는 클라이언트와 백엔드 서비스 사이의 단일 진입점으로..." + } + ], + "model": "claude-sonnet-4-5-20250929", + "stop_reason": "end_turn", + "usage": { + "input_tokens": 450, + "output_tokens": 120 + } + } +} +``` + +### 준호 (Backend Developer) +**데이터 아키텍처 및 API 설계**를 제안합니다: + +**1. 데이터 소스별 수집 전략** +``` +조직 문서: +├── Confluence API (주간 배치) +├── SharePoint Graph API (주간 배치) +├── Google Drive API (주간 배치) +└── 수동 업로드 (관리자 페이지) + +외부 문서: +├── GitHub 기술문서 크롤링 (월간 배치) +├── 공개 API 문서 (Swagger/OpenAPI) +└── 산업 표준 용어집 (수동 큐레이션) +``` + +**2. 데이터베이스 스키마** +```sql +-- 용어 마스터 테이블 +CREATE TABLE terms ( + term_id UUID PRIMARY KEY, + term_name VARCHAR(200) NOT NULL, + normalized_name VARCHAR(200) NOT NULL, + category VARCHAR(50), + source_type VARCHAR(20), -- 'internal', 'external' + definition TEXT, + context TEXT, + confidence_score DECIMAL(3,2), + created_at TIMESTAMP, + updated_at TIMESTAMP, + INDEX idx_normalized (normalized_name), + INDEX idx_category (category) +); + +-- 용어-문서 매핑 테이블 +CREATE TABLE term_documents ( + mapping_id UUID PRIMARY KEY, + term_id UUID REFERENCES terms(term_id), + document_id UUID, + relevance_score DECIMAL(3,2), + excerpt TEXT, + created_at TIMESTAMP +); + +-- 벡터 인덱스 메타데이터 +CREATE TABLE term_vectors ( + vector_id UUID PRIMARY KEY, + term_id UUID REFERENCES terms(term_id), + embedding_model VARCHAR(50), + vector_store_key VARCHAR(200), + created_at TIMESTAMP +); +``` + +**3. REST API 엔드포인트** +``` +POST /api/v1/terms/extract +- 회의 전사 텍스트에서 용어 추출 +- Request: { "transcript": "...", "meeting_id": "..." } +- Response: { "terms": [...], "extraction_time_ms": 1234 } + +GET /api/v1/terms/{term_id}/context +- 특정 용어의 맥락 정보 조회 +- Query params: meeting_context, max_results +- Response: { "term": {...}, "related_docs": [...], "ai_explanation": "..." } + +POST /api/v1/terms/search +- 벡터 유사도 검색 +- Request: { "query": "...", "filters": {...}, "top_k": 5 } +- Response: { "results": [...], "search_time_ms": 234 } +``` + +**4. 성능 최적화** +- Redis 캐싱: 자주 조회되는 용어 (TTL 1시간) +- CDN 캐싱: 외부 기술 문서 용어 (TTL 24시간) +- 배치 처리: 야간 시간대 벡터 업데이트 + +### 동욱 (Backend Developer) +**실용적인 구현 전략**을 제안합니다: + +**Phase 1: MVP (4주)** +- JSON 파일 기반 용어 사전 (terms-dictionary.json) +- 수동 큐레이션 50개 핵심 용어 +- Claude API 직접 호출 (RAG 없이 프롬프트 기반) +- 간단한 키워드 매칭 + +**Phase 2: 기본 RAG (4주)** +- PostgreSQL + pgvector 확장 +- 조직 문서 100개 수집/임베딩 +- 기본 벡터 검색 구현 +- Claude API + RAG context 연동 + +**Phase 3: 고도화 (8주)** +- Pinecone 또는 Qdrant 도입 +- 자동 수집 파이프라인 구축 +- 하이브리드 검색 (키워드 + 벡터) +- 피드백 루프 및 품질 개선 + +**인프라 구성**: +```yaml +services: + term-extraction: + - NLP 서버 (Python FastAPI) + - spaCy, transformers 라이브러리 + - 배치 작업용 Celery worker + + vector-search: + - Qdrant (self-hosted) 또는 Pinecone (managed) + - API Gateway 통합 + + claude-api-proxy: + - Rate limiting (100 req/min) + - Response caching (Redis) + - Error handling & retry logic +``` + +### 유진 (Frontend Developer) +**사용자 경험 관점**에서 고려사항: + +1. **실시간 용어 감지 UI** + - STT 텍스트 스트리밍 중 용어 하이라이트 + - 1초 이내 반응 속도 보장 + - 로딩 스피너 대신 skeleton UI + +2. **용어 설명 모달** + - 빠른 프리뷰: 2-3줄 요약 + - 상세보기 확장: 관련 문서 링크 + - 즐겨찾기 기능 + +3. **성능 최적화** + - 용어 데이터 prefetch (회의 시작 시) + - 자주 사용되는 용어 로컬 캐싱 + - Debouncing으로 API 호출 최소화 + +### 길동 (Architect) +**전체 시스템 아키텍처**를 제안합니다: + +``` +┌─────────────────────────────────────────────────────────┐ +│ Client (Web/Mobile) │ +└────────────────────┬────────────────────────────────────┘ + │ +┌────────────────────▼────────────────────────────────────┐ +│ API Gateway │ +│ (Rate Limiting, Auth, Routing) │ +└────────┬──────────────────────────┬─────────────────────┘ + │ │ +┌────────▼────────┐ ┌─────────▼──────────┐ +│ Meeting Service│ │ AI Service │ +│ - 회의 진행 │ │ - 용어 추출 │ +│ - STT 연동 │ │ - RAG 검색 │ +│ - 실시간 협업 │ │ - Claude API 연동 │ +└────────┬────────┘ └─────────┬──────────┘ + │ │ + │ ┌──────────▼───────────┐ + │ │ Vector Store │ + │ │ (Qdrant/Pinecone) │ + │ └──────────────────────┘ + │ +┌────────▼─────────────────────────────────────┐ +│ PostgreSQL │ +│ - 회의/회의록 데이터 │ +│ - 용어 마스터 데이터 │ +│ - 메타데이터 (pgvector 확장) │ +└───────────────────────────────────────────────┘ + +┌──────────────────────────────────────────────┐ +│ Data Pipeline (배치 처리) │ +│ 1. 문서 수집 (Confluence, SharePoint, etc) │ +│ 2. 용어 추출 & 정제 │ +│ 3. 벡터 임베딩 & 저장 │ +│ 4. 품질 검증 (Human-in-the-loop) │ +└──────────────────────────────────────────────┘ +``` + +**주요 아키텍처 결정**: +1. **벡터 DB 선택**: Qdrant (자체 호스팅) - 비용 절감, 커스터마이징 용이 +2. **임베딩 모델**: OpenAI text-embedding-3-large - 다국어 지원, 높은 정확도 +3. **캐싱 전략**: 3-tier (Browser → Redis → Vector DB) +4. **확장성**: 마이크로서비스 분리로 독립적 스케일링 가능 + +### 도현 (QA Engineer) +**품질 보증 관점**: + +1. **데이터 품질 검증** + - 용어 추출 정확도: 85% 이상 + - 용어 설명 적합성: 사용자 피드백 4.0/5.0 이상 + - False positive rate: 10% 이하 + +2. **성능 테스트 기준** + - 용어 추출: 1000자 텍스트 기준 < 2초 + - RAG 검색: < 500ms + - Claude API 응답: < 3초 + - 동시 사용자 100명 부하 테스트 + +3. **테스트 시나리오** + - 다양한 도메인 용어 (IT, 비즈니스, 법률, 의료) + - 약어 및 동음이의어 처리 + - 긴 문맥에서의 용어 추출 + - 오프라인/저속 네트워크 환경 + +### 도그냥 (서비스 기획자) +**사용자 시나리오 중심 접근**: + +**핵심 사용 케이스**: +1. **신입 직원**: 회의 중 모르는 약어 실시간 확인 +2. **PM**: 타부서 회의 참석 시 전문용어 이해 +3. **경영진**: 기술 용어를 비즈니스 맥락으로 이해 + +**차별화 요소**: +- 단순 사전이 아닌 "이 회의에서의 의미" 제공 +- 과거 회의록 맥락 자동 연결 +- 개인화된 용어 학습 이력 + +**MVP 검증 지표**: +- 용어 설명 조회율: 회의당 평균 3회 이상 +- 도움됨 비율: 80% 이상 +- 재사용률: 동일 용어 2회 이상 조회 50% + +### 주영 (DevOps) +**운영 관점 인프라 설계**: + +**CI/CD 파이프라인**: +```yaml +data-pipeline: + schedule: "0 2 * * *" # 매일 새벽 2시 + stages: + - collect-documents + - extract-terms + - generate-embeddings + - update-vector-store + - quality-check + + monitoring: + - Prometheus metrics + - Grafana dashboard + - Alert on failure +``` + +**벡터 DB 배포**: +```yaml +qdrant: + deployment: + replicas: 3 + resources: + cpu: 2 + memory: 4Gi + storage: 100Gi SSD + + backup: + schedule: daily + retention: 30 days + + monitoring: + metrics: + - query_latency_p95 + - index_size + - memory_usage +``` + +**비용 최적화**: +- Spot instances for 배치 작업 +- Auto-scaling for API 서버 +- CloudFront CDN for 정적 용어 사전 + +### 현정 (콘텐츠 기획자) +**콘텐츠 품질 관점**: + +1. **용어 분류 체계** + ``` + 기술 용어 + ├── 아키텍처 (API Gateway, Microservice, ...) + ├── 프로그래밍 (REST, JWT, ...) + └── 인프라 (Kubernetes, Docker, ...) + + 비즈니스 용어 + ├── 전략 (MVP, PMF, ...) + ├── 마케팅 (CAC, LTV, ...) + └── 재무 (ARR, MRR, ...) + + 조직 내부 용어 + ├── 프로젝트명 + ├── 팀/부서명 + └── 내부 프로세스 + ``` + +2. **설명 작성 가이드** + - 1문장: 핵심 정의 + - 2문장: 회의 맥락 + - 3문장: 관련 정보/링크 + +3. **데이터 큐레이션 프로세스** + - 주간 신규 용어 리뷰 + - 월간 품질 감사 + - 분기별 카테고리 재정비 + +### 지수 (Product Designer) +**UX/UI 디자인 관점**: + +**용어 표시 디자인 시스템**: +``` +용어 배지 스타일: +- 조직 용어: ⭐ 골드 배지 +- 기술 용어: 🔧 블루 배지 +- 비즈니스 용어: 💼 그린 배지 + +인터랙션: +- Hover: 1줄 미리보기 툴팁 +- Click: 상세 설명 모달 (슬라이드업) +- Long press (모바일): 빠른 설명 팝업 + +접근성: +- 키보드 네비게이션 지원 +- Screen reader 호환 +- High contrast mode 지원 +``` + +**모바일 최적화**: +- 스와이프로 용어 사전 열기 +- 음성으로 용어 질문 (STT 연동) +- 오프라인 캐싱 (자주 사용 50개 용어) + +--- + +## 2단계: 의견 종합 및 중복 제거 + +### A. 데이터 수집 전략 +1. **초기(MVP)**: 수동 큐레이션 50-100개 핵심 조직 용어 +2. **자동화**: Confluence, SharePoint, Google Drive API 배치 수집 +3. **외부 소스**: GitHub 기술문서, 공개 API 문서, 산업 표준 용어집 + +### B. 용어 추출 및 정제 +1. **추출 방법**: NER, TF-IDF, Claude API 활용 맥락 추출 +2. **정제 프로세스**: 중복 제거(Fuzzy matching), 품질 검증(HITL), 메타데이터 태깅 +3. **분류 체계**: 기술/비즈니스/조직 내부 용어 3대 카테고리 + +### C. 벡터라이징 및 저장 +1. **임베딩 모델**: OpenAI text-embedding-3-large (다국어, 고정확도) +2. **벡터 DB**: Qdrant (자체 호스팅, 비용 효율적) +3. **청크 전략**: 용어 정의 256토큰, 문맥 512토큰(128 오버랩) + +### D. Claude API 연동 +1. **모델**: claude-sonnet-4-5-20250929 +2. **프롬프트 구조**: System message + 회의 전사 + 용어 + RAG context +3. **최적화**: Rate limiting, Response caching(Redis), Error handling + +### E. 시스템 아키텍처 +1. **마이크로서비스 구성**: Meeting Service, AI Service 분리 +2. **데이터베이스**: PostgreSQL(메인) + pgvector + Qdrant(벡터) +3. **캐싱 전략**: 3-tier (Browser → Redis → Vector DB) + +### F. 성능 및 품질 목표 +1. **응답 시간**: 용어 추출 <2초, RAG 검색 <500ms, 전체 <3초 +2. **정확도**: 추출 85% 이상, 사용자 만족도 4.0/5.0 +3. **확장성**: 동시 사용자 100명 지원 + +--- + +## 3단계: 최적안 후보 5개 선정 + +### 후보 1: 단계적 MVP 접근 (Lean Startup) +**특징**: +- Phase 1(4주): JSON 기반 수동 큐레이션 50개 용어 +- Phase 2(4주): PostgreSQL + pgvector, 100개 문서 +- Phase 3(8주): Qdrant 도입, 자동 파이프라인 + +**장점**: +- 빠른 시장 검증 +- 초기 비용 최소화 +- 점진적 학습 및 개선 + +**단점**: +- 초기 기능 제한적 +- 기술 부채 발생 가능 +- 3번의 마이그레이션 필요 + +**비용**: $5K (Phase 1) → $20K (Phase 2) → $50K (Phase 3) + +--- + +### 후보 2: 하이브리드 RAG 시스템 (균형형) +**특징**: +- 조직 용어: JSON 사전 (관리자 큐레이션) +- 외부 용어: RAG 시스템 (Qdrant + OpenAI embedding) +- Claude API 통합: 맥락 기반 설명 생성 + +**장점**: +- 정확도와 확장성 균형 +- 조직 용어 품질 보장 +- 외부 용어 자동 업데이트 + +**단점**: +- 이원화된 시스템 관리 +- 동기화 복잡성 +- 중간 수준 개발 비용 + +**비용**: $35K (초기 구축) + $5K/월 (운영) + +--- + +### 후보 3: Full RAG 시스템 (최대 자동화) +**특징**: +- 모든 용어 자동 추출 및 RAG 검색 +- Pinecone 관리형 서비스 사용 +- 실시간 문서 크롤링 및 업데이트 +- 자동 품질 검증 (ML 모델) + +**장점**: +- 최대 확장성 +- 관리 오버헤드 최소 +- 최신 정보 자동 반영 + +**단점**: +- 초기 구축 비용 높음 +- 조직 특화 용어 정확도 낮을 수 있음 +- 운영 비용 지속 발생 + +**비용**: $80K (초기 구축) + $12K/월 (Pinecone + API) + +--- + +### 후보 4: 커뮤니티 큐레이션 모델 +**특징**: +- 사용자가 용어 추가/수정 제안 +- AI가 제안 검증 및 자동 반영 +- 크라우드소싱 기반 품질 관리 +- Wikipedia 스타일 버전 관리 + +**장점**: +- 조직 맞춤 용어 자연 축적 +- 사용자 참여도 향상 +- 지속적인 품질 개선 + +**단점**: +- 초기 콘텐츠 부족 +- 품질 관리 복잡 +- 악의적 편집 방지 필요 + +**비용**: $30K (초기) + $3K/월 (모더레이션) + +--- + +### 후보 5: LLM 직접 활용 (RAG-less) +**특징**: +- RAG 없이 Claude API에 전체 문서 임베딩 전달 +- Claude의 장문 처리 능력 활용 (200K tokens) +- 실시간 맥락 생성, 별도 벡터 DB 불필요 + +**장점**: +- 아키텍처 단순화 +- 개발 기간 단축 (4주) +- 인프라 비용 최소 + +**단점**: +- API 호출 비용 높음 (토큰당 과금) +- 응답 속도 느림 (5-10초) +- 확장성 제한 (회의 수 증가 시) + +**비용**: $15K (초기) + $8K/월 (Claude API) + +--- + +## 4단계: 각 최적안 후보 평가 + +### 평가 기준 (가중치) +1. **비즈니스 가치** (30%): 차별화, 사용자 만족도, ROI +2. **기술적 실현 가능성** (25%): 개발 난이도, 리스크, 일정 +3. **확장성** (20%): 사용자/데이터 증가 대응 +4. **비용 효율성** (15%): 초기 투자, 운영 비용 +5. **유지보수성** (10%): 관리 복잡도, 기술 부채 + +### 후보 1 평가: 단계적 MVP 접근 +| 평가 기준 | 점수 | 세부 평가 | +|---------|------|---------| +| 비즈니스 가치 | 7/10 | 빠른 검증 가능, 초기 기능 제한 | +| 기술적 실현 가능성 | 9/10 | 단계별 구현, 리스크 분산 | +| 확장성 | 6/10 | Phase 3 이후 확장 가능 | +| 비용 효율성 | 9/10 | 최소 초기 투자 | +| 유지보수성 | 7/10 | 마이그레이션 필요, 기술 부채 | +| **총점** | **7.6/10** | **실용적, 리스크 최소** | + +**종합 의견**: +- **민준**: 비즈니스 검증에 최적. MVP 철학에 부합. +- **동욱**: 실무적으로 가장 현실적. 팀 역량 고려 시 추천. +- **도현**: 단계별 테스트 가능, 품질 관리 용이. + +--- + +### 후보 2 평가: 하이브리드 RAG 시스템 +| 평가 기준 | 점수 | 세부 평가 | +|---------|------|---------| +| 비즈니스 가치 | 9/10 | 조직 용어 품질 + 확장성 균형 | +| 기술적 실현 가능성 | 7/10 | 이원화 시스템 복잡도 | +| 확장성 | 8/10 | 외부 용어 자동 확장 | +| 비용 효율성 | 7/10 | 중간 수준 비용 | +| 유지보수성 | 6/10 | 두 시스템 동기화 필요 | +| **총점** | **7.7/10** | **균형잡힌 솔루션** | + +**종합 의견**: +- **서연**: 기술적으로 가장 합리적. RAG의 장점 활용. +- **준호**: API 설계 명확, 확장 경로 분명. +- **길동**: 아키텍처 관점에서 장기적으로 유지 가능. + +--- + +### 후보 3 평가: Full RAG 시스템 +| 평가 기준 | 점수 | 세부 평가 | +|---------|------|---------| +| 비즈니스 가치 | 8/10 | 최대 확장성, 높은 비용 | +| 기술적 실현 가능성 | 6/10 | 복잡도 높음, 개발 기간 길어짐 | +| 확장성 | 10/10 | 무제한 확장 가능 | +| 비용 효율성 | 4/10 | 높은 초기/운영 비용 | +| 유지보수성 | 8/10 | 관리형 서비스로 간소화 | +| **총점** | **7.0/10** | **오버엔지니어링 리스크** | + +**종합 의견**: +- **민준**: 초기 스타트업에 과도한 투자. +- **주영**: 운영 비용 부담, 단계적 접근 권장. +- **도그냥**: 사용자 검증 전 과한 투자. + +--- + +### 후보 4 평가: 커뮤니티 큐레이션 모델 +| 평가 기준 | 점수 | 세부 평가 | +|---------|------|---------| +| 비즈니스 가치 | 7/10 | 사용자 참여 높으나 초기 부족 | +| 기술적 실현 가능성 | 6/10 | 모더레이션 시스템 복잡 | +| 확장성 | 8/10 | 자연스러운 콘텐츠 성장 | +| 비용 효율성 | 8/10 | 중간 비용, 모더레이션 필요 | +| 유지보수성 | 5/10 | 품질 관리 지속 필요 | +| **총점** | **6.9/10** | **차별화되나 리스크 있음** | + +**종합 의견**: +- **도그냥**: 장기적으로 차별화 포인트, MVP에는 부적합. +- **현정**: 콘텐츠 품질 관리 어려움. +- **지수**: UX는 흥미롭지만 초기 경험 부족. + +--- + +### 후보 5 평가: LLM 직접 활용 +| 평가 기준 | 점수 | 세부 평가 | +|---------|------|---------| +| 비즈니스 가치 | 6/10 | 빠른 구현, 성능 이슈 | +| 기술적 실현 가능성 | 8/10 | 단순 구조, 쉬운 구현 | +| 확장성 | 4/10 | API 비용 및 속도 문제 | +| 비용 효율성 | 5/10 | 낮은 초기, 높은 운영 비용 | +| 유지보수성 | 9/10 | 매우 단순한 구조 | +| **총점** | **6.1/10** | **단기 솔루션, 확장 제한** | + +**종합 의견**: +- **서연**: 기술적으로 단순하나 성능 문제 예상. +- **준호**: 확장 시 비용 폭발 가능성. +- **도현**: 3초 응답 시간은 UX에 부정적. + +--- + +## 5단계: 최적안 선정 (1차) + +**선정 결과**: **후보 2 - 하이브리드 RAG 시스템** (7.7/10) + +**선정 이유**: +1. 조직 특화 용어의 높은 정확도 보장 (JSON 큐레이션) +2. 외부 용어의 확장성 확보 (RAG 시스템) +3. 합리적인 비용 구조 ($35K 초기 + $5K/월) +4. 명확한 아키텍처, 유지보수 가능 +5. 차별화 포인트 구현 가능 + +--- + +## 반복 2-10: 개선 및 최종 선정 + +### 반복 2: 하이브리드 모델 개선안 + +**개선 포인트**: +- Phase 1에서 JSON 사전으로 시작 (후보 1의 장점) +- Phase 2에서 RAG 추가 (점진적 확장) +- 비용 최적화: pgvector 우선 사용 → Qdrant는 필요 시 전환 + +**개선된 점수**: 8.0/10 + +--- + +### 반복 3-9: 세부 조정 +(각 반복마다 팀원들의 피드백을 반영하여 아키텍처 미세 조정) + +--- + +### 반복 10: 최종 최적안 + +**최종 선정**: **하이브리드 RAG 시스템 (개선안)** + +**최종 아키텍처**: + +``` +Phase 1 (MVP - 4주): +├── JSON 기반 용어 사전 (50개 핵심 조직 용어) +├── Claude API 직접 연동 +├── 간단한 키워드 매칭 +└── 수동 큐레이션 프로세스 + +Phase 2 (기본 RAG - 6주): +├── PostgreSQL + pgvector 확장 +├── 조직 문서 100개 임베딩 +├── OpenAI text-embedding-3-small 사용 (비용 절감) +├── 기본 벡터 검색 + Claude API 연동 +└── 자동 배치 수집 파이프라인 + +Phase 3 (고도화 - 8주): +├── Qdrant 도입 (성능 개선) +├── 하이브리드 검색 (키워드 + 벡터) +├── 자동 품질 검증 시스템 +├── 사용자 피드백 루프 +└── 외부 문서 자동 크롤링 +``` + +--- + +## 6단계: Architectural Decision Records (ADR) + +### ADR-001: 벡터 데이터베이스 선택 + +**결정**: Phase 1-2는 PostgreSQL + pgvector, Phase 3에서 Qdrant로 전환 + +**컨텍스트**: +- 초기에는 데이터량이 적어 pgvector로 충분 +- Qdrant는 전문 벡터 DB로 성능 우수하나 인프라 복잡도 증가 +- 단계적 접근으로 리스크 분산 + +**대안 고려**: +1. **Pinecone**: 관리형 서비스, 쉬운 시작 vs 높은 운영 비용 ($70/월~) +2. **Weaviate**: 오픈소스, 풍부한 기능 vs 학습 곡선 높음 +3. **pgvector**: 간단한 설정, 기존 DB 활용 vs 성능 제한 + +**결정 근거**: +- MVP 단계에서는 pgvector의 단순성 우선 +- 사용자 1000명 이상 시 Qdrant 전환 (명확한 마이그레이션 기준) +- 비용 효율성: pgvector 무료 vs Qdrant 자체 호스팅 $500/월 vs Pinecone $1000/월 + +**결과**: +- 초기 인프라 비용 절감 (PostgreSQL 활용) +- 검증된 기술 스택 사용으로 팀 학습 부담 감소 +- 명확한 확장 경로 확보 + +--- + +### ADR-002: 임베딩 모델 선택 + +**결정**: OpenAI text-embedding-3-small (Phase 1-2), text-embedding-3-large (Phase 3) + +**컨텍스트**: +- 한국어/영어 혼용 문서 처리 필요 +- 비용과 성능의 균형 필요 +- 임베딩 차원수와 정확도 트레이드오프 + +**대안 고려**: +1. **OpenAI text-embedding-3-small**: + - 1536 차원, $0.02/1M tokens + - 적절한 정확도, 낮은 비용 + +2. **OpenAI text-embedding-3-large**: + - 3072 차원, $0.13/1M tokens + - 최고 정확도, 높은 비용 + +3. **Cohere embed-multilingual-v3.0**: + - 1024 차원, $0.10/1M tokens + - 다국어 특화, 중간 비용 + +**결정 근거**: +- Phase 1-2: small 모델로 MVP 검증 (월 1,000,000 토큰 = $20) +- Phase 3: 사용자 피드백 기반으로 large 모델 전환 고려 +- OpenAI 에코시스템 통일 (Claude 대안으로 GPT 활용 가능) + +**결과**: +- 초기 월 $20-50 임베딩 비용 +- 필요 시 모델 업그레이드 가능한 구조 +- 다국어 지원 검증 필요 (한국어 테스트) + +--- + +### ADR-003: 용어 수집 전략 + +**결정**: 수동 큐레이션 (Phase 1) → 반자동 (Phase 2) → 자동 (Phase 3) + +**Phase 1 - 수동 큐레이션**: +```json +{ + "terms": [ + { + "id": "term-001", + "name": "API Gateway", + "category": "기술-아키텍처", + "definition": "클라이언트와 백엔드 서비스 사이의 단일 진입점", + "context": "우리 시스템에서는 AWS API Gateway를 사용하여...", + "synonyms": ["게이트웨이", "API 게이트웨이"], + "source": "internal", + "confidence": 1.0, + "created_at": "2025-01-15", + "updated_at": "2025-01-15" + } + ] +} +``` + +**Phase 2 - 반자동 수집**: +```python +# 배치 수집 스크립트 +def collect_documents(): + sources = [ + ConfluenceAPI(url="...", token="..."), + SharePointAPI(site_url="...", token="..."), + GoogleDriveAPI(folder_id="...", token="...") + ] + + for source in sources: + documents = source.fetch_recent_documents(days=7) + for doc in documents: + terms = extract_terms(doc.content) + # 관리자 승인 대기열에 추가 + await add_to_approval_queue(terms) +``` + +**Phase 3 - 자동 수집**: +- ML 기반 자동 승인 (신뢰도 > 0.9) +- 주간 배치로 신규 용어 자동 추가 +- 품질 모니터링 대시보드 + +**결정 근거**: +- 초기 품질 보장 (수동 검증) +- 점진적 자동화로 관리 부담 감소 +- 조직 특화 용어의 정확도 우선 + +--- + +### ADR-004: Claude API 연동 구조 + +**결정**: RAG context + Structured prompt + +**API 요청 구조**: +```json +{ + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "temperature": 0.3, + "system": "당신은 전문 용어를 회의 맥락에 맞춰 설명하는 AI 어시스턴트입니다. 2-3문장으로 간결하게 설명하세요.", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "=== 회의 정보 ===\n회의 제목: API 설계 리뷰\n참석자: 김민준(PO), 이준호(BE), 박서연(AI)\n시간: 2025-01-15 14:00\n\n=== 회의 내용 (STT 전사) ===\n민준: 이번 프로젝트에서 API Gateway를 도입하려고 하는데...\n준호: REST API 설계는 이미 완료했고, 인증은 JWT로...\n서연: AI 서비스와의 연동은 비동기로 처리하면...\n\n=== 관련 문서 (RAG 검색 결과) ===\n[문서 1] API Gateway 설계 가이드 (2024-12-01)\n- API Gateway는 마이크로서비스 아키텍처에서 단일 진입점 역할\n- 요청 라우팅, 인증/인가, Rate limiting 기능 제공\n- AWS API Gateway 사용 시 Lambda 통합 가능\n\n[문서 2] 사내 기술 스택 표준 (2024-11-15)\n- API Gateway: AWS API Gateway 권장\n- 인증: JWT 토큰 기반 (만료 시간 1시간)\n\n=== 질문 ===\n용어: API Gateway\n\n위 회의 맥락에 맞춰 'API Gateway'를 2-3문장으로 설명해주세요." + } + ] + } + ] +} +``` + +**응답 처리**: +```json +{ + "id": "msg_01ABC...", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "API Gateway는 클라이언트와 백엔드 마이크로서비스 사이의 단일 진입점으로, 요청 라우팅, 인증, Rate limiting을 담당합니다. 이 회의에서는 AWS API Gateway 도입을 논의하고 있으며, JWT 기반 인증과 Lambda 통합을 통해 AI 서비스를 비동기로 연동하려는 계획입니다. 사내 기술 스택 표준에 따라 AWS API Gateway 사용이 권장됩니다." + } + ], + "model": "claude-sonnet-4-5-20250929", + "usage": { + "input_tokens": 523, + "output_tokens": 87 + } +} +``` + +**최적화 전략**: +1. **캐싱**: Redis에 (용어 + 회의 맥락 해시) → 설명 저장 (TTL 24시간) +2. **Rate limiting**: 사용자당 분당 10회, 회의당 시간당 100회 +3. **Fallback**: API 실패 시 RAG 문서 직접 반환 +4. **비용 관리**: 월 예산 $500 초과 시 알림 (input 500K tokens 기준) + +**결정 근거**: +- Claude의 장문 이해 능력 활용 (200K tokens) +- Structured prompt로 일관된 응답 품질 +- RAG context로 정확도 향상 +- 캐싱으로 중복 호출 방지 (비용 절감) + +--- + +### ADR-005: 데이터베이스 스키마 설계 + +**결정**: 정규화된 관계형 스키마 + JSON 메타데이터 + +**스키마 구조**: +```sql +-- 용어 마스터 테이블 +CREATE TABLE terms ( + term_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + term_name VARCHAR(200) NOT NULL, + normalized_name VARCHAR(200) NOT NULL, -- 소문자, 공백 제거 + category VARCHAR(50), -- '기술-아키텍처', '비즈니스-전략' 등 + source_type VARCHAR(20) CHECK (source_type IN ('internal', 'external')), + is_company_specific BOOLEAN DEFAULT false, + + -- 용어 정보 + definition TEXT NOT NULL, + context TEXT, -- 회사 내 사용 맥락 + synonyms JSONB, -- ["게이트웨이", "API GW"] + related_terms JSONB, -- [{"term_id": "...", "relation": "related"}] + + -- 메타데이터 + usage_count INTEGER DEFAULT 0, + confidence_score DECIMAL(3,2), -- 0.00 ~ 1.00 + last_used_at TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by UUID REFERENCES users(user_id), + + -- 인덱스 + INDEX idx_normalized_name (normalized_name), + INDEX idx_category (category), + INDEX idx_source_type (source_type), + INDEX idx_usage_count (usage_count DESC), + UNIQUE INDEX uniq_normalized_name (normalized_name, source_type) +); + +-- 용어-문서 매핑 (RAG용) +CREATE TABLE term_documents ( + mapping_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + term_id UUID REFERENCES terms(term_id) ON DELETE CASCADE, + + -- 문서 정보 + document_id VARCHAR(200) NOT NULL, -- 외부 시스템 ID (Confluence, SharePoint) + document_title VARCHAR(500), + document_url TEXT, + document_type VARCHAR(50), -- 'confluence', 'sharepoint', 'gdrive' + + -- 관련성 정보 + relevance_score DECIMAL(3,2), -- 0.00 ~ 1.00 + excerpt TEXT, -- 용어가 등장하는 문맥 (최대 500자) + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_term_id (term_id), + INDEX idx_document_id (document_id), + INDEX idx_relevance_score (relevance_score DESC) +); + +-- 벡터 임베딩 메타데이터 +CREATE TABLE term_vectors ( + vector_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + term_id UUID REFERENCES terms(term_id) ON DELETE CASCADE, + + -- 임베딩 정보 + embedding_model VARCHAR(50), -- 'text-embedding-3-small' + embedding_dimension INTEGER, -- 1536 + vector_store_type VARCHAR(20), -- 'pgvector', 'qdrant' + vector_store_key VARCHAR(200), -- 벡터 DB 내 키 + + -- pgvector 사용 시 직접 저장 + embedding vector(1536), -- pgvector 확장 타입 + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_term_id (term_id), + INDEX idx_embedding_model (embedding_model) +); + +-- 용어 사용 이력 (분석용) +CREATE TABLE term_usage_logs ( + log_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + term_id UUID REFERENCES terms(term_id) ON DELETE CASCADE, + + -- 사용 컨텍스트 + meeting_id UUID REFERENCES meetings(meeting_id), + user_id UUID REFERENCES users(user_id), + action VARCHAR(20), -- 'view', 'search', 'feedback' + + -- 피드백 정보 (선택) + feedback_rating INTEGER CHECK (feedback_rating BETWEEN 1 AND 5), + feedback_comment TEXT, + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_term_id_created (term_id, created_at DESC), + INDEX idx_meeting_id (meeting_id), + INDEX idx_user_id (user_id) +); +``` + +**데이터 예시**: +```sql +INSERT INTO terms (term_name, normalized_name, category, source_type, is_company_specific, definition, context, synonyms, confidence_score) VALUES +( + 'API Gateway', + 'api gateway', + '기술-아키텍처', + 'internal', + true, + '클라이언트와 백엔드 마이크로서비스 사이의 단일 진입점으로, 요청 라우팅, 인증, Rate limiting을 담당합니다.', + '우리 시스템에서는 AWS API Gateway를 사용하며, Lambda와 통합하여 서버리스 아키텍처를 구현합니다. API 설계 리뷰 회의(2024-09-28)에서 공식 채택되었습니다.', + '["게이트웨이", "API GW", "에이피아이 게이트웨이"]'::jsonb, + 0.95 +); +``` + +**결정 근거**: +- 정규화로 데이터 일관성 보장 +- JSONB로 유연한 메타데이터 저장 +- 인덱스로 검색 성능 최적화 +- 사용 이력 로그로 품질 개선 데이터 확보 + +--- + +### ADR-006: API 엔드포인트 설계 + +**결정**: RESTful API + GraphQL (Phase 3) + +**Phase 1-2: REST API** +``` +=== 용어 추출 === +POST /api/v1/terms/extract +Request: +{ + "meeting_id": "uuid", + "transcript": "회의 전사 텍스트...", + "language": "ko", + "options": { + "include_external": true, + "confidence_threshold": 0.7 + } +} + +Response: +{ + "terms": [ + { + "term_id": "uuid", + "name": "API Gateway", + "category": "기술-아키텍처", + "confidence": 0.95, + "is_company_specific": true, + "mention_count": 3, + "positions": [142, 456, 789] + } + ], + "extraction_time_ms": 1234, + "total_terms": 12 +} + +=== 용어 설명 조회 === +GET /api/v1/terms/{term_id}/explanation +Query params: +- meeting_id (optional): 회의 맥락 포함 +- include_rag (optional): RAG 문서 포함 여부 +- max_context_docs (optional): 최대 문서 수 (기본 3) + +Response: +{ + "term": { + "term_id": "uuid", + "name": "API Gateway", + "definition": "...", + "category": "기술-아키텍처", + "is_company_specific": true + }, + "explanation": { + "text": "API Gateway는 클라이언트와 백엔드...", + "context": "이 회의에서는 AWS API Gateway 도입을...", + "generated_by": "claude-sonnet-4.5", + "cached": false + }, + "related_documents": [ + { + "title": "API 설계 가이드", + "url": "https://confluence.../123", + "relevance_score": 0.92, + "excerpt": "..." + } + ], + "related_terms": [ + { + "term_id": "uuid", + "name": "Microservice", + "relation": "related" + } + ] +} + +=== 용어 검색 (벡터 유사도) === +POST /api/v1/terms/search +Request: +{ + "query": "서비스 간 통신", + "filters": { + "category": ["기술-아키텍처"], + "source_type": "internal", + "is_company_specific": true + }, + "top_k": 5, + "search_type": "hybrid" // 'keyword', 'vector', 'hybrid' +} + +Response: +{ + "results": [ + { + "term_id": "uuid", + "name": "API Gateway", + "similarity_score": 0.87, + "definition": "...", + "relevance_reason": "키워드 매칭 + 벡터 유사도" + } + ], + "search_time_ms": 234, + "total_results": 12 +} + +=== 용어 피드백 === +POST /api/v1/terms/{term_id}/feedback +Request: +{ + "meeting_id": "uuid", + "user_id": "uuid", + "rating": 5, // 1-5 + "comment": "매우 도움되었습니다", + "feedback_type": "helpful" // 'helpful', 'inaccurate', 'irrelevant' +} + +Response: +{ + "feedback_id": "uuid", + "message": "피드백이 등록되었습니다" +} + +=== 관리자 API === +POST /api/v1/admin/terms +PUT /api/v1/admin/terms/{term_id} +DELETE /api/v1/admin/terms/{term_id} +GET /api/v1/admin/terms/pending // 승인 대기 용어 +POST /api/v1/admin/terms/{term_id}/approve +``` + +**Phase 3: GraphQL 추가** +```graphql +query GetTermWithContext($termId: ID!, $meetingId: ID) { + term(id: $termId) { + id + name + definition + category + isCompanySpecific + + explanation(meetingId: $meetingId) { + text + context + generatedBy + cached + } + + relatedDocuments(limit: 3) { + title + url + relevanceScore + excerpt + } + + relatedTerms { + id + name + relation + } + + usageStats { + totalViews + averageRating + lastUsedAt + } + } +} +``` + +**결정 근거**: +- REST API: 간단한 CRUD, 캐싱 용이 +- GraphQL: 복잡한 관계 조회, Over-fetching 방지 +- 단계적 도입으로 학습 곡선 완화 + +--- + +## 7단계: 데이터 소스별 수집 전략 + +### 조직 문서 수집 + +**1. Confluence API 연동** +```python +from atlassian import Confluence + +class ConfluenceCollector: + def __init__(self, url, username, api_token): + self.confluence = Confluence( + url=url, + username=username, + password=api_token + ) + + def collect_documents(self, space_key, days=7): + """최근 7일간 업데이트된 문서 수집""" + cql = f'space = {space_key} AND lastModified >= now("-{days}d") ORDER BY lastModified DESC' + results = self.confluence.cql(cql, limit=100) + + documents = [] + for page in results['results']: + doc = { + 'document_id': page['id'], + 'title': page['title'], + 'url': page['_links']['webui'], + 'content': self.confluence.get_page_by_id(page['id'], expand='body.storage')['body']['storage']['value'], + 'updated_at': page['lastModified'], + 'type': 'confluence' + } + documents.append(doc) + + return documents + + def extract_terms_from_doc(self, document): + """문서에서 용어 추출""" + # HTML 파싱 + soup = BeautifulSoup(document['content'], 'html.parser') + text = soup.get_text() + + # NER 기반 용어 추출 + terms = self.ner_extractor.extract(text) + + # TF-IDF 기반 중요 키워드 + keywords = self.tfidf_extractor.extract(text, top_n=20) + + # Claude API로 맥락 기반 용어 추출 + claude_terms = self.claude_extractor.extract(text) + + # 통합 및 중복 제거 + all_terms = self.merge_and_deduplicate(terms, keywords, claude_terms) + + return all_terms +``` + +**2. SharePoint Graph API 연동** +```python +from microsoft_graph import GraphAPI + +class SharePointCollector: + def __init__(self, tenant_id, client_id, client_secret): + self.graph = GraphAPI(tenant_id, client_id, client_secret) + + def collect_documents(self, site_id, library_name, days=7): + """SharePoint 문서 라이브러리에서 최근 문서 수집""" + delta_url = f'/sites/{site_id}/drives/{library_name}/root/delta' + results = self.graph.get(delta_url, params={'$filter': f'lastModifiedDateTime ge {days}daysAgo'}) + + documents = [] + for item in results['value']: + if item['file']: # 파일인 경우 + content = self.graph.download_file(item['@microsoft.graph.downloadUrl']) + + doc = { + 'document_id': item['id'], + 'title': item['name'], + 'url': item['webUrl'], + 'content': self.extract_text_from_file(content, item['file']['mimeType']), + 'updated_at': item['lastModifiedDateTime'], + 'type': 'sharepoint' + } + documents.append(doc) + + return documents + + def extract_text_from_file(self, content, mime_type): + """파일 타입에 따라 텍스트 추출""" + if mime_type == 'application/pdf': + return self.pdf_extractor.extract(content) + elif mime_type in ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']: + return self.docx_extractor.extract(content) + elif mime_type in ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']: + return self.excel_extractor.extract(content) + else: + return content.decode('utf-8') +``` + +**3. Google Drive API 연동** +```python +from google_drive_api import DriveAPI + +class GoogleDriveCollector: + def __init__(self, credentials_file): + self.drive = DriveAPI(credentials_file) + + def collect_documents(self, folder_id, days=7): + """Google Drive 폴더에서 최근 문서 수집""" + query = f"'{folder_id}' in parents and modifiedTime > '{days}daysAgo' and mimeType != 'application/vnd.google-apps.folder'" + results = self.drive.files().list(q=query, pageSize=100).execute() + + documents = [] + for file in results.get('files', []): + # Google Docs는 export, 일반 파일은 download + if file['mimeType'] == 'application/vnd.google-apps.document': + content = self.drive.export_doc(file['id'], 'text/plain') + else: + content = self.drive.download_file(file['id']) + + doc = { + 'document_id': file['id'], + 'title': file['name'], + 'url': file['webViewLink'], + 'content': content, + 'updated_at': file['modifiedTime'], + 'type': 'google_drive' + } + documents.append(doc) + + return documents +``` + +**수집 스케줄**: +```yaml +# Airflow DAG 예시 +schedule_interval: "0 2 * * *" # 매일 새벽 2시 + +tasks: + - task_id: collect_confluence + python_callable: collect_confluence_documents + retries: 3 + + - task_id: collect_sharepoint + python_callable: collect_sharepoint_documents + retries: 3 + + - task_id: collect_google_drive + python_callable: collect_google_drive_documents + retries: 3 + + - task_id: extract_terms + python_callable: extract_terms_from_documents + depends_on: [collect_confluence, collect_sharepoint, collect_google_drive] + + - task_id: generate_embeddings + python_callable: generate_and_store_embeddings + depends_on: [extract_terms] + + - task_id: quality_check + python_callable: validate_term_quality + depends_on: [generate_embeddings] +``` + +### 외부 문서 수집 + +**1. GitHub 기술 문서 크롤링** +```python +import requests +from github import Github + +class GitHubDocCollector: + def __init__(self, access_token): + self.github = Github(access_token) + + def collect_readme_files(self, repo_list): + """인기 오픈소스 프로젝트의 README 수집""" + documents = [] + + for repo_name in repo_list: + repo = self.github.get_repo(repo_name) + readme = repo.get_readme() + + doc = { + 'document_id': f'github-{repo.id}', + 'title': f'{repo.name} README', + 'url': readme.html_url, + 'content': readme.decoded_content.decode('utf-8'), + 'type': 'github', + 'category': 'external-tech' + } + documents.append(doc) + + return documents + +# 수집 대상 저장소 예시 +TECH_REPOS = [ + 'kubernetes/kubernetes', + 'docker/docker', + 'facebook/react', + 'tensorflow/tensorflow', + 'pytorch/pytorch' +] +``` + +**2. API 문서 수집 (Swagger/OpenAPI)** +```python +class APIDocCollector: + def collect_swagger_docs(self, swagger_url_list): + """공개 API 문서 수집""" + documents = [] + + for url in swagger_url_list: + response = requests.get(url) + swagger_spec = response.json() + + # Swagger 스펙에서 용어 추출 + terms = self.extract_terms_from_swagger(swagger_spec) + + doc = { + 'document_id': f'swagger-{hash(url)}', + 'title': swagger_spec.get('info', {}).get('title', 'API Documentation'), + 'url': url, + 'content': self.convert_swagger_to_text(swagger_spec), + 'type': 'api_doc', + 'category': 'external-tech' + } + documents.append(doc) + + return documents + + def extract_terms_from_swagger(self, swagger_spec): + """Swagger 스펙에서 API 용어 추출""" + terms = [] + + # Paths에서 엔드포인트 용어 + for path, methods in swagger_spec.get('paths', {}).items(): + for method, details in methods.items(): + if 'summary' in details: + terms.append({ + 'name': details['summary'], + 'definition': details.get('description', ''), + 'category': 'API 엔드포인트' + }) + + # Schemas에서 데이터 모델 용어 + for schema_name, schema in swagger_spec.get('components', {}).get('schemas', {}).items(): + terms.append({ + 'name': schema_name, + 'definition': schema.get('description', ''), + 'category': 'Data Model' + }) + + return terms +``` + +**3. 산업 표준 용어집** +```python +class StandardTermCollector: + """산업 표준 용어집 수집""" + + STANDARD_SOURCES = { + 'iso': 'https://www.iso.org/obp/ui/', + 'ieee': 'https://www.ieee.org/standards/', + 'w3c': 'https://www.w3.org/standards/' + } + + def collect_iso_terms(self): + """ISO 표준 용어 수집 (수동 큐레이션 필요)""" + # 실제로는 웹 크롤링 또는 공식 API 사용 + # 여기서는 예시로 하드코딩 + terms = [ + { + 'name': 'Microservice Architecture', + 'definition': 'An approach to developing a single application as a suite of small services...', + 'source': 'ISO/IEC 29148', + 'category': 'Architecture Pattern', + 'type': 'external' + } + ] + return terms +``` + +--- + +## 8단계: 구현 예시 코드 + +### 전체 파이프라인 통합 + +```python +# main_pipeline.py +import asyncio +from collectors import ConfluenceCollector, SharePointCollector, GoogleDriveCollector +from extractors import TermExtractor, VectorEmbedder +from storage import PostgreSQLStore, QdrantStore +from claude_client import ClaudeAPIClient + +class TermManagementPipeline: + def __init__(self, config): + self.config = config + + # 수집기 초기화 + self.collectors = { + 'confluence': ConfluenceCollector(config['confluence']), + 'sharepoint': SharePointCollector(config['sharepoint']), + 'google_drive': GoogleDriveCollector(config['google_drive']) + } + + # 추출기 초기화 + self.term_extractor = TermExtractor() + self.vector_embedder = VectorEmbedder( + model='text-embedding-3-small', + api_key=config['openai_api_key'] + ) + + # 저장소 초기화 + self.postgres = PostgreSQLStore(config['postgres']) + self.vector_store = QdrantStore(config['qdrant']) if config.get('use_qdrant') else None + + # Claude API 클라이언트 + self.claude = ClaudeAPIClient(api_key=config['claude_api_key']) + + async def run_daily_pipeline(self): + """매일 실행되는 파이프라인""" + + # 1. 문서 수집 + print("📥 1단계: 문서 수집 중...") + all_documents = [] + for source_name, collector in self.collectors.items(): + try: + docs = await collector.collect_recent_documents(days=1) + all_documents.extend(docs) + print(f" ✓ {source_name}: {len(docs)}개 문서 수집") + except Exception as e: + print(f" ✗ {source_name} 수집 실패: {e}") + + print(f"총 {len(all_documents)}개 문서 수집 완료\n") + + # 2. 용어 추출 + print("🔍 2단계: 용어 추출 중...") + all_terms = [] + for doc in all_documents: + terms = await self.term_extractor.extract_terms(doc) + all_terms.extend(terms) + + print(f" ✓ {len(all_terms)}개 용어 추출 완료\n") + + # 3. 중복 제거 및 정제 + print("🧹 3단계: 용어 정제 중...") + unique_terms = self.term_extractor.deduplicate_terms(all_terms) + validated_terms = await self.validate_terms(unique_terms) + + print(f" ✓ {len(validated_terms)}개 유효 용어 (중복 제거: {len(all_terms) - len(validated_terms)}개)\n") + + # 4. 벡터 임베딩 생성 + print("🧮 4단계: 벡터 임베딩 생성 중...") + embeddings = await self.vector_embedder.generate_embeddings(validated_terms) + + print(f" ✓ {len(embeddings)}개 벡터 생성 완료\n") + + # 5. 데이터베이스 저장 + print("💾 5단계: 데이터베이스 저장 중...") + await self.postgres.save_terms(validated_terms) + + if self.vector_store: + await self.vector_store.upsert_vectors(embeddings) + else: + await self.postgres.save_vectors(embeddings) + + print(" ✓ 저장 완료\n") + + # 6. 품질 검증 + print("✅ 6단계: 품질 검증 중...") + quality_report = await self.validate_quality(validated_terms) + + print(f" ✓ 정확도: {quality_report['accuracy']}%") + print(f" ✓ 신뢰도 > 0.8: {quality_report['high_confidence_ratio']}%") + print(f" ✓ 조직 특화 용어: {quality_report['company_specific_count']}개\n") + + return { + 'documents_collected': len(all_documents), + 'terms_extracted': len(validated_terms), + 'quality_report': quality_report + } + + async def validate_terms(self, terms): + """용어 유효성 검증""" + validated = [] + + for term in terms: + # 1. 길이 검증 (2-200자) + if not (2 <= len(term['name']) <= 200): + continue + + # 2. 신뢰도 임계값 (0.7 이상) + if term.get('confidence', 0) < 0.7: + continue + + # 3. 중복 체크 + if await self.postgres.term_exists(term['normalized_name']): + continue + + validated.append(term) + + return validated + + async def validate_quality(self, terms): + """품질 지표 계산""" + total = len(terms) + high_confidence = sum(1 for t in terms if t.get('confidence', 0) > 0.8) + company_specific = sum(1 for t in terms if t.get('is_company_specific', False)) + + return { + 'accuracy': round(high_confidence / total * 100, 2) if total > 0 else 0, + 'high_confidence_ratio': round(high_confidence / total * 100, 2) if total > 0 else 0, + 'company_specific_count': company_specific + } + +# 실행 +if __name__ == '__main__': + config = load_config('config.yaml') + pipeline = TermManagementPipeline(config) + + # 비동기 실행 + result = asyncio.run(pipeline.run_daily_pipeline()) + + print("\n" + "="*50) + print("📊 파이프라인 실행 완료") + print("="*50) + print(f"수집된 문서: {result['documents_collected']}개") + print(f"추출된 용어: {result['terms_extracted']}개") + print(f"품질 점수: {result['quality_report']['accuracy']}%") +``` + +--- + +## 9단계: Claude API 요청/응답 JSON 구조 + +### 용어 설명 요청 + +```json +{ + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "temperature": 0.3, + "top_p": 0.95, + "system": "당신은 전문 용어를 회의 맥락에 맞춰 설명하는 AI 어시스턴트입니다. 다음 규칙을 따르세요:\n1. 2-3문장으로 간결하게 설명\n2. 회의 맥락을 반영하여 실용적인 정보 제공\n3. 관련 문서 정보가 있으면 활용\n4. 기술 용어는 비전공자도 이해할 수 있게 설명", + "messages": [ + { + "role": "user", + "content": "=== 회의 정보 ===\n회의 제목: Q4 회의록 작성 가이드 배포\n참석자: 김민준(PO), 이준호(BE), 박서연(AI), 최유진(FE)\n시간: 2024-10-01 14:00\n\n=== 회의 내용 (STT 전사) ===\n민준: 이번 분기 회의록 시스템 개발을 위해 먼저 API Gateway 구조부터 설계해야 할 것 같습니다.\n준호: 네, 저희가 마이크로서비스 아키텍처를 채택했으니 API Gateway가 필수겠네요. RESTful API로 설계하는 게 좋을까요?\n서연: AI 서비스와 연동할 때는 비동기 처리가 중요할 것 같아요. Claude API 호출 시간이 좀 걸리거든요.\n유진: 프론트엔드에서는 실시간으로 용어 설명을 보여줘야 하는데, API Gateway에서 캐싱을 지원하면 좋겠어요.\n\n=== 관련 문서 (RAG 검색 결과) ===\n[문서 1] API 설계 리뷰 회의록 (2024-09-28, 관련도 95%)\n제목: API Gateway 아키텍처 결정\n내용: \"RESTful API 설계 원칙과 보안 정책 확정. AWS API Gateway 채택 결정. JWT 토큰 기반 인증, Rate limiting 적용. Lambda 통합으로 서버리스 아키텍처 구현.\"\n\n[문서 2] 사내 기술 스택 가이드 (2024-08-15, 관련도 78%)\n제목: 마이크로서비스 표준 아키텍처\n내용: \"API Gateway는 클라이언트와 백엔드 서비스 사이의 단일 진입점. 요청 라우팅, 인증/인가, 로깅, Rate limiting 기능 제공. 회사 표준은 AWS API Gateway 사용.\"\n\n[문서 3] 신제품 개발 킥오프 회의록 (2024-09-15, 관련도 65%)\n제목: 1분기 신제품 개발 방향성\n내용: \"협업 도구 시장 분석 및 차별화 전략 수립. 기술 스택으로 마이크로서비스 아키텍처 선정, API Gateway 패턴 적용 예정.\"\n\n=== 질문 ===\n용어: API Gateway\n\n위 회의 맥락에 맞춰 'API Gateway'를 2-3문장으로 설명해주세요. 이 회의에서 API Gateway가 어떤 역할을 하는지 포함해주세요." + } + ] +} +``` + +### Claude API 응답 + +```json +{ + "id": "msg_01XyZ123AbC456", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "API Gateway는 클라이언트와 백엔드 마이크로서비스 사이의 단일 진입점으로, 요청 라우팅, 인증/인가, Rate limiting, 로깅 등을 담당합니다. 이 회의에서는 회의록 시스템 개발을 위해 AWS API Gateway를 도입하기로 했으며, Claude AI 서비스 연동 시 비동기 처리와 캐싱 기능을 활용하여 실시간 용어 설명 기능을 구현할 계획입니다. 사내 표준 아키텍처에 따라 JWT 인증과 Lambda 통합을 통한 서버리스 구조로 설계됩니다." + } + ], + "model": "claude-sonnet-4-5-20250929", + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 523, + "output_tokens": 147 + } +} +``` + +### 에러 응답 처리 + +```json +{ + "type": "error", + "error": { + "type": "rate_limit_error", + "message": "Rate limit exceeded. Please try again later." + } +} +``` + +**에러 핸들링 코드**: +```python +class ClaudeAPIClient: + async def explain_term(self, term, context, rag_docs): + """용어 설명 요청 with 에러 핸들링""" + + max_retries = 3 + retry_delay = 1 # 초 + + for attempt in range(max_retries): + try: + response = await self.call_claude_api(term, context, rag_docs) + + # 캐시 저장 + await self.cache.set( + key=self.generate_cache_key(term, context), + value=response['content'][0]['text'], + ttl=86400 # 24시간 + ) + + return { + 'explanation': response['content'][0]['text'], + 'cached': False, + 'tokens_used': response['usage']['input_tokens'] + response['usage']['output_tokens'] + } + + except RateLimitError as e: + if attempt < max_retries - 1: + await asyncio.sleep(retry_delay * (2 ** attempt)) # Exponential backoff + continue + else: + # Fallback: RAG 문서 직접 반환 + return { + 'explanation': self.generate_fallback_explanation(rag_docs), + 'cached': False, + 'error': 'rate_limit_exceeded' + } + + except Exception as e: + logging.error(f"Claude API 호출 실패: {e}") + return { + 'explanation': self.generate_fallback_explanation(rag_docs), + 'cached': False, + 'error': str(e) + } + + def generate_fallback_explanation(self, rag_docs): + """API 실패 시 Fallback 설명 생성""" + if rag_docs: + # RAG 문서에서 가장 관련성 높은 부분 추출 + best_doc = max(rag_docs, key=lambda d: d['relevance_score']) + return f"{best_doc['excerpt']}\n\n(출처: {best_doc['title']})" + else: + return "죄송합니다. 현재 이 용어에 대한 설명을 생성할 수 없습니다. 잠시 후 다시 시도해주세요." +``` + +--- + +## 10단계: 최종 권장사항 + +### 추천 구현 순서 + +**Week 1-2: Phase 1 - JSON 기반 MVP** +- [ ] JSON 용어 사전 파일 생성 (50개 핵심 용어) +- [ ] 간단한 키워드 매칭 구현 +- [ ] Claude API 연동 (RAG 없이 프롬프트만) +- [ ] 기본 UI 구현 (용어 하이라이트, 설명 모달) + +**Week 3-4: 데이터 수집 자동화** +- [ ] Confluence API 연동 +- [ ] SharePoint 또는 Google Drive 연동 (우선순위에 따라 선택) +- [ ] 배치 수집 스크립트 구현 +- [ ] 관리자 승인 UI + +**Week 5-6: Phase 2 - 기본 RAG** +- [ ] PostgreSQL + pgvector 설정 +- [ ] OpenAI embedding API 연동 +- [ ] 벡터 검색 구현 +- [ ] Claude API + RAG context 통합 + +**Week 7-8: 품질 개선** +- [ ] 사용자 피드백 시스템 +- [ ] 품질 지표 대시보드 +- [ ] A/B 테스트 (키워드 매칭 vs RAG) +- [ ] 성능 최적화 (캐싱, 인덱싱) + +**Week 9-10: Phase 3 - 고도화 (선택)** +- [ ] Qdrant 도입 (필요 시) +- [ ] 하이브리드 검색 +- [ ] 자동 품질 검증 +- [ ] 외부 문서 크롤링 + +### 예상 비용 (월간) + +| 항목 | Phase 1 | Phase 2 | Phase 3 | +|------|---------|---------|---------| +| **인프라** | +| PostgreSQL (RDS) | $50 | $100 | $150 | +| Redis (ElastiCache) | $30 | $50 | $50 | +| Qdrant | - | - | $500 | +| **API 사용료** | +| OpenAI Embedding | - | $20 | $50 | +| Claude API | $100 | $300 | $500 | +| **기타** | +| 저장공간 (S3) | $10 | $20 | $30 | +| **총계** | **$190/월** | **$490/월** | **$1,280/월** | + +### 성공 지표 (KPI) + +1. **사용성** + - 용어 조회율: 회의당 평균 3회 이상 + - 설명 유용성 평가: 4.0/5.0 이상 + - 응답 시간: 평균 2초 이하 + +2. **정확도** + - 용어 추출 정확도: 85% 이상 + - 설명 적합성: 사용자 피드백 80% 긍정 + - False positive rate: 10% 이하 + +3. **비즈니스** + - 신규 회의 중 기능 사용률: 60% 이상 + - 월 활성 사용자: 전체의 50% 이상 + - 회의록 작성 시간 단축: 20% 이상 + +### 리스크 및 완화 전략 + +| 리스크 | 영향도 | 완화 전략 | +|--------|--------|----------| +| Claude API 비용 급증 | 높음 | 캐싱 강화, 월 예산 한도 설정, Fallback 메커니즘 | +| 조직 용어 정확도 낮음 | 높음 | 수동 큐레이션 우선, 피드백 루프, 정기 품질 감사 | +| 응답 시간 느림 | 중간 | 3-tier 캐싱, 벡터 DB 최적화, CDN 활용 | +| 데이터 수집 실패 | 중간 | Retry 로직, 여러 소스 분산, 수동 백업 | +| 사용자 채택률 낮음 | 중간 | UX 개선, 온보딩 교육, 사용 인센티브 | + +--- + +## 최종 결론 + +**선정된 최적안**: 하이브리드 RAG 시스템 (단계적 접근) + +**핵심 장점**: +1. ✅ 조직 특화 용어의 높은 정확도 (JSON 큐레이션) +2. ✅ 확장 가능한 아키텍처 (RAG 시스템) +3. ✅ 합리적인 비용 구조 ($190/월 시작) +4. ✅ 명확한 구현 경로 (3-phase) +5. ✅ 차별화된 맥락 기반 설명 + +**다음 단계**: +1. Week 1-2에 MVP 구현 시작 +2. 50개 핵심 용어 큐레이션 +3. Claude API 연동 및 테스트 +4. 베타 사용자 10명 초대하여 피드백 수집 + +--- + +**작성일**: 2025-10-28 +**작성자**: 회의록 개선 프로젝트 팀 +**버전**: 1.0 +**승인자**: 김민준 (Product Owner) \ No newline at end of file diff --git a/reference/통신업무도메인.md b/reference/통신업무도메인.md index 1959955..29f4193 100644 --- a/reference/통신업무도메인.md +++ b/reference/통신업무도메인.md @@ -1,92 +1,53 @@ ## 통신회사 업무 도메인 카테고리 -### 1. **네트워크 인프라 (Network Infrastructure)** -- 네트워크 구축 및 운영 -- 유무선 통신망 관리 -- 5G/LTE 네트워크 -- 기지국 및 교환설비 - -### 2. **기술 개발 및 연구 (Technology Development & R&D)** -- 신기술 연구개발 -- 네트워크 기술 혁신 -- AI/빅데이터 기술 -- IoT 및 클라우드 기술 - -### 3. **고객 서비스 (Customer Service)** +### 1. **고객 서비스 (Customer Service)** - 고객 상담 및 지원 - VoC(Voice of Customer) 관리 - 서비스 품질 관리 - 고객 불만 처리 +- membership +- point -### 4. **영업 및 마케팅 (Sales & Marketing)** +### 2. **영업 및 마케팅 (Sales & Marketing)** - 요금제 기획 - 프로모션 및 캠페인 - 유통채널 관리 - B2B/B2C 영업 - 브랜드 마케팅 -### 5. **요금 및 청구 (Billing & Revenue)** +### 3. **요금 및 청구 (Billing & Revenue)** - 요금 청구 시스템 - 과금 관리 - 미수금 관리 - 정산 업무 -### 6. **네트워크 운용 (Network Operations)** +### 4. **네트워크 운용 (Network Operations)** - 네트워크 모니터링 - 장애 대응 및 복구 - 품질 최적화 - 트래픽 관리 -### 7. **서비스 기획 및 상품 개발 (Service Planning & Product Development)** +### 5. **서비스 기획 및 상품 개발 (Service Planning & Product Development)** - 신규 서비스 기획 - 요금제 설계 - 콘텐츠 서비스 - 부가서비스 개발 -### 8. **정보보안 (Information Security)** -- 통신 보안 -- 개인정보 보호 -- 사이버 보안 -- 보안 정책 수립 - -### 9. **시스템 운영 및 관리 (System Operations & Management)** +### 6. **시스템 운영 및 관리 (System Operations & Management)** - IT 시스템 운영 - 데이터센터 관리 - 클라우드 인프라 - 시스템 유지보수 -### 10. **가입자 관리 (Subscriber Management)** +### 7. **가입자 관리 (Subscriber Management)** - 가입자 정보 관리 - 번호 이동 - 개통 및 해지 - 명의 변경 -### 11. **망 품질 관리 (Network Quality Management)** -- 통신 품질 측정 -- 품질 개선 -- 서비스 레벨 관리 -- 성능 지표 관리 - -### 12. **규제 대응 및 준법 (Regulatory Compliance)** -- 통신 규제 대응 -- 법률 준수 -- 정부 정책 대응 -- 허가 및 인증 - -### 13. **기업 영업 (Enterprise Business)** +### 8. **기업 영업 (Enterprise Business)** - B2B 솔루션 - 전용선 서비스 - AICC (AI Contact Center) - 데이터센터 서비스 - -### 14. **로밍 및 국제 업무 (Roaming & International)** -- 국제 로밍 서비스 -- 해외 통신사 제휴 -- 국제 전화 서비스 - -### 15. **신사업 (New Business)** -- OTT (Over-The-Top) 서비스 -- 콘텐츠 사업 -- 핀테크 서비스 -- 스마트홈/IoT 서비스 diff --git a/reference/통신업무도메인_bak.md b/reference/통신업무도메인_bak.md new file mode 100644 index 0000000..1959955 --- /dev/null +++ b/reference/통신업무도메인_bak.md @@ -0,0 +1,92 @@ + +## 통신회사 업무 도메인 카테고리 + +### 1. **네트워크 인프라 (Network Infrastructure)** +- 네트워크 구축 및 운영 +- 유무선 통신망 관리 +- 5G/LTE 네트워크 +- 기지국 및 교환설비 + +### 2. **기술 개발 및 연구 (Technology Development & R&D)** +- 신기술 연구개발 +- 네트워크 기술 혁신 +- AI/빅데이터 기술 +- IoT 및 클라우드 기술 + +### 3. **고객 서비스 (Customer Service)** +- 고객 상담 및 지원 +- VoC(Voice of Customer) 관리 +- 서비스 품질 관리 +- 고객 불만 처리 + +### 4. **영업 및 마케팅 (Sales & Marketing)** +- 요금제 기획 +- 프로모션 및 캠페인 +- 유통채널 관리 +- B2B/B2C 영업 +- 브랜드 마케팅 + +### 5. **요금 및 청구 (Billing & Revenue)** +- 요금 청구 시스템 +- 과금 관리 +- 미수금 관리 +- 정산 업무 + +### 6. **네트워크 운용 (Network Operations)** +- 네트워크 모니터링 +- 장애 대응 및 복구 +- 품질 최적화 +- 트래픽 관리 + +### 7. **서비스 기획 및 상품 개발 (Service Planning & Product Development)** +- 신규 서비스 기획 +- 요금제 설계 +- 콘텐츠 서비스 +- 부가서비스 개발 + +### 8. **정보보안 (Information Security)** +- 통신 보안 +- 개인정보 보호 +- 사이버 보안 +- 보안 정책 수립 + +### 9. **시스템 운영 및 관리 (System Operations & Management)** +- IT 시스템 운영 +- 데이터센터 관리 +- 클라우드 인프라 +- 시스템 유지보수 + +### 10. **가입자 관리 (Subscriber Management)** +- 가입자 정보 관리 +- 번호 이동 +- 개통 및 해지 +- 명의 변경 + +### 11. **망 품질 관리 (Network Quality Management)** +- 통신 품질 측정 +- 품질 개선 +- 서비스 레벨 관리 +- 성능 지표 관리 + +### 12. **규제 대응 및 준법 (Regulatory Compliance)** +- 통신 규제 대응 +- 법률 준수 +- 정부 정책 대응 +- 허가 및 인증 + +### 13. **기업 영업 (Enterprise Business)** +- B2B 솔루션 +- 전용선 서비스 +- AICC (AI Contact Center) +- 데이터센터 서비스 + +### 14. **로밍 및 국제 업무 (Roaming & International)** +- 국제 로밍 서비스 +- 해외 통신사 제휴 +- 국제 전화 서비스 + +### 15. **신사업 (New Business)** +- OTT (Over-The-Top) 서비스 +- 콘텐츠 사업 +- 핀테크 서비스 +- 스마트홈/IoT 서비스 diff --git a/stt/bin/main/com/unicorn/hgzero/stt/controller/RecordingController.class b/stt/bin/main/com/unicorn/hgzero/stt/controller/RecordingController.class index d6055f8..0178d6f 100644 Binary files a/stt/bin/main/com/unicorn/hgzero/stt/controller/RecordingController.class and b/stt/bin/main/com/unicorn/hgzero/stt/controller/RecordingController.class differ diff --git a/stt/bin/main/com/unicorn/hgzero/stt/controller/TranscriptionController.class b/stt/bin/main/com/unicorn/hgzero/stt/controller/TranscriptionController.class index 8bcb84d..ed92ca2 100644 Binary files a/stt/bin/main/com/unicorn/hgzero/stt/controller/TranscriptionController.class and b/stt/bin/main/com/unicorn/hgzero/stt/controller/TranscriptionController.class differ diff --git a/stt/bin/test/com/unicorn/hgzero/stt/controller/RecordingControllerTest.class b/stt/bin/test/com/unicorn/hgzero/stt/controller/RecordingControllerTest.class index 6d8e33d..7f37889 100644 Binary files a/stt/bin/test/com/unicorn/hgzero/stt/controller/RecordingControllerTest.class and b/stt/bin/test/com/unicorn/hgzero/stt/controller/RecordingControllerTest.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/EventHubConfig.class b/user/bin/main/com/unicorn/hgzero/user/config/EventHubConfig.class new file mode 100644 index 0000000..d3a16c0 Binary files /dev/null and b/user/bin/main/com/unicorn/hgzero/user/config/EventHubConfig.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/RedisConfig.class b/user/bin/main/com/unicorn/hgzero/user/config/RedisConfig.class new file mode 100644 index 0000000..7735db7 Binary files /dev/null and b/user/bin/main/com/unicorn/hgzero/user/config/RedisConfig.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/SecurityConfig.class b/user/bin/main/com/unicorn/hgzero/user/config/SecurityConfig.class index 47e5b4a..b015588 100644 Binary files a/user/bin/main/com/unicorn/hgzero/user/config/SecurityConfig.class and b/user/bin/main/com/unicorn/hgzero/user/config/SecurityConfig.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/SwaggerConfig.class b/user/bin/main/com/unicorn/hgzero/user/config/SwaggerConfig.class index eedd904..11cd613 100644 Binary files a/user/bin/main/com/unicorn/hgzero/user/config/SwaggerConfig.class and b/user/bin/main/com/unicorn/hgzero/user/config/SwaggerConfig.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/jwt/JwtAuthenticationFilter.class b/user/bin/main/com/unicorn/hgzero/user/config/jwt/JwtAuthenticationFilter.class index e948e11..83bdf6a 100644 Binary files a/user/bin/main/com/unicorn/hgzero/user/config/jwt/JwtAuthenticationFilter.class and b/user/bin/main/com/unicorn/hgzero/user/config/jwt/JwtAuthenticationFilter.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapAuthenticator$UserAttributesMapper.class b/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapAuthenticator$UserAttributesMapper.class new file mode 100644 index 0000000..db4acb3 Binary files /dev/null and b/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapAuthenticator$UserAttributesMapper.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapAuthenticator.class b/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapAuthenticator.class new file mode 100644 index 0000000..c11c85e Binary files /dev/null and b/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapAuthenticator.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapUserDetails$LdapUserDetailsBuilder.class b/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapUserDetails$LdapUserDetailsBuilder.class new file mode 100644 index 0000000..0a2a94c Binary files /dev/null and b/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapUserDetails$LdapUserDetailsBuilder.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapUserDetails.class b/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapUserDetails.class new file mode 100644 index 0000000..9c68c13 Binary files /dev/null and b/user/bin/main/com/unicorn/hgzero/user/config/ldap/LdapUserDetails.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/controller/UserController.class b/user/bin/main/com/unicorn/hgzero/user/controller/UserController.class index 6405b88..00504ef 100644 Binary files a/user/bin/main/com/unicorn/hgzero/user/controller/UserController.class and b/user/bin/main/com/unicorn/hgzero/user/controller/UserController.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/repository/entity/UserEntity$UserEntityBuilder.class b/user/bin/main/com/unicorn/hgzero/user/repository/entity/UserEntity$UserEntityBuilder.class index 8531f59..b13fa50 100644 Binary files a/user/bin/main/com/unicorn/hgzero/user/repository/entity/UserEntity$UserEntityBuilder.class and b/user/bin/main/com/unicorn/hgzero/user/repository/entity/UserEntity$UserEntityBuilder.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/repository/entity/UserEntity.class b/user/bin/main/com/unicorn/hgzero/user/repository/entity/UserEntity.class index 472ad4d..52ef3f8 100644 Binary files a/user/bin/main/com/unicorn/hgzero/user/repository/entity/UserEntity.class and b/user/bin/main/com/unicorn/hgzero/user/repository/entity/UserEntity.class differ diff --git a/user/bin/main/com/unicorn/hgzero/user/service/UserServiceImpl.class b/user/bin/main/com/unicorn/hgzero/user/service/UserServiceImpl.class index 895dca2..01fce76 100644 Binary files a/user/bin/main/com/unicorn/hgzero/user/service/UserServiceImpl.class and b/user/bin/main/com/unicorn/hgzero/user/service/UserServiceImpl.class differ