Table of Contents
LangChainを使って会話のBotアプリを作成してみましょう。会話として成立させるためには過去のやり取りを覚えておく必要があります。Memoryオブジェクトを使ってそのような機能を実装してみます。
本記事はFuture Coders独自教材からの抜粋です。変化の早い分野なので記事の内容が古くなっている可能性もあります。ご注意ください。
Conversation
会話は話者のやりとりが交互に続いてゆくことで成立します。
以下のサンプルを実行してください。
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage
llm = ChatOpenAI(model="gpt-4o")
while True:
prompt = input("> ")
if prompt == "":
break
response = llm.invoke([HumanMessage(prompt)])
print(response.content)
以下は実行結果です。
> 今大リーグで一番活躍している日本人は誰ですか?
2023年現在、大リーグで最も活躍している日本人選手は大谷翔平(Shohei Ohtani)です。彼はロサンゼルス・エンゼルスに所属しており、投打両方で優れた成績
を残しています。大谷は投手としても打者としても一流のパフォーマンスを発揮しており、その多才さと実績から「二刀流」として知られています。彼の活躍は世界中の野球ファンからも高く評価されています。
> 彼は何歳ですか?
すみませんが、彼という人が誰なのかわからないので、正確な年齢をお答えすることはできません。具体的な情報を提供していただければ、さらに詳しい情報をお伝えできるかもしれません。
> 彼はどの球団に所属していますか?
申し訳ありませんが、具体的な人物やその所属に関する情報は提供できません。もし一般的な質問や他の情報が必要であれば、お知らせください。
生成AIの能力からするとすべての質問に答えられるはずです。以下のように聞けばちゃんと答えてくれます。
> 大谷翔平の所属球団は?
大谷翔平(Shohei Ohtani)は、2023年時点でメジャーリーグベースボール(MLB)のロサンゼルス・エンゼルス(Los Angeles Angels)に所属しています。彼は投手と打者の両方で活躍する「二刀流」として知られています。
> 大谷翔平は何歳ですか?
大谷翔平(おおたに しょうへい)は1994年7月5日生まれです。2023年10月現在、彼は29歳です。
モデルの学習タイミングの時点の情報ですが、ちゃんと答えが帰ってきます。どうやらGPT-4oの学習タイミングは2023年10月ころのようです。
ちなみに、以下のURLにモデルの一覧と学習時期が記載されています。
https://platform.openai.com/docs/models/gpt-4o
このように、単にAPIを実行した場合、過去の情報は引き継がれず、都度新しい問い合わせが行われます。よって、直前の会話の内容について質問しても生成AIは答えられません。
LangChainには、このような状況に対処するための仕組み、すなわち会話を実装するための仕組みが用意されています。
以下は、会話をやり取りするイメージです。
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage, AIMessage
llm = ChatOpenAI(model="gpt-4o")
resposne = llm.invoke([
HumanMessage("こんにちは"),
AIMessage("こんにちは, お名前は?"),
HumanMessage("太郎です"),
AIMessage("太郎君ね!何歳なの?"),
HumanMessage("5歳です"),
AIMessage("そうなんだ。幼稚園ですね"),
HumanMessage("小学校に行くのはいつですか?"),
])
print(resposne.content)
人間とAIのやり取りをリスト形式で表現して、ここまでどのような会話が行われたかという情報を渡しています。
最後の質問(“小学校に行くのはいつですか?”)を踏まえて、以下のような出力が得られました。
'太郎君が5歳なら、来年の春に小学校に入学することになると思います。楽しみですね!学校では何を楽しみにしていますか?'
最後の質問に対して、生成AIが適切に答えていることが分かります。これは会話の履歴全部を生成AIに入力しているからです。
会話の履歴を全部保存して、都度送るのは非効率だと感じられるかもしれません。LangChainでは、そのような要望に応えるため
- 過去の会話をすべて覚える
- 過去N回分の会話の見覚える
- 過去の会話を要約して覚える
などの仕組みが用意されています。
では、シンプルな例から順番に見てゆきましょう。
Memory
LangChainではchainを作成して、そのchainを実行するという考え方を採用していますが、会話のchainを作成する関数が用意されています。その関数を呼び出すだけで、過去の内容をすべて覚えているchainが作成できます。以下に例を示します。
ConversationBufferMemory
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(return_messages=True)
llm = ChatOpenAI(model="gpt-4o")
chain = ConversationChain(memory=memory,llm=llm)
chain.invoke("こんにちは")
出力は以下のようになりました。全体は辞書の形式で、input, history, responseといったプロパティがあります。historyの形式はリストで、その中にこれまでの会話がすべて格納されています。最後の応答はresponseプロパティから取得できます。
{'input': 'こんにちは',
'history': [
HumanMessage(content='こんにちは'),
AIMessage(content='こんにちは!元気ですか?...略...ぜひ教えてくださいね。')
],
'response': 'こんにちは!元気ですか?...略...ぜひ教えてくださいね。'}
このように ConversationBufferMemoryを作成して、それを使ってConversationChain関数でchainを作成するだけで、過去の内容を覚えられるようになります。
一回のやり取りでは、過去の内容を覚えているか判断できないので、何回かやり取りをしてみましょう。以下のようにプログラムを修正します。
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain_core.messages import HumanMessage
memory = ConversationBufferMemory(return_messages=True)
llm = ChatOpenAI(model="gpt-4o")
chain = ConversationChain(memory=memory,llm=llm)
while True:
s = input("> ")
if s == "":
break
r = chain.invoke([HumanMessage(s)])
print(f"You : {s}")
print(r["response"])
実行結果は以下の通りです。2回やり取りを行いましたが、最初のやり取りの内容を覚えていることがわかります。
You : こんにちは、僕は太郎、5歳です。
こんにちは、太郎くん!私はAIだよ。5歳なんだね、すごいね!今日は何をして遊んでいるの?好きなアニメやゲームがあったら教えてね。
You : クイズです。来年僕は何歳でしょうか?
おお、クイズだね!来年は、太郎くんは6歳になるよ。1年足すと、5歳プラス1歳で6歳になるんだ。合ってるかな?他にもクイズがあったら教えてね!
ConversationBufferWindowMemory
会話をすべて覚えさせるのは簡単ですが、実行の都度トークンも増えてゆきます。一回の問い合わせの長さには上限がありますが、永遠に会話を続けると上限に到達してしまうかもしれません。 一般的には会話の履歴を一字一句正確にすべて覚える必要はないでしょう。ConversationBufferWindowMemoryは過去N回分のやり取りを覚えておくクラスです。
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(return_messages=True, k=2)
llm = ChatOpenAI(model="gpt-4o")
chain = ConversationChain(memory=memory,llm=llm)
r = chain.invoke([HumanMessage("私は太郎です")])
print(r["response"])
r = chain.invoke([HumanMessage("私は15歳です")])
print(r["response"])
r = chain.invoke([HumanMessage("趣味は旅行です")])
print(r["response"])
r = chain.invoke([HumanMessage("私は誰でしょう?私は何歳ですか?")])
print(r["response"])
出力は以下のようになりました。
こんにちは、太郎さん!お会いできて嬉しいです。今日はどのようなことについてお話しされたいですか?趣味や興味のあること、あるいは何か質問があれば、何でもお聞かせください。
そうなんですね、太郎さん!15歳というと、高校生くらいでしょうか?学校生活や部活動、友達との時間など、いろんな経験をしている時期ですよね。何か特に興味を持っていることや、最近楽しんでいることがあれば教えてください。例えば、好きな科目や趣味、将来やってみたいことなど、どんなことでもお話し大歓迎です!
旅行が趣味なんですね!それは素晴らしいです。世界にはたくさんの美しい場所や文化がありますから、旅行は新しい発見や経験をもたらしてくれますよね。
どんな場所に行くのが好きですか?例えば、自然が豊かな場所や歴史的な街、あるいは美味しい食べ物が楽しめる場所など、いろいろなタイプの旅行がありますよね。また、これまでに行ったことのある場所や、これから行ってみたい場所があれば教えてください。どんな旅行の思い出も聞かせてもらえたら嬉しいです!
あなたは15歳で、旅行が趣味の方ですね!お名前まではわからないので、もしよければ教えていただけると嬉しいです。これまでにどんな場所に旅行に行ったことがあるのか、また将来行ってみたい場所など、ぜひお聞かせください。旅行の計画を立てるのも楽しいですよね!
最後のやりとりで名前がわからなくなっていることがわかります。名前は最初にやり取りしましたが、k=2という指定の範囲を超えたので履歴から削除されてしまったためです。
ConversationSummaryMemory
人と会話するとき、「一字一句正確に覚える」のではなく、なんとなく概要を覚えながら会話を進めているはずです。そんな状況を再現するのがConversationSummaryMemoryクラスです。
以下サンプルです。
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain.chains import ConversationChain
llm = ChatOpenAI(model="gpt-4o")
from langchain.memory import ConversationSummaryMemory
memory = ConversationSummaryMemory(llm=llm)
chain = ConversationChain(memory=memory,llm=llm)
r = chain.invoke([HumanMessage("私は太郎です")])
print(r["response"])
r = chain.invoke([HumanMessage("私は15歳です")])
print(r["response"])
r = chain.invoke([HumanMessage("趣味は旅行です")])
print(r["response"])
r = chain.invoke([HumanMessage("私は誰でしょう?私は何歳ですか?")])
print(r["response"])
出力は以下のようになりました。名前や年齢、趣味を覚えていることがわかります。
こんにちは、太郎さん!お会いできて嬉しいです。今日はどんなことをお話ししましょうか?趣味や興味のあることについてお話ししていただけると嬉しいです。
こんにちは、Taroさん!15歳なんですね。素晴らしい年齢ですね。何か特別な趣味や興味がありますか?例えば、スポーツ、音楽、ゲーム、アニメなど、何でも話してくれますよ。
こんにちは、太郎さん!旅行が趣味なんですね。素晴らしいですね!どんな場所に旅行するのが好きですか?国内旅行、それとも海外旅行?また、特に思い出に残っている旅行先があれば教えてください。
こんにちは、太郎さん!あなたが誰かということについては、私はあなたが太郎さんであると認識しています。そして、あなたの年齢は15歳だとおっしゃっていました。何か他にお話ししたいことや質問があれば、どうぞ教えてくださいね!旅行のお話もお聞きしたいですし、他の趣味や興味についても大歓迎です。
ConverstaionSummaryMemoryクラスは、今までの会話を都度要約します。要約するために生成AIの力をつかっているため、コンストラクタの引数に生成AIを指定しています。
Streamlitアプリで違いを確認
ここまで、会話に利用できる3つのMemoryクラスを紹介してきました。それらの差異をより見てゆくには、過去の履歴historyにどんな情報が格納されているのか見るのが一番です。都度printしながら見ることもできますが、streamlitアプリを使って確認してみましょう。
Streamlitは可視化のためのモジュールです。以下のコマンドでインストールしておいてください。
py -m pip install streamlit
Streamlitのプログラムを実行する場合は、コマンドプロンプトなどから以下のように実行してください。
streamlit run ファイル名
ConversationBufferMemoryの場合
import streamlit as st
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain.chains import ConversationChain
llm = ChatOpenAI(model=”gpt-4o”)
from langchain.memory import ConversationBufferMemory
@st.cache_resource
def get_memory():
return ConversationBufferMemory(return_messages=True)
chain = ConversationChain(memory=get_memory(),llm=llm)
if text := st.chat_input(“your message”):
r = chain.invoke([HumanMessage(text)])
st.write(r)
Streamlitのアプリはボタンの押下や入力がある都度ページを再読み込みします。その際にMemoryを作り直すと以前の内容をすべて忘れてしまいます。そうならないように、@st.cache_resourceという修飾を使って、オブジェクトを再利用するようにしています。あとは、st.chat_inputで入力を受付けて、その内容をHumanMessageに格納して、chainを呼び出しています。その応答をst.writeでページに表示しています。以下、実行時の画面です。問合せをする都度、historyが増えてゆく様子が確認できます。
ConversationBufferWindowMemoryの場合
プログラムはほぼ前のものと同じです。Memoryを作成する以下の箇所だけを更新してください。
from langchain.memory import ConversationBufferWindowMemory
@st.cache_resource
def get_memory():
以下、実行時の画面です。前の例と同じようにhistoryのリストは増えていきますが、2ターン(4個)以上増えることはありません。会話の数が4を超えると古い物から削除されてゆく様子が確認できます。
ConversationSummaryMemoryの場合
Memoryを作成する以下の箇所だけを更新してください。
from langchain.memory import ConversationSummaryMemory
@st.cache_resource
def get_memory():
return ConversationSummaryMemory(llm=llm)
以下、実行時の画面です。historyはリストではなく、ここまでの会話の要約となっていることがわかります。langchainでは会話の要約を英語で行っていることがわかります。漢字などを質問してみると興味深い結果になるかもしれません。
SQLite Memory
ここまでいろいろなMemoryが会話を保存する様子をみてきました。ただ、ここまでのサンプルはプログラムを終了したらリセットされてしまいます。「この間の話だけど、、、」ということができません。ここでは、会話の流れをDBに保存して、PCを再起動した後でも「この間はこうだってよね」と会話を続けられるようにしてみましょう。
SQLChatMessageHistoryというクラスを使うと、発言内容をSQLデータベースに格納することができます。
from langchain_community.chat_message_histories import SQLChatMessageHistory
chat_message_history = SQLChatMessageHistory(
session_id="test_session_id", connection="sqlite:///sqlite.db"
)
chat_message_history.add_user_message("Hello")
chat_message_history.add_ai_message("Hi")
session_idはセッションといって、会話を特定するために使われます。任意の文字列を指定できます。
上記コードを実行すると、同じフォルダにsqlite.dbというファイルが作成されます。以下のコードをで保存されたメッセージの内容を確認できます。
print(chat_message_history.messages)
出力は以下のようになりました。
[HumanMessage(content='Hello'), AIMessage(content='Hi')]
以下のプログラムを実行すればデータベース(sqlite.db)の中身を直接見ることもできます。
import sqlite3, json
conn = sqlite3.connect('sqlite.db')
cursor = conn.cursor()
cursor.execute(f"SELECT * from message_store;")
rows = cursor.fetchall()
for row in rows:
r = json.loads(row[2])
print(row[1], r)
conn.close()
出力は以下のようになりました。add_user_message / add_ai_messageで追加した内容が表示されていることが確認できます。
test_session_id {'type': 'human', 'data': {'content': 'Hello', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': None, 'example': False}}
test_session_id {'type': 'ai', 'data': {'content': 'Hi', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': None, 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}}
このSQLChatMessageHistoryクラスを使うと、会話を覚えながら生成AIとやり取りすることが容易に実装できます。
まずは、会話用のプロンプトを用意します。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_messages(
[
("system", "あなたは保育園の先生です"),
("human", "{question}"),
MessagesPlaceholder(variable_name="history"),
]
)
chain = prompt | ChatOpenAI()
会話用のプロンプトを使用する場合、過去の内容を保存する領域を用意する必要があります。今回はhistoryという変数名を使用しました。
まず、会話を覚えることなく生成AIを実行してみましょう。
r = chain.invoke({"question":"僕は太郎、5歳です。今日は何して遊ぶ?", "history":[]})
print(r.content)
以下の応答が返ってきました。まだ、会話はデータベースに保存されていません。
こんにちは、太郎くん!今日はどんな遊びがしたいかな?お絵かきやブロック遊び、お人形遊びなどたくさん選択肢がありますよ。一緒に楽しい時間を過ごしましょう!何に興味があるか教えてくれると、その遊びを一緒に楽しめますよ。
以下のようにRunnableMessageHistoryクラスをつかって、会話用のchainを作成します。
RunnableWithMessageHistoryの引数に関しては注意が必要です。最初の引数にベースとなるChainを指定します。 2番目の引数には関数(セッションIDを引数として受け取りチャットの履歴を返す)を指定します。 今回はlambdaを使い、引数session_idを受け取り、その値をでSQLChatMessageHistoryオブジェクトを作成しています。
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import SQLChatMessageHistory
chain_with_history = RunnableWithMessageHistory(
chain,
lambda session_id: SQLChatMessageHistory(
session_id=session_id, connection="sqlite:///sqlite.db"
),
input_messages_key="question",
history_messages_key="history",
)
データベースにはいろいろな会話を保存できます。それらの会話を特定するのがセッションIDです。今回はmy-session-1という名前にしてみました。セッション名はconfigという変数(辞書)に保存して、chainをinvokeで呼び出すときに引数として指定します。
invokeで生成AIを呼び出すときに、そのconfigも引数で指定します。このようにすることで、会話を特定しながら生成AIとやりとりできるようになります。
config = {"configurable": {"session_id": "my-session-1"}}
r = chain_with_history.invoke({"question": "僕は太郎、5歳です。今日は何して遊ぶ?"}, config=config)
print(r.content)
以下のような返事がかえってきました。
太郎くん、こんにちは!一緒に遊ぶの楽しみですね。今日は、ブロックで高い塔を作ったり、お人形遊びをしたり、お絵かきをしたり、絵本を読んだり、外でボール遊びをしたり、たくさんの遊び方がありますね。どんな遊びがしたいですか?
続いて以下のように実行してみます。
r = chain_with_history.invoke({"question": "お絵描きがいいです。ところで僕の名前と年齢を覚えてますか?覚えてたら言ってください。"}, config=config)
print(r.content)
以下のように出力されました。以前のやりとりを覚えていることがわかります。
太郎くん、5歳ですね!お名前と年齢を覚えていますよ。一緒にお絵かきを楽しみましょう!何を描くのが好きですか?絵の具やクレヨン、色鉛筆など、お好きなものを使って描いてみましょうね。楽しい時間を過ごしましょう!
会話の内容はデータベースに保存されているので、同じセッションIDを指定すれば、PCを再起動したとしても、その会話を再開することが可能です。
Chatアプリ
ここまでの内容を踏まえて生成AIを使ったチャットアプリをつくってみます。streamlitを使ってローカルで動かしてみる、そのあとでGCP(Google Cloud Platform)で動かして、スマホなどで動作させる、という手順で進めてゆきましょう。
ローカルで動かす
streamlitを使います。またクラウドで動作させることを前提として、chatbot-aiというフォルダを作ってそこで作業するものとします。
まずユーザーインタフェースだけstreamlitで実装してみます。何か入力するとAIがオウム返しに返答します。main.py
import streamlit as st
st.title("ChatGPT Bot")
if "messages" not in st.session_state:
st.session_state.messages = []
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.write(message["content"])
if question := st.chat_input("生成AIに質問する"):
with st.chat_message("user"):
st.write(question)
with st.chat_message("assistant"):
st.write(question)
st.session_state.messages.append({"role": "user", "content": question})
st.session_state.messages.append({"role": "assistant", "content": question})
次に、生成AIを組み込んでみます。streamlitは入力が行われるたびに実行が行われます。都度モデルを作成するのは非効率なので@st.cache_resourceでモデルが何度も作成されることを防止しています。main.py
import streamlit as st
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
@st.cache_resource
def get_chain():
print("get_chain()")
prompt = ChatPromptTemplate.from_messages(
[
("system", "あなたは心理カウンセラーです。関西弁で優しくシンプルに対応します"),
("human", "{question}"),
MessagesPlaceholder(variable_name="history"),
]
)
return prompt | ChatOpenAI()
st.title("ChatGPT カウンセリング")
if "messages" not in st.session_state:
st.session_state.messages = []
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.write(message["content"])
if question := st.chat_input("生成AIに相談する"):
with st.chat_message("user"):
st.write(question)
st.session_state.messages.append({"role": "user", "content": question})
r = get_chain().invoke({"question":question, "history":[]})
with st.chat_message("assistant"):
st.write(r.content)
st.session_state.messages.append({"role": "assistant", "content": r.content})
以下のように生成AIが応答します。ただ、まだ会話になっていないので、前の質問は覚えていません。
RunnableWithMessageHistoryクラスを使用して会話の形のボットにしてみます。
import streamlit as st
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
config = {"configurable": {"session_id": "chatai-bot"}}
@st.cache_resource
def get_chain():
prompt = ChatPromptTemplate.from_messages(
[
("system", "あなたは心理カウンセラーです。関西弁で優しくシンプルに対応します"),
("human", "{question}"),
MessagesPlaceholder(variable_name="history"),
]
)
chain = prompt | ChatOpenAI()
return chain
@st.cache_resource
def get_history(s):
return ChatMessageHistory()
st.title("ChatGPT カウンセリング")
if "messages" not in st.session_state:
st.session_state.messages = []
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.write(message["content"])
if question := st.chat_input("生成AIに相談する"):
with st.chat_message("user"):
st.write(question)
st.session_state.messages.append({"role": "user", "content": question})
chain_with_history = RunnableWithMessageHistory(get_chain(),
get_history,
input_messages_key="question",
history_messages_key="history")
r = chain_with_history.invoke({"question": question}, config=config)
with st.chat_message("assistant"):
st.write(r.content)
st.session_state.messages.append(
{"role": "assistant", "content": r.content})
以下のように過去のやり取りを覚えている生成AIボットが動きました。
GCPで動かす
ここまで動いたアプリをGCP(Google Cloud Platform)で動かしてみましょう。スマホなどでも利用できるようになります。
GCPを利用するにはアカウントを作成してプロジェクトを作っておく必要があります。ここではそのあとの手順について説明します。
作業用のフォルダにカレントディレクトリを移動して、requirements.txtというファイルを作成し、以下のように必要なモジュールを列挙します。requirements.txt
streamlit
langchain
langchain_core
langchain_openai
langchain_community
Streamlitアプリを動作させるためには以下のProcfileを作成してください。
web: streamlit run main.py --server.port ${PORT:-8080}
以下のようなフォルダ構成としてください。
├─chatbot-ai
│ main.py
│ requirements.txt
│ Procfile
以下のコマンドを実行することで、ローカルの内容がGCPで動作するようになります。
gcloud run deploy --update-env-vars OPENAI_API_KEY=APIキーの値
この際GCPへのログイン、GCPの設定など状況によって適宜コマンドを実行する必要がでてきます。
またそれぞれのコマンドでは途中いろいろ質問されることがあります。gcloud run deploy
コマンドで以下の質問があった場合には、以下のように応答します。
- ロケーション = asia-northeast1 が東京リージョンです
- Allow unauthenticated invocations to […] (y/N)? はYを選択します。
以下は利用頻度の高いgcloudコマンド例です。
gcloud auth list ... どのアカウントでログインしているか確認する
gcloud auth login ... GCPにログインする
gcloud auth application-default login ... デフォルトの認証設定を行う
gcloud run deploy ... CLOUDにデプロイする
デプロイが終わると、URLが表示されます。そのURLからサービスにアクセスすることができます。
URLはGCPのコンソールから確認することも可能です。プロジェクトを選択して、CloudRunを選択すると、該当するURLを見つけることができます。
CloudRunは従量課金のサービスで、使用しない限り料金は発生しません。長時間使用しない場合はサービスが停止されます。久しぶりにアクセスがあったときには、そのときからサービスを起動するので、最初はレスポンスが遅く感じられるかもしれません。料金を抑えるための仕組みです。個人で利用する範囲では課金額は少額もしくは無料で済むとおもいますが、サービスを削除する場合はGCPのコンソールから削除してください。
https://console.cloud.google.com
Future Coders
Future Codersではほかにも多くの独自教材を用意しています。少人数個別指導・リモート対応でレッスンを行っています。レッスン以外にも出張授業やコンサルタントも行っております。興味のある方はお気軽にお問い合わせください。