hgzero/ai-python/app/services/transcript_service.py
Minseo-Jo 0caa1ec3b6 Feat: AI 서비스 통합 및 회의록 기능 개선
- AI 서비스와 Meeting 서비스 통합 개선
  - AgendaSummaryDTO에 decisions 필드 추가 (안건별 결정사항 배열)
  - EndMeetingService에서 AI 서비스 타임아웃 처리 개선
  - AIServiceClient에 상세한 에러 로깅 추가

- 회의록 consolidate 프롬프트 개선
  - Todo 추출 로직 강화 (자연스러운 표현 인식)
  - 안건별 decisions 필드 추가 (대시보드 표시용)
  - 담당자 패턴 인식 개선

- Kubernetes 배포 설정 개선
  - meeting-service.yaml에 AI_SERVICE_URL 환경변수 추가
  - AI_SERVICE_TIMEOUT 설정 추가

- 데이터베이스 관리 SQL 스크립트 추가
  - check-agenda-sections.sql: 안건 섹션 확인
  - cleanup-test-data.sql: 테스트 데이터 정리
  - insert-test-data-final.sql: 최종 테스트 데이터

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 18:07:57 +09:00

124 lines
4.1 KiB
Python

"""Transcript Service - 회의록 통합 처리"""
import logging
from datetime import datetime
from app.models.transcript import (
ConsolidateRequest,
ConsolidateResponse,
AgendaSummary,
ExtractedTodo
)
from app.services.claude_service import claude_service
from app.prompts.consolidate_prompt import get_consolidate_prompt
logger = logging.getLogger(__name__)
class TranscriptService:
"""회의록 통합 서비스"""
async def consolidate_minutes(
self,
request: ConsolidateRequest
) -> ConsolidateResponse:
"""
참석자별 회의록을 통합하여 AI 요약 생성
"""
logger.info(f"회의록 통합 시작 - Meeting ID: {request.meeting_id}")
logger.info(f"참석자 수: {len(request.participant_minutes)}")
try:
# 1. 프롬프트 생성
participant_data = [
{
"user_name": pm.user_name,
"content": pm.content
}
for pm in request.participant_minutes
]
prompt = get_consolidate_prompt(
participant_minutes=participant_data,
agendas=request.agendas
)
# 입력 데이터 로깅
logger.info("=" * 80)
logger.info("INPUT - 참석자별 회의록:")
for pm in participant_data:
logger.info(f"\n[{pm['user_name']}]")
logger.info(f"{pm['content'][:500]}..." if len(pm['content']) > 500 else pm['content'])
logger.info("=" * 80)
# 2. Claude API 호출
start_time = datetime.utcnow()
ai_result = await claude_service.generate_completion(prompt)
processing_time = (datetime.utcnow() - start_time).total_seconds() * 1000
logger.info(f"AI 처리 완료 - {processing_time:.0f}ms")
# 3. 응답 구성
response = self._build_response(
meeting_id=request.meeting_id,
ai_result=ai_result,
participants_count=len(request.participant_minutes),
duration_minutes=request.duration_minutes
)
logger.info(f"통합 요약 완료 - 안건 수: {len(response.agenda_summaries)}, Todo 수: {response.statistics['todos_count']}")
return response
except Exception as e:
logger.error(f"회의록 통합 실패: {e}", exc_info=True)
raise
def _build_response(
self,
meeting_id: str,
ai_result: dict,
participants_count: int,
duration_minutes: int = None
) -> ConsolidateResponse:
"""AI 응답을 ConsolidateResponse로 변환"""
# 안건별 요약 변환
agenda_summaries = []
for agenda_data in ai_result.get("agenda_summaries", []):
# Todo 변환 (제목만)
todos = [
ExtractedTodo(title=todo.get("title", ""))
for todo in agenda_data.get("todos", [])
]
agenda_summaries.append(
AgendaSummary(
agenda_number=agenda_data.get("agenda_number", 0),
agenda_title=agenda_data.get("agenda_title", ""),
summary_short=agenda_data.get("summary_short", ""),
summary=agenda_data.get("summary", ""),
decisions=agenda_data.get("decisions", []),
pending=agenda_data.get("pending", []),
todos=todos
)
)
# 통계 정보
statistics = ai_result.get("statistics", {})
statistics["participants_count"] = participants_count
if duration_minutes:
statistics["duration_minutes"] = duration_minutes
# 응답 생성
return ConsolidateResponse(
meeting_id=meeting_id,
keywords=ai_result.get("keywords", []),
statistics=statistics,
decisions=ai_result.get("decisions", ""),
agenda_summaries=agenda_summaries,
generated_at=datetime.utcnow()
)
# 싱글톤 인스턴스
transcript_service = TranscriptService()