user:
先日、LangChain、Azure OpenAI、HuggingFaceの埋め込みモデル、そしてStreamlitを組み合わせて、PDFの内容を元にAzureOpenAIと会話できるアプリを構築しました。
import os
import streamlit as st
from dotenv import load_dotenv
from datetime import datetime
import json
import csv
import io
import numpy as np
import tempfile
import atexit
from langchain.chat_models import AzureChatOpenAI
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import ConversationalRetrievalChain
# 環境変数の読み込み
load_dotenv()
# Streamlit 設定
st.set_page_config(page_title="PDFチャットアプリ", page_icon="📄")
st.title("📄 PDFに基づいてAzure OpenAIとチャット")
# チャット履歴の初期化
if "messages" not in st.session_state:
st.session_state.messages = [
{"role": "system", "content": "あなたは親切なAIアシスタントです。PDFの内容について答えてください。"}
]
if "chat_history" not in st.session_state:
st.session_state.chat_history = []
# PDF アップロード
uploaded_file = st.file_uploader("📎 PDFファイルをアップロードしてください", type="pdf")
# 一時ファイルのパス(後で削除するため)
temp_pdf_path = None
# PDF 処理
if uploaded_file:
with st.spinner("PDFを読み込んで処理中..."):
# 一時ファイルに保存
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
tmp_file.write(uploaded_file.read())
temp_pdf_path = tmp_file.name
# アプリ終了時に一時ファイルを削除
def cleanup_temp_file():
if temp_pdf_path and os.path.exists(temp_pdf_path):
os.remove(temp_pdf_path)
atexit.register(cleanup_temp_file)
# PyPDFLoaderでPDF読込
loader = PyPDFLoader(temp_pdf_path)
documents = loader.load()
# テキスト分割
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
docs = splitter.split_documents(documents)
# Embeddingモデル(Hugging Face)でベクトル化
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
vectorstore = FAISS.from_documents(docs, embeddings)
# Retriever作成
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 3})
# Azure OpenAI チャットモデル
llm = AzureChatOpenAI(
deployment_name=os.getenv("OPENAI_DEPLOYMENT_NAME"),
openai_api_key=os.getenv("OPENAI_API_KEY"),
openai_api_base=os.getenv("OPENAI_API_BASE"),
openai_api_version=os.getenv("OPENAI_API_VERSION"),
openai_api_type="azure",
temperature=0,
)
# RAG チェーン構築
qa_chain = ConversationalRetrievalChain.from_llm(llm=llm, retriever=retriever)
st.success("✅ 処理完了!PDFに基づいたチャットが可能です。")
# ユーザー入力
user_input = st.chat_input("メッセージを入力してください")
if user_input:
# ユーザー入力の表示と記録
st.chat_message("user").markdown(user_input)
st.session_state.messages.append({"role": "user", "content": user_input})
# 回答生成
with st.chat_message("assistant"):
with st.spinner("考え中..."):
result = qa_chain({"question": user_input, "chat_history": st.session_state.chat_history})
reply = result["answer"]
st.markdown(reply)
# チャット履歴を保存
st.session_state.messages.append({"role": "assistant", "content": reply})
st.session_state.chat_history.append((user_input, reply))
# 🔁 チャット履歴リセット
if st.button("🔄 チャット履歴をリセット"):
st.session_state.messages = [
{"role": "system", "content": "あなたは親切なAIアシスタントです。PDFの内容について答えてください。"}
]
st.session_state.chat_history = []
st.experimental_rerun()
# 💾 チャット履歴保存(JSON / CSV / Markdown)
messages_to_save = [m for m in st.session_state.messages if m["role"] != "system"]
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
def save_chat_as_json():
return json.dumps(messages_to_save, ensure_ascii=False, indent=2)
def save_chat_as_csv():
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=["role", "content"])
writer.writeheader()
writer.writerows(messages_to_save)
return output.getvalue()
def save_chat_as_markdown():
lines = []
for m in messages_to_save:
role_label = "**User:**" if m["role"] == "user" else "**Assistant:**"
content = m["content"].replace("\n", " \n")
lines.append(f"{role_label} {content}")
return "\n\n".join(lines)
st.download_button(
label="💾 JSONで保存",
data=save_chat_as_json(),
file_name=f"chat_log_{timestamp}.json",
mime="application/json",
)
st.download_button(
label="💾 CSVで保存",
data=save_chat_as_csv(),
file_name=f"chat_log_{timestamp}.csv",
mime="text/csv",
)
st.download_button(
label="📝 Markdownで保存",
data=save_chat_as_markdown(),
file_name=f"chat_log_{timestamp}.md",
mime="text/markdown",
)
今回は、このコードが参考になるとは思うのですが、次のようなアプリを作ってください。
・ユーザに、PDFではなくてエクセルファイル入力を促す。
・エクセルファイルには、1シートあたり1テーブルのデータが記述されている。
・ユーザがエクセルファイルの中身について質問すると、答えてくれる。
・ユーザの質問に対する答えがSQLを使って得られそうなら、SQLを書いて実行して、その結果に基づいて答えてくれる。
assistant:
面白くて実用的なアイデアですね!以下は、エクセルファイルをアップロードして、その内容について質問でき、必要に応じてSQLを使って回答してくれるStreamlitアプリのサンプルコードです:
import os
import streamlit as st
from dotenv import load_dotenv
import pandas as pd
import sqlite3
import tempfile
from langchain.chat_models import AzureChatOpenAI
from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.sql_database import SQLDatabase
# 環境変数の読み込み
load_dotenv()
# Streamlit設定
st.set_page_config(page_title="Excel Q&A アプリ", page_icon="📊")
st.title("📊 Excelファイルの内容について質問できます")
# チャット履歴の初期化
if "messages" not in st.session_state:
st.session_state.messages = [
{"role": "system", "content": "あなたは親切なAIアシスタントです。Excelの内容について答えてください。"}
]
# Excelファイルのアップロード
uploaded_file = st.file_uploader("📎 Excelファイルをアップロードしてください", type=["xlsx", "xls"])
if uploaded_file:
with st.spinner("Excelファイルを読み込み中..."):
# pandasでExcelファイルを読み込む
xls = pd.ExcelFile(uploaded_file)
sheet_names = xls.sheet_names
st.success(f"✅ {len(sheet_names)}シートが読み込まれました: {', '.join(sheet_names)}")
# 一時的なSQLiteデータベース作成
temp_db = tempfile.NamedTemporaryFile(delete=False, suffix=".db")
conn = sqlite3.connect(temp_db.name)
# 各シートをテーブルとして保存
for sheet in sheet_names:
df = xls.parse(sheet)
df.to_sql(sheet, conn, if_exists="replace", index=False)
conn.commit()
# LangChain SQL Agent セットアップ
db = SQLDatabase.from_uri(f"sqlite:///{temp_db.name}")
llm = AzureChatOpenAI(
deployment_name=os.getenv("OPENAI_DEPLOYMENT_NAME"),
openai_api_key=os.getenv("OPENAI_API_KEY"),
openai_api_base=os.getenv("OPENAI_API_BASE"),
openai_api_version=os.getenv("OPENAI_API_VERSION"),
openai_api_type="azure",
temperature=0,
)
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
agent_executor = create_sql_agent(llm=llm, toolkit=toolkit, verbose=True)
# ユーザー入力
user_input = st.chat_input("💬 Excelの内容について質問してください")
if user_input:
# 入力を表示
st.chat_message("user").markdown(user_input)
st.session_state.messages.append({"role": "user", "content": user_input})
# LLMによる回答生成
with st.chat_message("assistant"):
with st.spinner("考え中..."):
try:
response = agent_executor.run(user_input)
except Exception as e:
response = f"⚠️ エラーが発生しました: {str(e)}"
st.markdown(response)
# チャット履歴に追加
st.session_state.messages.append({"role": "assistant", "content": response})
# チャット履歴リセット
if st.button("🔄 チャット履歴をリセット"):
st.session_state.messages = [
{"role": "system", "content": "あなたは親切なAIアシスタントです。Excelの内容について答えてください。"}
]
st.experimental_rerun()
🔍 このアプリの特徴
- ユーザーはExcelファイル(複数シート可)をアップロード。
- 各シートをSQLiteデータベースのテーブルとして保存。
- LangChainのSQL Agentを使って自然言語 → SQL → 結果を回答。
- SQLの自動生成&実行は、LLMが判断して行います。
✅ 必要なパッケージ(未導入ならインストール)
pip install streamlit pandas openpyxl sqlite3 langchain
Azure OpenAI用の設定が .env
に含まれていることを前提としています(例:OPENAI_API_KEY
など)。
拡張もできます:
- 質問によってSQLにするか、単純な文書理解にするかを自動判別
- Streamlit側でSQLの内容を明示的に表示 or 編集
- SQLの結果をテーブルとして表示
よかったらこのコードをもとに発展させていきましょうか?質問への正確さを増す工夫とか、自然言語→SQL変換を補強する工夫もできます。
user:
ありがとうございます。まずは、ユーザに入力させるエクセルファイルを作るPythonコードを書いてください。
assistant:
了解です!まずは、ユーザーがアップロードすることを想定したExcelファイルを自動生成するPythonコードを作成します。以下のコードは、複数のシートを持つExcelファイルを作成し、それぞれにテーブルデータを保存します。
import pandas as pd
# データ例(売上データと顧客情報)
sales_data = pd.DataFrame({
"OrderID": [1001, 1002, 1003, 1004],
"CustomerID": [1, 2, 1, 3],
"Product": ["Laptop", "Tablet", "Monitor", "Laptop"],
"Amount": [120000, 40000, 30000, 125000],
"Date": pd.to_datetime(["2024-10-01", "2024-10-03", "2024-10-04", "2024-10-05"])
})
customer_data = pd.DataFrame({
"CustomerID": [1, 2, 3],
"Name": ["Tanaka", "Suzuki", "Yamada"],
"Region": ["Tokyo", "Osaka", "Nagoya"]
})
# Excelファイルとして保存(シートごとにテーブルを分ける)
with pd.ExcelWriter("sample_data.xlsx", engine="openpyxl") as writer:
sales_data.to_excel(writer, sheet_name="Sales", index=False)
customer_data.to_excel(writer, sheet_name="Customers", index=False)
print("✅ sample_data.xlsx を作成しました。")
📄 出力されるExcelファイル(sample_data.xlsx
)
-
Sales
シート:売上データ(注文ID、顧客ID、商品名、金額、日付) -
Customers
シート:顧客情報(顧客ID、名前、地域)
💡 こんな質問ができるようになります
- 「東京の顧客の売上合計は?」
- 「Laptopの平均購入金額は?」
- 「2024年10月3日に購入した顧客の名前は?」
このファイルをアプリに入力すれば、自然言語→SQL変換→回答までできるようになります。
必要ならCSVや別フォーマットも生成できますので教えてください!
user:
「CustomerID2番の人の名前と、何を買ったのか答えてください。」と聞いたら、次のようなエラーになりました。
エラーが発生しました: An output parsing error occurred. In order to pass this error back to the agent and have it try again, pass handle_parsing_errors=True to the AgentExecutor. This is the error: Could not parse LLM output: I now know the final answer. The customer with CustomerID 2 is named Suzuki, and they bought a Tablet. For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE
assistant:
ナイスなテスト質問ですね!そして、そのエラーは LangChain の SQL Agent ではよくある「出力パース失敗(OUTPUT_PARSING_FAILURE)」の一種です。
🔍 エラーの意味
LangChainの create_sql_agent()
で作成されたエージェントは、LLMの出力を**構造化された形式(Intermediate Steps を含む JSON)**としてパースしようとしますが、LLMが「普通の自然言語」だけで回答してしまうと、LangChain側がそれをうまく処理できずエラーになります。
今回のエラー:
Could not parse LLM output: I now know the final answer...
はまさにそれです。
✅ 解決法 ①:handle_parsing_errors=True
を追加する
create_sql_agent()
に渡す AgentExecutor
に以下のようにオプションを追加すれば、失敗してもリトライしてくれるようになります:
agent_executor = create_sql_agent(
llm=llm,
toolkit=toolkit,
verbose=True,
handle_parsing_errors=True # ← これを追加!
)
これで、多くの場合は再実行して自動的に修正された回答を返してくれるようになります。
✅ 解決法 ②:LLMのプロンプトをチューニングする(本質的な対応)
LangChainのデフォルトプロンプトは時々曖昧です。必要であれば、以下のように出力形式を厳密に指定するカスタムプロンプトを作って、それを create_sql_agent()
に渡す方法もあります。
from langchain.agents.agent_toolkits.sql.prompt import SQL_PREFIX, SQL_SUFFIX
custom_prompt = SQL_PREFIX + "\n" + SQL_SUFFIX
# または、自作の stricter version を作る
✅ 解決法 ③:output_parser
をカスタムで定義する
ちょっと高度な方法ですが、LLMの出力パースをカスタムで実装して「自然文も許容する」ように書き換えることも可能です(必要ならサンプル出します)。
✨ おすすめの対応
まずは一番手軽で効果的な handle_parsing_errors=True
を入れるのがオススメです。
🔧 修正コード例(該当部分)
agent_executor = create_sql_agent(
llm=llm,
toolkit=toolkit,
verbose=True,
handle_parsing_errors=True # これを追加!
)
これでうまくいくか、一度再度質問をしてみてください!
もしそれでも同様のエラーが頻発するようなら、もう少し構成を見直す方向でご一緒しましょう。
user:
ありがとうございます。「CustomerID2番の人の名前と、何を買ったのか答えてください。」と聞いたら「CustomerID 2, named Suzuki, bought a Tablet.」と返してくれました。
assistant:
おおー!それはいい感じですね✨
「Suzuki さんが Tablet を買った」という、ちゃんと SQLによる結合+フィルタ を踏んだ回答になっていてバッチリです!
🎯 実際に裏側でやっていること(予想されるSQL)
おそらく以下のようなクエリが実行されています:
SELECT Customers.Name, Sales.Product
FROM Sales
JOIN Customers ON Sales.CustomerID = Customers.CustomerID
WHERE Sales.CustomerID = 2;
LangChainがLLMの力を借りて自動的にこういうSQLを生成し、実行して、自然文に変換してくれています。
✅ これでできること(今後の拡張のヒント)
こういう質問もいけます:
- 「Yamadaさんが買った商品の合計金額は?」
- 「最も高い商品を買った顧客の名前は?」
- 「2024年10月に購入があった商品は何か?」
さらに進化させるなら:
- 📊 グラフ出力(AltairやPlotly)
- 📈 Streamlitでフィルタ付きデータ可視化
- 🔄 チャット履歴に基づく文脈的な質問対応
- 📂 複数Excelファイルの一括読み込み+統合SQL処理
引き続き、追加したい機能や聞いてみたいことがあれば何でも言ってくださいね!
次は何やってみましょうか?😄
user:
ご提案いただいた質問と、それに対するチャットアプリの回答です。
Yamadaさんが買った商品の合計金額は?
→ Yamadaさんが買った商品の合計金額は125,000円です。
最も高い商品を買った顧客の名前は?
→ The name of the customer who bought the most expensive product is Yamada.
2024年10月に購入があった商品は何か?
→ The products purchased in October 2024 are Laptop, Monitor, and Tablet.
assistant:
素晴らしい!👏
その応答内容を見る限り、SQLの生成・実行・自然言語への変換まですべて正確に動作していることがわかります。これはかなり完成度の高い状態ですよ✨
🔍 各質問の裏で走っていると思われるSQL
🟡「Yamadaさんが買った商品の合計金額は?」
SELECT SUM(Sales.Amount)
FROM Sales
JOIN Customers ON Sales.CustomerID = Customers.CustomerID
WHERE Customers.Name = 'Yamada';
🟡「最も高い商品を買った顧客の名前は?」
SELECT Customers.Name
FROM Sales
JOIN Customers ON Sales.CustomerID = Customers.CustomerID
ORDER BY Sales.Amount DESC
LIMIT 1;
🟡「2024年10月に購入があった商品は何か?」
SELECT DISTINCT Product
FROM Sales
WHERE strftime('%Y-%m', Date) = '2024-10';
LangChain + SQL Agent がしっかり日付処理や JOIN をこなしていて、めちゃくちゃ良い仕上がりですね。
🔮 今後のおすすめ機能拡張(レベルアップしたい場合)
機能 | 説明 |
---|---|
📈 データの可視化(グラフ出力) | 例えば「月別売上を棒グラフで出して」と言うとAltairで描画 |
🧠 文脈を活かす多ターンチャット対応 | 「その人はどこに住んでる?」→「SuzukiさんはOsakaです」のような連続応答 |
🗂 複数ファイル対応 | 別のExcelも読み込んで「ファイルをまたいだ質問」が可能に |
🔐 認証機能 | ユーザーによって使えるデータや機能を制限(StreamlitのSecretsや認証) |
📝 ChatGPTへのSQL解説付き表示 | 「この答えはこのSQLで得られました」というログ表示 |
「グラフで見たい」「日本語でチャットし続けたい」「エラーに備えてチャット履歴残したい」などなど、やりたいことに合わせて拡張できます!
何か次にチャレンジしたい機能はありますか?それともデプロイや整理に進めましょうか?😎
user:
ありがとうございます。今日はここまででOKです。ブログに書きたいので、タイトル候補をいくつか提案していただけますか?
assistant:
ブログ記事のタイトル候補をいくつか提案しますね!以下のアイデアをご参考にしてみてください:
- 「Excelデータを活用!Azure OpenAIとSQLで質問応答できるStreamlitアプリを作成」
- 「PDFからExcelへ!SQLを使ったデータベースとのインタラクティブな会話アプリの作り方」
- 「ExcelデータをChatGPTで質問!SQLを駆使したインタラクティブアプリの構築方法」
- 「StreamlitとLangChainで作る、Excelを使ったデータ駆動型チャットアプリ」
- 「データベースを対話型で探索!Azure OpenAIとSQLを用いたExcelベースの質問応答アプリ」
どれも実装内容に基づいていますので、お好みに合わせて微調整も可能です。気になるタイトルがあれば教えてくださいね!
user:
「データベースを対話型で探索!Azure OpenAIとSQLを用いたExcelベースの質問応答アプリ」にしようと思います。ここまでのまとめを詳細に書いてください。
assistant:
素晴らしい選択ですね!タイトル「データベースを対話型で探索!Azure OpenAIとSQLを用いたExcelベースの質問応答アプリ」で、記事の内容を詳細にまとめます。以下がその構成案です。
目次
- はじめに
- 使用した技術スタック
- アプリの概要
-
アプリ構築の手順
- エクセルファイルの準備
- Azure OpenAIとSQL Agentの設定
- Excelデータを扱う流れ
- 質問応答機能の実装
- 実行例と出力
- 今後の拡張可能な機能
- まとめ
1. はじめに
本記事では、Excel形式のデータを使って、Azure OpenAIとSQLを駆使し、インタラクティブにデータベースを探索できるアプリの作成方法を紹介します。Streamlitを用いて、ユーザーが自然言語でデータにアクセスし、SQLクエリを実行して回答を得る仕組みを構築します。これにより、SQLに関する知識がなくても、直感的にデータを扱えるようになります。
2. 使用した技術スタック
- Azure OpenAI: Azure上のOpenAIを利用して、LLM(大規模言語モデル)を活用した自然言語処理機能を提供。
- SQL: データベースクエリを用いて、ユーザーの質問に基づくデータ抽出を実現。
- Streamlit: ユーザーインターフェースを作成し、アプリケーションのフロントエンドを構築。
- LangChain: OpenAI APIを使ってSQLエージェントを作成し、自然言語とSQLクエリを連携。
- Python: アプリケーション全体のロジックをPythonで実装。
3. アプリの概要
このアプリでは、エクセルファイルに格納されたデータを基に、ユーザーが質問を入力すると、アプリがその質問に合致するSQLクエリを生成して実行し、その結果を自然言語で返答します。これにより、ユーザーはデータベースの知識がなくても、Excelデータを活用した質問応答が可能になります。
4. アプリ構築の手順
4.1. エクセルファイルの準備
まず、アプリが利用するExcelファイルを準備します。このファイルには、複数のシート(例: 売上データ、顧客データ)が含まれています。各シートには、データがテーブル形式で格納されています。
import pandas as pd
# サンプルデータ(売上データと顧客データ)
sales_data = pd.DataFrame({
"OrderID": [1001, 1002, 1003, 1004],
"CustomerID": [1, 2, 1, 3],
"Product": ["Laptop", "Tablet", "Monitor", "Laptop"],
"Amount": [120000, 40000, 30000, 125000],
"Date": pd.to_datetime(["2024-10-01", "2024-10-03", "2024-10-04", "2024-10-05"])
})
customer_data = pd.DataFrame({
"CustomerID": [1, 2, 3],
"Name": ["Tanaka", "Suzuki", "Yamada"],
"Region": ["Tokyo", "Osaka", "Nagoya"]
})
# Excelファイルに保存
with pd.ExcelWriter("sample_data.xlsx", engine="openpyxl") as writer:
sales_data.to_excel(writer, sheet_name="Sales", index=False)
customer_data.to_excel(writer, sheet_name="Customers", index=False)
4.2. Azure OpenAIとSQL Agentの設定
次に、Azure OpenAIを利用したLLMと、SQLエージェントを作成します。LangChain
を活用して、ユーザーからの自然言語の質問に基づいて、SQLクエリを自動生成して実行するエージェントを設定します。
from langchain.chat_models import AzureChatOpenAI
from langchain.agents import create_sql_agent
from langchain.agents import Tool
# LLMの設定
llm = AzureChatOpenAI(
deployment_name="your_deployment_name",
openai_api_key="your_api_key",
openai_api_base="your_api_base",
)
# SQLエージェントの作成
sql_agent = create_sql_agent(
llm=llm,
toolkit=your_toolkit,
verbose=True,
handle_parsing_errors=True
)
4.3. Excelデータを扱う流れ
ユーザーがExcelファイルをアップロードすると、そのデータを読み込んでSQLクエリを実行できるようにします。pandas
ライブラリを使用して、Excelファイルを読み込んでDataFrameに変換します。
4.4. 質問応答機能の実装
ユーザーの質問がSQLクエリに変換され、その結果が返答として表示されます。例えば、「Yamadaさんが買った商品の合計金額は?」という質問があれば、次のようなSQLが実行されます。
SELECT SUM(Sales.Amount)
FROM Sales
JOIN Customers ON Sales.CustomerID = Customers.CustomerID
WHERE Customers.Name = 'Yamada';
5. 実行例と出力
ユーザーが次のような質問をすると、アプリはSQLクエリを生成し、実行結果を自然言語で返します。
質問 1:
「Yamadaさんが買った商品の合計金額は?」
出力:
「Yamadaさんが買った商品の合計金額は125,000円です。」
質問 2:
「最も高い商品を買った顧客の名前は?」
出力:
「The name of the customer who bought the most expensive product is Yamada.」
質問 3:
「2024年10月に購入があった商品は何か?」
出力:
「The products purchased in October 2024 are Laptop, Monitor, and Tablet.」
6. 今後の拡張可能な機能
- データの可視化: 購入金額の分布や月別売上をグラフで表示する機能。
- 複数ファイルのサポート: 異なる形式のExcelやCSVファイルを読み込む機能。
- チャット履歴の保存: ユーザーとの会話履歴を保存し、再利用可能にする機能。
- 複雑なクエリ対応: より複雑なSQLクエリのサポートや、グループ化・集計などの高度な処理。
7. まとめ
このアプリは、ユーザーが自然言語でExcelデータにアクセスし、SQLを利用して情報を抽出するインタラクティブな体験を提供します。Azure OpenAIとSQLエージェントを組み合わせることで、データ分析の敷居を下げ、誰でも簡単にデータベースを操作できる環境を作り上げることができます。今後も機能の拡張や改良を加え、さらに便利なアプリを目指していきましょう。
これで、ブログ記事がかなり詳細にまとめられると思います!
もし具体的に加えたい内容があれば、さらに調整しますので教えてくださいね。
user:
ありがとうございます。また次回お願いしますね!
assistant:
どういたしまして!記事の作成が順調に進むことを願っています。また次回、お手伝いできることがあればいつでもお声かけくださいね!お疲れさまでした!😊