랭체인(LangChain)이란?

랭체인은 대규모 언어 모델 기반의 어플 제작을 쉽게 지원해줄 수 있는 프레임워크를 말합니다.

참고

코드간략화를 통한 개발환경 개선으로 표현하기에는 갖고있는 기능들이 많아 일단 위와 같이 서술하였습니다.


LangChain은 아래의 기능들을 제공하며 본문의 코드에서는 챗봇에 관해서만 다루었습니다.

  • Retrieval augmented generation(외부데이터를 참조하여 LLM이 답변할 수 있도록함)
  • Analyzing structured data(SQL DB와 상호작용)
  • Chatbots(챗봇)


랭체인의 구성 요소

LangChain 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})


출력 예시

OutputImage