eunja511005 / AutoCoding

0 stars 0 forks source link

랭체인의 모델 컨셉을 이해하기 쉽게 설명해 주는 챗봇 만들기 #193

Open eunja511005 opened 7 months ago

eunja511005 commented 7 months ago

작업 개요

ChatGPT 질문 내용

  1. RAG란
  2. 좋아. 그럼 https://js.langchain.com/docs/modules/model_io/concepts 정보를 DB화하고 사용자의 질문을 받으면 생성된 DB에서 검색해서 답변하는 파이썬 프로그램을 만들어 줘.
  3. 제대로 답변이 안나와서 질문 수정
  4. 좋아. 그럼 https://js.langchain.com/docs/modules/model_io/concepts 정보를 load, split, vectorstore에 embedding 과정을 통해 토큰을 저장한 후 사용자의 질문을 받으면 retriever를 이용해서 관련 내용을 찾은 후 답변하는 파이썬 프로그램을 만들어줘. 제약 사항은 Ollama LLM을 이용해야 하는거야.
  5. 그래도 답변 내용이 마음에 들지 않아서 질문 다시 한번 수정
  6. 아래와 같이 질문 내용 수정 후 다시 submit
    
    좋아. 그럼 RAG를 파이썬을 이용하여 구현해 보자.
    파이썬 프로그램 구현시 아래 단계로 작업 수행해줘.
    1. load : 웹다큐먼트로더를 사용해서 아래 주소에 포함된 문서를 로딩 한다.
    https://js.langchain.com/docs/modules/model_io/concepts 
    2. split : 텍스트 스프리터를 이용하여 적당한 단위로 문서를 분할 한다.
    3. store : Ollama Embedding을 이용하셔 Chroma vectorstore에 저장 한다.
    4. retrieve : retriever를 생성 한다.
    5. generate : 사용자 질문을 받으면 retriever를 이용하여 관련 문서를 검색 하고 검색한 결과를 포함하여 다용자에게 답변 한다.

[주의 사항]

  1. 반드시 Ollama Embedding과 LLM을 이용해야 한다.
  2. 답변은 파이썬 소스코드만 제공 한다.
  3. 만약 네가 파이썬 코딩을 하는데 어려운 부분이 있으면 아래 싸이트를 참고 한다. https://python.langchain.com/docs/use_cases/question_answering/quickstart/
    1. 그래도 마음에 다는 답변이 나오지 않아 질문을 영어로 변역 한 후 질문
    1. https://www.deepl.com/translator 이용

    Okay, let's implement RAG using Python. Follow the steps below to implement the Python program.

  4. load : Use the webdocument loader to load the document contained in the address below. https://js.langchain.com/docs/modules/model_io/concepts
  5. split : Use a text splitter to split the document into appropriate units.
  6. store: Save the document to the Chroma vectorstore using Ollama Embedding.
  7. retrieve : Create a retriever.
  8. generate : When a user question is received, the retriever is used to search for related documents and answer the user with the search results.

[Notes]

  1. you must use Ollama Embedding and LLM.
  2. only provide Python source code in your answers.
  3. if you have difficulties with Python coding, please refer to the following sites. https://python.langchain.com/docs/use_cases/question_answering/quickstart/

Translated with DeepL.com (free version)

1. 그래도 답변이 마음에 들지 않아 질문 내용 다시 작성

좋아. 그럼 RAG를 파이썬을 이용하여 구현해 보자. 파이썬 프로그램 구현시 아래 단계로 작업 수행해줘.

  1. load : 웹다큐먼트로더를 사용해서 아래 주소에 포함된 문서를 로딩 한다. https://js.langchain.com/docs/modules/model_io/concepts
  2. split : 텍스트 스프리터를 이용하여 적당한 단위로 문서를 분할 한다.
  3. store : Ollama Embedding을 이용하셔 Chroma vectorstore에 저장 한다.
  4. retrieve : retriever를 생성 한다.
  5. generate : 사용자 질문을 받으면 retriever를 이용하여 관련 문서를 검색 하고 검색한 결과를 포함하여 다용자에게 답변 한다.

[주의 사항]

  1. https://python.langchain.com/docs/use_cases/question_answering/quickstart/ 싸이트 내용을 이해하고 코딩 한다.
  2. 아래 API 들을 이용하여 코딩 한다. import bs4 from langchain import hub from langchain_community.document_loaders import WebBaseLoader from langchain_chroma import Chroma from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain_openai import OpenAIEmbeddings from langchain_text_splitters import RecursiveCharacterTextSplitter
  3. 반드시 Ollama Embedding과 LLM을 이용해야 한다.
    1. 그래도 답변이 마음에 들지 않아 참조 싸이트도 바꾸고 참조하는 API도 바꾸어 줌.

    RAG를 파이썬을 이용하여 구현해 보자. 파이썬 프로그램 구현시 아래 단계로 작업 수행해줘.

  4. load : 웹다큐먼트로더를 사용해서 아래 주소에 포함된 문서를 로딩 한다. https://js.langchain.com/docs/modules/model_io/concepts
  5. split : 텍스트 스프리터를 이용하여 적당한 단위로 문서를 분할 한다.
  6. store : Ollama Embedding을 이용하셔 Chroma vectorstore에 저장 한다.
  7. retrieve : retriever를 생성 한다.
  8. generate : 사용자 질문을 받으면 retriever를 이용하여 관련 문서를 검색 하고 ChatOllama를 이용해서 사용자에게 답변 한다.

[주의 사항]

  1. https://python.langchain.com/docs/use_cases/chatbots/quickstart/ 반드시 싸이트 내용을 이해하고 코딩 한다.
  2. 아래 API 들을 이용하여 코딩 한다. from langchain_community.document_loaders import WebBaseLoader from langchain_chroma import Chroma from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain_community.embeddings import OllamaEmbeddings from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain.chains import create_history_aware_retriever from langchain_core.prompts import MessagesPlaceholder from langchain_community.chat_models import ChatOllama from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
  3. 반드시 Ollama Embedding과 LLM을 이용해야 한다.
eunja511005 commented 7 months ago
# %%
from langchain_community.document_loaders import WebBaseLoader

loader = WebBaseLoader("https://js.langchain.com/docs/modules/model_io/concepts")
data = loader.load()

data

# %%
!pip install lxml

# %% [markdown]
# ### 좀 더 정재된 데이터를 가져 오기 위해 HTMLHeaderTextSplitter 사용
# - https://python.langchain.com/docs/modules/data_connection/document_transformers/HTML_header_metadata/

# %%
from langchain_text_splitters import HTMLHeaderTextSplitter

html_string = """
<!DOCTYPE html>
<html>
<body>
    <div>
        <h1>Foo</h1>
        <p>Some intro text about Foo.</p>
        <div>
            <h2>Bar main section</h2>
            <p>Some intro text about Bar.</p>
            <h3>Bar subsection 1</h3>
            <p>Some text about the first subtopic of Bar.</p>
            <h3>Bar subsection 2</h3>
            <p>Some text about the second subtopic of Bar.</p>
        </div>
        <div>
            <h2>Baz</h2>
            <p>Some text about Baz</p>
        </div>
        <br>
        <p>Some concluding text about Foo</p>
    </div>
</body>
</html>
"""

headers_to_split_on = [
    ("h1", "Header 1"),
    ("h2", "Header 2"),
    ("h3", "Header 3"),
]

html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
html_header_splits = html_splitter.split_text(html_string)
html_header_splits

# %%
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_text_splitters import HTMLHeaderTextSplitter

# url = "https://js.langchain.com/docs/modules/model_io/concepts"

headers_to_split_on = [
    ("h1", "Header 1"),
    ("h2", "Header 2"),
    ("h3", "Header 3")
]

html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)

# html_header_splits = html_splitter.split_text_from_url(url)
html_header_splits = html_splitter.split_text_from_file("Concepts_Langchain.html")

print(html_header_splits)

chunk_size = 1000
chunk_overlap = 60
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=chunk_size, chunk_overlap=chunk_overlap
)

splits = text_splitter.split_documents(html_header_splits)
print(len(splits))
splits[0:5]

# %% [markdown]
# ### embedding, vertorstore, retriever 까지 생성
# - https://python.langchain.com/docs/modules/data_connection/retrievers/vectorstore/

# %%
from langchain_community.embeddings import OllamaEmbeddings
from langchain_chroma import Chroma

embeddings = OllamaEmbeddings(model="gemma:2b")

db = Chroma.from_documents(splits, embeddings)

query = "What is the difference of LLMs and Chat Models?"
docs = db.similarity_search(query)
print(docs)

retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 6})

docs = retriever.invoke(query)
print(docs)

# %% [markdown]
# ### RAG 체인 생성
# - https://python.langchain.com/docs/use_cases/question_answering/quickstart/

# %% [markdown]
# ### Vector Store 참조
# - https://python.langchain.com/docs/modules/data_connection/vectorstores/
# 
# ### Vector Store DB 저장
# - https://python.langchain.com/docs/integrations/vectorstores/chroma/

# %%
from langchain import hub
from langchain_community.llms import Ollama
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

llm = Ollama(model="gemma:2b")
# prompt = hub.pull("rlm/rag-prompt")
prompt = ChatPromptTemplate.from_template("""You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {question} 
Context: {context} 
Answer:""")

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

rag_chain.invoke({"question": query})

# %%
# save to disk
db2 = Chroma.from_documents(docs, embeddings, persist_directory="./chroma_db")
docs = db2.similarity_search(query)

# load from disk
db3 = Chroma(persist_directory="./chroma_db", embedding_function=embeddings)
docs = db3.similarity_search(query)
print(docs[0].page_content)

# %%
import os
from langchain_chroma import Chroma

# Function to check if the directory exists and contains files
def directory_exists_and_not_empty(path):
    return os.path.exists(path) and os.path.isdir(path) and len(os.listdir(path)) > 0

# Path to the directory where the Chroma DB will be stored
persist_directory = "./chroma_db"

# Check if the directory exists and is not empty
if directory_exists_and_not_empty(persist_directory):
    # Load database from existing directory
    db = Chroma(persist_directory=persist_directory, embedding_function=embeddings)
else:
    # Create a new database from documents since directory does not exist or is empty
    db = Chroma.from_documents(docs, embeddings, persist_directory=persist_directory)

# Perform a similarity search
search_results = db.similarity_search(query)

# Print the page content of the first document in the search results
if search_results:
    print(search_results[0].page_content)
else:
    print("No documents found.")

# %% [markdown]
# ### 도전 과제
# - https://github.com/ollama/ollama/tree/main/examples/langchain-python-rag-privategpt
eunja511005 commented 7 months ago

최종 소스

import os
from langchain_chroma import Chroma
from langchain_community.embeddings import OllamaEmbeddings
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_text_splitters import HTMLHeaderTextSplitter
from langchain_community.llms import Ollama
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

headers_to_split_on = [
    ("h1", "Header 1"),
    ("h2", "Header 2"),
    ("h3", "Header 3")
]

html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)

# html_header_splits = html_splitter.split_text_from_url(url)
html_header_splits = html_splitter.split_text_from_file("Concepts_Langchain.html")

chunk_size = 1000
chunk_overlap = 60
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=chunk_size, chunk_overlap=chunk_overlap
)

splits = text_splitter.split_documents(html_header_splits)

model_name = "llama2"
embeddings = OllamaEmbeddings(model=model_name)

def directory_exists_and_not_empty(path):
    return os.path.exists(path) and os.path.isdir(path) and len(os.listdir(path)) > 0

persist_directory = "./chroma_db"

if directory_exists_and_not_empty(persist_directory):
    db = Chroma(persist_directory=persist_directory, embedding_function=embeddings)
else:
    db = Chroma.from_documents(splits, embeddings, persist_directory=persist_directory)

retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 6})

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

llm = Ollama(model=model_name)
# prompt = hub.pull("rlm/rag-prompt")
prompt = ChatPromptTemplate.from_template("""You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {question} 
Context: {context} 
Answer:""")

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

query = "What is the difference of LLMs and Chat Models?"
rag_chain.invoke({"question": query})