2023년 10월 5일 목요일

Langchain 기반 개인화된 ChatGPT 대화, 코딩, QA 챗봇 자체 서비스 개발 방법

이 글은 개인화된 ChatGPT와 같은 생성AI 서비스 개발을 지원하는 Langchain (랭체인) 사용법을 나눔한다. 랭체인을 통해, LLM 모델을 사용한 자체 에이전트 서비스를 개발할 수 있다. 이 글은 ChatGPT, LLAMA2과 같은 LLM모델을 이용해, RAG(검색증강생성)을 통해 전문적으로 개인화된 결과를 생성하는 개발과정을 포함한다.  
랭체인 개념

LangChain은 LLM 애플리케이션을 구축하기 위한 오픈 소스 개발자 프레임워크이다. 이 글은LangChain 사용사례, 예를 들어, 본인의 데이터를 이용해 AI 에이전트를 개발하는 방법에 중점을 둘 것이다.
개요
LangChain은 Andrew Ng 교수, Harrison Chase에 의해 개발되었다. 랭체인은 다음 기능을 지원한다. 
  • 문서 로딩
  • 문서 분할
  • RAG, 벡터 스토어임베딩 (참고. Usage Guide | Chroma)
  • 100개 이상의 문서 로더 (예. PDF, JSON, HTML, FILE 등), 문서 이미지 추출
  • 검색
  • 질의응답
  • 메모리 기록
  • LangServe 기반 배포 지원
LangChain 모듈 아키텍처
LangChain RAG 개념

랭체인은 다음과 같은 다양한 문서를 데이터로 사용할 수 있다.

랭체인이 제공하는 주요 인터페이스는 다음과 같다.
  • Prompt: Dictionary 유형 프롬프트 정의
  • Model: LLM 모델
  • OutputParser: 모델 출력
  • Retriever: RAG 처리
이외, 랭체인은 비동기 병렬 처리인 ainvoke, abatch, astream_event를 제공한다. 이벤트는 체인을 구성하는 파이프의 실행 시작, 종료 등에 따라 콜백함수 호출을 제공한다. 파이프 입출력은 Pydantic으로 체크, 생성되므로, 데이터 무결성이 보장된다(참고. Cerberus). 

사용자는 랭체인에 등록된 에이전트에게 LLM을 통해 삭제를 지시할 수 있다. 이 경우 중요한 정보가 삭제될 수 있으므로, 이에 대한 권한을 부여하거나, 에이전트가 컨테이너 샌드박스에서 실행될 수 있도록 제한 할 수 있다.

설치하기
터미널에서 다음 명령을 실행한다.
pip install langchain

상세 내용은 다음을 참고한다. 

기본 사용법
여기서는 OLLAMA를 설치하여, LLAMA2를 모델로 사용한다. 만약, ChatGPT 모델을 이용한다면, OpenAI에 API Token을 신청(유료)해야 한다(다음 소스코드에서 해당 모델로 변경하면 됨). 여기서는 프롬프트를 입력해 원하는 결과를 얻는 방법을 코드로 보여준다. 
다음같이 올라마를 설치한다. 
다음과 같이 LLAMA2학습 모델을 터미널에서 다운로드한다.
ollama pull llama2

다음과 같이 코딩한다.
# export LANGCHAIN_TRACING_V2="true"
# export LANGCHAIN_API_KEY="..."
# from langchain_openai import ChatOpenAI # ChatGPT 사용 시 라이브러리
from langchain_community.llms import Ollama  

llm = Ollama(model="llama2")  # 라마2 모델 사용. 챗GPT사용의 경우, llm = ChatOpenAI(model="gpt-4", openai_api_key="your-api-key")

out = llm.invoke("what is BIM?")  #프롬프트 질문
print('llm. ', out)

from langchain_core.prompts import ChatPromptTemplate

# 프롬프트 질문 > Agent A 입력 > 출력 > Agent B 입력... 이런식으로 조합할 수 있음
prompt = ChatPromptTemplate.from_messages([
    ("system", "You are world class technical documentation writer."),
    ("user", "{input}")
])  

chain = prompt | llm 
out = chain.invoke({"input": "what is BIM?"})
print('\n\n*prompt | llm. ', out)

from langchain_core.output_parsers import StrOutputParser
output_parser = StrOutputParser()

chain = prompt | llm | output_parser
out = chain.invoke({"input": "what is BIM?"})
print('\n\n*prompt | llm | output_parser. ', out)

결과는 다음과 같다. 

여기서 사용한 프롬프트는 기본 템플릿을 사용했지만, 채팅, 코딩, Few Shot QA, 요약 등 목적에 따라 매우 다양한 템플릿을 제공한다. 다음에는 그 예이다. 
chat_template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a helpful AI bot. Your name is {name}."),
        ("human", "Hello, how are you doing?"),
        ("ai", "I'm doing well, thanks!"),
        ("human", "{user_input}"),
    ]
)

example_prompt = PromptTemplate(
    input_variables=["question", "answer"], template="Question: {question}\n{answer}"
)

LCEL(LangChain) 언어 기반 서비스 개발
LangChain Expression Language인 LCEL을 지원한다. 이를 통해, 100개 단계 이상 연결된 랭체인이 배포될 수 있다. LCEL언어는 유닉스 파이프라인과 유사하다. 

이 언어는 스트리밍, 비동기, 병렬실행을 지원한다. LangSmith를 통해 각 단계 상황을 추적할 수 있다. LangServe를 통해 체인 배포가 가능하다.

실습을 위해, 다음 명령을 터미널에서 실행한다.
pip install langchain-core langchain-community langchain-openai
 
다음을 코딩한다. ChatGPT4 사용의 경우, 주석의 model과 key는 적절히 수정하라.
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_community.llms import Ollama
# from langchain_openai import ChatOpenAI # ChatGPT 사용 시 라이브러리

prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}") # 변수정의

model = Ollama(model="llama2") # ChatOpenAI(model="gpt-4", openai_api_key="your-api-key")
output_parser = StrOutputParser()

chain = prompt | model | output_parser  # LCEL 파이프라인
out = chain.invoke({"topic": "ice cream"})  # 전달 변수
print(out)

결과는 다음과 같다. ice cream에 맞는 적당한? 조크를 한다.

이 코드는 topic 변수를 파이프라인에 전달함으로써, 프롬프트 입력에 대한 LLM모델의 출력에 가변성을 주고 있다. 

아래 코드는 동일한 invoke 동작을 한다.
(prompt | model | output_parser).invoke({"topic": "ice cream"})  # 전달 변수

다음과 같이, chain.batch 이용해 배치로 실행할 수 있다.
chain.batch(['ice cream', 'spaghetti'])

RAG 기반 개인화된 에이전트 개발
일반 상식적인 대답보다는 좀 더 전문적이고 개인화된 결과를 생성할 수 있다. 이는 RAG(Retrieval Augmented Generation. 검색증강생성)으로 알려진 기술을 통해 개선될 수 있다. 

RAG는 LLM에 프롬프트를 전달하기 전에 미리 저장된 지식 데이터베이스에서 보강된 컨텍스트를 미리 전달하고, 이 문맥을 고려한 프롬프트 쿼리를 수행하는 절차를 수행한다. 다음은 그 과정을 간략히 보여준다. 
OpenAI의 샘알트만 사건을 왕좌의 게임과 비교한 질문 예시

RAG는 LLM의 자연스로운 출력 생성 기능을 최대한 활용하면서, 얻고 싶은 대답에 집중할 수 있는 문맥을 청크들로 제공해주는 기술로 볼 수 있다. 

실습을 위해 다음을 설치한다. 
pip install ollama llamaapi chromadb docarray

RAG를 이용해 랭체인을 코딩해 본다. 
from langchain_community.vectorstores import DocArrayInMemorySearch
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_community.llms import Ollama
from langchain_community.embeddings import OllamaEmbeddings

vectorstore = DocArrayInMemorySearch.from_texts(
    ["Mac worked at Seoul", "Mac is researcher who develop AI, 3D computer vision, BIM and Digital Twin", "Mac manages BIM principle and Daddy Makers Blog", "Lynn Minmay is an idol in Macross Movie", "Apple makes Macintosh"],
    embedding=OllamaEmbeddings(), 
) # RAG vector store에 질문 맥락 설정. Mac단어가 포함된 다른 정보도 함께 제공
retriever = vectorstore.as_retriever()
setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)

template = """Answer the question based only on the following context:
{context}
Question: {question}
"""  # 템플릿 변수 정의
prompt = ChatPromptTemplate.from_template(template) # 프롬프트 생성
model = Ollama(model="llama2")
output_parser = StrOutputParser()

chain = setup_and_retrieval | prompt | model | output_parser
out = chain.invoke("where did Mac work and what is his job?") # RAG기반 질문
print(out)  

이를 실행하면, 다음과 같이 정확한 답을 생성한다.

코딩 생성 서비스 개발
랭체인은 템플릿을 이용해 코딩 생성 서비스를 개발할 수 있다. 
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import (
    ChatPromptTemplate,
)
from langchain_experimental.utilities import PythonREPL
from langchain_community.llms import Ollama
from langchain_community.embeddings import OllamaEmbeddings


template = """Write some python code to solve the user's problem. 
Return only python code in Markdown format, e.g.:
```python
....
```""" # ㅌ템플릿 변수 정의
prompt = ChatPromptTemplate.from_messages([("system", template), ("human", "{input}")])
model = Ollama(model="llama2")
def _sanitize_output(text: str):  # 출력 포맷
    _, after = text.split("```python")
    return after.split("```")[0]

chain = prompt | model | StrOutputParser() | _sanitize_output | PythonREPL().run

input = {"input": "whats 2 plus 2"}  # 파이썬 코드 질문
out = chain.invoke(input)
print(input, '=', out)

이 코드를 실행하면, 다음 결과를 얻을 수 있다.

QA 서비스 개발
랭체인은 PromptTemplate을 이용해 편리하게 QA 서비스를 개발할 수 있다.
from langchain.prompts import PromptTemplate

template = """Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer. Use three sentences maximum. Keep the answer as concise as possible. Always say "thanks for asking!" at the end of the answer. 
{context}
Question: {question}
Helpful Answer:"""

QA_CHAIN_PROMPT = PromptTemplate.from_template(template)# Run chain
qa_chain = RetrievalQA.from_chain_type(
    llm,
    retriever=vectordb.as_retriever(),
    return_source_documents=True,
    chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
)

마무리
랭체인은 다양한 학습 모델 에이전트들을 이용해 사용자가 원하는 결과를 생성하는 데 특화된 라이브러리이다. 이를 이용해, 다양한 인공지능 에이전트 서비스를 개발할 수 있다.

참고: Pinecore 벡터DB
임베딩된 데이터 벡터를 저장, 검색, 관리하는 데이터베이스는 여러가지가 있다. Pinecore는 그 중에 하나이다. 

참고: 파인튜닝, RAG, VectorDB

레퍼런스

댓글 없음:

댓글 쓰기