241124 TIL 챌린지 반 과제 1번

윤수용·2024년 11월 24일
0

TIL

목록 보기
63/89

1. 챌린지 반 과제 1번: AI 뉴스 정보 제공 및 요약 RAG

과제 사항 확인

**1단계:**

(1) AI 뉴스 크롤링 및 DB 구축

- AI기사크롤링_AI타임스_1-850페이지.ipynb
- AI_TIMES_크롤링파일_1-850페이지.egg

(2) AI 뉴스 정보 제공 및 요약 RAG

- 직접 해보기 **(챌린지반 과제 - 다음주 화요일 전까지)**
    - 뉴스 **content를 LLM으로 요약해서 임베딩 후 벡터 저장 (1)**
    - 뉴스 content자체를 임베딩 후 벡터저장 (2)
    - (1), (2) RAG 성능 비교

(3) streamlit 챗봇 UI (특강때 제공, 코드 인터넷)

→ 금요일 이후에 진행 **(가능하신 분만 다음 주 화요일까지 같이)**

→ input()입력, print()출력 → streamlit 연동
  • AI 뉴스 크롤링은 이미 파일로 주어진 상태

  • 뉴스 content 자체를 임베딩하는 것과 LLM으로 요약해서 임베딩하는 것을 구분해야 함
    - 'LLM으로 요약 후 임베딩'의 사용 여부를 사용자가 선택하고, 뉴스 자료를 업로드
    - 사용자의 선택에 따라 임베딩 방식을 바꿔서 진행
    - 대화 중간에 사용자가 임베딩 방식을 바꿀 경우, 기존 대화 내역을 삭제하고 문서를 다시 임베딩

  • streamlit으로 UI 제작

동작 순서

  1. 사이드바에서 문서 요약 여부를 선택하고, 뉴스 content가 저장된 json 파일 업로드
with st.sidebar:
	# 문서 요약 여부
    summarize = st.radio(
        "Summarize content for embedding?",
        ["No", "Yes"]
    )
    file = st.file_uploader("Upload a .json file", type=["json"])
  1. 파일이 업로드 되면, summarize 값에 따라 다른 임베딩 방식 적용
    • summarize 값이 변경되면 기존 대화 내역 삭제
# 파일이 업로드 되었을 때 
if file:
    # 1. summarize == 'Yes'
    #     - 업로드한 문서를 LLM을 통해 요약한 후 임베딩 
    #     - summarize 값이 이전 상태와 다른 경우, 기존 대화 내역을 삭제 
    #     - 문서를 요약해서 임베딩한 파일을 retriever로 불러와 사용
    
    # 2. summarize == 'No'
    #     - 업로드한 문서를 그대로 임베딩 
    #     - summarize 값이 이전 상태와 다른 경우, 기존 대화 내역을 삭제 
    #     - 문서를 그대로 임베딩한 파일을 retriever로 불러와 사용
        
    # 3. 임베딩이 완료되어야 메시지 창 활성화 

    if summarize == "Yes":
        if summarize != st.session_state.previous_radio_value:
            if "messages" in st.session_state:
                st.session_state["messages"] = []
        st.session_state.previous_radio_value = summarize
        retriever = embed_file(file, summarize=True)
    else:
        if summarize != st.session_state.previous_radio_value:
            if "messages" in st.session_state:
                st.session_state["messages"] = []
        st.session_state.previous_radio_value = summarize
        retriever = embed_file(file, summarize=False)
  1. summarize 값에 맞게 파일 임베딩
# 같은 file에 대해 embed_file()을 실행했었다면 cache에서 결과를 바로 반환하는 decorator
@st.cache_resource(show_spinner="Embedding file...")
def embed_file(file, summarize=False):
    file_content = file.read()  # 파일의 내용을 읽어오기
    file_path = f"./.cache/files/{file.name}"  # 저장될 파일의 경로
    with open(file_path, "wb") as f:
        f.write(file_content)  # 선택한 파일의 내용을 .cache/files 디렉토리로 옮김

    splitter = CharacterTextSplitter.from_tiktoken_encoder(
        separator="\n",
        chunk_size=1000,
        chunk_overlap=100,
    )

    loader = JSONLoader(
        file_path=file_path,
        jq_schema=".[:5] | .[].content",  # 5개의 기사만 가져오기
        text_content=True,
    )

    data = loader.load()
    
    # 요약하기 버튼을 선택한 경우
    if summarize:
        # 요약한 문서의 임베딩 결과를 저장할 디렉토리 만들기
        cache_dir_path = f"./.cache/summarized_embeddings/{file.name}"
        os.makedirs(cache_dir_path, exist_ok=True) # 디렉토리가 없으면 만들기
        cache_dir = LocalFileStore(cache_dir_path)
        
        docs = splitter.split_documents(data)  # splitter에 맞게 문서 분할
        
        # 문서 요약
        docs = summarize_documents(docs, llm_silent)
        
        embeddings = OpenAIEmbeddings()

        # 중복 요청 시 캐시된 결과를 반환
        cached_embeddings = CacheBackedEmbeddings.from_bytes_store(
            embeddings, cache_dir
        )
        
    # 요약하기 버튼을 선택하지 않은 경우
    else:
        # 문서의 임베딩 결과를 저장할 디렉토리 만들기
        cache_dir_path = f"./.cache/embeddings/{file.name}"
        os.makedirs(cache_dir_path, exist_ok=True) # 디렉토리가 없으면 만들기
        cache_dir = LocalFileStore(cache_dir_path)
        
        docs = splitter.split_documents(data)  # splitter에 맞게 문서 분할

        embeddings = OpenAIEmbeddings()

        # 중복 요청 시 캐시된 결과를 반환
        cached_embeddings = CacheBackedEmbeddings.from_bytes_store(
            embeddings, cache_dir
        )

    # FAISS 라이브러리로 캐시에서 임베딩 벡터 검색
    vectorstore = FAISS.from_documents(docs, cached_embeddings)

    # docs를 불러오는 역할
    retriever = vectorstore.as_retriever()

    return retriever
  1. 임베딩 된 결과를 바탕으로 챗봇과 대화
 # 임베딩 완료 신호 
    send_message("준비됐습니다! 질문해주세요!","ai", save=False)
    paint_history()
    message = st.chat_input("Ask anything about your file...")

    if message:
        send_message(message, "human")

        # chain_type = stuff
        chain = {
            "context": retriever | RunnableLambda(format_docs),
            "question": RunnablePassthrough()
        } | prompt | llm

        with st.chat_message("ai"):
            chain.invoke(message)
profile
잘 먹고 잘 살자

0개의 댓글