영리의 테크블로그

소설 캐릭터 정보 추출 & 대화 토이프로젝트 (Book Buddy) 본문

dev/AI

소설 캐릭터 정보 추출 & 대화 토이프로젝트 (Book Buddy)

영리0 2024. 11. 24. 23:34

소설 캐릭터 챗봇 개발기: 텍스트 처리부터 채팅 구현까지

웹소설을 읽다가 궁금한 점이 생기면 바로 캐릭터와 대화하면서 확인하고 싶었습니다. 소설을 읽기 전 등장인물들과 대화할 수 있는 기능을 만들고자 했습니다.

 

달빛조각사 / 주인공

https://github.com/YoonJae00/BookBuddy

 

GitHub - YoonJae00/BookBuddy: 소설원문 기반 채팅 app

소설원문 기반 채팅 app. Contribute to YoonJae00/BookBuddy development by creating an account on GitHub.

github.com

 

 

1. 텍스트 분할 (Text Splitting)

소설 텍스트를 효율적으로 처리하기 위해 청크 단위로 분할했습니다:

            # 2. 텍스트 분할
            chunks = self.text_splitter.split_text(content)
            if not chunks:
                raise NovelProcessingError("Failed to split text into chunks")

이 과정에서 고려한 점들:

  • 문맥 유지를 위한 적절한 청크 크기 설정
  • 청크 간 오버랩으로 문맥 연결성 확보
  • 메모리 효율성 고려

https://smith.langchain.com/public/8bc2632b-70ea-4471-9e1c-0ae45b153331/r

 

LangSmith

 

smith.langchain.com

 

2. 캐릭터 추출 (Character Extraction)

분할된 텍스트에서 등장인물 정보를 추출합니다:

            # 1. 초기 캐릭터 식별
            characters = await self._identify_characters(content, novel_id)
            if not characters:
                raise NovelProcessingError("No characters found in the novel")

            for char in characters:
                self.name_resolver.add_character(char['full_name'], char['aliases'])
                self.logger.log_character_found(char['full_name'], char)

                # 캐릭터 정보 저장 (동기식)
                db.save_character({
                    'id': str(uuid.uuid4()),
                    'novel_id': novel_id,
                    **char
                })

추출하는 캐릭터 정보:

  • 이름과 별칭
  • 성격 특성
  • 배경 정보
  • 관계도
  • 역할

이름/성격/특성

 

상세 등등
관계

3. 이벤트 추출 (Event Extraction)

캐릭터들의 행동과 사건을 추출합니다:

            # 3. 각 청크 처리
            all_events = []
            for i, chunk in enumerate(chunks):
                try:
                    normalized_chunk = self._normalize_names(chunk)
                    events = await self._extract_events(normalized_chunk, i)
                    for event in events:
                        # 이벤트 저장 (동기식)
                        event['id'] = str(uuid.uuid4())
                        event['novel_id'] = novel_id
                        db.save_event(event)
                        self.logger.log_event_extracted(event['summary'], i)
                    all_events.extend(events)
                except Exception as e:
                    self.logger.log_error(f"Error processing chunk {i}", {"error": str(e)})
                    continue

이벤트 데이터 구조:

  • 사건 요약
  • 관련 캐릭터
  • 발생 위치(챕터)
  • 중요도
  • 감정 상태

https://smith.langchain.com/public/8cc0e5b5-c393-4745-8f09-a237c7856b8d/r

 

LangSmith

 

smith.langchain.com

 

4. 채팅 시스템 구현

추출된 정보를 바탕으로 캐릭터와의 대화를 구현합니다:

class CharacterChatbot:
    def __init__(self, character_data: Dict, events: List[Dict], settings, user_id: str):
        self.character = character_data
        self.events = events
        self.user_id = user_id
        self.db = DatabaseService()
        self.vector_store = VectorStore(settings)
        self.llm = ChatOpenAI(
            temperature=0.7,
            model="gpt-4o-mini",
            openai_api_key=settings.OPENAI_API_KEY
        )
        )
        self.chat_history = ChatMessageHistory()
        # 응답 생성
        # 이전 대화 기록 로드
        history = self.db.get_chat_history(
            character_id=character_data['id'],
            user_id=user_id
        )
            성격: {character[personality_traits]}
        for msg in history:
            if msg['role'] == 'user':
                self.chat_history.add_user_message(msg['content'])
            else:
                self.chat_history.add_ai_message(msg['content'])

채팅 시스템의 주요 특징:

  • 캐릭터 성격 반영
  • 관련 이벤트 참조
  • 대화 맥락 유지
  • 이전 대화 기록 활용

데이터 구조 설계

캐릭터 정보

    def save_character(self, character_data: Dict) -> None:
        """캐릭터 정보 저장"""
        try:
            character_id = character_data.get('id')
            if not character_id:
                raise Exception("Character ID is required")
                .where('novel_id', '==', novel_id)\
            self.db.collection('characters')\
                .document(character_id)\
                .set(character_data)
                .where('novel_id', '==', novel_id)\
        except Exception as e:
            raise Exception(f"Failed to save character: {str(e)}")

이벤트 저장

    def save_event(self, event_data: Dict) -> None:
        """이벤트 정보 저장"""
        try:
            self.db.collection('events').document(event_data['id']).set(event_data)
        except Exception as e:
            raise Exception(f"Failed to save event: {str(e)}")

대화 기록

    def save_chat_history(self, character_id: str, user_id: str, message: Dict) -> None:
        """대화 기록 저장"""
        try:
            self.db.collection('chat_history').add({
                'character_id': character_id,
                'user_id': user_id,
                'content': message['content'],
                'role': message['role'],
                'timestamp': firestore.SERVER_TIMESTAMP
            })
        except Exception as e:
            print(f"Failed to save chat history: {str(e)}")

개발 과정에서 배운 점

  1. 텍스트 처리 최적화
    • 전체 텍스트를 저장할 필요 없이, 필요한 정보만 추출하여 저장
    • 벡터 검색으로 관련 컨텍스트 효율적 검색
  2. 캐릭터 정보 구조화
    • 캐릭터의 다양한 특성을 체계적으로 저장
    • 관계 정보를 통한 캐릭터 간 상호작용 지원
  3. 이벤트 기반 기억
    • 캐릭터의 경험과 기억을 이벤트 단위로 관리
    • 대화 중 관련 이벤트 참조로 맥락 유지
  4. 실시간 대화 처리
    • 대화 기록 유지로 일관성 있는 대화 흐름
    • 캐릭터 특성에 맞는 응답 생성

향후 개선 방향

  1. 텍스트 분석 고도화
    • 더 정확한 캐릭터 정보 추출
    • 감정 분석 추가
  2. 대화 품질 향상
    • 캐릭터 성격 더 정교하게 반영
    • 다중 캐릭터 대화 지원
  3. 사용자 경험 개선
    • 실시간 피드백 반영
    • 대화 컨텍스트 시각화