랭체인(LangChain) 이해 및 Django 기반 ChatBot 실습
랭체인(LangChain)이란?
랭체인은 대규모 언어 모델 기반의 어플 제작을 쉽게 지원해줄 수 있는 프레임워크를 말합니다.
참고
코드간략화를 통한 개발환경 개선으로 표현하기에는 갖고있는 기능들이 많아 일단 위와 같이 서술하였습니다.
LangChain은 아래의 기능들을 제공하며 본문의 코드에서는 챗봇에 관해서만 다루었습니다.
- Retrieval augmented generation(외부데이터를 참조하여 LLM이 답변할 수 있도록함)
- Analyzing structured data(SQL DB와 상호작용)
- Chatbots(챗봇)
랭체인의 구성 요소
LangChain 라이브러리는 여러 가지 패키지로 구성되어있으며 크게 기능에 따라 LangChain, LangChain-Community,LangChain-Core로 3개로 분류할 수 있습니다.
LangChain
LangChain은 파이썬(Python)과 자바스크립트(JavaScript)를 사용하여 개발된 프레임워크의 핵심 부분입니다. 이는 ‘Chains’, ‘Agents’, ‘Advanced Retrieval Strategies’라는 세 가지 주요 기능을 중심으로 구성되어 있습니다.
-
Chains
여러 데이터 처리 단계를 연결하여 효율적인 정보 흐름을 구축합니다. -
Agents
자율적으로 특정 작업을 수행하는 소프트웨어 엔티티로 복잡한 태스크를 수행하고 사용자의 요구에 응답합니다. -
Advanced Retrieval Strategies
단순 키워드 검색을 넘어서 문맥이나 의미를 고려한 고급 정보 검색 방식을 제공합니다.
LangChain-Community
LangChain의 커뮤니티 버전으로 모델의 입력과 출력을 관리하는 ‘Models I/O’, 데이터 검색을 위한 ‘Retrieval’, 그리고 에이전트의 도구를 제공하는 ‘Agent Tooling’ 섹션으로 나뉩니다. 이는 개발자 커뮤니티에 의해 지원되며 누구나 참여하여 기여할 수 있는 오픈소스 프로젝트의 성격을 가지고 있습니다.
LangChain-Core
LangChain Expression Language(LCEL)를 포함하여 여러 복잡한 작업을 동시에 처리할 수 있는 병렬 처리 기능, 에러 발생 시 대처할 수 있는 폴백 메커니즘, 프로세스 추적, 배치 처리, 스트리밍 데이터 처리, 비동기 작업 처리, 그리고 다양한 구성요소를 조합할 수 있는 기능 등이 포함됩니다.
랭체인 챗봇 실습
질문 텍스트와 fetch api를 이용하여 비동기적으로 대답을 받을 수 있는 방식으로 프로젝트를 예제를 구성하였습니다.
사용된 LLM
- GPT 3.5 Turbo
- FLAN-T5-base
참고 사항 await fetch(‘/api/모델) ← urls.py를 참고하여 ‘모델’ 부분을 바꾸게되면 다양한 LLM으로 챗봇을 사용할 수 있습니다.
챗봇 사전 환경 설정
[home.html]
<!DOCTYPE html>
<html>
<head>
<title>LLM기반 답변 생성기</title>
<link rel="stylesheet" href="/path/to/style.css">
<script src="/path/to/csrftoken.js" defer></script>
</head>
<body>
<div id="container">
<div id="outputText"></div>
<input type="text" id="inputText" placeholder="질문을 입력하세요">
<button onclick="sendText()">제출</button>
</div>
<script>
async function sendText() {
let question = document.getElementById('inputText').value;
let response = await fetch('/api/gpt', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({text: question}),
});
let data = await response.json();
document.getElementById('outputText').innerText = data.answer;
}
</script>
<input type="hidden" name="csrfmiddlewaretoken" value="">
</body>
</html>
[urls.py]
from django.contrib import admin
from django.urls import path
from mainapp.views import home,gpt_api,flan_t5_api
urlpatterns = [
path('', home),
path('admin/', admin.site.urls),
path('api/gpt', gpt_api),
path('api/flan_t5', flan_t5_api),
]
[csrftoken.js]
function getCsrfToken() {
return document.querySelector('[name=csrfmiddlewaretoken]').value;
}
async function sendText() {
let question = document.getElementById('inputText').value;
let response = await fetch('/api/gpt', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
},
body: JSON.stringify({text: question}),
});
let data = await response.json();
document.getElementById('outputText').innerText = data.answer;
}
[style.css]
html, body {
height: 90%;
width: 90%;
display: flex;
justify-content: center;
align-items: center;
}
#container {
width: 80%;
text-align: center;
}
#outputText {
border: 1px solid black;
padding: 10px;
margin-bottom: 10px;
min-height: 600px;
width: 100%;
text-align: left;
box-sizing: border-box;
}
#inputText {
width: 80%;
margin-bottom: 10px;
box-sizing: border-box;
}
랭체인을 사용하지 않았을 때
랭체인을 사용하지 않았을 때의 챗봇을 위한 api는 각각 아래와 같이 작성될 수 있습니다.
[views.py]
from django.shortcuts import render
import requests
import json
from django.http import JsonResponse
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
def home(request):
return render(request, 'home.html')
def gpt_api(request):
if request.method == 'POST':
request_data = json.loads(request.body)
text_input = request_data['text']
response = requests.post(
'https://api.openai.com/v1/chat/completions',
json={
'model': 'gpt-3.5-turbo',
'messages': [{'role': 'user', 'content': text_input}],
},
headers={
'Authorization': 'Bearer (Open api key입력-괄호제거)'
}
)
response_data = response.json()
return JsonResponse({'answer': response_data['choices'][0]['message']['content']})
def flan_t5_api(request):
if request.method == 'POST':
request_data = json.loads(request.body)
text_input = request_data['text']
model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-base")
tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-base")
inputs = tokenizer(text_input, return_tensors="pt")
outputs = model.generate(**inputs)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return JsonResponse({'answer': response})
랭체인을 사용했을 때
랭체인을 사용한다면 아래와 같이 일관된 형식으로 각각의 LLM들을 지원할 수 있습니다.
[views.py]
from django.shortcuts import render
from django.http import JsonResponse
import json
import os
from langchain import LLMChain
from langchain.llms import OpenAI, HuggingFaceHub
from langchain.prompts import PromptTemplate
gpt_model = OpenAI(model='gpt-3.5-turbo-instruct', api_key='api key 입력')
huggingface_hub_api_token = os.getenv('HUGGINGFACEHUB_API_TOKEN', 'api key 입력')
plant5_model = HuggingFaceHub(repo_id = "google/flan-t5-base", huggingfacehub_api_token=huggingface_hub_api_token)
def home(request):
return render(request, 'home.html')
def gpt_api(request):
if request.method == 'POST':
request_data = json.loads(request.body)
text_input = request_data['text']
prompt = PromptTemplate(template=text_input, input_variables=["question"])
llm_chain = LLMChain(prompt=prompt,llm=gpt_model)
response = llm_chain.run({"question": text_input})
return JsonResponse({'answer': response})
def flan_t5_api(request):
if request.method == 'POST':
request_data = json.loads(request.body)
text_input = request_data['text']
prompt = PromptTemplate(template=text_input, input_variables=["question"])
llm_chain = LLMChain(prompt=prompt,llm=plant5_model)
response = llm_chain.run({"question": text_input})
return JsonResponse({'answer': response})
출력 예시
