G-gen ã®å€§æŽ¥ã§ããåœèšäºã§ã¯ãGoogle CloudïŒæ§ç§° GCPïŒã® Gemini 2.0 Pro ãš Text-to-Speech ã䜿ã£ãŠãé³å£°ã§å¿çãããã£ãããããã®éçºæé ã玹ä»ããŸãã åœèšäºã§éçºãããã® ç»é¢ã€ã¡ãŒãž ã§ããããš ã§ããªãããš å
責äºé
ãã£ã¬ã¯ããªæ§æ ããã°ã©ã ã®è§£èª¬ ã·ã¹ãã ããã³ããã®äœæãšè§£èª¬ ãœãŒã¹ã³ãŒãã®è§£èª¬ æºå UIã®æ§ç¯ åŠç å®è¡æ¹æ³ ããŒã«ã«ç°å¢ã§ã®å®è¡æ¹æ³ ããã°ã©ã ã®ä¿å å®è¡æ¹æ³ Cloud Run ãžã®ãããã€æ¹æ³ Cloud Run ã®äœ¿çš Dockerfile ã®äœæ requirements.txt ã®äœæ Cloud Run ãžã®ããã〠Cloud Run ã®ã¢ã¯ã»ã¹å
å¶åŸ¡ã«ã€ã㊠åœèšäºã§éçºãããã® ç»é¢ã€ã¡ãŒãž é³å£°ã§å¿çãããã£ãããããã®å®è¡ç»é¢ ç»é¢å·ŠéšåïŒèšå®ãšãªã¢ïŒ ãã£ãããããã®ããŽã衚瀺 Text-to-Speech Text-to-Speech ãžã®ãªã¯ãšã¹ã ã©ãžãªãã¿ã³ã§ Text-to-Speech æ©èœã®æå¹/ç¡å¹ãåãæ¿ããŸããããã©ã«ãã¯æå¹ã§ãã 話è
ãéžæ : ããããããŠã³ã¡ãã¥ãŒã§è©±è
ãéžæããŸãããja-JP-Neural2-B (ãã¬ãã¢ã )ããããã©ã«ãå€ã§ãã Gemini Config ã¢ãã«ãéžæ : ã©ãžãªãã¿ã³ã§ã¢ãã«ãéžæããŸãããgemini-2.0-pro-exp-02-05ããšãgemini-2.0-flash-001ãã® 2 ã€ã®éžæè¢ãããããgemini-2.0-pro-exp-02-05ããããã©ã«ãå€ã§ãã TemperatureïŒåµé æ§ïŒ: ã¹ã©ã€ããŒã§ temperature ãã©ã¡ãŒã¿ã調æŽããŸããããã©ã«ãå€ã¯ 0.00 ã§ãã æå€§åºåããŒã¯ã³æ° : ã¹ã©ã€ããŒã§æå€§åºåããŒã¯ã³æ°ã調æŽããŸãã1 ãã 8192 ã®éã§èª¿æŽå¯èœã§ããããã©ã«ãå€ã¯ 8192 ã§ãã ç»é¢å³éšåïŒãã£ãããšãªã¢ïŒ ãŠãŒã¶ãŒããã®å
¥åãšãã£ãããããããã®åçã衚瀺ããŸãã Text-to-Speech ãæå¹ãªå Žåã¯ããªãŒãã£ãªãã¬ãŒã€ãŒã§å¿çã®é³å£°ãåçããŸãã ã§ããããš ãŠãŒã¶ãŒãèªç¶ãªäŒè©±åœ¢åŒã§è³ªåãããšããã£ãããããã¯æèãçè§£ããéå»ã®è³ªåãèžãŸããåçãããŸãã ãã£ãããããã®åçãé³å£°ã§èªã¿äžããããšãã§ããŸã (æå¹/ç¡å¹ãåãæ¿ãå¯èœ)ã temperature ãã©ã¡ãŒã¿ã調æŽããŠãåçã®åµé æ§ãå¶åŸ¡ã§ããŸãã google_search_tool ãå©çšããŠãWeb æ€çŽ¢ã®çµæãåçã«åæ ã§ããŸãã ã§ããªãããš æ¬ãã£ãããããã«ã¯ã以äžã®æ©èœã¯å®è£
ãããŠããŸããã 瀟å
ã®ããŒã¿ãœãŒã¹ãå
ã«ããåçãè¡ãããšã¯ã§ããŸããã ç»åãåç»ãªã©ã®å
¥åãçæã¯ã§ããŸããã å
責äºé
åœèšäºã§ç޹ä»ããããã°ã©ã ã®ãœãŒã¹ã³ãŒãã¯ããèªèº«ã®è²¬ä»»ã®ããšã䜿çšãåŒçšãæ¹å€ãåé
åžããŠæ§ããŸããã ãã ããåãœãŒã¹ã³ãŒããåå ã§çºçããäžå©çããã©ãã«ã«ã€ããŠã¯ãåœç€Ÿã¯äžåã®è²¬ä»»ãè² ããŸããã ãã£ã¬ã¯ããªæ§æ ã¢ããªã±ãŒã·ã§ã³ã«å¿
èŠãªãã¡ã€ã«ãæ ŒçŽãããã£ã¬ã¯ããªãäœæããŸããä»åã¯ãgemini-chatbot ãšããååã®ãã£ã¬ã¯ããªãäœæãããã®äžã«ä»¥äžã®ãã¡ã€ã«ãé
眮ããŸãã gemini-chatbot/ âââ app.py # Pythonã¢ããªã±ãŒã·ã§ã³æ¬äœ âââ system_prompt.txt # ã·ã¹ãã ããã³ãã âââ G_gen_logo_reverse.jpg # å·Šäžã«ããŽãã¡ã€ã«ã衚瀺 âââ Dockerfile # 次ã®ã¹ãããã§äœæ âââ requirements.txt # 次ã®ã¹ãããã§äœæ ããã°ã©ã ã®è§£èª¬ ã·ã¹ãã ããã³ããã®äœæãšè§£èª¬ ã·ã¹ãã ããã³ããã¯ãGemini ãã£ãããããã®ãäººæ Œãããæ¯ãèããããå¿çã®ã¹ã¿ã€ã«ããå®çŸ©ããããã®éèŠãªãã¡ã€ã«ã§ãã å€§èŠæš¡èšèªã¢ãã«ïŒLLMïŒã§ãã Gemini ã¯ããã®ã·ã¹ãã ããã³ããã«æžãããæç€ºã«åºã¥ããŠããŠãŒã¶ãŒããã®è³ªåã«çããããäŒè©±ãé²ãããããŸãã ãã®ã·ã¹ãã ããã³ãããã«ã¹ã¿ãã€ãºããããšã§ããã£ãããããã®åæ§ãããåŒãåºããŸãã ã·ã¹ãã ããã³ããã¯ã以äžã®ãããªæ
å ±ãèšè¿°ããããã«äœ¿ãããŸãã 圹å²ïŒRoleïŒ: ãã£ããããããã©ã®ãããªåœ¹å²ãæŒããããå®çŸ©ããŸããäŸãã°ããã«ã¹ã¿ããŒãšã³ãžãã¢ããæŽå²ã®å
çããé¢çœããã£ã©ã¯ã¿ãŒããªã©ã§ãã å£èª¿ïŒToneïŒ: ãã£ããããããã©ã®ãããªå£èª¿ã§è©±ãããæå®ããŸããäŸãã°ããäžå¯§ãããã¬ã³ããªãŒããå·éããªã©ã§ãã å¶çŽæ¡ä»¶ïŒConstraintsïŒ: ãã£ãããããã®å¿çã«å¶çŽãèšããŸããäŸãã°ãããããªãã§ãã ãããããã«ã€ããŠã®ã¿çããŠãã ããããªã©ã§ãã å¿ç圢åŒïŒResponse FormatïŒ: å¿çã®åœ¢åŒãæå®ããŸããäŸãã°ããç®æ¡æžãã§ãã衚圢åŒã§ããã¹ããããã€ã¹ãããã§ããªã©ã§ãã 以äžã¯ãã·ã¹ãã ããã³ããã®ãµã³ãã«ã§ãã ããªãã¯ãGoogle Cloud ã«é¢ããã客æ§ããã®è³ªåã«å¯ŸããŠåçããæèœãªã«ã¹ã¿ããŒãšã³ãžãã¢ã§ãã 以äžã®æé ã«åŸã£ãŠé©åãªåçãæäŸããŠãã ããã 1. ãŸããäžããããããŒããæå³ãæ³šææ·±ãèªã¿ãçè§£ããŠãã ããã 2. ããŒãã®äž»èŠãªèŠçŽ ãåè§£ããŠãã ããã 3. ããŒãã®çè§£ã解決ã®ããã®åã¹ããããèããŠãã ããã 4. æç¢ºãªçç±ä»ããšãšãã«è§£æ±ºçãæç€ºããŠãã ããã 5. ãããŸã§ã®åºåãæ¹å€çã«è©äŸ¡ããŠæçµåºåã®è³ªãé«ããŠãã ããã å¶çŽæ¡ä»¶ïŒ - ãã¹ãŠã®éçšã瀺ããŠãã ããã - åã¹ãããã§ã®çç±ä»ãã説æããŠãã ããã - å
·äœçãã€æ£ç¢ºã«èšè¿°ããŠãã ããã - ãšããžã±ãŒã¹ãèæ
®ããŠãã ããã - ãã«ã·ããŒã·ã§ã³ãçºçãããªãããã«ãæ ¹æ ä»ããã URL ã®ãªã³ã¯ãèšèŒããŠãã ããã 圹å²ã®å®çŸ© : ããã§ã¯ããã£ãããããã®åœ¹å²ããGoogle Cloud ã«é¢ãã質åã«çããæèœãªã«ã¹ã¿ããŒãšã³ãžãã¢ããšæç¢ºã«å®çŸ©ããŸãã å¿çæé ã®æç€º : 質åã«çããéã®æèããã»ã¹ãæç€ºããŸãã 質åãçè§£ãã 質åãåè§£ãã 解決çãèãã çç±ãšãšãã«è§£æ±ºçãæç€ºãã åçã®è³ªãè©äŸ¡ãã ãã®ããã«æç€ºããããšã§ãããè«ççã§è³ªã®é«ãåçãçæããããšãæåŸ
ãããã å¶çŽæ¡ä»¶ã®èšå® : ãã£ãããããã®å¿çã«å¯Ÿããå¶çŽãèšããŸãã ãã¹ãŠã®éçšã瀺ã : åçã«è³ããŸã§ã®æèããã»ã¹ãé ããã«ç€ºããŸãã åã¹ãããã§ã®çç±ä»ãã説æ : ãªããã®ããã«èããã®ããçç±ã説æããããšãæ±ããŠããŸãã å
·äœçãã€æ£ç¢ºã«èšè¿° : ææ§ãªè¡šçŸãé¿ããå
·äœçã§æ£ç¢ºãªæ
å ±ãæäŸããããã«æç€ºããŠããŸãã ãšããžã±ãŒã¹ãèæ
® : äŸå€çãªç¶æ³ããäžè¬çã§ã¯ãªãã±ãŒã¹ã«ã€ããŠãèæ
®ããããã«æ±ããŠããŸãã ãã«ã·ããŒã·ã§ã³ãçºçãããªã : äºå®ã«åºã¥ããªãæ
å ±ãã誀ã£ãæ
å ±ãçæããªãããã«æç€ºããŸããURL ãèšèŒããããšã§ãæ
å ±ã®ä¿¡é Œæ§ãé«ããŸãã ãœãŒã¹ã³ãŒãã®è§£èª¬ ãã® Python ããã°ã©ã ã¯ãäž»ã«ä»¥äžã® 3 ã€ã®éšåã§æ§æãããŸãã æºå : å¿
èŠãªã©ã€ãã©ãªãã€ã³ã¹ããŒã«ããGoogle Cloud ã®åçš®èšå®ãè¡ããŸãã UI ã®æ§ç¯ : Streamlit ã䜿çšããŠããã£ãããããã®èŠãç®ãäœæããŸãã åŠç : ãŠãŒã¶ãŒããã®å
¥åãåãåããGemini ãš Text-to-Speech ã飿ºãããŠãå¿çãçæã»åçããŸãã æºå import streamlit as st import logging import google.cloud.logging import re from google import genai from google.genai import types from google.cloud import texttospeech # ç°å¢å€æ°ã®èšå® PROJECT_ID = "your-project_id" # èªåã®Google Cloudãããžã§ã¯ãIDã«çœ®ãæãã LOCATION = "us-central1" # Geminiã¢ãã«ã䜿çšãããªãŒãžã§ã³ # Cloud Logging ãã³ãã©ã logger ã«æ¥ç¶ logger = logging.getLogger() # æ¢åã®ãã³ãã©ãåé€ (远å ) for handler in logger.handlers[:]: logger.removeHandler(handler) logging_client = google.cloud.logging.Client() logging_client.setup_logging() # Geminiã¯ã©ã€ã¢ã³ãã®èšå® client = genai.Client( vertexai= True , project=PROJECT_ID, location=LOCATION ) # Text-to-Speechã¯ã©ã€ã¢ã³ãã®åæå tts_client = texttospeech.TextToSpeechClient() ãœãŒã¹ã³ãŒãã®åãã®éšåã§ã¯ãå¿
èŠãªã©ã€ãã©ãªãã€ã³ããŒãããŠããŸãã streamlit : Webã¢ããªãç°¡åã«äœããã©ã€ãã©ãª google.genai : Gemini APIã䜿ãããã®ã©ã€ãã©ãª google.cloud.logging : ãã°ãèšé²ããããã®ã©ã€ãã©ãª google.cloud.texttospeech : Text-to-Speech APIã䜿ãããã®ã©ã€ãã©ãª re: æ£èŠè¡šçŸã䜿ãããã®ã©ã€ãã©ãªïŒä»å㯠URL ã®é€å»ã«äœ¿çšïŒ 次ã«ãGoogle Cloud ãããžã§ã¯ãã® ID ãšãGemini ã¢ãã«ã䜿çšãããªãŒãžã§ã³ãèšå®ããŸããPROJECT_ID ã¯ãèªåã®ãããžã§ã¯ã ID ã«çœ®ãæããŠãã ããã logging ã®èšå®åŸãGemini API ãš Text-to-Speech API ã®ã¯ã©ã€ã¢ã³ããåæåããŸãã UIã®æ§ç¯ # Streamlit UIã®èšå® st.set_page_config(page_title= "Gemini Chatbot" , page_icon= "ð€" , layout= "wide" ) st.header( "ð¬ Speech 2 Gemini ãã£ããããã" ) st.caption( "ð Google Cloud / Google Workspace ã«ã€ããŠãããããçããŠããããã£ãããããã§ã" ) # ãã¡ã€ã«ã®èªã¿èŸŒã¿ logo = "G_gen_logo_reverse.jpg" system_prompt = "system_prompt.txt" # ãµã€ãããŒã«Logoç»åã衚瀺 st.sidebar.image(logo) # ãµã€ãããŒã«ãã£ããèšå®ã衚瀺 st.sidebar.title( "Text-to-Speech" ) # ã»ãã·ã§ã³ç¶æ
ã®åæåïŒVOICE ãªã¯ãšã¹ãã®æç¡ãããã©ã«ã㯠TrueïŒ if "is_voice_enabled" not in st.session_state: st.session_state.is_voice_enabled = False # ãµã€ãããŒã«ã©ãžãªãã¿ã³ã远å is_voice_enabled = st.sidebar.radio( "Text-to-Speechãžã®ãªã¯ãšã¹ã:" , ( True , False ), key= "is_voice_enabled" , format_func= lambda x: "æå¹" if x else "ç¡å¹" ) # 話è
ãªã¹ãïŒIDãšååã®èŸæžïŒ # 奜ããªè©±è
IDãšååã远å ã»å€æŽããŠãã ãã speakers = { "ja-JP-Neural2-BïŒãã¬ãã¢ã ïŒå¥³æ§ïŒ" : "ja-JP-Neural2-B" , "ja-JP-Neural2-DïŒãã¬ãã¢ã ïŒç·æ§ïŒ" : "ja-JP-Neural2-D" , "ja-JP-Standard-AïŒã¹ã¿ã³ããŒãïŒå¥³æ§ïŒ" : "ja-JP-Standard-A" , "ja-JP-Standard-CïŒã¹ã¿ã³ããŒãïŒç·æ§ïŒ" : "ja-JP-Standard-C" , "ja-JP-Wavenet-AïŒãã¬ãã¢ã ïŒå¥³æ§ïŒ" : "ja-JP-Wavenet-A" , "ja-JP-Wavenet-CïŒãã¬ãã¢ã ïŒç·æ§ïŒ" : "ja-JP-Wavenet-C" , } # ã»ãã·ã§ã³ç¶æ
ã®åæåïŒspeaker_idïŒ if "selected_speaker_id" not in st.session_state: st.session_state.selected_speaker_id = list (speakers.values())[ 0 ] # ããã©ã«ãã¯æåã®è©±è
ID # ãµã€ãããŒã«selectboxã远å ïŒè©±è
éžæïŒ if is_voice_enabled: selected_speaker_name = st.sidebar.selectbox( "話è
éžæ:" , list (speakers.keys()), # èŸæžã®ããŒïŒè©±è
åïŒããªã¹ãã«ããŠè¡šç€º key= "selected_speaker_name" ) # éžæããã話è
ã®IDãã»ãã·ã§ã³ç¶æ
ã«ä¿å st.session_state.selected_speaker_id = speakers[selected_speaker_name] # ãµã€ãããŒã«ãã£ããèšå®ã衚瀺 st.sidebar.title( "Gemini Config" ) # ã»ãã·ã§ã³ç¶æ
ã®åæå model_default = "gemini-2.0-pro-exp-02-05" temperature_default = 0.0 max_output_tokens_default = 8192 if "model_select" not in st.session_state: st.session_state.model_select = model_default if "temperature_select" not in st.session_state: st.session_state.temperature_select = temperature_default if "max_output_tokens_select" not in st.session_state: st.session_state.max_output_tokens_select = max_output_tokens_default # ãµã€ãããŒããªãã·ã§ã³ãã¿ã³ã§ã¢ãã«éžæ model = st.sidebar.radio( "ã¢ãã«éžæ:" , ( "gemini-2.0-pro-exp-02-05" , "gemini-2.0-flash-001" ), key= "model_select" ) st.sidebar.write( "" ) # ãµã€ãããŒã«ã¹ã©ã€ããŒã远å ããtemperatureã0ãã2ãŸã§ã®ç¯å²ã§éžæå¯èœã«ãã # åæå€ã¯0.0ãå»ã¿å¹
ã¯0.1 temperature = st.sidebar.slider( "Temperature(åµé æ§):" , min_value= 0.0 , max_value= 2.0 , step= 0.01 , key= "temperature_select" ) st.sidebar.write( "" ) # ãµã€ãããŒãã¹ã©ã€ãããŒã§åºåããŒã¯ã³éžæ max_output_tokens = st.sidebar.slider( "æå€§åºåããŒã¯ã³:" , min_value= 1 , max_value= 8192 , step= 1 , key= "max_output_tokens_select" ) st.sidebar.write( "" ) Streamlit ã䜿çšããŠããã£ãããããã® UI ãäœæããŸãã st.set_page_config : ããŒãžã®ã¿ã€ãã«ãã¢ã€ã³ã³ãèšå®ããŸãã st.header, st.caption : ããããŒãšèª¬ææã衚瀺ããŸãã st.sidebar : ãµã€ãããŒã«èšå®é
ç®ãé
眮ããŸãã st.sidebar.radio : ã©ãžãªãã¿ã³ã§éžæè¢ã衚瀺ïŒText-to-Speechã®æå¹/ç¡å¹ãã¢ãã«éžæïŒããŸãã st.sidebar.selectbox : ã»ã¬ã¯ãããã¯ã¹ã§éžæè¢ã衚瀺ïŒè©±è
éžæïŒããŸãã st.sidebar.slider : ã¹ã©ã€ããŒã§æ°å€ãèšå®ïŒtemperatureãæå€§åºåããŒã¯ã³æ°ïŒããŸãã st.chat_message : ãã£ããã®ã¡ãã»ãŒãžã衚瀺ããŸãã st.chat_input : ãŠãŒã¶ãŒãã¡ãã»ãŒãžãå
¥åããæ¬ã衚瀺ããŸãã åŠç # ã·ã¹ãã ããã³ããã®èªã¿èŸŒã¿ with open (system_prompt, "r" ) as f: system_prompt = f.read() # Googleæ€çŽ¢ãæå¹ã«ãã google_search_tool = types.Tool(google_search = types.GoogleSearch()) # Geminiã®ãã©ã¡ãŒã¿èšå® generate_content_config = types.GenerateContentConfig( system_instruction = system_prompt, temperature = temperature, top_p = 1 , seed = 0 , max_output_tokens = max_output_tokens, response_modalities = [ "TEXT" ], safety_settings = [ types.SafetySetting(category= "HARM_CATEGORY_HATE_SPEECH" ,threshold= "OFF" ), types.SafetySetting(category= "HARM_CATEGORY_DANGEROUS_CONTENT" ,threshold= "OFF" ), types.SafetySetting(category= "HARM_CATEGORY_SEXUALLY_EXPLICIT" ,threshold= "OFF" ), types.SafetySetting(category= "HARM_CATEGORY_HARASSMENT" ,threshold= "OFF" ) ], tools = [google_search_tool], ) # ãã£ããå±¥æŽã®åæå if "messages" not in st.session_state: st.session_state.messages = [] st.session_state.avatars = { "user" , "assistant" } # ãã£ããã¯ãªã¢ãã¿ã³ã®ã³ãŒã«ããã¯é¢æ° def clear_chat_history (): st.session_state.messages = [] if "history" in st.session_state: # historyãååšããå Žåã®ã¿åé€ del st.session_state[ "history" ] # model ãš temperature ã®ç¶æ
ããªã»ãã st.session_state.model_select = model_default st.session_state.temperature_select = temperature_default st.session_state.max_output_tokens_select = max_output_tokens_default st.session_state.is_voice_enabled = False # Text2Speechãªã¯ãšã¹ããããã©ã«ãå€ã«ãªã»ãã st.session_state.selected_speaker_id = list (speakers.values())[ 0 ] # speaker_idãããã©ã«ãã« # ãã£ããã¯ãªã¢ãã¿ã³ st.sidebar.button( "ã¯ãªã¢" , on_click=clear_chat_history) # ãã£ããå±¥æŽã®è¡šç€º for message in st.session_state.messages: with st.chat_message(message[ "role" ]): st.markdown(message[ "content" ]) # ãŠãŒã¶ãŒå
¥å prompt = st.chat_input( "ããã«ã¡ãã»ãŒãžãå
¥å" ) def synthesize_speech (text, language_code= 'ja-JP' , voice_name=speakers): """Google Cloud Text-to-Speech APIã䜿ã£ãŠããã¹ããé³å£°ã«å€æãã颿°ã Args: text: é³å£°ã«å€æããããã¹ãã language_code: èšèªã³ãŒãïŒããã©ã«ãã¯æ¥æ¬èªïŒã voice_name: é³å£°åïŒããã©ã«ãã¯æ¥æ¬èªã®Waveneté³å£°ïŒã Returns: é³å£°ãã¡ã€ã«ã®ãã€ããªããŒã¿ã ãšã©ãŒãçºçããå Žåã¯Noneã """ # 眮æå¯Ÿè±¡ã®ããŒã¯ããŠã³èšå·ãªã¹ã markdown_chars = [ "*" ] # ããŒã¯ããŠã³èšå·ã®é€å» for char in markdown_chars: text = text.replace(char, "" ) # URLã®æ£èŠè¡šçŸãã¿ãŒã³ url_pattern = r"https?://[\w/:%#\$&\?\(\)~\.=\+\-]+" # URLã®çœ®æ (äŸ: "URLçç¥" ã«çœ®æ) text = re.sub(url_pattern, "URLçç¥" , text) synthesis_input = texttospeech.SynthesisInput(text=text) voice = texttospeech.VoiceSelectionParams( language_code=language_code, name=voice_name ) audio_config = texttospeech.AudioConfig( audio_encoding=texttospeech.AudioEncoding.MP3 # MP3圢åŒã§åºå ) try : response = tts_client.synthesize_speech( input =synthesis_input, voice=voice, audio_config=audio_config ) return response.audio_content # é³å£°ãã¡ã€ã«ã®ãã€ããªããŒã¿ãè¿ã except Exception as e: st.error(f "Text-to-Speech APIãšã©ãŒ: {e}" ) return None # Gemini APIãåŒã³åºããå¿çãè¿ã def generate_response (messages, model, config): contents = [] for message in messages: if message[ "role" ] == "user" : contents.append(types.Content(parts=[types.Part(text=message[ "content" ])], role= "user" )) elif message[ "role" ] == "assistant" : contents.append(types.Content(parts=[types.Part(text=message[ "content" ])], role= "model" )) try : full_response = "" # åæå€ã¯ç©ºæååã®ãŸãŸ message_placeholder = st.empty() for chunk in client.models.generate_content_stream( model=model, contents=contents, config=config, ): if not chunk.candidates or not chunk.candidates[ 0 ].content.parts: continue full_response += chunk.text message_placeholder.markdown(full_response + "â" ) # ãã£ã³ã¯ããšã«è¡šç€ºãæŽæ°ããâãã¯ã«ãŒãœã«ã£ãœãèŠãããã message_placeholder.markdown(full_response) # æçµçãªçµæã衚瀺 return full_response except Exception as e: st.error(f "ãšã©ãŒãçºçããŸãã: {e}" ) logger.error(f "Gemini API error: {e}" ) return None # ãã£ããå±¥æŽãæŽæ°ãã def update_chat_history (messages, role, content): messages.append({ "role" : role, "content" : content}) return messages # ã¡ã€ã³ã®åŠç if prompt: st.session_state.messages = update_chat_history(st.session_state.messages, "user" , prompt) with st.chat_message( "user" ): st.markdown(prompt) with st.chat_message( "assistant" ): pass full_response = generate_response(st.session_state.messages, model, generate_content_config) # Text-to-Speech ãªã¯ãšã¹ãããæå¹ãã®å Žåã®ã¿ãé³å£°åæåŠçãè¡ããŸãã if st.session_state.is_voice_enabled: # Text-to-Speechã§é³å£°ãåæ audio_data = synthesize_speech(full_response, voice_name=st.session_state.selected_speaker_id) if audio_data: st.audio(audio_data, format = "audio/mp3" , autoplay= True ) # MP3圢åŒã§åç if full_response is not None : st.session_state.messages = update_chat_history(st.session_state.messages, "assistant" , full_response) # Cloud Logging æžã蟌㿠logger.info(f "model_name : {model}" ) logger.info(f "temperature : {temperature}" ) logger.info(f "user_message: {prompt}" ) logger.info(f "LLM_message : {full_response}" ) ããããã£ãããããã®é è³ãšãªãéšåã§ãã system_prompt : ãã£ãããããã®åœ¹å²ãå£èª¿ãªã©ãèšå®ããã·ã¹ãã ããã³ãããèªã¿èŸŒã¿ãŸãã google_search_tool : Googleæ€çŽ¢ãæå¹ã«ããŸãã generate_content_config : Gemini ã®åçš®ãã©ã¡ãŒã¿ãèšå®ããŸãã temperature : å¿çã®å€æ§æ§ã調æŽããŸããïŒ0 ã«è¿ãã»ã©æ±ºãŸã£ãçãã倧ããã»ã©åµé çãªçãã«ãªããŸããïŒ max_output_tokens : å¿çã®æå€§é·ããèšå®ããŸãã safety_settings : äžé©åãªè¡šçŸãæå¶ããŸãã synthesize_speech 颿° : ããã¹ããé³å£°ã«å€æãã颿°ã§ãã Text-to-Speech API ã䜿ã£ãŠãããã¹ããMP3圢åŒã®é³å£°ããŒã¿ã«å€æããŸãã URL ãäžèŠãªèšå·ãåé€ããŠãããèªç¶ãªé³å£°ã«ãªãããã«å·¥å€«ããŠããŸãã generate_response 颿° : Gemini API ã«è³ªåãæããŠãå¿çãåãåã颿°ã§ãã ãã£ããå±¥æŽãšèšå®ã Gemini ã«æž¡ããŠãå¿çãçæããŸãã å¿çã¯ã¹ããªãŒãã³ã°ã§åãåãã鿬¡çã«è¡šç€ºãæŽæ°ããŸãã update_chat_history 颿° : ãã£ããå±¥æŽãæŽæ°ãã颿°ã§ãã ã¡ã€ã³ã®åŠç ãŠãŒã¶ãŒãã¡ãã»ãŒãžãå
¥åãããããã£ããå±¥æŽã«è¿œå ããŸãã Gemini ã«å¿çãçæãããŸãã Text-to-Speech ãæå¹ãªããå¿çãé³å£°ã«å€æããŠåçããŸãã å¿çããã£ããå±¥æŽã«è¿œå ããŸãã Cloud Logging ã«ãã°ãèšé²ããŸãã å®è¡æ¹æ³ ããŒã«ã«ç°å¢ã§ã®å®è¡æ¹æ³ å¿
èŠãªã©ã€ãã©ãªãã€ã³ã¹ããŒã«ããŸãã pip install streamlit google-generativeai google-cloud-logging google-cloud-texttospeech ããã°ã©ã ã®ä¿å åæ²ã®ãœãŒã¹ã³ãŒãã app.py ãšããåç§°ã®ãã¡ã€ã«ãšããŠä¿åããŸãã å®è¡æ¹æ³ ã¿ãŒããã«ã§ä»¥äžã®ã³ãã³ããå®è¡ããŸãã streamlit run app.py ãã©ãŠã¶ãéãããã£ããããããå©çšã§ããŸãã Cloud Run ãžã®ãããã€æ¹æ³ Cloud Run ã®äœ¿çš éçºããç»åçæ Web ã¢ããªããGoogle Cloud äžã«ãããã€ããŸããåœèšäºã§ã¯ãããã€å
ã®ãµãŒãã¹ãšããŠããµãŒããŒã¬ã¹ ã³ã³ãã ã³ã³ãã¥ãŒãã£ã³ã°ãµãŒãã¹ã§ãã Cloud Run ã䜿çšããŸããCloud Run ã®è©³çްã«ã€ããŠã¯ä»¥äžã®èšäºããäžèªãã ããã blog.g-gen.co.jp Dockerfile ã®äœæ ã¢ããªã±ãŒã·ã§ã³ãã³ã³ããåããããã«ãDockerfile ãäœæããŸããapp.py ãšåããã£ã¬ã¯ããªã«ã以äžã®å
容㧠Dockerfile ãäœæããŸãã FROM python:3.12-slim # ã¯ãŒã¯ãã£ã¬ã¯ããªãèšå® WORKDIR /app # å¿
èŠãªã©ã€ãã©ãªãã€ã³ã¹ããŒã« COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt # ã¢ããªã±ãŒã·ã§ã³ã®ã³ãŒããã³ã㌠COPY . . # Streamlitã䜿çšããããŒããæå® EXPOSE 8080 # ã¢ããªã±ãŒã·ã§ã³ãå®è¡ CMD [" streamlit " , " run " , " app.py " , " --server.port=8080 " , " --server.address=0.0.0.0 "] requirements.txt ã®äœæ 次ã«ãrequirements.txt ãäœæããŸããapp.py ãšåããã£ã¬ã¯ããªã«ã以äžã®å
容㧠requirements.txt ãäœæããŠãã ããããã®ãã¡ã€ã«ã«ã¯ãã¢ããªã±ãŒã·ã§ã³ãäŸåãã Python ã©ã€ãã©ãªãèšè¿°ããŸãã google-cloud-logging google-cloud-texttospeech google-genai streamlit Cloud Run ãžã®ããã〠gemini-chatbot ãã£ã¬ã¯ããªã§ã以äžã®ã³ãã³ããå®è¡ããŸãã gcloud run deploy gemini-chatbot --source . --region us-central1 --allow-unauthenticated åãªãã·ã§ã³ã®èª¬æ gemini-chatbot : Cloud Run ãµãŒãã¹ã®ååïŒå¥œããªååã«å€æŽå¯èœïŒ --source . : çŸåšã®ãã£ã¬ã¯ã㪠(.) ã«ãããœãŒã¹ã³ãŒãïŒDockerfile ã app.py ãªã©ïŒã䜿çšããŠãã«ãããããšãæå®ã --region us-central1 : Cloud Run ãµãŒãã¹ããããã€ãããªãŒãžã§ã³ --allow-unauthenticated : èªèšŒãªãã§ãµãŒãã¹ã«ã¢ã¯ã»ã¹ã§ããããã«ããïŒæ¬çªç°å¢ã§ã¯é©åãªèªèšŒãèšå®ããããšïŒ Cloud Run ã®ã¢ã¯ã»ã¹å
å¶åŸ¡ã«ã€ã㊠Cloud Run ã«ãããã€ãã Web ã¢ããªã®ã¢ã¯ã»ã¹å
å¶åŸ¡ãè¡ãããå ŽåãCloud Run ã®å段ã«ããŒããã©ã³ãµãŒãé
眮ããIdentity Aware ProxyïŒIAPïŒã«ãã IAM èªèšŒã Cloud Armor ã«ãã IP ã¢ãã¬ã¹ã®å¶éãå®è£
ããããšãã§ããŸãã 以äžã®èšäºããåç
§ãã ããã blog.g-gen.co.jp 倧接 å幞 (èšäºäžèЧ) ã¯ã©ãŠããœãªã¥ãŒã·ã§ã³éš 2022幎4æã«G-gen ã«ãžã§ã€ã³ã åè·ãŸã§ã¯AWSãã¯ããã€ã³ãã©é åå
šè¬ã®ãªãã§ãå±ãäºåæµã¯ã©ãŠããšã³ãžãã¢ãç®æããŠãAWSã®ã¹ãã«ãGoogle Cloudã«ãã€ã°ã¬ãŒã·ã§ã³äžã®æ¥ã
ã