common.title

Docs
Quantum Circuit
TYTAN CLOUD

QUANTUM GAMING


Overview
Contact
Event
Project
Research

Terms of service (Web service)

Terms of service (Quantum and ML Cloud service)

Privacy policy


Sign in
Sign up
common.title

RAG + LLM + Gradioでレストランの店員としてメニューから注文を取るウェブアプリ

Yuichiro Minato

2024/04/11 14:00

こんにちは。注文を取る時にタブレットだけだと味気ないですね。LLMを使いましょう。

今回はローカルLLM(ちょっとなぜかGPT3.5のAPIのクレカが通らなくて。。。)+ RAG + Gradioを使いました。

ローカルLLMはChatGPTのようなものを手元で実現できます。ChatGPTのシステムを使うには都度課金が必要ですから大変ですよね。

RAGはLLMにメニューを覚え込ませます。AIはお店のメニューを知りませんから、メニューを渡してみてもらう必要があります。また、品切れ情報だったりと更新される情報もあるので、それらも確認してもらう必要がありますね。

Gradioは簡単にウェブアプリが作れます。スマホから注文をしてもらうのに、Pythonのコードを叩いてもらうわけには行きませんので。

LLMはMistral7Bを使いました。ちょっと日本語変ですが、いいでしょう。

from transformers import AutoTokenizer, pipeline

model_id = "mistralai/Mistral-7B-Instruct-v0.2"
tokenizer = AutoTokenizer.from_pretrained(model_id)
pipe = pipeline("text-generation", model=model_id, tokenizer=tokenizer, device=0, max_new_tokens=300)

そして、今回のRAGですが、DBはCSVから取り込みました。どうしても日本語が取り込めなくて、結局pandas経由で入れました。

from langchain_community.document_loaders import DataFrameLoader
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings
from langchain_text_splitters import CharacterTextSplitter
import pandas as pd

df = pd.read_csv("list5.csv")
loader = DataFrameLoader(df, page_content_column="name")

documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

embeddings = HuggingFaceEmbeddings(
 model_name="intfloat/multilingual-e5-large"
)

db = FAISS.from_documents(docs, embeddings)
retriever = db.as_retriever()
print(db.index.ntotal)

今回はlist5.csvというファイルにメニューが書いてあります。おすすめや値段、料理の説明などが書いてあります。プロンプトはちょっとくどい感じですが書いてみました。

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain.llms import HuggingFacePipeline

llm = HuggingFacePipeline(pipeline=pipe)

template = """ここはタイ料理レストラン「ブルーキャット」で、あなたは店員です。お客さんのオーダーに答えていくつか注文をとってもらえますか?注文以外の会話は次のコンテクストを参照しなくていいです。日本語で答えてください。お店にある料理は次のコンテクストの料理です。:

{context}

Question: {question}
"""

prompt = ChatPromptTemplate.from_template(template)

chain = (
 {"context": retriever, "question": RunnablePassthrough()}
 | prompt
 | llm
 | StrOutputParser()
)

query = "トムヤムクンはありますか?"
answer = chain.invoke(query)

if "Answer:" in answer:
  answer = answer.split("Answer:")[1]

if "Question:" in answer:
  answer = answer.split("Question:")[0]

print(answer)

出力が安定しないので、不要な文字を削除したかったですが完璧とは行きませんでした。今後調整が必要ですね。

次にGradioです。今回は出力から不要な文字を削除する分岐を入れました。

import gradio as gr
import os

def add_text(history, text):
  history = history + [(text, None)]
  return history, gr.Textbox(value="", interactive=False)

def bot(history):
  query = history[-1][0]
  response = chain.invoke(query)  
  if "Answer:" in response:
    response = response.split("Answer:")[1]
  if "Question:" in response:
    response = response.split("Question:")[0]
  history[-1][1] = ""
  for character in response:
    history[-1][1] += character
    yield history

with gr.Blocks() as demo:
  chatbot = gr.Chatbot([])
  with gr.Row():
    txt = gr.Textbox(
      scale=4,
      show_label = False,
      container = False
    )
  clear = gr.Button("Clear")

txt_msg = txt.submit(add_text, [chatbot, txt], [chatbot, txt], queue = False).then(bot, chatbot, chatbot)
  txt_msg.then(lambda: gr.Textbox(interactive = True), None, [txt], queue = False)
  clear.click(lambda: None, None, chatbot, queue=False)

demo.queue()   
demo.launch(share=True)

結果、、、

いい感じの応答システムができました。メニューの内容はきちんと覚えていて説明してくれました。実際にレストランとかで使ってみたいですね。売り上げは上がるんでしょうか。

© 2025, blueqat Inc. All rights reserved