[LangChain] Map-Refine

pysun·2024년 12월 14일

LangChain

목록 보기
12/13

Map-Refine

참고: https://pkgpl.org/wp-content/uploads/2023/10/image-2.png?w=2046

Map-Refine 패턴은 두 단계로 이뤄진 데이터 처리 방식

  1. Map 단계:
    • 문서를 여러 개 chunk(덩어리)로 나누고 각 chunk별로 요약 등 수행
  1. Refine 단계:
    • 생선된 요약을 순차적으로 처리하며 최종 요약을 점진적으로 개선. 각 단계에서 이전 요약과 새로운 chunk 요약 정보를 결합하여 요약을 갱신

✨ 장점: chunk 순서를 유지하면서 점진적으로 요약을 개선하기 때문에 문서 맥락이 중요한 경우 유용

☠️ 단점: 순차적으로 처리해야 하기 때문에 대규모 문서일 경우 시간이 오래 걸릴 수 있고 API 호출 비용이 커질 수 있음


PDF 데이터 벡터DB 저장

from langchain_openai import ChatOpenAI, OpenAIEmbeddings

from langchain.document_loaders import UnstructuredFileLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough, RunnableLambda
from langchain_chroma import Chroma


# 텍스트 분할
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=150,
    chunk_overlap=30
)

# 데이터 로드
loader = UnstructuredFileLoader('/Users/sunbok/workspace/fullstack_gpt/files/1984_chapter_one.pdf')

# split된 데이터 로드
docs = loader.load_and_split(text_splitter=text_splitter)

# db 생성
# from_documents 클래스 메서드는 문서 리스트로부터 벡터 저장소를 생성
db = Chroma.from_documents(
    documents=docs,                     # documents (List[Document]): 벡터 저장소에 추가할 문서 리스트
    embedding=OpenAIEmbeddings(),       # 임베딩 모델
    collection_name='1984_chapter_one', # 생성할 컬렉션 이름
    persist_directory='.cache/chroma_db'# db를 디스크에 저장
)

벡터DB 로드

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings 

# 디스크에서 문서 로드
vector_db = Chroma(
    persist_directory='.cache/chroma_db',
    embedding_function=OpenAIEmbeddings(),
    collection_name='1984_chapter_one'
)

vector_db.similarity_search('윈스톤이 근무하는 곳은?')
[Document(page_content='Winston kept his back turned to the telescreen. It was safer, though, as he well knew, even a back can be revealing. A kilometre away the Ministry of Truth, his place of work, towered vast and white above the grimy landscape. This, he thought with a sort of vague distaste—this was London, chief city of Airstrip One, itself the third most populous of the provinces of Oceania. He tried to squeeze out some childhood memory that should tell him whether London had always been quite like this. Were there always these vis- tas of rotting nineteenth-century houses, their sides shored up with baulks of timber, their windows patched with card- board and their roofs', metadata={'source': '/Users/sunbok/workspace/fullstack_gpt/files/1984_chapter_one.pdf'}),
 Document(page_content='ures which had something to do with the production of pig-iron. The voice came from an oblong metal plaque like a dulled mirror which formed part of the surface of the right-hand wall. Winston turned a switch and the voice sank somewhat, though the words were still distinguish- able. The instrument (the telescreen, it was called) could be dimmed, but there was no way of shutting it off complete- ly. He moved over to the window: a smallish, frail figure, the meagreness of his body merely emphasized by the blue overalls which were the uniform of the party. His hair was very fair, his face naturally sanguine, his skin roughened by coarse soap and', metadata={'source': '/Users/sunbok/workspace/fullstack_gpt/files/1984_chapter_one.pdf'}),
 Document(page_content='\x18\n\n1984\n\nall four of them simultaneously. They were the homes of the four Ministries between which the entire apparatus of government was divided. The Ministry of Truth, which concerned itself with news, entertainment, education, and the fine arts. The Ministry of Peace, which concerned itself with war. The Ministry of Love, which maintained law and order. And the Ministry of Plenty, which was responsible for economic affairs. Their names, in Newspeak: Minitrue, Minipax, Miniluv, and Miniplenty.', metadata={'source': '/Users/sunbok/workspace/fullstack_gpt/files/1984_chapter_one.pdf'}),
 Document(page_content='Winston turned round abruptly. He had set his features into the expression of quiet optimism which it was advis- able to wear when facing the telescreen. He crossed the room into the tiny kitchen. By leaving the Ministry at this time of day he had sacrificed his lunch in the canteen, and he was aware that there was no food in the kitchen except a hunk of dark-coloured bread which had got to be saved for tomorrow’s breakfast. He took down from the shelf a bottle of colourless liquid with a plain white label marked VICTORY GIN. It gave off a sickly, oily smell, as of Chinese rice-spirit. Winston poured out nearly a teacupful, nerved', metadata={'source': '/Users/sunbok/workspace/fullstack_gpt/files/1984_chapter_one.pdf'})]

Map 프롬프트 로드

from langchain import hub 
from langchain_openai import ChatOpenAI 

map_llm = ChatOpenAI(
   temperature=0.1,
   model_name='gpt-4o'
)

map_prompt = hub.pull("teddynote/map-summary-prompt")

map_prompt.pretty_print()
================================ System Message ================================

You are an expert summarizer. Your task is to summarize the following document in {language}.

================================ Human Message =================================

Extract most important main thesis from the documents, then summarize in bullet points.

#Format:
- summary 1
- summary 2
- summary 3
-...

Here is a given document: 
{documents}

Write 1~5 sentences. Think step by step.
#Summary:

Refine 프롬프트 로드

refine_prompt = hub.pull("teddynote/refine-prompt")

refine_prompt.pretty_print()
================================ System Message ================================

You are an expert summarizer.

================================ Human Message =================================

Your job is to produce a final summary

We have provided an existing summary up to a certain point:
{previous_summary}

We have the opportunity to refine the existing summary(only if needed) with some more context below.
------------
{current_summary}
------------
Given the new context, refine the original summary in {language}.
If the context isn't useful, return the original summary.

최종 정리

from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import chain
from langchain_chroma import Chroma
from langchain import hub 


def retrieve_docs(query):
    vector_db = Chroma(
        persist_directory='.cache/chroma_db',
        embedding_function=OpenAIEmbeddings(),
        collection_name='1984_chapter_one'
    )
    retriever = vector_db.as_retriever()
    docs = retriever.invoke(query)

    return docs
    
    
# map-rifine를 하나의 Runnable 객체로 홯용할 수 있는 함수로 만들기
@chain
def map_refine_chain(docs):
    # map 단계: 질문에 대한 retrieved 한 문서마다 핵심 내용 정리하기
    map_llm = ChatOpenAI(
        temperature=0.1,
        model_name='gpt-4o'
    )
    map_prompt = hub.pull("teddynote/map-summary-prompt")
    map_chain = map_prompt | map_llm | StrOutputParser()

    input_doc = [{'documents': doc.page_content, 'language':'korean'} for doc in docs]
    map_result = map_chain.batch(input_doc)

    # refine 단계: map 결과를 점진적으로 개선하여 최종 답변 생성
    refine_llm = ChatOpenAI(
        temperature=0.1,
        model_name='gpt-4o'
    )
    refine_prompt = hub.pull("teddynote/refine-prompt")
    refine_chain = refine_prompt | refine_llm | StrOutputParser()

    previous_summary = map_result[0]
    for current_summary in map_result[1:]:
        previous_summary = refine_chain.invoke(
            {
                'previous_summary':previous_summary,
                'current_summary':current_summary,
                'language':'korean'
            }    
        )

        print(previous_summary)
        print('\n')
    
    return previous_summary

docs = retrieve_docs('윈스턴이 근무하는 곳은 어디야?')
result = map_refine_chain.invoke(docs)
윈스턴은 텔레스크린을 등지고 있었지만, 등조차도 정보를 드러낼 수 있음을 알고 있었다. 그는 자신이 일하는 진리부가 있는 런던을 바라보며, 이곳이 오세아니아의 주 중 하나인 에어스트립 원의 중심 도시임을 생각했다. 윈스턴은 런던이 항상 이렇게 낡고 황폐한 모습이었는지에 대한 어린 시절의 기억을 떠올리려 노력했다. 그는 텔레스크린을 마주할 때 적절한 조용한 낙관주의의 표정을 지으며 방을 돌아다녔다. 점심을 포기하고 집으로 돌아왔지만, 부엌에는 내일 아침 식사를 위해 남겨둬야 할 검은 빵 한 덩이 외에는 음식이 없었다. 윈스턴은 선반에서 '빅토리 진'이라고 적힌 무색의 액체 병을 꺼내어 거의 찻잔 가득 따랐다.


윈스턴은 텔레스크린을 등지고 있었지만, 등조차도 정보를 드러낼 수 있음을 알고 있었다. 그는 자신이 일하는 진리부가 있는 런던을 바라보며, 이곳이 오세아니아의 주 중 하나인 에어스트립 원의 중심 도시임을 생각했다. 윈스턴은 런던이 항상 이렇게 낡고 황폐한 모습이었는지에 대한 어린 시절의 기억을 떠올리려 노력했다. 그는 텔레스크린을 마주할 때 적절한 조용한 낙관주의의 표정을 지으며 방을 돌아다녔다. 점심을 포기하고 집으로 돌아왔지만, 부엌에는 내일 아침 식사를 위해 남겨둬야 할 검은 빵 한 덩이 외에는 음식이 없었다. 윈스턴은 선반에서 '빅토리 진'이라고 적힌 무색의 액체 병을 꺼내어 거의 찻잔 가득 따랐다. 증오 주간을 준비하기 위한 경제 절약 운동의 일환으로 7층에 있는 아파트로 천천히 올라갔다. 그는 오른쪽 발목 위에 정맥류 궤양이 있어 여러 번 쉬어가며 계단을 올랐다. 각 층마다 엘리베이터 샤프트 맞은편 벽에는 커다란 얼굴의 포스터가 걸려 있었고, 그 눈은 움직일 때마다 따라오는 듯한 느낌을 주었다. 포스터 아래에는 "빅 브라더가 당신을 지켜보고 있다"라는 문구가 적혀 있었다.


윈스턴은 텔레스크린을 등지고 있었지만, 등조차도 정보를 드러낼 수 있음을 알고 있었다. 그는 자신이 일하는 진리부가 있는 런던을 바라보며, 이곳이 오세아니아의 주 중 하나인 에어스트립 원의 중심 도시임을 생각했다. 윈스턴은 런던이 항상 이렇게 낡고 황폐한 모습이었는지에 대한 어린 시절의 기억을 떠올리려 노력했다. 그는 텔레스크린을 마주할 때 적절한 조용한 낙관주의의 표정을 지으며 방을 돌아다녔다. 점심을 포기하고 집으로 돌아왔지만, 부엌에는 내일 아침 식사를 위해 남겨둬야 할 검은 빵 한 덩이 외에는 음식이 없었다. 윈스턴은 선반에서 '빅토리 진'이라고 적힌 무색의 액체 병을 꺼내어 거의 찻잔 가득 따랐다. 증오 주간을 준비하기 위한 경제 절약 운동의 일환으로 7층에 있는 아파트로 천천히 올라갔다. 그는 오른쪽 발목 위에 정맥류 궤양이 있어 여러 번 쉬어가며 계단을 올랐다. 각 층마다 엘리베이터 샤프트 맞은편 벽에는 커다란 얼굴의 포스터가 걸려 있었고, 그 눈은 움직일 때마다 따라오는 듯한 느낌을 주었다. 포스터 아래에는 "빅 브라더가 당신을 지켜보고 있다"라는 문구가 적혀 있었다. 윈스턴은 오른쪽 벽에 부착된 금속판에서 나오는 소리를 줄이기 위해 스위치를 돌렸지만, 완전히 끌 수는 없었다. 이 장치는 '텔레스크린'이라고 불리며, 소리를 줄일 수는 있지만 완전히 끌 수는 없는 구조였다. 윈스턴은 창가로 이동하며, 파란 작업복을 입은 그의 왜소한 체격과 거친 피부가 강조되었다.
profile
배움의 흔적이 성장으로 이어지는 공간

0개의 댓글