눈오지
눈오지
🗡️ AI 레전드
🌿 뉴비 파트너
🌈 지피터스금손

카톡봇 : 이제 반려동물 대신 반려 카톡 봇 친구를 가져보는건 어떨까요?

저번 카톡봇을 업데이트했습니다.

(사진 첨부 예정...)

이번 목표는 1) 카톡에서 검색 기능을 사용하기(퍼플렉시티 api 이용) 2) 대화 내용을 기억하게 하기(멀티턴) 3) 유튜브 요약 그리고 1),2)는 성공했지만, 3)은 실패하였습니다.
또한 해당 과정에 시행착오가 많았습니다.

1. 퍼플렉시티 api 이용 with n8n

카톡방 스터디는 동물의 왕국입니다. 오류가 난 봇들이 해당 키워드에 맞춰 우는 모습을 보면 브레멘 악단이 생각이 났습니다.

지옥불에서 죽어가고 있을 때 영웅들이 등장하는데요. 친히 샘호트만님께서 n8n을 이용해 해결 방법을 제시했습니다.

방법은 아래와 같은데요.
https://www.gpters.org/nocode/post/kakaotalk-originally-used-like-oSb4F5vHfMW1MjF

카톡방에서 특정 내용에 대해 검색을 실시한다. → 메신저봇을 이용해서 알람 신호를 받아온다. → 받아온 신호를 n8n을 이용해서 받는다.(이때 웹훅을 사용) → n8n에서 받은 신호를 바탕으로 퍼플렉시티로 검색을 실시한다. → 검색한 내용을 다시 메신저봇에게 전달한다.

웹 응용 프로그램의 프로세스를 보여주는 다이어그램


이걸 보고 메신저봇이라는 앱에서 코드를 다 구현해도 되는거 아닌가? 이렇게 생각할 수도 있는데요.

메신저봇의 언어는 익숙하지 않은 Rhino JavaScript입니다. 이건 마치 언어로 따지면 사투리, 혹은 힌두어 같습니다. 익숙하지 않은 언어로 모든 코드를 구현하기 보다는 해당 신호만 n8n으로 보내는 방법을 제시했습니다.

그리고 해당 내용은 샘호트만님이 깃허브에 상세하게 적어주셨습니다. (다시 한 번 외쳐 샘갓호트만!)

이때 주의할 내용이 몇 가지 있었는데요.

n8n 서버가 로컬 호스트여서는 안됩니다. (예시 local8000 뭐시기) 외부에 등록된 서버여야만 합니다. (예를 들어 지피터스 서버)

그래서 해당 내용은 성공했습니다.

2. 대화 내용을 기억하기 (멀티턴의 세계)

2번째는 대화 내용을 기억하기였습니다. 있어보이게 용어를 쓰면 멀티턴인데요. 대화를 기억해야 서로 오고가는 말이 있겠죠? (방금 무슨 말했지 짤)

코드는 메신저봇에서 훅을 받고, 해당 내용을 감지합니다. 대화 중 하나가 트리거이고, 트리거로 대화가 진행되면 멀티턴이 진행되게 작성했습니다. DB는 파이어베이스로 연동되게 진행했는데요.

이 과정에서 중요한 것은 메신저봇이 카톡 대화내용을 후킹할 때 어떻게 자료를 가지고 오냐입니다.

이게 무슨소리냐면, 대화 내용에 누가, 어느방에서, 무슨 대화를 나눴는지에 대한 자료를 어떻게 저장했는지를 찾는 과정인데요. 이 과정이 필요합니다.

처음엔 너무 단순하게 생각했어요

실제로는 아직 아이디를 그대로 기억하는 방법을 택하려고 했는데요. 그렇지 않았습니다.

처음 설계할 때는 "그냥 user_id만 저장하면 되겠지?" 이런 천진난만한 생각을 했어요.

완전히 틀렸습니다.

왜냐하면 카톡에서는 같은 사람이 여러 방에 있을 수 있고, 심지어 닉네임도 방마다 다를 수 있거든요. "철수"라는 사람이 회사방에서는 "김대리"이고, 친구방에서는 "철수야"일 수 있잖아요.

실제 구현해보니 더 복잡했습니다

FastAPI에서 받는 데이터 구조를 보면:

python

class ChatRequest(BaseModel):
    user_hash: str      # 누가 (메신저봇이 만들어주는 해시값)
    room: str          # 어느방에서
    content: str       # 무슨말을
    is_new_conversation: bool  # 새로운 대화인지

여기서 핵심은 user_hash입니다. 메신저봇이 사용자마다 고유한 해시값을 만들어주는데, 이게 진짜 신의 한수였어요.

그런데 또 함정이 있었습니다

사용자별로만 구분하면 안되고, 방별로도 구분해야 했어요. 왜냐하면 같은 사람이 A방에서 한 대화와 B방에서 한 대화는 완전히 다른 맥락이잖아요.

그래서 결국 이렇게 했습니다:

python

# 사용자별 세션 분리를 위해 더 정확한 사용자 ID 생성
conversation_key = f"{user_hash}_{room}"

이러면 "철수_회사방", "철수_친구방" 이런 식으로 완전히 분리된 대화 세션을 만들 수 있어요.

파이어베이스 저장 구조도 깔끔하게

python

doc_ref.set({
    "user_hash": user_hash,    # 원본 사용자 해시
    "room": room,              # 방 정보도 따로 저장
    "session_active": True,    # 세션 활성화 상태
    "last_updated": datetime.datetime.now(datetime.timezone.utc),
    "messages": message_history  # 실제 대화 내용들
})

이렇게 하니까 나중에 디버깅할 때도 "아, 이 대화는 어느 방에서 누가 한 거구나" 바로 알 수 있어요.

5분 타임아웃 기능도 추가했어요

대화를 계속 저장하다 보면 파이어베이스 용량이 터질 것 같아서, 5분 동안 대화가 없으면 자동으로 세션을 종료하게 했습니다.

python

# 세션이 활성화되어 있고, 마지막 업데이트가 5분 이상 지났으면 자동 종료
if session_active and last_updated:
    time_diff = current_time - last_updated
    if time_diff.total_seconds() > 300:  # 5분 = 300초
        print(f"  - ⏰ 5분 타임아웃으로 자동 세션 종료")
        doc_ref.update({
            "session_active": False,
            "last_updated": current_time
        })

마치 카페에서 자리 비우면 30분 후에 정리하는 것처럼요.

시고르라는 캐릭터도 추가했어요

그냥 밋밋한 챗봇보다는 캐릭터가 있는 게 재밌잖아요. 산타 대신 썰매를 끄는 허스키 "시고르"라는 설정으로 했습니다.

만화 허스키가 눈에 썰매를 당겼다
전화 화면에 두 개의 한국 문자 메시지

python

system_prompt = """
- 이름: 시고르
- 정체: 산타 대신 썰매를 끄는 허스키 썰매견
- 성격: 충성심 넘치고, 열정적이지만 조금 덤벙댄다
- 말투: 말 끝에 '멍'을 자주 붙여서 감정을 강조한다
"""

3. 유튜브 요약의 배신

3번째 목표였던 유튜브 요약은... 완전히 실패했습니다.

API 제한이나 복잡한 처리 과정 때문인지, 아니면 제가 뭔가 놓친 부분이 있는지 모르겠지만, 이 부분은 아직 숙제로 남아있습니다.

마치 3인조 아이돌 그룹에서 한 명만 데뷔를 못한 느낌이랄까요. 언젠가는 완성시켜야겠습니다.

결과와 배운 점

이번 카톡봇 업데이트를 통해 몇 가지를 깨달았습니다.

  1. 데이터 구조화의 중요성: 단순히 "사용자별로만 구분하자"가 아니라 "사용자+방별로 구분해야 한다"는 것을 깨달았습니다.

  2. 예상치 못한 복잡성: 같은 사람도 방마다 다른 닉네임을 쓸 수 있다는 것, 세션 관리가 생각보다 복잡하다는 것.

  3. 자동화의 필요성: 5분 타임아웃 같은 자동 정리 기능이 없으면 DB가 금방 터진다는 것.

  4. 캐릭터의 힘: 그냥 답변하는 봇보다 시고르처럼 캐릭터가 있는 봇이 훨씬 재밌다는 것.

  5. 포트 충돌의 현실: 8000포트가 사용 중이면 8001로 바꿔서 실행하는 예외 처리도 필요하다는 것.

다음에는 유튜브 요약 기능까지 완성해서 진짜 완전체 카톡봇을 만들어보려고 합니다. 그때까지 이 글이 누군가에게는 도움이 되길 바라며...

4
4개의 답글

👉 이 게시글도 읽어보세요