ChatGPT로 일본어 공부하기(1)

Genne Chung·2025년 1월 21일
0

chatgpt 일본어

목록 보기
1/1

신년목표로
1. 면허따기
2. 일본어 자격증 따기

를 잡았다. 공부하다보니 재밌기도 하고...

근데 생각보다 쓰면서 하자니 지하철 타면서 하기에 좀 한계가 있다. 원체 디지털 인간이기도 하고, 겸사겸사 agent 공부도 하고, 기타등등으로 신년 프로젝트 달성

일단 필요한 기능들을 정리해 두고 하나씩 진행할 예정.

설계

시나리오

  • 초기 입력에서 학습 '주제' 와 '레벨'을 받아 해당하는 대화를 시작한다
    • 2차 버전에서는 여러 주제 중 하나를 선택하는 것도 나쁘지 않을 것 같다
  • 유저가 대화를 하는 중간에 단어 및 표현 검색을 할 수 있도록 한다
  • 유저의 대답을 교정하는 기능이 중간에 끼어 있다
  • 담화가 끝났는지를 판단한다.
  • 담화가 끝나고 나면 요약을 하고, 괜찮은 표현들을 추천한다.
  • 담화들을 전부 기억하고 있어, 사용자가 얼마나 성장했는지(?) 알 수 있게 한다.

아이디어를 듣고 나더니 스픽같다고 하신다. 역시 사람 생각 다 똑같음

기능

  • 대화 및 주제 추천
  • 대화 기능
  • 유저 답변 교정 기능
  • 짧은 번역 기능
  • 담화 종료 판단
  • 요약 기능
  • 표현 추천 기능
  • LTM / STM 를 포함한 메모리 기능
  • 메모리 활용해서 유저의 성능을 평가 (일본어 능력을 평가?)

프레임워크

  • streamlit. gradio만 썼었는데 새로운 거 써 보고 싶기도 했고, 예쁘다. 안되면 다시 gradio로 리턴.
  • langchain & openai (ollama로 하다가 작은 모델은 너무 힘들어서.... 포기했습니다 gpu가 작아요 로또당첨되면좋겠다)

구현

  • 위의 기능들 중 일부만 구현했다. 더 귀찮아하지 않는다면... 남는시간에 더 할수도

  • ㅠㅠ 비어 있는 demo....

메모리

from pydantic import BaseModel


class ShortTermMemory(BaseModel):
    subject: str
    object: str
    action: str


class LongTermMemory(BaseModel):
    description: str
    stm_list: list[ShortTermMemory]코드를 입력하세요
  • 일단 개념이 한 담화에 대해 표현 정리 -> 요약본도 추가하기 이거여서 일단 이 정도로 하고, 아마 여기에서 subject / object / action 부분이 조금씩 바뀔 것 같다. 표현이라던지, 아니면 오류사항이라던지로 바뀔 듯
  • 구조도 바뀔 가능성이 존재.

agent

  • 사실 외부 툴이라던지 붙은 게 없어서 정확히는 orchestration
    • 기능이 커지면(db라던지) action하는 부분과 orchestration으로 분리
  • 미니인 이유: 돈없음

초기 액션들

class ConversationAgent:
    def __init__(self, model_name='gpt-4o-mini'):
        self.chatbot = ChatOpenAI(model=model_name)
        self.resources_path = get_resources_path()
        self.init_conversations_prompt = ChatPromptTemplate.from_messages(
            messages=[('user', get_prompt_txt(self.resources_path.joinpath('init_conversation.txt')))],
            template_format='jinja2'
        )
        self.conversation_prompt = ChatPromptTemplate.from_messages(
            messages=[('user', get_prompt_txt(self.resources_path.joinpath('conversation.txt')))],
            template_format='jinja2'
        )
        self.revision_prompt = ChatPromptTemplate.from_messages(
            messages=[('user', get_prompt_txt(get_resources_path().joinpath('revision.txt')))],
            template_format='jinja2'
        )
        self.check_end_conversation_prompt = ChatPromptTemplate.from_messages(
            messages=[('user', get_prompt_txt(get_resources_path().joinpath('check_end_conversation.txt')))],
            template_format='jinja2'
        )
        self.history: list[tuple] = []
        self.memories: list[LongTermMemory] = []
        
    def start(self, topic: str, level: Literal['starter', 'basic', 'intermediate', 'advanced', 'master']):
        self.topic = topic
        self.level = level
        chain = self.init_conversations_prompt | self.chatbot | JsonOutputParser()
        conversation_dict = chain.invoke({'topic': self.topic, 'level': self.level})
        self.history.append(('ai', conversation_dict['ai']))
        return conversation_dict

json을 사랑하기 때문에 모든 템플릿은 jinja2로 했다.

  • 히스토리 형태로 보관하는 방식
  • 후리가나가 없이는 아직 한자를 읽지 못하기 때문에, 아직 추가하지만 능력이 올라가면 더 이상 후리가나를 넣지 않도록 만들고 싶다. 이게 후반부 메모리에서 할 부분.
  • json의 경우 JsonOutputParser를 쓰는 게 제일 속이 편하다. 그래서 맘놓고 프롬프트에다가 json을 내보내라고 추가함. Return only the json output and nothing else. 최고의문장
    def predict_conversations(self, user_input: str):
        try:
            self.check_end_conversation()

            self.history.append(('human', user_input))
            revision_dict = self.revise(user_input)

            conversations = [f'[{role}]: {value}' for role, value in self.history]
            chain = self.conversation_prompt | self.chatbot | JsonOutputParser()
            conversation_dict = chain.invoke({'topic': self.topic, 'level': self.level, 'history': conversations})
            self.history.append(('ai', conversation_dict['ai']))

            return conversation_dict, revision_dict
        except ConversationEndException:
            return {'ai': 'DONE'}, {}
            
       def summarize(self, history: list[tuple]) -> LongTermMemory:
          # dummy
          return LongTermMemory(description='', stm_list=[])

      def memorize(self):
          memory = self.summarize(self.history)
          self.history = []
          self.memories.append(memory)

      def check_end_conversation(self):
          def sanitize(output: str):
              if output not in ['DONE', 'CONTINUE']:
                  raise ParseException
              return output

          chain = self.check_end_conversation_prompt | self.chatbot | StrOutputParser() | RunnableLambda(sanitize)
          state = chain.invoke({'dialog': self.history})
          if state == 'DONE':
              self.memorize()
  • 대화할 때마다 대화가 끝났는지 아닌지 체킹을 하고, 끝났으면 기억 관련 과정을 거친다
    • 요약 / 표현 추천 및 오류 저장 등의 기능을 아직 붙이지 않고 동작만 체크

오늘은 일단 여기까지

실행 예시

일본어 입력기가 없다는걸 깨달았다... 그래서 웹검색을 빌린 첫 문장을 제외하면 초보 그자체인 답변모음

보강해야 할 것

  1. stream 안하고 있는데 마지막에는 다 추가해야 한다. 하지만 모든 함수 밑에 쓰기 조금 그렇기 때문에... 추후에는 chain만 리턴하고 stream decorator를 사용하는 orchestrator를 따로 사용은 해야 할 것으로 보임
  2. '주제'를 주면 잘하는데 '학과 목표'를 주면 못한다. 이건 프롬프팅을 해야 할 것으로 생각됨
  3. 좀 구질구질하게 질질 끌고 가기 때문에(진짜계속깎아달라함), 단호하게 끝낼 수 있도록 담화를 조금 적당한 길이로 끊으라는 내용을 넣던지 반복되면 멈추라는 조항을 넣던지 해야 할 것 같다. 그리고 난 초보자인데 진짜 엄청 길게 말하는거 진짜 너무함 한자도 너무많은데... 뭔담화쪽에 프롬프트 수정은 해야 할 것 같다. 아니면 레벨에 대해 설명하던지... starter정도면 초보자 아니었나?
  4. streamlit이든 gradio든 일단 데모 만들 때에는 무조건 일본어 입력기가 있어야 한다. 컴퓨터에서 제공하는지 알아봐야 할것

아 근데 주말이나 되어야 이어서 할 수 있겠네...

profile
NLP / LLM

0개의 댓글

관련 채용 정보