見出し画像

AI元年! langChain+PDF (RAG)で製品比較

ChatGPTがAIの実利用が誰にでも可能なことを証明したあと、コスト面のブレイクスルーb1.58が出現したのが1年前。昨年中は質的な向上やAPIの低価格かも進みました。 今年はいよいよ「AI利用が当たり前」で「活用と進化が本格化」「するAI元年になると予想しています。
そんなわけで、進歩と利用急拡大が顕著なlangChainを年の瀬〜正月に遊んでみました。簡単にできるのでご興味ある方でまだの方は是非お試しください。

よかったこと

改めてRAGをコードとしていじってみて良かったと思ったのはLLMというモノの正体が実感できたことです。具体的には、生成AIと言われているものの本質が「新型検索エンジン」だということです。
本体は10行足らずの短いコードなので、逆にシンプルに理解が進みました。  
考えようによっては私達人間も知識を蓄えて、それを元に判断・反応をするので、人間を模倣した記憶機械に見せ方を工夫した「商品」ということなのでしょう。


LangChain

langChainはシーケンシャルチェーン、会話bot、RAG/検索QA、pdfベースのQ&A、複数のAI組み合わせシステムなど様々な方法で利用されていますが、検索すると一番熱そう(需要高そうな)PDF利用のシステムを実験してみました。

もちろん直接APIを叩くのもありなのですか、複数のAIサービスをフレームワークから呼び出すことでコードが小さくからも楽になるので複雑なことをするときには有用と思います。こんな記事もありますので一応。

(記事「AI エージェント」の誇大宣伝を信じているあなたはまったくのバカです。)


更新が盛んなlangChain

langChainハンズオン出だしでハマりかけたのがバージョン。
AI関連のなかでも出来立てホカホカ感のあるlangChainは現在v0.3.13。
短期間で3回バージョンアップされていまして、とくに0.1と0.2は互換性がありません。サンブルや文書を見るときにはv0.3(今の最新)であることを必ず確認しましょう。



LangChanについてざっくり

LangChainを使って製品カタログPDFをロードし、質問に基づいて適切な製品を提案するタスクを作成するには、以下の手順を参考にしてください:


1. 必要なライブラリのインストール

pip install langchain pypdf unstructured openai

2. 環境変数の設定

LangChainでOpenAI APIを使用する場合、APIキーを環境変数として設定します。

import os

os.environ["OPENAI_API_KEY"] = "your_openai_api_key"

3. PDFデータのロード

各PDFを分割してテキストデータを抽出します。
(ローダーの種類は最後に追記ました。)

from langchain.document_loaders import PyPDFLoader

# 複数PDFをロード
pdf_files = ["catalog1.pdf", "catalog2.pdf", "catalog3.pdf"]
documents = []
for pdf in pdf_files:
    loader = PyPDFLoader(pdf)
    documents.extend(loader.load_and_split())



4. テキストを埋め込みベクトルに変換

埋め込みモデルを使用して文書を検索可能な形式にします。

from langchain.vectorstores import FAISS 
from langchain.embeddings import OpenAIEmbeddings 

embeddings = OpenAIEmbeddings() 
vectorstore = FAISS.from_documents(documents, embeddings)

5. 質問への回答を生成

質問を投げかけて関連製品情報を検索します。

from langchain.chains import RetrievalQA
from langchain.llms import OpenAI

qa_chain = RetrievalQA.from_chain_type(
    llm=OpenAI(),
    retriever=vectorstore.as_retriever()
)

# 質問に対する回答
question = "どのカタログに〇〇製品がありますか?"
response = qa_chain.run(question)

print(response)

6. 各PDFの提案を整理

各カタログごとに提案を分けて出力する処理を追加します。

for pdf in pdf_files:
    loader = PyPDFLoader(pdf)
    doc = loader.load()
    retriever = FAISS.from_documents(doc, embeddings).as_retriever()
    local_qa_chain = RetrievalQA.from_chain_type(llm=OpenAI(), retriever=retriever)
    result = local_qa_chain.run(question)
    print(f"{pdf} の結果: {result or '該当する製品はありません'}")

補足

  • PDFの内容が複雑な場合、unstructuredライブラリを使ってPDFをより適切に処理できます。

  • 質問の意図を明確にするため、プロンプトエンジニアリングを工夫してください。



簡単にできるのはわかったのでモチベーションを上げるために、任意の文書をロードできると何が幸せなのか聞いてみます。

お答:LangChainを用いたPDFカタログ検索システムをマーケティングに活かす具体例

1. 顧客ニーズに応じた製品提案

  • 顧客からの「〇〇に適した製品はありますか?」という質問に基づき、カタログから適切な製品を自動提案。

  • 顧客の購買意欲を高め、コンバージョン率を向上。


2. カタログ内製品のクロスセル提案 

  • 顧客が興味を示した製品に関連するアクセサリやサービスをカタログ内から提案。

  • 単価向上に貢献。


3. 営業チーム向けサポートツール

  • 営業が現場で顧客の質問に瞬時に答えるためのツールとして活用。

  • カタログ全体を検索することで即時回答を提供し、商談成功率を向上。


4. カタログ間の比較提案 / 複数ソースの分析chat bot

  • 複数カタログの中から、同一カテゴリの製品を比較し、利点を強調。

  • 競合製品との差別化ポイントを明確化。


5. リードジェネレーションの効率化 /

  • ウェブサイトの問い合わせフォームに組み込み、顧客の質問に対するリアルタイム回答を提供。

  • 潜在顧客の関心度合いを高める。


6. サポートチームの自動化 / chatbot

  • 問い合わせ対応に使用し、製品仕様や適用例などを即座に回答。

  • サポートコスト削減と顧客満足度向上。


7. メールマーケティングのパーソナライズ / text & pdf

  • 顧客の過去の購入履歴や問い合わせ内容に基づき、カタログから適切な製品をメールで提案。

  • 開封率とクリック率の向上。


8. トレンド分析による新製品開発支援

  • 顧客からの質問や検索パターンを分析し、人気のある要望や未解決の課題を特定。

  • 新製品やサービスの開発に役立てる。


9. 展示会やイベントでの活用 / chatbot

  • 来場者が求める製品を瞬時に検索し、適切な提案を行うタブレット端末やチャットボットを提供。

  • 展示会でのリード獲得を効率化。


10. 多言語対応のカタログ提案 / pdf

  • 言語別のPDFカタログを登録し、世界中の顧客にローカライズされた製品情報を提供。

  • グローバルマーケティングの強化。

なるほど、有用なことがわかりました。じゃあやってみます(ヨッコラショ


やってみる

サンプルではpdfやwebページを与えて質問して、日本語で返答してくれる様に作りました。AIはchatgptを使っていて英語など外国語でも読んでくれるのが素晴らしいところです。

サンプルで使うLangChain RAGコンポーネント


Document loader:データソースからドキュメントを読み込み
Document transformer:ドキュメントに何らかの変換
Embedding model:ドキュメントをベクトル化
Vector store:ベクトル化したドキュメントの保存先
Retriever:入力のテキストと関連するドキュメントを検索

コードの概要

(app.py)

with tab1:
    uploaded_file1 = st.file_uploader("Choose a .pdf file", "pdf", key="file1")
    uploaded_file2 = st.file_uploader("Choose a .pdf file", "pdf", key="file2")
    uploaded_file3 = st.file_uploader("Choose a .pdf file", "pdf", key="file3")
    questionPdf = st.text_input("Enter your query:", key="questionPdf")
    numStringLimit = st.number_input("Max len after summary: ( 0 = no limitation )", min_value=0, step=10, key="limitPdf")

    # control events
    if st.button("Submit"):
        if uploaded_file1 is not None and questionPdf != "":
            with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
                tmp_file.write(uploaded_file1.getvalue())
                tmp_file_path = tmp_file.name

            loader_py = PyMuPDFLoader(tmp_file_path)
            docs = splitter(loader_py.load())           # splitter
            docs = [remove_ws(d) for d in docs]  # cleaning

            # uses OpenAI embeddings to build a retriever
            embeddings = OpenAIEmbeddings(api_key=api_key)
            # Creates the document retriever using docs and embeddings
            db = FAISS.from_documents(docs, embeddings)

            
            # Asking the retriever to do similarity search based on Query
            query = "Foreign Aid for Lowari Road Tunnel & Access Roads Project (2nd Revised )"
            answer = db.similarity_search(query)

            # Building the retriever
            retriever = db.as_retriever(search_kwargs={'k': 3})

            #questionPdf
            template = """
            You are a information retrieval AI. Format the retrieved information as a table or text

            Use only the context for your answers, do not make up information

            query: {query}

            {context} 
            """
            prompt = ChatPromptTemplate.from_template(template)
            model = ChatOpenAI(api_key =api_key )
            chain = (
                {"context":retriever,
                 "query":RunnablePassthrough()
                }
                |  prompt  | model | StrOutputParser()
            )
            res = chain.invoke(questionPdf)
            st.write(res)
        else:
            st.error("Please upload a document and enter a query!")
 

実行

streamlit と langchain + openaiAPI だけでチョイチョイと出来上がる感じですねー、便利便利

streamlit run app.py

実行結果

返答する文字数の指定

あっけなく動きます。動かす環境作る時間のほうが長かったかも(笑

複数のPDFから返答

langChain v0.20 と v0.3.0のページを読ませて比較をしています。

手軽に使えて便利すぎる〜!

AIエンジンやpdf loader などがあり、必要に応じて選べるようになっていて、今後もどんどん増えそうです。実行する組み合わせが膨大になって選択に迷うこともありそうなので、同じソースで複数のセッティングを比較するモードがあると面白そうです。 次は画像対応もやってみたいですね。gitリポジトリは(試行錯誤して荒れてるので)キレイにしたら公開するようにします。

自分はmac環境で試していますがこの本のやり方ですと自前サーバーやPC環境の設定無しでブラウザ上で作業体験できます。


注意点:コスト

下のグラフはOpenaiAPI のsettingsー>Usage で確認できるトークン利用量です。 普段使っているテキストベースで問い合わせをしているAPI利用のなかで突出しているのは、PDFや画像の解析で実験していたものです。
普段の日は数万トークンなのが600万トークンを超えています。
o4-miniでは僅かな金額ですが、実験段階で使用料の試算をして実践利用するかの判断は必須かと思います。


参考:

こちらの記事が最強でした。メンテされていて微妙な時期に買った本よりこっちのほうが情報が最新で助かりました。

こちらを参考に作りました。


Streamlit の使い方

デプロイ方法


おまけ:

用語

日本人が解説やコードを見ていて段々と「何だこりゃ?」となる理由の大半は英語。根底にある意味を汲み取れないことが原因なことが多いので軽くまとめました。

RAG(検索拡張生成) Retrieval-Augumented Generation

特定の情報ソースからの検索結果を元に回答を生成させる手法。
検索結果はIn-context learningを用いてプロンプトに組み込みAIに知識を提供します。 ChatGPTなど一般公開サービスは訓練時のデータが古かったり企業内のプライベートな情報を持っていないため、用途に合わせたデータをAIに学習させることで特定のニーズを満たしたり、鮮度の高い状態を作ります。

In-context learning (文脈内学習)

プロンプト内で外部知識や例示を与えてAIにそのばで学習させる手法。
たとえばWeb検索した結果をプロンプトに入れて、AIの学習した知識以外の情報で補完します。外部ソース(webサイトや社内ドキュメントなど)から回答を生成するchatbot、FAQシステムに応用可能。

Embedding (埋め込み)

AIシステムでは「情報の埋め込み」、の意味で使われます。
LLMでは自然言語の文や単語をベクトルに置き換えて数値化し、ベクトル空間に埋め込むことを意味します。この仕組みの応用でRAGも実現できます。
単語のベクトル化はたとえば、 king - man + woman = queen  といった形でコンピュータが意味の関連、類似性を計算できるようになります。

Multi-modal (マルチモーダル)

「AIへの入力情報が複数種類に対応していること」の意で使われます。
AIが利用する入力情報の種類を「モーダル」と呼びます。この種類が複数あることをマルチモーダルと呼びます。ChatGPTの利用は基本テキストですが、これに画像や音声、動画など違う種類の情報入力を利用できるAIがマルチモーダルです。

ハルシネーション

生成AIがつく「嘘」のこと。RAGシステムでは具体的な情報を与えるので信用してしまいがちですが100%は無い前提でプロンプトを生成したり、検証を行う必要があります。


ローダーについて

langchain.document_loader についてはこちらに詳細があります。

といっても、膨大なのでこのページは目次になってて、Web、PDF,Cloud,SNS,メッセンジャーアプリのAPI,開発ツール、ファイルの種類…とカテゴリーごとに対応しているリンクが並んでいます。

オンラインからロードするモジュールはたくさん列挙されています。対応ファイルは2つのリストがあり、Common File Typesという一般的に使われるファイルのリスト(CSV,JSON,HTML, Unstructuredという便利モジュールとディレクトリ指定用モジュール)があります。これ以外のファイルフォーマットを読みたい場合は、その下のAll document loadersという「他奥モジュールすべて」のリストを確認するとdocx,xlsxなど特定のアプリのフォーマット用モジュールが見つかるかもしれません。

LLMに検索させたい特定のファイル種類がわかっている場合は、まずこちらを確認してみてください。↓

もう1つのリストは langchain.langchain_community.document_leaders です。この中には markdownやsql , mongodb のモジュールなどがあります。
この2つのローダーを先に当たれば開発のビジョンを楽に見立てることができるかと思います。

生成AIはデータベースか?

ChatGPTに直球に聞くと、このような返答が得られました。


生成AI(例えばChatGPT)は、従来のデータベースとは異なる仕組みを持っていますが、情報を扱う観点ではデータベースの一形態として捉えることもできます。以下にその特徴とアプローチをわかりやすく解説します:

1. 従来のデータベースとの比較

従来のデータベース:
情報は構造化され、テーブルやフィールドに格納されます(例: SQLデータベース)。
明確なクエリ(SQL文)でデータを取得します。
データは事前に入力され、変更がなければ固定的です。

生成AI(ChatGPTなど):
情報は「モデル」として大量のデータから学習され、固定のテーブルにはありません。
自然言語(質問)で情報を取得し、結果を動的に生成します。
学習時のデータを基にしていますが、新しいデータを参照するには再学習が必要です(もしくは外部データを活用)。


2. 生成AIを「データベース」として捉えると

類似点:
情報(データ)が入力(学習)され、それに基づいて応答(検索結果)を返す点。
質問に応じて、必要な情報を抽出して提供する点。

相違点:
明確なデータ構造がない: 従来のデータベースは構造化データを扱いますが、生成AIは非構造化データ(文書や会話)を処理します。
柔軟性が高い: ChatGPTは曖昧な質問にも対応でき、組み合わせた情報を動的に生成します。
正確性に制限: 従来のデータベースは格納されたデータそのものを返すので正確性が高いですが、生成AIは推論を伴うため、誤りや不正確な応答もあり得ます。


3. 具体例で考える

従来のデータベースの場合:
質問:「2023年の売上データを教えて」
→ テーブルにアクセスして「売上データ」を直接返す。

生成AIの場合:
質問:「2023年の売上データを教えて」
→ データそのものを持たないが、質問の意図を理解して解説を生成。文脈やパターンから適切な回答を生成する。


4. 生成AIを補完的に利用する

生成AIをデータベースの代わりに完全に置き換えることはできませんが、以下のような形で補完的に利用できます:
自然言語インターフェース: データベースにクエリを投げる代わりに、生成AIを介して自然言語で情報を検索。
データの前処理や要約: 大量のデータを要約して提供。
非構造化データの活用: 文書や音声、画像などのデータから情報を抽出。

まとめ
生成AIは、従来のデータベースと異なり、固定データを持つのではなく、学習データを基にした「動的な情報生成ツール」として機能します。正確性や構造化データのニーズが高い場合には従来型のデータベースが適し、柔軟性や自然言語処理が求められる場面では生成AIが強みを発揮します。両者を補完的に利用することで、効率的な情報管理が可能です。


ステップアップ!

実際にやると「肝心の元になるデータが⋯」が壁になるような気がしています。こちらの記事を読むと理解が捗るかもです。

NOTEを徘徊したらたくさんためになる記事がありました。順次勉強したいと思います。


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

そんちゃー君
サポートありがとうございます😊 ベトナムにお越しの際はお声がけくださいね🌻