여러분들은 위의 사진과 같은 포스기를 보신 적이 있으신가요?
쉬운 사용법, 우수한 디자인을 가진 포스기들이 카페, 음식점 등 어디서나 쉽게 볼 수 있었습니다.
평소 무인 매장이나 자영업에 대해 관심이 많아 포스기를 검색해보았더니,
생각보다 많은 포스기 업체들이 존재하는 것을 알 수 있었습니다.
하지만 각 포스기마다 어떤 기능이 있는지, 어떤 장점과 단점이 있는 지를 알기 위해서는
각 포스기 업체 홈페이지를 참고하거나, 자영업자 커뮤니티에 검색하는 시간이 많이 할애되었습니다.
이에 ‘다양한 포스기 업체와 기능들을 비교하고, 사용자의 요구에 맞는 최적의 포스기를 추천하는 서비스’ 가 있으면 예비 자영업자들이 쉽게 포스기를 찾을 수 있겠다! 라는 생각이 들었습니다.
예비 자영업자를 위한 포스기 추천 서비스 “포스모아“ (feat. RAG)
서비스 명칭은 포스기들을 모아 볼 수 있다는 의미를 가진 “포스모아“로 지었습니다.
타겟 고객은 예비 자영업자로 고객이 원하는 기능을 작성하면 업체 추천, 추천 이유를 작성해주는 AI 개발을 목표로 하였습니다.
1. RAG의 이해
RAG를 활용해 개발하려면 먼저 RAG에 대한 이해가 필요하다고 생각하여 ,
제가 이해한 RAG를 간단하게 설명 드리겠습니다.
“
외부 문서를 가져와서(Document Loaders),
토큰 제한이 있는 LLM을 분할하고(Text Splitters),
분할한 텍스트를 숫자로 변환한다.(Vector Embeddings)
변환된 숫자를 저장한다. (Vector Store)
사용자가 질문을 했을 때 질문과 유사한 대답을 Vector Store에 찾아서 텍스트로 변환하여 출력한다.
“
2. 서비스 설명
데이터: 포스기 업체 3곳을 선정하여 홈페이지 URL을 통해 데이터를 가져왔습니다.
기능: 유저가 원하는 포스기 기능을 작성하면 추천 업체, 추천 이유를 작성해줍니다.
UI: Streamlit을 통해 구성하였습니다.
최초 화면
답변 화면
3. 코드
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.runnables import RunnablePassthrough
from langchain.document_loaders import UnstructuredURLLoader
import streamlit as st
# 포스기 업체 3곳의 URL을 리스트로 정의합니다.
urls = ["https://tossplace.com/product/front", "https://cherryforce.kr/#index", "https://payhere.in/retail/all/"]
# URL 로더를 생성합니다. 이 로더는 비정형 데이터를 로드하는 데 사용됩니다.
loader = UnstructuredURLLoader(urls)
# 로더를 사용하여 URL에서 데이터를 로드합니다. 'data' 변수에 로드된 내용을 저장합니다.
data = loader.load()
# 텍스트 분할기를 설정합니다. 각 텍스트 조각의 최대 크기는 1000자로 하고, 각 조각 간에 200자의 중복이 있습니다.
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
# 로드된 데이터를 설정된 크기의 조각으로 나눕니다. 'splits' 변수에 나눠진 텍스트 조각들을 저장합니다.
splits = text_splitter.split_documents(data)
print(len(splits))
# 텍스트 조각을 벡터로 변환하여 저장소에 저장합니다. 여기서 OpenAI 임베딩을 사용합니다.
vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())
# 벡터 저장소를 검색기로 변환합니다. 'retriever' 변수에 검색기를 저장합니다.
retriever = vectorstore.as_retriever()
# 프롬프트 템플릿을 정의합니다. 이 템플릿은 질문에 대한 추천 업체를 소개하고, 이유를 설명하도록 설계되었습니다.
template="{context}\n\n{question}에 대한 추천 업체를 선정해줘. 해당 업체를 선정한 이유도 같이 설명해줘. 아래 포맷으로 답변 생성해줘. \
- 추천 업체 : \
- 추천 이유 : "
# 정의된 템플릿을 사용하여 채팅 프롬프트 템플릿을 생성합니다.
prompt = ChatPromptTemplate.from_template(template)
# OpenAI의 채팅 모델을 초기화합니다. 여기서는 'gpt-3.5-turbo' 모델을 사용합니다.
llm = ChatOpenAI(model="gpt-3.5-turbo")
# RAG 체인을 설정합니다.
# 'context'는 검색 결과를 통해 제공되고, 'question'은 사용자가 입력한 질문을 그대로 전달합니다.
chain = (
{"context": retriever, "question": RunnablePassthrough()} # 검색기와 질문을 설정합니다.
| prompt # 프롬프트 템플릿을 연결합니다.
| llm # OpenAI 채팅 모델을 연결합니다.
| StrOutputParser() # 문자열 출력 파서를 연결합니다.
)
# Streamlit을 통해 UI를 구성합니다.
st.title('포스모아')
st.write("포스모아는 포스기를 선택하는데 도움을 줄 수 있는 서비스입니다.")
st.write("원하는 기능을 자유롭게 작성해주세요😜")
user_input = st.text_input('🔍원하는 기능을 작성해주세요:', '')
# 체인을 실행하여 질문에 대한 답변을 생성합니다.
result = chain.invoke(user_input)
if user_input:
st.write('결과:\n', result)
4. 마무리
RAG가 무엇인지, 어떻게 사용하는 지를 대략적으로 알 수 있었습니다.
RAG를 활용하는 것은 재밌지만…어려워요…😭
#11기랭체인