Local에서 LLM 모델 사용해보기 ( 무료!! ) - 2
출처 : 테디노트
해당 포스트는 Local에서 LLM 모델 사용해보기 ( 무료!! ) - 1를 진행한 상태라고 가정하고 진행한다.
환경소개
python 3.11.8
requirements.txt 구성
1
2
3
4
5
6
fastapi==0.110.1 ; python_version >= "3.11.dev0" and python_version < "3.12.dev0"
langchain-community==0.0.32; python_version >= "3.11.dev0" and python_version < "3.12.dev0"
langchain-core==0.1.42; python_version >= "3.11.dev0" and python_version < "3.12.dev0"
langserve==0.0.51 ; python_version >= "3.11.dev0" and python_version < "3.12.dev0"
Jinja2==3.1.4
밑 명령어를 통해 작성한 라이브러리를 설치해주자.
1
pip install -r requirements.txt
index.html 구성
질문을 할 html 파일을 만들어주자 간단하게 form태그를 사용하여 구성했다.
스트림형태로 데이터가 받아지는 것을 확인하기 위해 body태그에 onload 속성을 활용했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>LangChain Topic Form</title>
<script>
function updateResponse(event) {
const responseDiv = document.getElementById('response');
responseDiv.innerHTML += event.data + "<br>";
}
function setupEventSource() {
const eventSource = new EventSource("/submit/");
eventSource.onmessage = updateResponse;
}
</script>
</head>
<body onload="setupEventSource()">
<h1>Enter a topic</h1>
<form action="/submit/" method="post">
<label for="topic">Topic:</label>
<input type="text" id="topic" name="topic">
<button type="submit">Submit</button>
</form>
<h2>Response:</h2>
<div id="response"></div>
</body>
</html>
server.py 구성
FastAPI를 사용하여 html 파일을 랜더링하고 LLM 모델을 사용할 앤드포이트를 구성했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
from langchain_community.chat_models import ChatOllama
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain_core.callbacks.manager import CallbackManager
callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])
llm = ChatOllama(model="EEVE:latest",
callback_manager=callback_manager, stream=True)
prompt = ChatPromptTemplate.from_template("{topic} 에 대하여 간략히 설명해 줘.")
chain = prompt | llm | StrOutputParser()
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)
# Jinja2 템플릿 설정
from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
from langchain_community.chat_models import ChatOllama
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain_core.callbacks.manager import CallbackManager
callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])
llm = ChatOllama(model="EEVE:latest",
callback_manager=callback_manager, stream=True)
prompt = ChatPromptTemplate.from_template("{topic} 에 대하여 간략히 설명해 줘.")
chain = prompt | llm | StrOutputParser()
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)
# Jinja2 템플릿 설정
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.post("/submit/", response_class=HTMLResponse)
async def handle_form(request: Request, user_input: str = Form(...)):
def generate():
# 주어진 주제에 대한 설명을 스트리밍합니다.
for token in chain.stream({"topic": user_input}):
yield token
# StreamingResponse를 사용하여 스트리밍 응답을 반환합니다.
return StreamingResponse(generate(), media_type="text/html")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
최종 파일 구조
테스트
1
python server.py
http://127.0.0.1:8000/
로 접속하면 아래와 같은 화면에서 질문을 전송하자
답변이 생성될 때까지 조금 기다리면 스트림형태로 답변이 화면에 출력된다.
해당 세팅은 질문 후에 답변이 새로운페이지에서 생성이되기 때문에 내가 했던 질문이나 답변이 화면에 계속해서 쌓이는 형태로 진행되진 않는다.
ChatGPT 서비스처럼 질문과 답변이 한 화면에서 계속해서 저장되어서 출력되려면 소켓을 통한 통신이 이루어져야 할 것 같고, 질문과 답변을 저장할 데이터베이스를 활용해야 할 것이다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.