-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* docker-compose contest 작업 추가 * Update PR with added and removed files * Update README.md * Update docker-compose.yaml * Update README.md
- Loading branch information
Showing
5 changed files
with
147 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
## LLM chatbot | ||
### 소개 | ||
chat-gpt와 비슷하게 local LLM(Llama3) 과 대화를 할 수 있는 간단한 웹 애플리케이션입니다. | ||
- LLM(Large Language Models)은 대규모 언어 모델이라는 뜻으로, 사용자 질문을 입력하면 질문에 대한 답변을 생성하는 언어 AI 모델입니다. | ||
- OpenAI의 chat-gpt, Meta의 Llama, Google의 Gemma 등이 대표적인 예입니다. | ||
- 구현된 앱에는 비교적 작은 Meta의 Llama3-8B 모델을 사용했기 때문에 한국어에 대한 성능은 좋지 않습니다. | ||
<img width="724" alt="스크린샷 2024-05-15 오후 10 34 17" src="https://github.com/jinkimmy/docker-pro/assets/145090267/c71b69da-48ae-4fd5-9ade-e9042388fc05"> | ||
|
||
|
||
### 실행 방법 | ||
`docker-compose.yml`파일이 있는 폴더로 이동하여 Docker Compose를 사용하여 실행합니다. | ||
~~~sh | ||
docker-compose up | ||
~~~ | ||
- `streamlit` 컨테이너가 실행되고 있는 모습 | ||
<img width="934" alt="스크린샷 2024-05-15 오후 10 18 05" src="https://github.com/jinkimmy/docker-pro/assets/145090267/84a602e9-f60e-4fdf-90b8-acb1cbf6c474"> | ||
|
||
터미널에서 streamlit 컨테이너 실행되고 있으면 아래의 명령어를 통해 Llama3를 ollama 컨테이너에 실행합니다. | ||
~~~sh | ||
docker-compose exec ollama ollama run llama3 | ||
~~~ | ||
|
||
Llama3-8B 모델은 용량이 크기 때문에 실행되는데 약간의 시간이 소요됩니다. | ||
- llama3가 ollama 컨테이너에서 안정적으로 서빙되고 있다면 다음과 같은 문구가 나타납니다. | ||
~~~sh | ||
>>> Send a message (/? for help) | ||
~~~ | ||
|
||
이제 웹브라우저에서 `localhost:8502`로 접속하여 채팅을 시작합니다. | ||
|
||
### 참고 | ||
- 이 프로젝트는 streamlit 프레임워크를 사용하여 개발되었습니다. | ||
- 챗봇은 사용자가 질문을 하면 llm이 대답하는 간단한 기능이 포함됐습니다. | ||
- Ollama는 다양한 LLM을 로컬PC 환경에서 쉽고 빠르게 배포할 수 있게 도와주는 오픈소스입니다. | ||
- 따라서, 개인 PC 리소스가 매우 제한적이라면 모델이 실행되지 않거나 답변이 느립니다. | ||
- 이 프로젝트에서 사용되는 llama3-8B는 최소 8GB의 RAM을 필요로 합니다. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import streamlit as st | ||
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler | ||
from langchain.callbacks.manager import CallbackManager | ||
from langchain_community.llms import ollama | ||
from langchain_community.chat_models import ollama | ||
from langchain_core.output_parsers import StrOutputParser | ||
from langchain_core.prompts import ChatPromptTemplate | ||
from utils import print_messages, StreamHandler | ||
|
||
|
||
# llama3-8B 모델 생성 | ||
llm =ollama.ChatOllama( | ||
model="llama3", | ||
base_url="http://ollama:11434", | ||
verbose=True, | ||
callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]) | ||
) | ||
|
||
def get_response(user_input): | ||
global llm | ||
# Prompt 생성 | ||
prompt = ChatPromptTemplate.from_messages( | ||
[ | ||
("system", "You are helpful ai assistant. If possible, please answer in Korean "), | ||
("user", "{question}") | ||
] | ||
) | ||
chain = prompt | llm | StrOutputParser() | ||
# LLM 답변 생성 | ||
response = chain.invoke({"question": user_input}) | ||
return response | ||
|
||
|
||
st.title("Chat with Llama3") | ||
if "messages" not in st.session_state.keys(): | ||
st.session_state["messages"] = [ | ||
{"role": "assistant", "content": "저에게 질문을 해주세요."} | ||
] | ||
|
||
if user_input := st.chat_input("메세지를 입력해 주세요."): | ||
# 사용자 입력 | ||
st.session_state.messages.append({"role": "user", "content": user_input}) | ||
# st.chat_message("user").write(f"{user_input}") | ||
# st.session_state["messages"].append(ChatMessage(role="user", content=user_input)) | ||
|
||
for message in st.session_state.messages: | ||
with st.chat_message(message["role"]): | ||
st.write(message["content"]) | ||
|
||
if st.session_state.messages[-1]["role"] != "assistant": | ||
with st.chat_message("assistant"): | ||
with st.spinner("답변 생성중..."): | ||
stream_handler = StreamHandler(st.empty()) | ||
response = get_response(user_input) | ||
st.write(response) | ||
message = {"role": "assistant", "content": response} | ||
st.session_state.messages.append(message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
version: '3' | ||
name: docker-ai-chatbot | ||
services: | ||
streamlit: | ||
image: python:latest | ||
ports: | ||
- '8502:8501' | ||
networks: | ||
- internal-net | ||
volumes: | ||
- ./chat:/app | ||
working_dir: /app | ||
command: bash -c "pip install -r requirements.txt && streamlit run chat.py" | ||
restart: unless-stopped | ||
|
||
ollama: | ||
image: ollama/ollama | ||
ports: | ||
- '11434:11434' | ||
networks: | ||
- internal-net | ||
volumes: | ||
- ./llm:/root/.ollama | ||
restart: unless-stopped | ||
|
||
networks: | ||
internal-net: | ||
driver: bridge | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
streamlit | ||
langchain | ||
langchain-community |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import Any | ||
from uuid import UUID | ||
from langchain_core.outputs import ChatGenerationChunk, GenerationChunk | ||
import streamlit as st | ||
from langchain_core.callbacks.base import BaseCallbackHandler | ||
|
||
|
||
class StreamHandler(BaseCallbackHandler): | ||
def __init__(self, container, initial_text=""): | ||
self.container = container | ||
self.text = initial_text | ||
|
||
def on_llm_new_token(self, token: str, **kwargs) -> None: | ||
self.text += token | ||
self.container.markdown(self.text) | ||
|
||
|
||
def print_messages(): | ||
# 이전 대화기록 출력 | ||
if "messages" in st.session_state and len(st.session_state["messages"]) > 0: | ||
for chat_message in st.session_state["message"]: | ||
st.chat_message(chat_message.role).write(chat_message.content) |