アプリの進捗状況

アプリへのリンク
(Streamlit Community Cloud)

webbrowserの出力設定がデフォルトブラウザになってしまっているので、streamlit上ではブラウザオープンが失敗しています。修正します。

開発環境

 Windows11, anaconda, python3.10.12,
 VScode, Chrome,
 Github, Streamlit Community Cloud

使用ライブラリ

 streamlit, langchain, openAI,
 google-api-python-client, wikipedia,
 python-dotenv, GitPython

 


「料理検索」部分のコーディング

まずはサイドバーのコア機能となる「料理検索」部分を作成します。
使いたい食材をinputすると、プロンプティングによって外部検索(Google検索)が走り、それを基にLLMから回答を得て、検索結果ページのURLをブラウザに表示させるところまでコーディングします。

以下のライブラリを使用します。

openai==0.28.0
langchain==0.0.292
python-dotenv==1.0.0
google-api-python-client==2.125.0


まずはローカルのJupyterでコーディングです。

retrieval.ipynb

import os
from dotenv import load_dotenv
load_dotenv()
import webbrowser
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain.agents import load_tools, initialize_agent, AgentType


# ChatPromptTemplateの定義

system_template="""
あなたは、与えられた条件をもとにGoogleでレシピ検索を実行して回答を返すことが得意な、忠実なアシスタントです。
"""

human_template="""
以下の「検索条件」をキーワードとしてGoogle検索を実行し、回答は「回答ルール」に従うこと。

検索条件: {site} {material} レシピ

回答ルール: Google検索結果のページのみ表示
[Google Search Result]といったheaderを付けることを禁止する。
Google検索結果ページのURLを""で囲った文字列のみ回答すること。
URL以外の余計な情報は付与してはいけない。
Google検索結果からHumanが選ぶため、AIが勝手に料理を選んでそのURLに飛んではならない。
"""


# 最終的にAgentに渡す messages を作成する → Streamlit上では関数化するか

chat_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(system_template),
    HumanMessagePromptTemplate.from_template(human_template)
])

# {site}の定義
# それぞれ特徴を持たせてクックパッド検索、マカロニ検索ができるよう
# ここにも特徴的なキーワードを忍ばせる
# streamlitでselectboxを意識して選択肢を考えておく

# site_list = ["クックパッド 殿堂入り", "マカロニ Youtube"]

messages = chat_prompt.format_prompt(site="クックパッド 殿堂入り", material=input("材料:")).to_messages()



# LangChain Agent の振る舞い方を決める

# OpenAI Chat Completion API の定義
# 外部検索が目的なので、LangChainのAgentを使用する
# gpt-4モデルを使いたいので、.envにOPENAI_API_KEYを記述
# ツールは google-search を採用するため、.envにGOOGLE_API_KEY, GOOGLE_CSE_IDを追記

# 練習の為、.envからAPIモデル名とtemperatureを読み込む実験。他に流用できる箇所があれば取り入れよう。

chat = ChatOpenAI(
    model_name=os.environ["OPENAI_API_MODEL"],
    temperature=os.environ["OPENAI_API_TEMPERATURE"],
)

tools = load_tools(["google-search"])


# Agentを初期化して変数に入れる
# AgentTypeはOPENAI_FUNCTIONSを採用

agent_chain = initialize_agent(
        tools,
        chat,
        agent=AgentType.OPENAI_FUNCTIONS,
)

result = agent_chain.run(messages)


# デフォルトブラウザでURLを開く
url = result
webbrowser.open_new(url)

.env

OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OPENAI_API_MODEL=gpt-4
OPENAI_API_TEMPERATURE=0.5
GOOGLE_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxx
GOOGLE_CSE_ID=xxxxxxxxxxxxxxx


いつもレシピ検索で利用している「cookpad」と「macaro-ni」どちらかで検索したいので、Streamlit上では st.selectbox() で選択できるようにする予定です。

余っている食材を入力したいので、messages 変数を定義する中に material=input(“食材:”) を忍ばせています。これはStreamlit上で st.text_input() になる予定です。


Agentの振る舞いに遊びを持たせてしまうと、検索結果にばらつきが出てしまいます。
 (勝手にヘッダを振ったり、書き方に変化を持たせてくる)
最終的に戻り値のURLを使ってブラウザを開きたいので、「URLだけ」返させるプロンプトでないとなりません。意外とこの調整に時間がかかりました。

HumanMessagePromptTemplateの記述は試行錯誤を重ねて、やっと何とか安定的に「URLだけ」を返すようになったテンプレです。

system_template="""
あなたは、与えられた条件をもとにGoogleでレシピ検索を実行して回答を返すことが得意な、忠実なアシスタントです。
"""

human_template="""
以下の「検索条件」をキーワードとしてGoogle検索を実行し、回答は「回答ルール」に従うこと。

検索条件: {site} {material} レシピ

回答ルール: Google検索結果のページのみ表示
[Google Search Result]といったheaderを付けることを禁止する。
Google検索結果ページのURLを""で囲った文字列のみ回答すること。
URL以外の余計な情報は付与してはいけない。
Google検索結果からHumanが選ぶため、AIが勝手に料理を選んでそのURLに飛んではならない。
"""

 ※ChatOpenAI() は temperature=0.5(.env)に設定していますが、今のところ結果に影響せず、きっちり「URLだけ」返してくれています

# print(result)の結果
"https://www.google.com/search?q=%E3%82%AF%E3%83%83%E3%82%AF%E3%83%91%E3%83%83%E3%83%89+%E6%AE%BF%E5%A0%82%E5%85%A5%E3%82%8A+%E3%81%AA%E3%81%99+%E3%83%AC%E3%82%B7%E3%83%94&rlz=1C1GCEU_enUS832US832&oq=%E3%82%AF%E3%83%83%E3%82%AF%E3%83%91%E3%83%83%E3%83%89+%E6%AE%BF%E5%A0%82%E5%85%A5%E3%82%8A+%E3%81%AA%E3%81%99+%E3%83%AC%E3%82%B7%E3%83%94&aqs=chrome.0.0i512l10.6128j0j7&sourceid=chrome&ie=UTF-8"


ブラウザに、意図したとおりの検索結果ページが立ち上がりました。


Webアプリのサイドバーに組み込む

以下のライブラリを追加で使用します。

streamlit==1.26.0

ソースコード

import os
from dotenv import load_dotenv
load_dotenv()
import webbrowser
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain.agents import load_tools, initialize_agent, AgentType
import streamlit as st



## ★メインエリア ##

# 横幅いっぱいに表示
st.set_page_config(layout="wide")

# タブ分け
tab1, tab2, tab3 = st.tabs(["履歴管理","今週の献立","買い物リスト"])

with tab1:
    st.header("前週の献立を履歴に移動して保存")
    
    col1, col2 = st.columns((1,1))
    with col1:
        st.text_area("履歴")

    with col2:
        st.text_area("前週食べたもの")
        st.button("履歴に移動して消去")
    
with tab2:
    st.header("向こう一週間の献立を考える")
    
    col1, col2 = st.columns((1,1))
    with col1:
        st.text_area("お気に入りレシピ")
        st.button("お気に入り 再読込")
    with col2:
        st.text_area("今週の献立を考える")
        st.button("今週の献立 更新")
        st.button("今週の献立 再読込")    
with tab3:
    st.header("買い物リストを作る")
    
    col1, col2 = st.columns((1,1))
    with col1:
        st.text_area("今週の献立")

    with col2:
        st.text_area("買い物リスト")
        st.button("買い物リスト 更新")








## ★サイドバーの料理検索チャットボット部分 ##


# {site}の定義
select_site = st.sidebar.selectbox("検索したいサイト:",("クックパッド", "マカロニ"))
if select_site == "クックパッド":
    site = "クックパッド 殿堂入り"
elif select_site == "マカロニ":
    site = "マカロニ Youtube"

# {material}の定義
material = st.sidebar.text_input("食材:")
retrieve = st.sidebar.button("検索")


def retrieve_recipe(site, material):

    # ChatPromptTemplateの定義
    system_template="""
    あなたは、与えられた条件をもとにGoogleでレシピ検索を実行して回答を返すことが得意な、忠実なアシスタントです。
    """

    human_template="""
    以下の「検索条件」をキーワードとしてGoogle検索を実行し、回答は「回答ルール」に従うこと。

    検索条件: {site} {material} レシピ

    回答ルール: Google検索結果のページのみ表示
    [Google Search Result]といったheaderを付けることを禁止する。
    Google検索結果ページのURLを""で囲った文字列のみ回答すること。
    URL以外の余計な情報は付与してはいけない。
    Google検索結果からHumanが選ぶため、AIが勝手に料理を選んでそのURLに飛んではならない。
    必ず検索結果ページに留まること。Youtubeの動画ページに移動することを禁止する。
    """

    # promptの定義
    chat_prompt = ChatPromptTemplate.from_messages([
        SystemMessagePromptTemplate.from_template(system_template),
        HumanMessagePromptTemplate.from_template(human_template)
    ])



    # {site}の定義
    # それぞれ特徴を持たせてクックパッド検索、マカロニ検索ができるよう
    # ここにも特徴的なキーワードを忍ばせる
    # streamlitでselectboxを意識して選択肢を考えておく



    messages = chat_prompt.format_prompt(site=site, material=material).to_messages()



    # OpenAI Chat Completion API の定義
    # 外部検索が目的なので、LangChainのAgentを使用する
    # gpt-4モデルを使いたいので、.envにOPENAI_API_KEYを記述
    # ツールは google-search を採用するため、.envにGOOGLE_API_KEY, GOOGLE_CSE_IDを追記

    # 練習の為、.envからAPIモデル名とtemperatureを読み込む実験。他に流用できる箇所があれば取り入れよう。

    chat = ChatOpenAI(
        model_name=os.environ["OPENAI_API_MODEL"],
        temperature=os.environ["OPENAI_API_TEMPERATURE"],
    )

    tools = load_tools(["google-search"])


    # Agentを初期化して変数に入れる
    # AgentTypeはOPENAI_FUNCTIONSを採用

    agent_chain = initialize_agent(
            tools,
            chat,
            agent=AgentType.OPENAI_FUNCTIONS,
    )
    
    return agent_chain.run(messages)

# 初期設定
if material == "":
    exit()
if retrieve:
    result = retrieve_recipe(site, material)
    material = ""
else:
    exit()



# result = retrieve_recipe(site, material)


# デフォルトブラウザでURLを開く
url = result
webbrowser.open_new(url)

<< Prev Next >>