PaloAlto TechDocs 向け RAG ボットを作ってみた
はじめに
最近、企業内のナレッジベースや製品マニュアルは量も種類も増え続けており、
「欲しい情報がどのドキュメントにあるのか分からない」「読むのに時間がかかる」といった課題が顕在化しています。
それらを解決する手段として注目されているのが RAG(Retrieval‑Augmented Generation)です。
RAG とは、検索(Retrieval)と生成(Generation)を組み合わせた手法で、
- 大量テキストから関連部分を検索し
- 最新の LLM(大規模言語モデル)で自然言語回答を生成
といったパイプラインを実現します。
これにより、単なる全文検索では難しい「自然な会話形式での Q&A」や「要点を踏まえた要約」「ステップバイステップの手順説明」が可能になります。
本記事では、PaloAlto Networks の公式技術ドキュメントサイト(TechDocs)を例に取り、Python+LangChain+FAISS を用いて次の流れを実装します。
- スクレイピング:TechDocs を robots.txt に従って安全にクロール
- チャンク化:ドキュメントを適切なサイズに分割しトークン制限を回避
- ベクトル化&検索:OpenAI Embeddings でチャンクをベクトル化し、FAISS に登録
- 回答生成:RetrievalQA チェーンで GPT に質問を投げ、具体的な手順をステップで回答
これにより、TechDocs を読み込んでチャット感覚で質問できる自動応答ボットを手軽に構築できます。
環境準備
今回、TechDocs ボットを動かすために以下を用意しました。
- Python 3.9+
- 必要ライブラリのインストール
pip install requests beautifulsoup4 langchain faiss-cpu tiktoken tqdm
- OpenAI API キー を環境変数に設定
export OPENAI_API_KEY="sk-あなたのAPIキー"
- FAISS でベクトル検索を行うために faiss-cpu をインストール
- ネットワーク接続と十分なディスク容量を確認(クロール&インデックス用)
サイトクロールの実装
TechDocs のページを安全かつ再帰的に取得するためのクロール処理を実装します。
robots.txtの確認
robots.txt
はウェブサイトのルートに配置されるテキストファイルで、
クローラー(検索エンジンやボット)がどのページを取得して良いかをサイト運営者が指定するための仕組みです。
これを読み取ることで、スクレイピング時にポリシー違反や過負荷を防ぎます。
from urllib.parse import urljoin
from urllib.robotparser import RobotFileParser
# ベース URL とユーザーエージェントを設定
BASE_URL = "https://docs.paloaltonetworks.com"
USER_AGENT = "TechDocsBot/1.0"
# robots.txt を読み込む
rp = RobotFileParser()
rp.set_url(urljoin(BASE_URL, "/robots.txt"))
rp.read()
-
ポイント:
rp.can_fetch(USER_AGENT, url)
でクロール許可を判定し、サーバ負荷と規約違反を防ぐ。
ページ取得とテキスト抽出
HTML ページそのままではスクリプトやナビゲーションなどノイズが多く、そのまま Embeddings に渡すと意味のないベクトルが生成されたりトークン浪費につながります。 そこで、必要な本文だけを抽出し、ノイズを除去したプレーンテキストを取得します。
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
# 不要なタグを削除
for tag in soup(["script", "style", "nav", "footer"]):
tag.decompose()
# プレーンテキスト化
text = "\n".join(
[line.strip() for line in soup.get_text().splitlines() if line.strip()]
)
- ポイント:不要な HTML タグを削除し、文章だけを抽出。
-
リンク検索:同一ドメインの
<a>
タグを再帰的にキューに追加し、網羅的に取得。
テキストチャンク化
長いドキュメントを小分けにすることで、Embeddings やモデルへの投げ込み時のトークン制限を回避。
CharacterTextSplitter の利用
一つの Web ページ全体のテキストは非常に長くなることがあり、そのままでは OpenAI のトークン上限(入力制限)を超える可能性があります。
そのため、文章を適度なサイズに分割(チャンク化)することで、後工程での埋め込み生成や回答生成がスムーズに行えるようになります。
from langchain.text_splitter import CharacterTextSplitter
splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=100)
chunks = splitter.split_text(full_text)
- chunk_size:一度に送る最大文字数を 500 に設定
- chunk_overlap:100 文字重複させ、分割境界で情報が途切れないように調整
埋め込み&FAISS インデックス構築
チャンクをベクトル化し、FAISS にインデックスを作成する処理を紹介します。
バッチ化による埋め込み
分割されたチャンクごとに OpenAI Embeddings を用いてベクトル化(=数値化)します。この処理は API を通じて行われるため、1件ずつ送信すると非効率かつレート制限のリスクがあります。そこで、複数件(例:10件単位)をまとめて送ることで、処理効率を向上させ、通信回数を削減できます。また、API エラーが発生した際にはスリープやリトライ処理を入れることで、堅牢性を高めることが重要です。
embeddings = OpenAIEmbeddings()
for batch in chunked(chunks, size=10):
embs = embeddings.embed_documents(batch)
# FAISS インデックスに upsert などの処理
- ポイント:10 件ずつのバッチでリクエストし、レート制限やトークン超過を避ける。
- エラー対策:エラー時には間隔を空けて再試行
FAISS への保存
ベクトル化されたチャンクを、検索可能な状態でローカルに保存するために FAISS(Facebook AI Similarity Search)を利用します。FAISS は、高速かつスケーラブルな類似検索を可能にするライブラリであり、Embedding ベクトルとの類似度を基に関連チャンクを効率よく見つけ出すことができます。保存後のインデックスは再読み込みして何度でも利用できるため、一度構築すれば何度も再利用可能です。
from langchain.vectorstores import FAISS
pairs = list(zip(texts, embedding_vectors))
store = FAISS.from_embeddings(pairs, embedding=embeddings, metadatas=metadatas)
store.save_local(INDEX_PATH)
-
pairs:
(テキスト, 埋め込みベクトル)
のリスト - save_local:ローカルにインデックスを残し、次回実行時に再利用可能
RetrievalQA とカスタムプロンプト
FAISS で検索したチャンクをもとに、GPT に具体的な回答を生成させる流れを構築します。
PromptTemplate の定義
以下では、取得したチャンク({context}
)とユーザーの質問({question}
)を受け取り、
「熟練エンジニアとしてステップバイステップで回答する」ことをモデルに明示しています。
from langchain.prompts import PromptTemplate
prompt = PromptTemplate(
input_variables=["context", "question"],
template=(
"あなたは Paloalto の熟練エンジニアです。\n"
"-----\n"
"{context}\n"
"-----\n"
"上記情報を参照し、質問に対してステップバイステップで具体的に回答してください。\n"
"質問: {question}\n"
"回答:"
)
)
- context:検索でヒットしたチャンク
- question:ユーザーの質問内容
RetrievalQA の組み立て
ここでは、FAISS の検索結果と先ほど作成したプロンプトを組み合わせて、
実際に質問応答チェーン(QA チェーン)を構築しています。
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
qa = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model_name="gpt-4-turbo", temperature=0),
retriever=store.as_retriever(search_kwargs={"k":3}),
chain_type="stuff",
chain_type_kwargs={"prompt": prompt}
)
- k=3:上位3チャンクを参照
- chain_type="stuff":全チャンクをそのままモデルに注入し、一度に回答生成
実行フローとコマンド
以下のコマンドでインデックス作成および質問モードを起動します。
インデックス作成
python techdoc.py --mode index
- TechDocs をクロール→チャンク化→Embedding→FAISS 保存
質問モード
python techdoc.py --mode query
- 対話的に質問入力
- 回答およびソース URL をターミナルに出力
-
exit
で終了
実行結果例:実際の問い合わせと応答内容
構築した QA ボットに対して、実際の質問を投げて応答の内容と挙動を確認しました。
質問
PrismaAccessのモバイルユーザの設定手順を教えてください
応答結果
回答:
PrismaAccessのモバイルユーザの設定手順は以下の通りです。
1. Prisma Accessライセンスを取得します。モバイルユーザをセキュアにするためには、Prisma Accessライセンスが必要です。
2. モバイルユーザの接続方法を選択します。GlobalProtect、Explicit Proxy、または両方の接続タイプを選択できます。初期設定を行う際には、Prisma Accessがモバイルユーザの環境をプロビジョニングできるように、いくつかの必須設定を入力する必要があります。
3. インフラストラクチャをセットアップします。基本的なインフラストラクチャ設定を行った後、接続タイプ(GlobalProtectまたはExplicit Proxy)に特有のインフラストラクチャ設定を構成します。モバイルユーザデバイス上のプロキシ自動構成(PAC)ファイルは、ブラウザのトラフィックをPrisma Accessにリダイレクトします。
4. Prisma Accessのロケーションを追加します。ユーザが利用するためのPrisma Accessのロケーションを追加する設定を行います。ユーザの地域に合わせたユーザーエクスペリエンスを提供するために、各地域内に複数のロケーションが用意されています。
5. モバイルユーザを認証します。ユーザ認証を設定して、正当なユーザのみがサービスやアプリケーションにアクセスできるようにします。初期設定をテストするためには、Prisma Accessがローカルで認証するユーザを追加するか、企業レベルの認証方法を直接設定します。
6. 初期設定をPrisma Accessにプッシュした後、Prisma Accessはモバイルユーザ環境のプロビジョニングを開始します。これには最大15分かかる場合があります。モバイルユーザのロケーションが稼働し始めたら、Mobile Usersのセットアップページ、Summary Overviewページ、およびPrisma Access Insights内で確認できます。
以上がPrismaAccessのモバイルユーザの設定手順です。
参照元:
- https://docs.paloaltonetworks.com/content/techdocs/en_US/prisma-access/administration/prisma-access-mobile-users
- https://docs.paloaltonetworks.com/content/techdocs/en_US/prisma-access/administration/prisma-access-setup/set-up-prisma-access
- https://docs.paloaltonetworks.com/content/techdocs/en_US/strata-cloud-manager/getting-started/workflows/workflows-prisma-access-setup/workflows-mobile-users.html
所感
- 情報の正確性には十分確認が必要ではあるものの、ユーザーの質問意図に沿った情報を引き出せている点で実用性は高いのではないかと感じました!
- 特に、関連ドキュメントの出典がそのまま表示されるため、詳細な確認や補足がしやすいのが大きな利点です。
まとめと今後に向けて
ポイント振り返り
- robots.txt で規約遵守:安全かつ合法的にスクレイピング
- チャンク化でトークン制限を回避:分割するとモデル呼び出しが安定
- バッチ化で埋め込み安定化:API 制限への対応
- カスタムプロンプトで具体手順を誘導:明確な回答フォーマット
応用例
- 社内への RAG ボット展開
- Slack/Teams Bot 連携
- 定期クロール+自動再インデックス実装
- UI 組み込みやダッシュボード化
Discussion