common.title

Docs
Quantum Circuit
TYTAN CLOUD

QUANTUM GAMING


autoQAOA
Desktop RAG

Overview
Terms of service

Privacy policy

Contact
Research

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