빌릿 챗봇 프로젝트 4


챗봇 프로젝트 리뷰 4

getdti, funcsfortool

getdti는 dti라는 컬럼을 구하기 위한 모듈.
여기서 dti를 구한 후 머신러닝 모델에 입력값으로 쓰인다.
funcsfortool은 챗봇이 쓸 툴을 모아놓은 모듈.
챗봇은 필요에 따라 여기 있는 툴을 활용해서 정확한 답변을 생성하게 된다.

getdti

import pymysql

conn = pymysql.connect(host = 'localhost', port = 3306, user = 'root', password='1234', db = 'mydata')

def _get_data(user_pn): # dti를 구할 때 필요한 데이터를 불러오는 함수.
    mortgage_debt = 0
    mortgage_repayment = 0
    installment = 0
    mortgage_term = 0
    cursor = conn.cursor()
    cursor.execute(f'SELECT mortgage_debt, mortgage_repayment, installment, mortgage_term FROM mydata.user_log WHERE user_pn="{user_pn}"')
    row = cursor.fetchall()
    row0 = row[0]
    mortgage_debt = row0[0]
    mortgage_debt = float(mortgage_debt)
    mortgage_repayment = row0[1]
    mortgage_repayment = float(mortgage_repayment)
    installment = row0[2]
    mortgage_term = row0[3]
    conn.close()
    return mortgage_debt, mortgage_repayment, installment, mortgage_term


def calculate_dti(user_pn, income):
    mortgage_debt, mortgage_repayment, installment, mortgage_term = _get_data(user_pn)
    dti = ((((mortgage_debt/mortgage_term) + mortgage_repayment) + installment) / income) * 100 # 실제 dti를 구하는 부분.
    return dti
  • DB에 저장된 데이터들을 바탕으로 dti를 계산하는 방법
    • 원래는 마이데이터를 활용해야 하지만 마이데이터는 활용할 수 없어 임시로 가상의 데이터를 만든 후 사용
  • DB에 저장된 데이터는 사용자의 핸드폰번호를 사용하여 불러와진다.
    • 사용자 편의를 위해 이런 방식을 사용.

_get_data

  • 우선 pymysql 콘솔 생성
  • 그 후 생성된 콘솔로 커서를 생성
  • 생성된 커서를 이용해 user_pn을 키로 써서 mortgage_debt, mortgage_repayment, installment, mortgage_term을 불러옴.
    • 이렇게 하면 유저에 맞는 데이터가 불러와진다.
  • 불러온 데이터를 모델 입력 형식에 맞게 바꿔준다.
    • 이는 처음에 DB를 만들 때 고려를 못해서 임시로 해둠. 지금은 의미가 없기는 하다.

calculate_dti

  • 불러온 데이터를 이용해 dti를 계산하는 함수
  • dti를 계산한 후 계산된 dti를 반환
  • 이렇게 해서 모델의 입력값으로 사용

funcsfortool

'''
agent가 쓸 툴들을 구현해놓은 모듈
SimpleScreening은 간이대출심사 툴
retrieve는 서비스 사용법 알려주는 툴을 위한 모듈
fin_retirever는 금융 어휘 사전 툴을 위한 모듈

SimpleScreening 모듈은 getdti 모듈을 통해 dti를 구함
그 후 loan_amnt, dti, issue_d_peroid, annual_income을 간이대출심사 모델에 입력
그 결과값을 통해 대출 가능 여부 확인

retrieve는 서비스 사용방법 질의응답 툴을 위한 모듈
agentic rag 개념을 활용해 구현

fin_retirever는 간단한 금융 어휘를 알려주는 툴을 위한 모듈
이 역시 agentic rag 개념을 활용해 구현현
'''

from langchain_core.tools import BaseTool
import pickle
from pydantic import BaseModel, Field
import VectorStore
from retriever import Retriever
from langchain.tools.retriever import create_retriever_tool
from typing import Optional, Type
from langchain.callbacks.manager import CallbackManagerForToolRun
import pandas as pd
import getdti
from flask import request

import requests


dirpath = './MLOps_chatbot'
collection = 'testdb'
vec_db = VectorStore.load_vectorstore(dir_path = dirpath, collection_name = collection)
ret = Retriever(dirpath, collection, searched = 2)
agent_retirever = ret.as_retriever()

dir2 = './fin_vocab'
collection2 = 'findb'
vec_db2 = VectorStore.load_vectorstore(dir_path = dir2, collection_name = collection2)
ret2 = Retriever(dir2, collection2, searched = 2)
agent_retirever2 = ret2.as_retriever()


ml = pickle.load(open('ml_for_tool.pkl', 'rb')) #간이대출심사 모델

class ScreeningInput(BaseModel):
   annual_income:int = Field(..., description = "사용자가 입력한 질문에 있는 연봉. 혹은 사용자가 입력한 질문에 있는 소득.")
   period:int = Field(..., description = "사용자가 입력한 질문에 있는 마지막 대출 후 경과한 햇수. 단위는 무조건 년으로 받음.")
   loan_amount:int = Field(..., description = "사용자가 입력한 질문에 있는 대출 희망 금액.")

class SimpleScreening(BaseTool):
    name = 'simple_screening'
    description = """
    주어진 머신러닝 모델을 이용해 간단한 대출심사를 진행.
    대출 심사 요청이 왔을때만 실행.
    예시
    - 나 대출 가능해?
    - 나 대출 가능한지 봐줘.
    이런 질문들이 들어왔을 때 파라미터를 입력받은 후 이 도구를 사용합니다.
    누락된 파라미터가 있을 땐 체인 종료 후 사용자에게 누락된 파라미터를 요청하세요.
    TypeError시 사용자에게 다시 입력 받으세요.
    """
    args_schema : Type[BaseModel] = ScreeningInput


    def _run(self, annual_income:int, period:int, loan_amount:int, run_manager: Optional[CallbackManagerForToolRun] = None):
        pn_data = request.get_json() #getdti 모듈을 통해 dti를 구하려면 사용자의 전화번호가 필요함. 이 경우 세션이 아니라 로컬 스토리지를 통해 로그인 정보를 확인해서 서버에 전화번호를 요청함.
        user_pn = pn_data['user_pn'] 
        dti = getdti.calculate_dti(user_pn, annual_income) #사용자 전화번호는 DB에서 dti를 구할 때 필요한 데이터들을 찾을 때 씀.
        input_data = pd.DataFrame([{'loan_amnt' : loan_amount, 'dti' : dti, 'issue_d_period' : period, 'annual_inc' : annual_income}])
        prediction = ml.predict(input_data)
        screening_result = ''
        if prediction[0] == 0:
            screening_result = '대출이 가능합니다.'
        elif prediction[0] == 1:
            screening_result = '대출이 가능하지만 금리가 조금 더 높을 수 있어요.'
        elif prediction[0] == 2:
            screening_result = '아쉽지만 탈락입니다.'
        return screening_result

def retrieve():
    retriever_tool = create_retriever_tool(
        agent_retirever,
        name = 'local_retriever',
        description = '''서비스 사용법, 대출 방법, 투자 방법에 관한 문서들을 검색할 수 있는 검색기 도구입니다. 
        서비스 사용법, 대출 방법, 투자 방법에 대한 질문이 들어오면 사용합니다.
        예시
        - 나 대출 받는 방법이 궁금해.
        - 투자를 하고싶은데 어떻게 해.
        - Billit 어떻게 써?
        - 이 서비스는 뭐하는 서비스야?
        이런 질문에는 이 local retriever를 써 관련된 문서를 찾은 후 그 문서를 기반으로 답하세요.'''
    )
    return retriever_tool

def fin_retirever():
    retriever_tool = create_retriever_tool(
        agent_retirever2,
        name = 'financial_vocabulary',
        description = '''금융지식에 관한 문서들을 검색할 수 있는 검색기 도구입니다.
        금융지식 관련 질문이 들어오면 사용합니다.
        예시
        - 자료열람요구권이 뭐야?
        - 개인신용평가대응권이 뭐야?
        - 인지세가 뭐야?
        - 개인신용평가대응권을 못쓰는 상황이 어떻게 돼?
        - 인지세는 누가, 얼마나 부담해?
    '''
    )
    return retriever_tool
  • 에이전트가 쓸 툴을 모아놓은 모듈
  • 이렇게 하면 툴들을 더 효과적으로 관리할 수 있음
  • 만약 툴을 추가하거나 수정하고 싶으면 여기서 수정만 하면 됨

SimpleScreening

  • 간이 대출심사를 진행할 때 쓰는 툴
  • ScreeningInput에선 pydantic을 사용해 입력값을 받는다.
    • 여기서 description을 써서 이 입력값이 무엇인지 알 수 있게 함.
  • SimpleScreening에선 ScreeningInput으로 받은 입력값을 가져옴
  • 서버에 user_pn(전화번호)를 요청함
    • 여기선 로그인 관리를 세션이 아니라 로컬 스토리지를 활용해서 함.
    • 그래서 세션을 통해 가져오는건 안됨
  • getdti모듈을 활용해 dti를 계산
  • dti를 불러왔으면 입력된 값과 dti를 머신러닝 모델에 입력
  • 결과에 따라 대출 가능 여부 알려준다

retrieve

  • 서비스 사용메뉴얼 질의응답에 쓰인다.
  • crete_retriever_tool 메서드를 사용해 retriever를 툴로 활용
  • description에서 어떤 상황에 이 툴이 쓰이는지 예시와 함께 정의

fin_retirever

  • 이름에 오타가 있긴 함
  • 이 툴은 금융어휘 질의응답에 사용
  • retrieve 모듈과 똑같은 방식으로 만듬
  • 다만 retriever에 사용된 벡터DB가 다름
  • 이 방식으로 서로 다른 retriever를 사용할 수 있게 됨
  • 또한 벡터DB를 분리해 검색 시간과 정확도를 높일 수 있게됨

getdti funcsfortool2