ベクトル化と文章生成にはgemini 2.0を用いました。
ベクトルデータベースとしてはコードだけで簡単に構築ができるchromaDBを、pdfを取得する際のフレームワークとしてはLangChainを用いました
5社の保険会社様のPDFを使用しました。
(病気に関する保険以外も入れました)
明治安田生命様
https://www.meijiyasuda.co.jp/find/list/beststyle/pdf/beststyle_guide.pdf
東京海上日動様
https://www.tokiomarine-nichido.co.jp/service/pdf/sick_shogai_pamphlet_210101.pdf
あいおいニッセイ同和損保様
https://www.aioinissaydowa.co.jp/personal/product/other/pdf/kokuryo.pdf
日新火災海上保険様
https://www.nisshinfire.co.jp/service/pdf/hatarakenaitoki2301.pdf
損保ジャパン様
https://www.sompo-japan.co.jp/-/media/SJNK/files/kinsurance/yakkan/karada2501.pdf?la=ja-JP
geminiのAPI KEYが必要になります。
gemini2.0は2025年2月時点では無料で使うことができました。
api_key=""
!pip install -U --quiet langchain-google-genai
!pip install langchain openai langchain_community
!pip install chromadb tiktoken
!pip install Spire.PDF
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain.chains import RetrievalQA
from google import genai
from langchain_google_genai import GoogleGenerativeAIEmbeddings
from langchain.vectorstores import Chroma
from spire.pdf import PdfDocument
from spire.pdf import PdfTextExtractOptions
from spire.pdf import PdfTextExtractor
import sqlite3
import pandas as pd
import os
os.environ['GOOGLE_API_KEY'] = api_key
Google Colabのフォルダに、参照させたいPDFをアップロードし、そのファイル名を file_name= の欄に記入してください。
コードを複数回実行することで、複数のPDFをベクトルデータベースに保存できます。
file_name="file.pdf"
pdf = PdfDocument()
#ここにファイル名を入力
pdf.LoadFromFile(file_name)
pdfから文章を抽出します。
#pdfから文章を抽出
for i in range(pdf.Pages.Count):
# ページを取得
page = pdf.Pages.get_Item(i)
# ページをパラメータとして渡してPdfTextExtractorのオブジェクトを作成
text_extractor = PdfTextExtractor(page)
# ページからテキストを抽出
texts = text_extractor.ExtractText(PdfTextExtractOptions())
# 抽出されたテキストを文字列オブジェクトに追加
docs = texts
pdf.Close()
テキストから空白、\n、\rを削除するコードと、長すぎるpdfを分割するコードです。なくても動きますが、以下必要なら確認ください。
docs=docs[71:].replace(" ", "")
docs=docs.replace("\n", "")
docs=docs.replace("\r", "")
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(separator=".",
chunk_size=6000,
chunk_overlap=15)
docs = text_splitter.split_text(docs)
#分割時についてしまう文字列を削除します。
cleaned_docs = []
for doc in docs:
cleaned_doc = doc.replace("PDFforPython.", "")
cleaned_doc = cleaned_doc.replace("EvaluationWarning:ThedocumentwascreatedwithSpire.PDFforPython.", "")
cleaned_docs.append(cleaned_doc)
docs=cleaned_docs
ベクトルデータベースを作成します。データベースがある場合はデータベースに追加で記述されます。
#ベクトル化方法を設定
embeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
chroma_store = Chroma.from_texts(
docs,
embedding=embeddings,
collection_name="chromedb"
)
print(len(docs))
for doc in docs:
print(doc)
まずはデータフレームを作り、それをsqlに変換します。
# データベース接続を作成(存在しない場合は作成されます)
conn = sqlite3.connect('data.db')
# データフレームを作成
data = {
'last_name': ["田中", "山田", "田中", "一ノ瀬"],
'first_name': ["太郎", "太郎", "次郎", "一郎"],
'age': [45, 45, 12, 111],
'information': ['15年前に歯牙腫を手術。既婚。子供2人', '健康。既存契約あり', '保険未加入', '要介護状態']
}
df = pd.DataFrame(data)
# データをSQLに変換し、データベースに挿入
df.to_sql('persons', conn, if_exists='replace', index=False)
# データベース接続を閉じる
conn.close()
データベースへの接続手順は以下の通りです。
text="田中太郎さん45歳に個人データを参照しておすすめの保険を教えて。"
保健に関する質問か、顧客の詳細データが必要か、の2点を判断させるコードです。
customer_information=" "
#保険の質問かどうか
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents=text+'これは保険に関する質問か、はいかいいえの一言だけで答えて')
if response.text.strip().lower() == "はい" :
print("保険に関する質問であると判断したため、関連する保険マニュアルを元に回答します。")
#詳細が必要な質問かどうか
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents=text+'これは個人データが追加で必要な質問か、はいかいいえの一言だけで答えて')
if response.text.strip().lower() == "はい" :
#文章から個人情報取得
print("データベースから顧客の詳細情報を取得します")
もし顧客情報が必要だとgeminiが判断した場合に、文章から名前と年齢を抜き取るコードです。
初めにjson形式を指定していますが、稀にjson形式が崩れるうえ、初めから名前だけを抽出することも難しいので、動作を分割させています。
生成する文章が短いため処理はすぐ終わります。
response_json = client.models.generate_content(model='gemini-2.0-flash-exp', contents='''以下の文章の中から顧客1人の名前と年齢を抽出してjson形式のみで返して。例{first_name='一郎',last_name='順規',age=22}'''+text)
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents='以下の文章からlast_nameを取得して一言で答えて'+str(response_json))
print("性 :",response.text.strip().lower())
last_name='"'+response.text.strip().lower()+'"'
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents='以下の文章から1人目のfirst_nameを取得して一言で答えて'+str(response_json))
print("名 :",response.text.strip().lower())
first_name='"'+response.text.strip().lower()+'"'
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents='以下の文章からageを取得してint型で答えて、取得できないならnullで'+str(response_json))
print("年齢 :",response.text.strip().lower())
age='"'+response.text.strip().lower()+'"'
取得した名前と年齢を元にデータベースから情報を取得するコードです。
# データベース接続
conn = sqlite3.connect('data.db')
cursor = conn.cursor()
#情報取得
try:
query = f"SELECT information FROM persons WHERE last_name = {last_name} AND first_name = {first_name} AND age = {age}"
cursor.execute(query)
customer_information = cursor.fetchone()
except:
print("顧客情報を取得できませんでした。")
# データベース接続を閉じる
conn.close()
text= text + str(customer_information)+" この情報のみの時に最適な答えを出して"
else:
else:
print("顧客の詳細情報取得は必要ないと判断しました。")
入力した質問と顧客情報をベクトル化し、ベクトル類似上位4つの文章を参照しながら応答するシステムを作成します。
search_kwargs={"k": 4}をsearch_kwargs={"k": 1}に変えると類似度上位1つを参照するようになります。
embeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
qa = RetrievalQA.from_chain_type(
llm=ChatGoogleGenerativeAI(model='gemini-2.0-flash-exp',temperature=0.7, top_p=0.85),
chain_type="stuff",
retriever=chroma_store.as_retriever(search_kwargs={"k": 4}),
return_source_documents=True
)
回答と参照したマニュアルを取得するコードです
#実行
result = qa.invoke({"query":text})
print(result["result"])
for document in result["source_documents"]:
print("取得したpdf最初の40文字 :",document.page_content[:40])
保険の質問でない時は通常のgeminiとして動作させるシステムです。
かなり離れていますが、「geminiによるデータベース接続判断」の一番初めのif文とつながっています。
else:
print("保険に関する質問ではないと判断したため通常のgeminiとして回答します。")
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents=text)
print(response.text.strip().lower())
全てのコードを結合させたものも記載しておきます。
api_key="API KEY"
text="田中太郎さん45歳に個人データを参照しておすすめの保険を教えて。"
#pdfはコードで何個でも保存しておけます。
file_name="file.pdf"
!pip install google-genai
!pip install -U --quiet langchain-google-genai
!pip install langchain openai langchain_community
!pip install chromadb tiktoken
!pip install Spire.PDF
from google import genai
from langchain_google_genai import ChatGoogleGenerativeAI
import google.generativeai as generativeai
from langchain.text_splitter import CharacterTextSplitter
import pandas as pd
import numpy as np
import os
from langchain_google_genai import GoogleGenerativeAIEmbeddings
from langchain.chains import RetrievalQA
from langchain.vectorstores import Chroma
from spire.pdf import PdfDocument
from spire.pdf import PdfTextExtractOptions
from spire.pdf import PdfTextExtractor
os.environ['GOOGLE_API_KEY'] = api_key
client = genai.Client(api_key=api_key)
generativeai.configure(api_key=api_key)
#個人情報sql作成
# ライブラリをインポート
import sqlite3
import pandas as pd
# データベース接続を作成(存在しない場合は作成されます)
conn = sqlite3.connect('data.db')
# データフレームを作成
data = {
'last_name': ["田中", "山田", "田中", "一ノ瀬"],
'first_name': ["太郎", "太郎", "次郎", "一郎"],
'age': [45, 45, 12, 111],
'information': ['15年前に歯牙腫を手術。既婚。子供2人', '健康。既存契約あり', '保険未加入', '要介護状態']
}
df = pd.DataFrame(data)
# データをSQLに変換し、データベースに挿入
df.to_sql('persons', conn, if_exists='replace', index=False)
# データベース接続を閉じる
conn.close()
#pdfをベクトルDBに追加
pdf = PdfDocument()
#ここにファイル名を入力
pdf.LoadFromFile(file_name)
# テキストを保存するための文字列オブジェクトを作成
extracted_text = ""
# シンプルな抽出方法を使用するように設定
extract_options = PdfTextExtractOptions()
#extract_options.IsSimpleExtraction = True
# ドキュメント内のページをループ
for i in range(pdf.Pages.Count):
# ページを取得
page = pdf.Pages.get_Item(i)
# ページをパラメータとして渡してPdfTextExtractorのオブジェクトを作成
text_extractor = PdfTextExtractor(page)
# ページからテキストを抽出
texts = text_extractor.ExtractText(extract_options)
# 抽出されたテキストを文字列オブジェクトに追加
extracted_text += texts
pdf.Close()
# 抽出されたテキストをテキストファイルに書き込む
extracted_text=extracted_text[71:].replace(" ", "")
extracted_text=extracted_text.replace("\n", "")
extracted_text=extracted_text.replace("\r", "")
#テキストを分割
text_splitter = CharacterTextSplitter(separator=".",
chunk_size=6000,
chunk_overlap=15)
docs = text_splitter.split_text(extracted_text)
cleaned_docs = []
for doc in docs:
cleaned_doc = doc.replace("PDFforPython.", "")
cleaned_doc = cleaned_doc.replace("EvaluationWarning:ThedocumentwascreatedwithSpire.PDFforPython.", "")
cleaned_docs.append(cleaned_doc)
embeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
#データベース作成
chroma_store = Chroma.from_texts(
cleaned_docs,
embedding=embeddings,
collection_name="my_collection"
)
print(len(cleaned_docs))
for doc in cleaned_docs:
print(doc)
#入力情報 #情報入力をプロンプトのみでできる
#######################以下システム内部###########################
customer_information=" "
#保険の質問かどうか
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents=text+'これは保険に関する質問か、はいかいいえの一言だけで答えて')
if response.text.strip().lower() == "はい" :
print("保険に関する質問であると判断したため、関連する保険マニュアルを元に回答します。")
#詳細が必要な質問かどうか
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents=text+'これは個人データが追加で必要な質問か、はいかいいえの一言だけで答えて')
if response.text.strip().lower() == "はい" :
#文章から個人情報取得
print("データベースから顧客の詳細情報を取得します")
response_json = client.models.generate_content(model='gemini-2.0-flash-exp', contents='''以下の文章の中から顧客1人の名前と年齢を抽出してjson形式のみで返して。例{first_name='一郎',last_name='順規',age=22}'''+text)
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents='以下の文章からlast_nameを取得して一言で答えて'+str(response_json))
print("性 :",response.text.strip().lower())
last_name='"'+response.text.strip().lower()+'"'
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents='以下の文章から1人目のfirst_nameを取得して一言で答えて'+str(response_json))
print("名 :",response.text.strip().lower())
first_name='"'+response.text.strip().lower()+'"'
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents='以下の文章からageを取得してint型で答えて、取得できないならnullで'+str(response_json))
print("年齢 :",response.text.strip().lower())
age='"'+response.text.strip().lower()+'"'
# データベース接続
conn = sqlite3.connect('data.db')
cursor = conn.cursor()
#情報取得
try:
query = f"SELECT information FROM persons WHERE last_name = {last_name} AND first_name = {first_name} AND age = {age}"
cursor.execute(query)
customer_information = cursor.fetchone()
except:
print("顧客情報を取得できませんでした。")
# データベース接続を閉じる
conn.close()
content= text + "以下が詳細な個人情報"+ str(customer_information)+" この情報のみの時に最適な答えを出して"
else:
print("顧客の詳細情報取得は必要ないと判断しました。")
# rangchain llm 作成
qa = RetrievalQA.from_chain_type(
llm=ChatGoogleGenerativeAI(model='gemini-2.0-flash-exp',temperature=0.7, top_p=0.85),
chain_type="stuff",
retriever=chroma_store.as_retriever(search_kwargs={"k": 4}),
return_source_documents=True
)
# クエリを実行
result = qa.invoke({"query": content})
print("取得した顧客情報 :",customer_information)
for document in result["source_documents"]:
print("使用したマニュアル最初の40文字 :",document.page_content[:40])
print("回答 :", result["result"])
else:
print("保険に関する質問ではないと判断したため通常のgeminiとして回答します。")
response = client.models.generate_content(model='gemini-2.0-flash-exp', contents=text)
print(response.text.strip().lower())
弊社ではデータ基盤やLLMのご相談や構築も可能ですので、お気軽にお問合せください。
また、中途採用やインターンの問い合わせもお待ちしています!