ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [AWS Bedrock] Langchain과 Bedrock 함께쓰기
    Programing/AWS 2024. 3. 12. 18:43
    반응형

    이 전 글에서는 RAG를 위한 지식베이스를 생성할 때, AWS Opensearch serverless를 사용했습니다.

     

    [AWS Bedrock] RAG를 위한 지식베이스 생성

    이전 글에서는 Bedrock을 이용해 간단한 서비스를 생성했습니다. [AWS Bedrock] Bedrock 시작하기 LLM의 빠른 도입과 테스트를 위해 Bedrock을 이용해 LLM서비스를 구축하기로 선택했습니다. (아래의 이전

    brain-nim.tistory.com

     

    S3의 데이터를 바로 활용 가능하고 따로 서버를 관리할 필요가 없다는 장점이 있지만,

    비용이 생각 이상으로 비싸다는 단점이 있었습니다.

    (짐작은 했지만 실험해보니 생각 이상으로 더 비쌌습니다.)

     

    이번엔 Langchain을 이용해 지식베이스 벡터를 로컬에 생성한 뒤 Bedrock을 연동해보도록 하겠습니다.

     

     

    사전준비

    1) pip install

    pip install boto3 awscli langchain faiss-cpu

    벡터 임베딩 결과를 저장하는 방식은 여러가지가 있겠지만, 여기서는 faiss를 사용하겠습니다.

     

    2) aws cli 환경설정

    C:\Users\OOOOO> aws configure
    AWS Access Key ID [None]: $$$$$$$$$$$$$$$$$$$$
    AWS Secret Access Key [None]: $$$$$$$$$$$$$$$$$$$$$$
    Default region name [None]: us-west-2
    Default output format [None]:   # 전 입력 안했습니다.

     

    3) 사용할 모델의 modelID 확인

    2024년 3 현시점, Bedrock에서 제공하는 multilingual 임베딩 모델을 2개입니다.

    • Cohere Embed Multilingual: "cohere.embed-multilingual-v3"
    • Amazon Titan Embeddings G1 -text: "amazon.titan-embed-text-v1"

     

    임베딩용 모델, 질의응답용 모델을 포함해 model ID를 확인하는 방법은 지난글의 Bedrock으로 request 를 확인해주세요.

     

     

    벡터저장소 만들기

    1) load documents

    LangChain에서는 벡터화 하기 위해 먼저 자료를 langchain document로 변환해야 합니다.

    일반적인 csv, PDF같은 파일소스는 물론이고, (참고1)

    DiscordGitHub, PubMed, AWS S3등등도 가능합니다. (참고2)

     

    저는 단순하게 특정 디렉토리에 있는 모든 파일을 참고하는 DirectoryLoader를 사용해서 진행하겠습니다.

    from langchain_community.document_loaders import DirectoryLoader
    from langchain_community.document_loaders import TextLoader
    
    loader = DirectoryLoader('./sample_data/', glob="**/*.json",  # 디렉토리 위치, 파일 형식
                             loader_cls=TextLoader,  # 기본load 방식 설정
                             loader_kwargs={'autodetect_encoding': True})  # utf8 인코딩 에러 해결
    documents = loader.load()

     

    json 파일을 가져오려고 함에도 JSONLoader가 아닌 DirectoryLoader를 사용한 이유는

    제가 테스트한 환경이 Windows이기 때문입니다.

    JSONLoader는 jq 라이브러리를 추가 설치해야하는데, 윈도우를 지원하지 않습니다...

    해결방법이 있다고는 하는데 어처피 테스트 용도니 그냥 진행했습니다.

     

    2) split documents

    하나의 document가 너무 방대할 경우 토큰도 많이 잡아먹고 탐색도 수월하지 않을 수 있습니다.

    적당히 잘라주는 것이 성능에 큰 영향을 미칩니다.

    from langchain.text_splitter import CharacterTextSplitter
    
    text_splitter = CharacterTextSplitter(chunk_size=1000,  # 분할 문서의 최대 청크 크기
                                          chunk_overlap=0,  # 문서 별 오버랩 청크 크기
                                          separator=',')    # 문서 별 구분자
    docs = text_splitter.split_documents(documents)

     

    3) Bedrock을 이용한 임베딩

    Bedrock이라고 특별할건 없고, embedding용 llm 모델을 Bedrock으로 지정하는 것 뿐입니다.

    import boto3
    from langchain.embeddings import BedrockEmbeddings
    from langchain_community.vectorstores import FAISS
    
    # bedrock 임베딩 모델 설정
    bedrock = boto3.client(service_name='bedrock-runtime')
    bedrock_embeddings = BedrockEmbeddings(model_id='amazon.titan-embed-text-v1', client=bedrock)
    
    # 벡터 임베딩
    vector = FAISS.from_documents(docs, bedrock_embeddings)
    vector.save_local("faiss_index")  # 로컬에 저장
    
    # 저장한 벡터 불러오기
    vector = FAISS.load_local("faiss_index", bedrock_embeddings, allow_dangerous_deserialization=True)
    
    # 테스트
    query = "보석을 줍는 꿈은?"
    print(vector.similarity_search(query, k=1))
    # [Document(page_content='{"보석반지꿈": "보석반지를 끼는 꿈은 운수가 좋을 꿈입니다. 평소보다 두뇌회전이 빨라져서 재빠르게 행동을 하게되어 행운을 얻게 됩니다."', metadata={'source': 'sample_data\\꿈해몽.json'})]
    
    print(vector.similarity_search_with_score(query, k=1))  # score: L2 distance
    # [(Document(page_content='{"보석반지꿈": "보석반지를 끼는 꿈은 운수가 좋을 꿈입니다. 평소보다 두뇌회전이 빨라져서 재빠르게 행동을 하게되어 행운을 얻게 됩니다."', metadata={'source': 'sample_data\\꿈해몽.json'}), 150.60712)]

     

     

    LLM에 질의하기

    질의하는 부분 역시 Bedrock이라고 특별할 건 없습니다.

    llm모델을 Bedrock으로 지정하는 것 뿐입니다.

    LangChain을 이용해 질의하는 일반적인 방식과 동일합니다.

     

    1) Bedrock LLM 모델 설정 (※ 24년 10월 수정)

    from langchain_aws import ChatBedrock
    
    llm = ChatBedrock(
        model_id="anthropic.claude-3-5-sonnet-20240620-v1:0",
        model_kwargs=dict(temperature=0),
        region_name='us-east-1'
        # other params...
    )

    수정내용참조) https://brain-nim.tistory.com/145

     

    [AWS Bedrock] Claude3.5와 Langchain 연동하기 (24년 10월 기준)

    올해 초에는 AWS Bedrock에서 제공하는 모델이 그렇게 많지는 않았는데 그 사이에 많이 늘었습니다. 기존에는 Claude 2.1을 사용하고 있었는데 이제 Claude 3.5를 사용해볼까 하고 요금을 확인해봤습니

    brain-nim.tistory.com

     

     

     

    2) 프롬프트 템플릿 설정

    from langchain.prompts import ChatPromptTemplate
    
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                """
                You are a helpful assistant. 
                Answer questions using only the following context. 
                If you don't know the answer just say you don't know, 
                don't make it up:
                \n\n
                {context}
                """,
            ),
            ("human", "{question}"),
        ]
    )

     

     

    3) LangChain 설정

    from langchain.schema.runnable import RunnablePassthrough
    
    retriever = vector.as_retriever(search_kwargs={'k': 1})
    chain = (
        {
            "context": retriever,
            "question": RunnablePassthrough(),
        }
        | prompt
        | llm
    )
    
    result = chain.invoke("보석을 줍는 꿈 해몽해줘")
    print(result)

    프롬프트 템플릿을 잘 수정하면 그럴싸한 결과가 나올 것 같습니다.

    반응형

    댓글

Designed by Tistory.