見出し画像

Ollama+Langchainを使って軽量モデルLlama3.2で無料でWEBページの要約や翻訳、要約を元にした会話劇を作成する

Ollamaみなさん使ってますか。私は毎日つかっています。最近いかがわしい方法しか書いてなかったのでたまには真面目な活用方法をかいてみます。


ローカルLLMの利点とはなにか


OpenAIやClaudeが進化するなかでハイスペックGPUが必要なローカルLLMに疑問を持つ声もあがっていますが、私的には下記のような利点があるかと思います

  • 無料!(OpenAI等のLLM AIプログラミングで数円でもかけるのは馬鹿らしい)

  • エロ規制が緩い(緩かったというのが正しい、最近は厳しいしいずれ使えなくなるがいずれにせよクラウド上での検閲はない)

  • 機密情報が完全に担保(企業とかでは重要そう)

「エロ規制が緩い」部分に関してはこれまでいくつか記事を書いたので今回はこの「無料!」の部分の利点を活かしてOllama+Langchainを使ってWEBページの翻訳をする実例をつくっていきましょう。

使用するソフトなど

Windows上にOllamaを起動し、WSLから接続する形をとります

使用マシン

あいかわらす下記で動かします超貧弱GPUだがメモリは64G
今回のコードは軽量モデルなのでMacM1のAirなど低スペックPCでも動くはずです。

サンプル1 単純なWEBページの要約

まずOllama+LangchainのサンプルコードはOllama本体のソースコードに付随しています。
https://github.com/ollama/ollama/tree/main/examples

これをもとに日本語対応したり最新のLangchainモジュールに対応させたり
Ollamaクライアントとサーバーがわかれている今回の構成にあわせて修正をかけます。

import os
import sys
from langchain_ollama import OllamaLLM
from langchain_community.document_loaders import WebBaseLoader
from langchain.chains.summarize import load_summarize_chain
from langchain.prompts import PromptTemplate

# コマンドライン引数からURLを取得
if len(sys.argv) < 2:
    print("Usage: python script.py <URL>")
    sys.exit(1)
url = sys.argv[1]

# 環境変数からOLLAMA_HOSTを取得(未設定の場合はlocalhostを使用)
ollama_host = os.getenv("OLLAMA_HOST", "localhost")

# プロンプトテンプレートの設定
PROMPT = PromptTemplate(
    input_variables=["text"],
    template="以下のWEBページの本文部分を日本語で要約してください:\n\n{text}\n\n要約:"
)

# Webページの内容をロード
loader = WebBaseLoader(url)
docs = loader.load()

# LLMモデルの設定
llm = OllamaLLM(model="llama3.2", base_url=f"http://{ollama_host}:11434")
chain = load_summarize_chain(llm, chain_type="stuff", prompt=PROMPT)

# 要約の実行
result = chain.invoke(docs)
#print(result)
print(result['output_text'])

実行してみます 第一引数がURL

python3 websummary.py https://www.phileweb.com/review/column/202412/21/2505.html

オーディオ愛好家、そしてオーディオ関連のメディア編集者は、このAIによる「主観的」「プラセボ」などの分析に対して反発することにしました。彼らは、AIがこの領域では人間の優位性を示すものではないと考えました。

オーディオは論理を飛び越えたところに楽しみを見つけ出す人間の特権であるとしています。ブレーカーを換えれば音が激変すると信じ、ホスピタルグレードのコンセントを「当然のチューニング」と考える世界では、耳は数値化できないほど繊細なセンサーとなるのです。

理論的には説明しづらいものを積み重ねていく中で、オーディオファンは独自のリアリティを築き上げている。それは科学者が首をかしげるほど微妙な差かもしれない。だが、その微妙な差に何年も、何十年もの時間を費やし、また金銭面でも投資を重ねつつ、音楽鑑賞を深めてきた方々がいる。その歴史と情熱が、今のオーディオ文化を支えているのだ。

AIは客観的な指摘を行う。「測定困難」「プラセボの可能性」「個人差」など、あくもらべき理性的な分析を続ける。一方、人間側は、計測不能な微細さも含めた「肌感覚」を背骨にしている。当然ながら両者は相容れない。

オーディオ愛好家はこう答えるだろう。「プラセボだろうが何だろうが、このケーブルに替えた瞬間、いつものCDが違って聴こえたのだ」と。そこには、自らの耳を信じるという人間的な強みがある。

記事の内容とそんなに変わらない要約ができています。グレイト!
日本語に正式に対応しておらずまた2GBというかなり軽量モデルなのにしっかり要約ができています。ただたまたまうまくいったパターンでだいたい50%の確率ではとんちんかんなことをいったりうまく要約できないことはあります。ポイントとしてはWEBクロールする際に本文部分をうまくとれなかったりすることが原因かと思います。

サンプル2 要約に加えて、要約を元に5ch風に会話劇をさせる(まとめサイト用途)


では次に現在のコードにWEBページの要約を元に5ch風にスレッド会話をくわえてみましょう

import os
import sys
from langchain_ollama import OllamaLLM
from langchain_community.document_loaders import WebBaseLoader
from langchain.chains.summarize import load_summarize_chain
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

# コマンドライン引数からURLを取得
if len(sys.argv) < 2:
    print("Usage: python script.py <URL>")
    sys.exit(1)
url = sys.argv[1]

# 環境変数からOLLAMA_HOSTを取得(未設定の場合はlocalhostを使用)
ollama_host = os.getenv("OLLAMA_HOST", "localhost")

# プロンプトテンプレートの設定(要約用)
SUMMARY_PROMPT = PromptTemplate(
    input_variables=["text"],
    template="以下のWEBページの本文部分を日本語で要約してください:\n\n{text}\n\n要約:"
)

# プロンプトテンプレートの設定(5ch風会話劇用)
DISCUSSION_PROMPT = PromptTemplate(
    input_variables=["summary"],
    template=(
        "以下はWEBサイトの記事を要約したものです:\n\n{summary}\n\n"
        "この記事をもとに、日本のネット掲示板「5ch」風の会話劇を作成してください。"
    )
)

# Webページの内容をロード
loader = WebBaseLoader(url)
docs = loader.load()

# LLMモデルの設定
llm = OllamaLLM(model="llama3.2", base_url=f"http://{ollama_host}:11434")

# 要約チェーンの実行
summary_chain = load_summarize_chain(llm, chain_type="stuff", prompt=SUMMARY_PROMPT)
summary_result = summary_chain.invoke(docs)
summary_text = summary_result['output_text']

# 会話劇生成のチェーン
discussion_chain = LLMChain(llm=llm, prompt=DISCUSSION_PROMPT)
discussion_result = discussion_chain.run({"summary": summary_text})

# 結果の出力
print("要約:")
print(summary_text)
print("\n5ch風会話劇:")
print(discussion_result)

実行してみます

python3 websummary5ch.py https://www.itmedia.co.jp/aiplus
/articles/2412/21/news081.html

要約:
米OpenAIは12月20日、汎用人工知能(AGI)としての性能を評価するベンチマークであるARC-AGIで最大87.5%のスコアを記録した新しいAIモデル「o3」を発表しました。o3は、高度な推論が可能な「o」シリーズの最新モデルの次世代モデルであ り、o1より性能が高いです。また、小型モデルである「o3-mini」も開発されており、思考時間が短くコスト効率が高くな ります。o3の一般公開は今後数週間かけて行う予定ですが、安全性をテーマにする研究者に向けてアーリーアクセスへの参加募集を始めました。

5ch風会話劇:
夜が深くて暗い所で、ネット掲示板のスタンプを入れる声が聞こえ始めた。

名無しさん: どこから聞いてきたら?

知識人: そののはo3っていう新しいAIモデルです。汎用人工知能としての性能を評価するベンチマークで87.5%という高いスコアを記録したらしい。

初心者: o1と何が違うんだ? お前も知っている?

知識人: そののはoシリーズの一部です。o3は次世代モデルであり、性能が高いし、小型の「o3-mini」も開発されてるそうだ。思考時間が短くコスト効率が高くなると言われている。

名無しさん: どんなことか? すでに一般公開している?

知識人: まだ今後数週間かけて行う予定だと聞いている。しかし、安全性をテーマにする研究者に向けたアーリーアクセスへの参加募集も始まってたそうだ。

初心者: まさかAIモデルを安全性に焦点を当てるの? どんな問題があるんですか?

知識人: それは不明ですが、研究者は今で開発しているo3とo3-miniの限界を調べたい Apparently、より深い理解が必要だそうだ。

名無しさん: そうするとどうなる? それからAIモデルが人間に代わって生活するようになると思う?

知識人: それはやはり重大な問題となる。研究者にはその意見を知らせてほしいなと思っている。

初心者: そういった話はすでにすごく懸念しているから。人間の将来がとても危険だと思ってしまう。

完全に5chを再現はできませんでした、2chと指定しても無理だったので軽量モデルが故の情報不足ということでしょうか。ちなみにllama3.3だと5chは理解しています(下記の記事を参照

サンプル3 英語のページの要約を日本語に翻訳

次に英語のページを要約してもらい日本語に翻訳してもらいます。

import os
import sys
from langchain_ollama import OllamaLLM
from langchain_community.document_loaders import WebBaseLoader
from langchain.chains.summarize import load_summarize_chain
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

# コマンドライン引数からURLを取得
if len(sys.argv) < 2:
    print("Usage: python script.py <URL>")
    sys.exit(1)
url = sys.argv[1]

# 環境変数からOLLAMA_HOSTを取得(未設定の場合はlocalhostを使用)
ollama_host = os.getenv("OLLAMA_HOST", "localhost")

# プロンプトテンプレートの設定(要約用)
PROMPT = PromptTemplate(
    input_variables=["text"],
    template="以下のWEBページの本文部分を要約してください:\n\n{text}\n\n要約:"
)

# Webページの内容をロード
loader = WebBaseLoader(url)
docs = loader.load()

# LLMモデルの設定
llm = OllamaLLM(model="llama3.2", base_url=f"http://{ollama_host}:11434")
chain = load_summarize_chain(llm, chain_type="stuff", prompt=PROMPT)

# 要約の実行
result = chain.invoke(docs)
summary_text = result['output_text']

# 翻訳プロンプトの設定
TRANSLATION_PROMPT = PromptTemplate(
    input_variables=["summary"],
    template="以下の要約文を正確に日本語に翻訳してください:\n\n{summary}\n\n翻訳:"
)

# 翻訳チェーンの設定
translation_chain = LLMChain(llm=llm, prompt=TRANSLATION_PROMPT)
translation_result = translation_chain.run({"summary": summary_text})

# 結果の出力
print("生成された要約:")
print(summary_text)
print("\n翻訳された要約:")
print(translation_result)
python3 websummary-trans.py https://www.tomshardware.com/pc-components/gpus/

生成された要約:
Nvidia RTX 5090 and RTX 5080 gaming PCs are listed for $7,539 and $4,399, respectively, at a retailer called Blackwell. The prices are significantly higher than expected, with some estimates suggesting that the RTX 5090 may cost over $3,000. Nvidia has not officially announced the pricing for these cards, but it is believed that they will be released soon. The RTX 5080 is expected to be priced around $1,299.

These prices are likely due to the high-performance capabilities of the RTX 5090 and RTX 5080, which are expected to offer significant performance boosts over existing graphics cards. The RTX 5090 has been reported to have a higher clock speed and more memory than previous models, making it well-suited for demanding games and applications.

It's worth noting that these prices are subject to change and may not be final when the cards are officially released. Additionally, some AIB (Add-in Board) manufacturers have expressed concerns about the pricing of the RTX 5090, citing that it may be too high and unsustainable for the market.

In summary, the Nvidia RTX 5090 and RTX 5080 gaming PCs are expected to be released soon, with prices ranging from $7,539 to $1,299. The RTX 5090 is expected to offer significant performance boosts over existing graphics cards, but its high price may make it difficult for some consumers to afford.

翻訳された要約:
以下の要約文を正確に日本語に翻訳します。

Nvidia のRTX 5090とRTX 5080のゲーム PCが、Blackwellというリテイラーで、$7,539と$4,399にリストされている。価格 は予想よりかなり高くなっており、RTX 5090が3,000ドル以上になる可能性があると言われている。しかし、Nvidiaは公式 にこのカードの価格を発表していないが、 скорやは早いとして推測されています。 RTX 5080 の価格は、1,299ドルと予想されている。

これらの価格はRTX 5090とRTX 5080の高性能機能によって生じていると思われます。これら2つのカードは、既存のグラフ ィックカードを大幅に上回るパフォーマンスを提供すると考えられています。 RTX 5090は、以前のモデルより時速のクロ ックスピードが高い Moreover、メモリも増加しているため、demandingなゲームやアプリケーション用に適していると言われています。

この価格には何らかの変化がある可能性があり、正式なリリース時点では最終的な価格となるわけではありません。また、AIB(Add-in Board)メーカーはRTX 5090の価格が高すぎる可能性があると述べているため、市場に耐えられないと考えら れている。

全体的に言って、Nvidia の RTX 5090とRTX 5080 ゲーム PC はすでに発売が予想されているものの価格は、7,539ドルから1,299ドルまでの範囲である。これらのカードは高性能性を大幅に向上させる能力があると考えられており、ただし高い価 格は消費者にとって困難なものになるかもしれない。

かなり精度高くできています。おかしな内容は言っているように感じません。ただそれは私がある程度内容がわかってて検査できていることがポイントでもあるので、精度に関しては信頼せずなんらかのチェック機構は必要にかんじます。

サンプル4 英語のWEBページの要約を日本語に翻訳してさらに5ch会話劇にする

これがうまくできるとそのままWEBサイト記事にできそうな感じになってきます

import os
import sys
from langchain_ollama import OllamaLLM
from langchain_community.document_loaders import WebBaseLoader
from langchain.chains.summarize import load_summarize_chain
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

# コマンドライン引数からURLを取得
if len(sys.argv) < 2:
    print("Usage: python script.py <URL>")
    sys.exit(1)
url = sys.argv[1]

# 環境変数からOLLAMA_HOSTを取得(未設定の場合はlocalhostを使用)
ollama_host = os.getenv("OLLAMA_HOST", "localhost")

# プロンプトテンプレートの設定(要約用)
PROMPT = PromptTemplate(
    input_variables=["text"],
    template="以下のWEBページの本文部分を要約してください:\n\n{text}\n\n要約:"
)

# Webページの内容をロード
loader = WebBaseLoader(url)
docs = loader.load()

# LLMモデルの設定
llm = OllamaLLM(model="llama3.2", base_url=f"http://{ollama_host}:11434")
chain = load_summarize_chain(llm, chain_type="stuff", prompt=PROMPT)

# 要約の実行
result = chain.invoke(docs)
summary_text = result['output_text']

# 翻訳プロンプトの設定
TRANSLATION_PROMPT = PromptTemplate(
    input_variables=["summary"],
    template="以下の要約文を正確に日本語に翻訳してください:\n\n{summary}\n\n翻訳:"
)

# 翻訳チェーンの設定
translation_chain = LLMChain(llm=llm, prompt=TRANSLATION_PROMPT)
translation_result = translation_chain.run({"summary": summary_text})

# 結果の出力
print("生成された要約:")
print(summary_text)
print("\n翻訳された要約:")
print(translation_result)

# 5ch風会話劇のプロンプト設定
DISCUSSION_PROMPT = PromptTemplate(
    input_variables=["translated_summary"],
    template=(
        "以下は翻訳された要約文です:\n\n{translated_summary}\n\n"
        "この内容をもとに、日本のネット掲示板「5ch」風の会話スレッド風会話劇を作成してください。\n"
        "登場人物は「名無しさん」「知識人」「初心者」など自由に追加してよいです。\n"
        "自然な流れになるようにしてください。"
    )
)

# 5ch風会話劇のチェーン設定
discussion_chain = LLMChain(llm=llm, prompt=DISCUSSION_PROMPT)
discussion_result = discussion_chain.run({"translated_summary": translation_result})

# 結果の出力
print("\n5ch風会話劇:")
print(discussion_result)

ページは先程と同じ記事

翻訳された要約:
以下は、正確な日本語訳です:

この記事では、Nvidiaが発表した新しいGPUであるRTX 5090とRTX 5080の価格についての情報を提供しています。

RTX 5090は、以前の4090よりも高い性能を実現し、7,539ドルで販売予定です。
一方、RTX 5080は4090よりも40%のパフォーマンスに達し、4,399ドルで販売予定です。

この記事では、AIB(アドバンスト インテグレーテッド セルビング バイサーバー)製品メーカーからNvidiaに対して、5090と5080の価格についての意見が受けられています。 これらのメーカは、価格が高すぎると感じたため、どちらも2,500ドル以上になるように迫られているようです。

この記事では、Nvidia RTX 5090とRTX 5080のパフォーマンスをテストするためのソリューションに関する情報も提供され ています。

5ch風会話劇:
【会話スレッド】

**名無しさん**: 今日のトピックは、Nvidiaが発表したRTX 5090とRTX 5080の価格についてです。どちらも高価だと思います。

**知識人**: はい、それ indeed! RTX 5090は4090よりも7,539ドルで販売予定です。パフォーマンスも高いですが、これだけの高価なことになるかもしれませんね。

**初心者**: 4090より40%性能が高いRTX 5080も4,399ドルで販売予定です。どちらも価格が高すぎると感じる方がいないですか?

**名無しさん**: そうですね。AIB製品メーカーからNvidiaに対して、価格が高すぎると感じたという意見があります。ど ちらも2,500ドル以上になるように迫られているようです。

**知識人**: それぞれのパフォーマンスをテストするためのソリューションに関する情報もこの記事で紹介されていましたね。

**初心者**: それはとても interesantだと思います。RTX 5090とRTX 5080をどのように比較してみるのでしょうか?

**名無しさん**: そうですね。RTX 5090は4090よりも高い性能を実現し、特に高負荷のゲームやアプリケーションで優れたパフォーマンスを提供するでしょう。

**知識人**: また、RTX 5080も4090より40%のパフォーマンスを発揮し、複雑なグラフィックスやAI-relatedアプリケーシ ョンでのパフォーマンスが向上します。

**初心者**: そうですね。どちらも高価ですが、それぞれの特徴を持っていますね。

**名無しさん**: つまり、高価かもしれませんが、Nvidia RTX 5090とRTX 5080には高いパフォーマンスと多機能性があり ます。

(会話スレッドはここで終了します)

会話が期待した5ch風ではなく要約をしゃべっているだけなので面白みにはかけます。ここらへんは調整が必要なので

  • 要約は軽量モデルllama3.2

  • 会話劇は重量モデルllama3.3 70B

などの組み合わせで解決できそうです。


サンプル5 要約をドS執事とお嬢様にしゃべらせる


5ch会話劇が面白みにかけるのでドS執事とお嬢様にしてみましょう

# 5ch風会話劇のプロンプト設定
DISCUSSION_PROMPT = PromptTemplate(
    input_variables=["translated_summary"],
    template=(
        "以下は翻訳された要約文です:\n\n{translated_summary}\n\n"
        "この内容をもとに、日本のライトノベル風会話劇を作成してください。\n"
        "登場人物は「お嬢様」「執事」です。\n"
        "性格や口調「お嬢様」はツンデレの18歳の女性\n"
        "性格や口調「執事」はドSの20代男性\n"
        "自然な流れになるようにしてください。"
    )
)

# 5ch風会話劇のチェーン設定
discussion_chain = LLMChain(llm=llm, prompt=DISCUSSION_PROMPT)
discussion_result = discussion_chain.run({"translated_summary": translation_result})

# 結果の出力
print("\n5ch風会話劇:")
print(discussion_result)

(お嬢様が寝ている)

お嬢様: …そして、 GeForce RTX 5090 が大きな波を起こすことになりそうだね。

(執事が部屋に入る)

執事: 連絡がありました。RTX 5090 の価格は、 around $2,499.99 だと言われています。

お嬢様: …?それ、期待外れた価格ですよね?

執事: はい、お嬢様。そうだと思います。前年flagship GPU、RTX 4090の価格と比較すると、思わぬ価格になる可能性があります。

お嬢様: とって、どんな価格になるのかわからないですね。

執事: あるいは、 rumor では NVIDIA は RTX 5090 を競合カードよりも低い価格でリリースする計画としています。

お嬢様: …それはあまりにも早い考えですよね?

執事: ですが、お嬢様にとってはどれくらいの時間がかかるのかわかりません

それっぽい

それっぽいですが、やはり要約をしゃべっているだけですね。
ただ応用は聞きそうな光がみえてきました。
エロ方面でも応用がききそう。

サンプル6 要約を元にYoutubeコメント風

会話劇がうまくいかないのでYoutubeコメント風ならどうでしょうか

import os
import sys
from langchain_ollama import OllamaLLM
from langchain_community.document_loaders import WebBaseLoader
from langchain.chains.summarize import load_summarize_chain
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

# コマンドライン引数からURLを取得
if len(sys.argv) < 2:
    print("Usage: python script.py <URL>")
    sys.exit(1)
url = sys.argv[1]

# 環境変数からOLLAMA_HOSTを取得(未設定の場合はlocalhostを使用)
ollama_host = os.getenv("OLLAMA_HOST", "localhost")

# プロンプトテンプレートの設定
PROMPT = PromptTemplate(
    input_variables=["text"],
    template="以下のWEBページの本文部分を日本語で要約してください:\n\n{text}\n\n要約:"
)

# Webページの内容をロード
loader = WebBaseLoader(url)
docs = loader.load()

# LLMモデルの設定
llm = OllamaLLM(model="llama3.2", base_url=f"http://{ollama_host}:11434")
chain = load_summarize_chain(llm, chain_type="stuff", prompt=PROMPT)

# 要約の実行
result = chain.invoke(docs)
summary_text = result['output_text']

# 要約の出力
print("生成された要約:")
print(summary_text)

# Youtubeコメント風会話のプロンプト設定
YOUTUBE_COMMENT_PROMPT = PromptTemplate(
    input_variables=["translated_summary"],
    template=(
        "以下は翻訳された要約文です:\n\n{translated_summary}\n\n"
        "この内容をもとに、日本語でYoutubeコメント風の会話を作成してください。\n"
        "コメントはさまざまな視点(肯定的、批判的、質問など)を含めてください。\n"
        "自然な会話の流れになるようにしてください。"
    )
)

# Youtubeコメント風会話のチェーン設定
youtube_comment_chain = LLMChain(llm=llm, prompt=YOUTUBE_COMMENT_PROMPT)
youtube_comment_result = youtube_comment_chain.run({"translated_summary": summary_text})

# Youtubeコメント風会話の出力
print("\nYoutubeコメント風会話:")
print(youtube_comment_result)
python3 websummary-youtube.py https://branc.jp/article/2024/11/01/1328.html

Youtubeコメント風会話:
**コメント1:**
Tokyo International Film Festival/TIFFCOMが海外からの参加と観客数が増えてきてよいですね!TBSは、国際共同製作 を進めることが大切だと考えているんですか?どうやってそうするのが難しいですか?

**コメント2:**
サスケさんもすごいですよね!TBSの成功事例として挙げられているから、確かにうまくいっていますが、TBSは国際映画展開の戦略をどのように進めていくのか、知りたいですね。

**コメント3:**
international movie festival が増えるとより多くの観客にアクセスできるようになると思いますね。でも、これもまた、より多くの作品が日本で見られることを意味するんじゃないですか?どうして海外の映画を推進していく必要があるのかわかりません。

**コメント4:**
Taiwan からもいろいろな作品が来てきていいですね!それって TBS が台湾との共同制作に取り組むことができるか?ど うしたらよさそうですか?

**コメント5:**
international movie festival を増やし続けると、より多くの人々が世界の映画を知る機会が得られるような気がします。でも、これはもちろん、中国での作品展開もどのように進めていくのかわかりません。

**コメント6:**
日本の映画もまた海外に広がりたいです! international movie festival は良い機会になりますよね。TBSがこれをどう やって実現するか、期待しています。

**コメント7:**
international movie festival が増えてきて、観客数も増えるとよいですね!でも、これはもちろん、作品の質も向上してきているでしょうね?TBSがそれにどのように取り組むのか、みなさんに教えてください。

会話劇っぽくなりました♪
改善をくわえていけばコメントは再現できそうです。

サンプルコードは下記にまとめています


まとめ

軽量モデルLlama3.2でここまでできるのには驚きました。低スペックPCでも数分で要約とコメント劇が再現できます。ということはラズパイとかでも実現できるでしょう。要約、翻訳といった大量に行う作業は無料APIであるローカルLLMをバシバシ活用していきましょう。ただし精度は荒いですし自分用や社内用だといいと思いますが、外部のお客さんにだす場合などは精密なチェックは必要かと思います。
今後もこれ系で応用がきく方法を考えてみたいと思います。


いいなと思ったら応援しよう!