Ollama+Langchainを使って軽量モデルLlama3.2で無料でWEBページの要約や翻訳、要約を元にした会話劇を作成する
Ollamaみなさん使ってますか。私は毎日つかっています。最近いかがわしい方法しか書いてなかったのでたまには真面目な活用方法をかいてみます。
ローカルLLMの利点とはなにか
OpenAIやClaudeが進化するなかでハイスペックGPUが必要なローカルLLMに疑問を持つ声もあがっていますが、私的には下記のような利点があるかと思います
無料!(OpenAI等のLLM AIプログラミングで数円でもかけるのは馬鹿らしい)
エロ規制が緩い(緩かったというのが正しい、最近は厳しいしいずれ使えなくなるがいずれにせよクラウド上での検閲はない)
機密情報が完全に担保(企業とかでは重要そう)
「エロ規制が緩い」部分に関してはこれまでいくつか記事を書いたので今回はこの「無料!」の部分の利点を活かしてOllama+Langchainを使ってWEBページの翻訳をする実例をつくっていきましょう。
使用するソフトなど
Ollama https://ollama.com/
LLMモデル Llama3.2 (2Gバイトサイズの軽量モデル) https://ollama.com/library/llama3.2
言語 Python
実行環境 Ollama(Windows) Python(WSL)
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
記事の内容とそんなに変わらない要約ができています。グレイト!
日本語に正式に対応しておらずまた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
完全に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/
かなり精度高くできています。おかしな内容は言っているように感じません。ただそれは私がある程度内容がわかってて検査できていることがポイントでもあるので、精度に関しては信頼せずなんらかのチェック機構は必要にかんじます。
サンプル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)
ページは先程と同じ記事
会話が期待した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)
それっぽいですが、やはり要約をしゃべっているだけですね。
ただ応用は聞きそうな光がみえてきました。
エロ方面でも応用がききそう。
サンプル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
会話劇っぽくなりました♪
改善をくわえていけばコメントは再現できそうです。
サンプルコードは下記にまとめています
まとめ
軽量モデルLlama3.2でここまでできるのには驚きました。低スペックPCでも数分で要約とコメント劇が再現できます。ということはラズパイとかでも実現できるでしょう。要約、翻訳といった大量に行う作業は無料APIであるローカルLLMをバシバシ活用していきましょう。ただし精度は荒いですし自分用や社内用だといいと思いますが、外部のお客さんにだす場合などは精密なチェックは必要かと思います。
今後もこれ系で応用がきく方法を考えてみたいと思います。