Claude 3.7Sonnet の力を借りて簡易 RAG を実装する
はじめに
本記事で書くのはタイトル部分程度です。RAG の詳細や Claude 3.7Sonnet の性能比較などについては書きません。
前提条件
- サーバーサイドエンジニアで、 RAG については聞きかじった程度の知識
- 一度 Claude 3.5Sonnet で RAG 実装しようとしたらクローリングで躓いた
2つ目については後々少し記載します。
簡易的にRAG構築させてみる
とりあえず簡易的に Claude3.7Sonnet だけでどこまで実装できるのかやらせてみました。
どんな指示をしたのか
いきなりコードを書かせるのではなく、まずは大枠の工程を出させるようにしました。
特定の QAサイトをクローリングしてそのサイトの情報を元に RAG でチャットボットを作る方法を教えて下さい。まずは大枠として何をすればよいかだけを洗い出してください。
なお、QAサイトは https://xxxxxxxx で、ページ内リンクによっていくつかの階層に分かれていて、これらすべてをクローリング対象とします。
※サイトURL は実際には個人的に使いたい QA サイトを指定しました。以降のコードでも関連個所は XXXX のようにしています
Claude3.7Sonnet の回答は以下です。
RAGを用いたQAサイトベースのチャットボット構築の大枠をご説明します。
1. **クローリング**
- ウェブクローラーを構築して https://xxxxxx のページとそのすべてのリンク先を取得
- ページ間の階層構造を保持しながらデータを収集
2. **データ処理**
- 取得したHTMLからテキスト情報を抽出(HTMLタグの除去)
- 重要な構造情報(見出し、リスト、表など)を保持
- 必要に応じてデータのクリーニングと正規化
3. **ベクトル化とインデックス作成**
- テキストをチャンク(小さな区切り)に分割
- 各チャンクをエンベディングモデルでベクトル化
- ベクトルデータベース(Pinecone、Qdrant、Faissなど)に保存
4. **RAGシステム構築**
- 質問応答パイプラインの設計
- 質問のベクトル化と類似度検索機能の実装
- チャットモデル(GPT、Claude等)との統合
5. **チャットボットインターフェース実装**
- バックエンドAPIの構築
- フロントエンドUIの開発またはチャットプラットフォームとの連携
6. **評価とチューニング**
- 応答品質の評価指標設定
- チャンクサイズやインデックス手法の最適化
- プロンプトエンジニアリングによる回答精度の向上
7. **デプロイと運用**
- サーバーまたはクラウドサービスへのデプロイ
- 定期的なデータの更新機能の実装
- モニタリングとログ収集の仕組み
これらの各ステップについてさらに詳しい情報が必要でしたら、お気軽にお知らせください。
おぉ~なるほど?ひとまずローカルで動かす程度なら 「4. RAGシステム構築」までは
なんとなくコード生成させられそうだと思ったので、以下のような指示をさせました。
(ついでに言うとここまででいったん動作確認したかったのと、フロントエンドは v0とか lovable で作った方が良さそうかなとか思ってました)
1. RAGシステム構築 この工程までのコードを python で生成してください
Claude3.7Sonnet の回答は以下です。
回答
RAGシステム構築のためのPythonコードを生成します。クローリングからRAGシステム構築までの工程をカバーします。
pythonコード
import os
import requests
from bs4 import BeautifulSoup
import re
import time
from urllib.parse import urljoin, urlparse
import pandas as pd
import numpy as np
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
import logging
# 1. ロギング設定
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# 2. 環境変数設定(APIキーなど)
os.environ["OPENAI_API_KEY"] = "your-openai-api-key" # APIキーを設定してください
# 3. クローリング関数
class WebCrawler:
def __init__(self, base_url, max_pages=100):
self.base_url = base_url
self.max_pages = max_pages
self.visited_urls = set()
self.content_data = []
def is_valid_url(self, url):
"""URLが有効かつクロール対象かを確認"""
parsed_url = urlparse(url)
base_parsed = urlparse(self.base_url)
# 同じドメイン内のURLかつ、パスが/hc/jaで始まるURLのみ許可
return (parsed_url.netloc == base_parsed.netloc and
parsed_url.path.startswith('/hc/ja'))
def extract_text(self, soup):
"""BeautifulSoupオブジェクトからテキストを抽出"""
# スクリプトとスタイル要素を削除
for script_or_style in soup(["script", "style"]):
script_or_style.extract()
# 主要コンテンツを抽出(サポートサイトの主要コンテンツ部分を特定)
# 必要に応じてCSSセレクタを調整してください
main_content = soup.select('.article-body') or soup.select('main') or soup.select('.main-content')
if main_content:
# 主要コンテンツのテキストを抽出
text = main_content[0].get_text(separator="\n")
else:
# 主要コンテンツが見つからない場合は全体のテキストを取得
text = soup.get_text(separator="\n")
# 余分な空白と改行を整理
lines = (line.strip() for line in text.splitlines())
text = "\n".join(line for line in lines if line)
return text
def extract_metadata(self, soup, url):
"""ページのメタデータを抽出"""
title = soup.title.string if soup.title else ""
# h1タグから見出しを取得
h1 = soup.find('h1')
heading = h1.get_text().strip() if h1 else title
# メタディスクリプションがあれば取得
meta_desc = soup.find('meta', attrs={'name': 'description'})
description = meta_desc['content'] if meta_desc and 'content' in meta_desc.attrs else ""
return {
"url": url,
"title": title,
"heading": heading,
"description": description
}
def crawl(self):
"""指定されたURLからクローリングを実行"""
queue = [self.base_url]
while queue and len(self.visited_urls) < self.max_pages:
url = queue.pop(0)
if url in self.visited_urls:
continue
logger.info(f"クローリング中: {url}")
try:
# リクエスト送信(サーバに負荷をかけないよう間隔を空ける)
time.sleep(1)
response = requests.get(url, headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
})
if response.status_code == 200:
self.visited_urls.add(url)
soup = BeautifulSoup(response.text, 'html.parser')
# テキストとメタデータを抽出
text = self.extract_text(soup)
metadata = self.extract_metadata(soup, url)
# データを保存
data = {
**metadata,
"content": text
}
self.content_data.append(data)
# リンクを抽出して追加
for link in soup.find_all('a', href=True):
href = link['href']
full_url = urljoin(url, href)
if self.is_valid_url(full_url) and full_url not in self.visited_urls:
queue.append(full_url)
else:
logger.warning(f"ステータスコード {response.status_code}: {url}")
except Exception as e:
logger.error(f"エラー {url}: {str(e)}")
logger.info(f"クローリング完了。{len(self.content_data)}ページを取得しました。")
return self.content_data
# 4. テキスト分割とベクトル化
class DocumentProcessor:
def __init__(self, chunk_size=1000, chunk_overlap=200):
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
separators=["\n\n", "\n", " ", ""]
)
def process_documents(self, documents):
"""ドキュメントを処理してチャンクに分割"""
docs_with_metadata = []
for doc in documents:
text = doc["content"]
metadata = {k: v for k, v in doc.items() if k != "content"}
# テキストを分割
chunks = self.text_splitter.split_text(text)
# メタデータを各チャンクに追加
for i, chunk in enumerate(chunks):
docs_with_metadata.append({
"content": chunk,
"chunk_id": i,
**metadata
})
return docs_with_metadata
def create_vector_index(self, processed_docs):
"""ドキュメントをベクトル化してインデックスを作成"""
embeddings = OpenAIEmbeddings()
texts = [doc["content"] for doc in processed_docs]
metadatas = [{k: v for k, v in doc.items() if k != "content"} for doc in processed_docs]
# FAISSベクトルストアの作成
vector_store = FAISS.from_texts(texts=texts, embedding=embeddings, metadatas=metadatas)
return vector_store
# 5. RAGシステム
class RAGSystem:
def __init__(self, vector_store, model_name="gpt-3.5-turbo", temperature=0):
self.vector_store = vector_store
self.model_name = model_name
self.temperature = temperature
# レトリバルシステムの作成
self.retriever = vector_store.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# プロンプトテンプレートの作成
template = """
あなたはXXXXサポートの専門アシスタントです。
以下の情報を参考にして、ユーザーの質問に丁寧に回答してください。
関連コンテンツ:
{context}
質問: {question}
回答:
"""
self.prompt = PromptTemplate(
template=template,
input_variables=["context", "question"]
)
# LLMの初期化
self.llm = ChatOpenAI(model_name=self.model_name, temperature=self.temperature)
# QAチェーンの作成
self.qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff",
retriever=self.retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": self.prompt}
)
def answer_question(self, question):
"""質問に回答する"""
response = self.qa_chain({"query": question})
# 回答とソースドキュメントを返す
answer = response["result"]
source_docs = response["source_documents"]
# ソースURLを重複なく取得
source_urls = []
for doc in source_docs:
if "url" in doc.metadata and doc.metadata["url"] not in source_urls:
source_urls.append(doc.metadata["url"])
return {
"answer": answer,
"sources": source_urls
}
# 6. メイン関数:全体の実行
def build_rag_system(base_url="https://xxxxxxx", max_pages=100):
"""RAGシステムを構築する"""
# 1. クローリング
logger.info("クローリングを開始します...")
crawler = WebCrawler(base_url=base_url, max_pages=max_pages)
crawled_data = crawler.crawl()
# データフレームに変換して保存(オプション)
df = pd.DataFrame(crawled_data)
df.to_csv("crawled_data.csv", index=False)
logger.info(f"クロール済みデータを保存しました。{len(crawled_data)}件のドキュメントを取得。")
# 2. テキスト処理
logger.info("テキスト処理を開始します...")
processor = DocumentProcessor(chunk_size=1000, chunk_overlap=200)
processed_docs = processor.process_documents(crawled_data)
logger.info(f"テキスト処理完了。{len(processed_docs)}件のチャンクに分割されました。")
# 3. ベクトルインデックス作成
logger.info("ベクトルインデックスを作成中...")
vector_store = processor.create_vector_index(processed_docs)
# インデックスを保存(オプション)
vector_store.save_local("faiss_index")
logger.info("ベクトルインデックスを保存しました。")
# 4. RAGシステム構築
logger.info("RAGシステムを構築中...")
rag_system = RAGSystem(vector_store)
return rag_system
# 7. 実行例
if __name__ == "__main__":
# RAGシステムの構築
rag_system = build_rag_system()
# テスト質問
test_question = "XXXXXのパスワードをリセットする方法は?"
result = rag_system.answer_question(test_question)
print("\n=== 質問 ===")
print(test_question)
print("\n=== 回答 ===")
print(result["answer"])
print("\n=== 情報ソース ===")
for url in result["sources"]:
print(f"- {url}")
このコードは以下の機能を実装しています:
-
クローリング機能
-
WebCrawler
クラスでサポートサイトの全ページを収集 - リンクを辿って階層構造を保持
- HTMLからテキストと関連メタデータ(タイトル、見出し、URL)を抽出
-
-
テキスト処理
-
DocumentProcessor
クラスでテキストを適切なサイズのチャンクに分割 - 重複を許容して文脈を保持(チャンクオーバーラップ)
- ドキュメントメタデータを各チャンクに付与
-
-
ベクトル化とインデックス
- OpenAIのエンベディングモデルを使用
- FAISSベクトルデータベースにインデックスを構築
- 検索効率のためにインデックスをローカルに保存
-
RAGシステム
- 質問に対して関連コンテンツを検索
- カスタムプロンプトでLLM(GPT-3.5-turbo)に指示
- 回答と情報ソースを返す機能
使用する際の注意点:
- OpenAI APIキーを設定する必要があります
- 必要に応じてCSSセレクタを調整して正確にコンテンツを抽出
- チャンクサイズとオーバーラップは性能に大きく影響するため調整が必要
このコードを基盤として、フロントエンドインターフェースの追加や、回答品質の評価・改善を行うことができます。何か特定の部分について詳細が必要でしたらお知らせください。
(コードは長いので二重に折り畳みにしました)
この時点で、前提条件にある通り私は対象サイトのクローリングでドン詰まった経験があるので
クローリングに request を使うのではなく playwright を使うよう指示しました。加えてクローリング対象のサイトの特性も付与して指示しました。
クローリング部分は playwright を使うように変更してください。
なお、通常のリクエストでは cloudflare がレンダリング完了するまで
ページタイトルが "Just a moment..." となってしまうので
レンダリング完了するまで待ってからページ情報を取得するよう注意してください。
Claude3.7Sonnet の回答は以下です。
回答
Playwrightによるクローリングコードへと変更します。Cloudflareによる保護に対応し、ページが完全にレンダリングされるまで待機する機能を追加します。
修正されたコード
import os
import re
import time
import asyncio
from urllib.parse import urljoin, urlparse
import pandas as pd
import numpy as np
from bs4 import BeautifulSoup
from playwright.async_api import async_playwright
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
import logging
# 1. ロギング設定
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# 2. 環境変数設定(APIキーなど)
os.environ["OPENAI_API_KEY"] = "your-openai-api-key" # APIキーを設定してください
# 3. Playwrightを使用したクローリング関数
class PlaywrightCrawler:
def __init__(self, base_url, max_pages=100):
self.base_url = base_url
self.max_pages = max_pages
self.visited_urls = set()
self.content_data = []
def is_valid_url(self, url):
"""URLが有効かつクロール対象かを確認"""
parsed_url = urlparse(url)
base_parsed = urlparse(self.base_url)
# 同じドメイン内のURLかつ、パスが/hc/jaで始まるURLのみ許可
return (parsed_url.netloc == base_parsed.netloc and
parsed_url.path.startswith('/hc/ja'))
async def extract_page_info(self, page, url):
"""Playwrightのページからテキストとメタデータを抽出"""
# Cloudflareが完了するまで待機
await self.wait_for_cloudflare(page)
# ページのHTMLを取得
html_content = await page.content()
soup = BeautifulSoup(html_content, 'html.parser')
# メインコンテンツを抽出
main_content = soup.select('.article-body') or soup.select('main') or soup.select('.main-content')
if main_content:
# 主要コンテンツのテキストを抽出
text = main_content[0].get_text(separator="\n")
else:
# 主要コンテンツが見つからない場合は全体のテキストを取得
text = soup.get_text(separator="\n")
# 余分な空白と改行を整理
lines = (line.strip() for line in text.splitlines())
text = "\n".join(line for line in lines if line)
# メタデータの抽出
title = await page.title()
# h1タグから見出しを取得
h1 = soup.find('h1')
heading = h1.get_text().strip() if h1 else title
# メタディスクリプションがあれば取得
meta_desc = soup.find('meta', attrs={'name': 'description'})
description = meta_desc['content'] if meta_desc and 'content' in meta_desc.attrs else ""
# リンクを抽出
links = []
for link in soup.find_all('a', href=True):
href = link['href']
full_url = urljoin(url, href)
if self.is_valid_url(full_url) and full_url not in self.visited_urls:
links.append(full_url)
return {
"url": url,
"title": title,
"heading": heading,
"description": description,
"content": text,
"links": links
}
async def wait_for_cloudflare(self, page):
"""Cloudflareのチェックが完了するまで待機"""
# タイムアウト設定(秒)
timeout = 30
start_time = time.time()
while time.time() - start_time < timeout:
title = await page.title()
if title and "Just a moment..." not in title:
# Cloudflareのチェックが完了
# 追加の待機時間を設けて完全なレンダリングを確保
await page.wait_for_load_state('networkidle')
return
# 少し待機してから再試行
await asyncio.sleep(0.5)
# タイムアウトした場合でもページの読み込みを最大限待機
await page.wait_for_load_state('networkidle')
logger.warning(f"Cloudflareチェックの待機がタイムアウトしました: {await page.url()}")
async def crawl(self):
"""Playwrightを使用してクローリングを実行"""
async with async_playwright() as p:
# ブラウザを起動(ヘッドレスモード)
browser = await p.chromium.launch(headless=True)
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
)
# 初期ページをキューに追加
queue = [self.base_url]
try:
while queue and len(self.visited_urls) < self.max_pages:
url = queue.pop(0)
if url in self.visited_urls:
continue
logger.info(f"クローリング中: {url}")
try:
# ページを開く
page = await context.new_page()
await page.goto(url, wait_until="domcontentloaded")
# ページ情報を抽出
page_info = await self.extract_page_info(page, url)
# 訪問済みURLに追加
self.visited_urls.add(url)
# データを保存(リンクを除外)
data = {k: v for k, v in page_info.items() if k != "links"}
self.content_data.append(data)
# 新しいリンクをキューに追加
for link_url in page_info["links"]:
if link_url not in self.visited_urls:
queue.append(link_url)
# ページを閉じる
await page.close()
# サーバーに負荷をかけないよう待機
await asyncio.sleep(1)
except Exception as e:
logger.error(f"ページ処理中のエラー {url}: {str(e)}")
try:
await page.close()
except:
pass
finally:
# ブラウザを閉じる
await context.close()
await browser.close()
logger.info(f"クローリング完了。{len(self.content_data)}ページを取得しました。")
return self.content_data
# 4. テキスト分割とベクトル化
class DocumentProcessor:
def __init__(self, chunk_size=1000, chunk_overlap=200):
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
separators=["\n\n", "\n", " ", ""]
)
def process_documents(self, documents):
"""ドキュメントを処理してチャンクに分割"""
docs_with_metadata = []
for doc in documents:
text = doc["content"]
metadata = {k: v for k, v in doc.items() if k != "content"}
# テキストを分割
chunks = self.text_splitter.split_text(text)
# メタデータを各チャンクに追加
for i, chunk in enumerate(chunks):
docs_with_metadata.append({
"content": chunk,
"chunk_id": i,
**metadata
})
return docs_with_metadata
def create_vector_index(self, processed_docs):
"""ドキュメントをベクトル化してインデックスを作成"""
embeddings = OpenAIEmbeddings()
texts = [doc["content"] for doc in processed_docs]
metadatas = [{k: v for k, v in doc.items() if k != "content"} for doc in processed_docs]
# FAISSベクトルストアの作成
vector_store = FAISS.from_texts(texts=texts, embedding=embeddings, metadatas=metadatas)
return vector_store
# 5. RAGシステム
class RAGSystem:
def __init__(self, vector_store, model_name="gpt-3.5-turbo", temperature=0):
self.vector_store = vector_store
self.model_name = model_name
self.temperature = temperature
# レトリバルシステムの作成
self.retriever = vector_store.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# プロンプトテンプレートの作成
template = """
あなたはXXXXサポートの専門アシスタントです。
以下の情報を参考にして、ユーザーの質問に丁寧に回答してください。
関連コンテンツ:
{context}
質問: {question}
回答:
"""
self.prompt = PromptTemplate(
template=template,
input_variables=["context", "question"]
)
# LLMの初期化
self.llm = ChatOpenAI(model_name=self.model_name, temperature=self.temperature)
# QAチェーンの作成
self.qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff",
retriever=self.retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": self.prompt}
)
def answer_question(self, question):
"""質問に回答する"""
response = self.qa_chain({"query": question})
# 回答とソースドキュメントを返す
answer = response["result"]
source_docs = response["source_documents"]
# ソースURLを重複なく取得
source_urls = []
for doc in source_docs:
if "url" in doc.metadata and doc.metadata["url"] not in source_urls:
source_urls.append(doc.metadata["url"])
return {
"answer": answer,
"sources": source_urls
}
# 6. メイン関数:全体の実行
async def build_rag_system(base_url="https://xxxxxxx", max_pages=100):
"""RAGシステムを構築する"""
# 1. クローリング
logger.info("クローリングを開始します...")
crawler = PlaywrightCrawler(base_url=base_url, max_pages=max_pages)
crawled_data = await crawler.crawl()
# データフレームに変換して保存(オプション)
df = pd.DataFrame(crawled_data)
df.to_csv("crawled_data.csv", index=False)
logger.info(f"クロール済みデータを保存しました。{len(crawled_data)}件のドキュメントを取得。")
# 2. テキスト処理
logger.info("テキスト処理を開始します...")
processor = DocumentProcessor(chunk_size=1000, chunk_overlap=200)
processed_docs = processor.process_documents(crawled_data)
logger.info(f"テキスト処理完了。{len(processed_docs)}件のチャンクに分割されました。")
# 3. ベクトルインデックス作成
logger.info("ベクトルインデックスを作成中...")
vector_store = processor.create_vector_index(processed_docs)
# インデックスを保存(オプション)
vector_store.save_local("faiss_index")
logger.info("ベクトルインデックスを保存しました。")
# 4. RAGシステム構築
logger.info("RAGシステムを構築中...")
rag_system = RAGSystem(vector_store)
return rag_system
# 7. 実行例
if __name__ == "__main__":
# asyncioでメイン関数を実行
async def main():
# RAGシステムの構築
rag_system = await build_rag_system()
# テスト質問
test_question = "XXXXのパスワードをリセットする方法は?"
result = rag_system.answer_question(test_question)
print("\n=== 質問 ===")
print(test_question)
print("\n=== 回答 ===")
print(result["answer"])
print("\n=== 情報ソース ===")
for url in result["sources"]:
print(f"- {url}")
# asyncioイベントループで実行
asyncio.run(main())
クローリング部分をPlaywrightを使用するように変更しました。主な変更点と特徴は以下の通りです:
-
Playwright対応
- 非同期処理を使用したクローリング実装
- ヘッドレスChromeブラウザによる完全なレンダリング
-
Cloudflare対策
-
wait_for_cloudflare
メソッドを実装 - "Just a moment..."タイトルが消えるまで待機
-
networkidle
イベントまで待機してレンダリング完了を確認
-
-
安定性の向上
- 例外処理の強化
- ページごとに新しいページインスタンスを作成
- リソース解放の徹底(ページ・コンテキスト・ブラウザの適切なクローズ)
-
非同期対応
-
async/await
を使用した効率的な実装 -
asyncio
によるメインルーチンの実装
-
-
その他の改善点
- ページ処理中のエラーハンドリング強化
- クロール間隔の適切な調整
- 全体的な処理をasync関数に統一
このコードはPlaywrightの特性を活かし、JavaScriptが動的に生成するコンテンツやCloudflareのような保護機能が有効なサイトでも適切にデータを取得できます。また、エラー処理も強化されており、より堅牢なクローリングが可能になっています。
なかなか読み応えのある長さのコードですが、ひとまず動かせそうなコードが出てきました。
また、ここに書くには長すぎるので省略しますが、この後いくつか pip や playwright コマンドで依存ライブラリをインストールする作業を挟み、かつ既に作成済みのベクトルインデックスが見つかった場合は構築をスキップする処理を挟みまして、
無事このコードを実行することができました 🎉
実行の様子
2025-03-03 16:49:46,525 - INFO - インデックスが見つからないため、新規構築を開始します。
2025-03-03 16:49:46,525 - INFO - クローリングを開始します...
2025-03-03 16:49:47,579 - INFO - クローリング中: https://xxxxxxxxxx
2025-03-03 16:49:50,644 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609--禁止行為-と-注意事項-について
2025-03-03 16:49:53,283 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037781751-配信中の楽曲使用-歌唱や演奏-BGM-について
2025-03-03 16:49:55,799 - INFO - クローリング中: https://xxxxxxxxxx/articles/900004645046-コミュニティランクについて
2025-03-03 16:49:58,312 - INFO - クローリング中: https://xxxxxxxxxx/articles/20056952605721-ウェルカムパスについて
2025-03-03 16:50:00,898 - INFO - クローリング中: https://xxxxxxxxxx/articles/360039870171-まいにち配信について
2025-03-03 16:50:03,563 - INFO - クローリング中: https://xxxxxxxxxx/articles/900004645206-あんしんランクスコアについて
2025-03-03 16:50:06,171 - INFO - クローリング中: https://xxxxxxxxxx/articles/4404867147161-IRIAMトップバナーチャレンジの参加対象期間について
2025-03-03 16:50:08,783 - INFO - クローリング中: https://xxxxxxxxxx/articles/16778352977305-Live2Dモデルの実装権利について
2025-03-03 16:50:11,374 - INFO - クローリング中: https://xxxxxxxxxx/articles/360043005992-ファンバッジについて
2025-03-03 16:50:13,998 - INFO - クローリング中: https://xxxxxxxxxx/articles/22397285778713-X-アカウントの連携について
2025-03-03 16:50:16,589 - INFO - クローリング中: https://xxxxxxxxxx/articles/9335787962905-IRIAMおトクチャージでのポイント購入-カード決済-について
2025-03-03 16:50:19,215 - INFO - クローリング中: https://xxxxxxxxxx/articles/900004128066-ダイヤについて
2025-03-03 16:50:21,692 - INFO - クローリング中: https://xxxxxxxxxx/articles/10177213060249-IRIAMおトクチャージ利用中に不具合が起きた
2025-03-03 16:50:24,241 - INFO - クローリング中: https://xxxxxxxxxx/categories/5561879948697-ルール-規約
2025-03-03 16:50:26,616 - INFO - クローリング中: https://xxxxxxxxxx/categories/5561305857689-配信
2025-03-03 16:50:29,066 - INFO - クローリング中: https://xxxxxxxxxx/categories/5561290443545-視聴-応援
2025-03-03 16:50:31,848 - INFO - クローリング中: https://xxxxxxxxxx/categories/5561492784153-アカウント
2025-03-03 16:50:34,405 - INFO - クローリング中: https://xxxxxxxxxx/categories/5561869764377-ポイント購入-ダイヤ
2025-03-03 16:50:36,909 - INFO - クローリング中: https://xxxxxxxxxx/categories/5772802524441-通報-ブロック
2025-03-03 16:50:39,371 - INFO - クローリング中: https://xxxxxxxxxx/categories/13556159617689-トラブルシュート
2025-03-03 16:50:41,887 - INFO - クローリング中: https://xxxxxxxxxx/categories/5561289211545-その他
2025-03-03 16:50:44,349 - INFO - クローリング中: https://xxxxxxxxxx/sections/17765939698201-IRIAM-Chara-Checker-イリアムキャラチェッカー
2025-03-03 16:50:46,832 - INFO - クローリング中: https://xxxxxxxxxx/articles/43452283650073-IRIAM-Chara-Checkerで共同編集者を招待する方法
2025-03-03 16:50:49,442 - INFO - クローリング中: https://xxxxxxxxxx/sections/5696256947353-視聴
2025-03-03 16:50:52,454 - INFO - クローリング中: https://xxxxxxxxxx/articles/40983792407577-ホーム画面のサムネイルに表示されるアイコンについて
2025-03-03 16:50:55,150 - INFO - クローリング中: https://xxxxxxxxxx/sections/5696117944345-アカウント
2025-03-03 16:50:57,681 - INFO - クローリング中: https://xxxxxxxxxx/articles/39128558706713-Xアカウントの連携に失敗する-連携したXアカウントでログインができない
2025-03-03 16:51:00,175 - INFO - クローリング中: https://xxxxxxxxxx/sections/5696189536537-配信
2025-03-03 16:51:02,772 - INFO - クローリング中: https://xxxxxxxxxx/articles/39124670914969-初心者歓迎認定ライバーについて
2025-03-03 16:51:05,472 - INFO - クローリング中: https://xxxxxxxxxx/articles/35527635492505-ミニキャラ機能について
2025-03-03 16:51:08,072 - INFO - クローリング中: https://xxxxxxxxxx/sections/5696309246361-ルール-ガイドライン
2025-03-03 16:51:10,846 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYWZ84QGT5DKYBZG81
2025-03-03 16:51:13,477 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYJKBFE75QNDB02Y1K
2025-03-03 16:51:16,128 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYB75BCN1SKKAYRH3Q
2025-03-03 16:51:18,675 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYXN068YTBVGK5B4WB
2025-03-03 16:51:21,170 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYH2Z519KVP8AXV5MS
2025-03-03 16:51:23,777 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYF78DT8EFX5M0Y37P
2025-03-03 16:51:26,280 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYYNJSY1X8SY4E973S
2025-03-03 16:51:28,803 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYBW626R7NX2HHT528
2025-03-03 16:51:31,436 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYM3NVNN7R8XZ8FEZJ
2025-03-03 16:51:33,950 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYSGQK176EJSTQE7KE
2025-03-03 16:51:36,512 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RY8FC4ET0EN2N3HMC6
2025-03-03 16:51:39,093 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RY9J77RAAMGDKDBPSX
2025-03-03 16:51:41,760 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYWTWAEPT9ABRWNS42
2025-03-03 16:51:44,500 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYK42FNPEVQVTNYMN6
2025-03-03 16:51:47,255 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RY2V4B7S70SH4D9P5S
2025-03-03 16:51:49,916 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYHJWJWS4PEG4ZN9BD
2025-03-03 16:51:53,126 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYAGQ23J2916KASKCT
2025-03-03 16:51:55,788 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609#h_01HFTWH4RYN9TB7WRSF2Q2B1VY
2025-03-03 16:51:58,354 - INFO - クローリング中: https://xxxxxxxxxx/articles/8740937435801
2025-03-03 16:52:01,270 - INFO - クローリング中: https://xxxxxxxxxx/articles/8741202584729
2025-03-03 16:52:04,152 - INFO - クローリング中: https://xxxxxxxxxx/articles/19268276041625
2025-03-03 16:52:06,996 - INFO - クローリング中: https://xxxxxxxxxx/articles/8747559671321
2025-03-03 16:52:09,990 - INFO - クローリング中: https://xxxxxxxxxx/articles/8822470139801
2025-03-03 16:52:12,990 - INFO - クローリング中: https://xxxxxxxxxx/articles/8747597132953
2025-03-03 16:52:15,924 - INFO - クローリング中: https://xxxxxxxxxx/articles/8822766192409
2025-03-03 16:52:18,862 - INFO - クローリング中: https://xxxxxxxxxx/articles/8822803359513
2025-03-03 16:52:22,011 - INFO - クローリング中: https://xxxxxxxxxx/articles/8747660228761
2025-03-03 16:52:25,282 - INFO - クローリング中: https://xxxxxxxxxx/articles/8823063488281
2025-03-03 16:52:28,170 - INFO - クローリング中: https://xxxxxxxxxx/articles/8822874845465
2025-03-03 16:52:31,146 - INFO - クローリング中: https://xxxxxxxxxx/articles/8822991317273
2025-03-03 16:52:33,972 - INFO - クローリング中: https://xxxxxxxxxx/articles/4404862567705-寝落ち配信や無言配信について
2025-03-03 16:52:36,869 - INFO - クローリング中: https://xxxxxxxxxx/articles/8822909998617
2025-03-03 16:52:39,769 - INFO - クローリング中: https://xxxxxxxxxx/articles/8750940878489
2025-03-03 16:52:42,661 - INFO - クローリング中: https://xxxxxxxxxx/articles/8747618026905
2025-03-03 16:52:45,637 - INFO - クローリング中: https://xxxxxxxxxx/articles/8823046195737
2025-03-03 16:52:48,701 - INFO - クローリング中: https://xxxxxxxxxx/articles/8822871157657
2025-03-03 16:52:51,692 - INFO - クローリング中: https://xxxxxxxxxx/articles/8747717050393
2025-03-03 16:52:55,277 - INFO - クローリング中: https://xxxxxxxxxx/articles/11803689419289
2025-03-03 16:52:58,170 - INFO - クローリング中: https://xxxxxxxxxx/articles/8823250959385
2025-03-03 16:53:01,086 - INFO - クローリング中: https://xxxxxxxxxx/articles/360041998511-ミュート機能とはなんですか-
2025-03-03 16:53:04,050 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037419372-ブロック機能とはなんですか-
2025-03-03 16:53:06,881 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037419752-通報機能について
2025-03-03 16:53:09,465 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037419972-不適切な行為をするユーザーがいる場合
2025-03-03 16:53:12,194 - INFO - クローリング中: https://xxxxxxxxxx/articles/8823069583769
2025-03-03 16:53:15,069 - INFO - クローリング中: https://xxxxxxxxxx/articles/25507921507609--禁止行為-と-注意事項-について#article-container
2025-03-03 16:53:17,806 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCJlkRjjzBzoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCBm1mQYzFzoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIB2y9oYy9qYS9hcnRpY2xlcy84NzQxMjAyNTg0NzI5LSVFMyU4MiU4RiVFMyU4MSU4NCVFMyU4MSU5QiVFMyU4MSVBNCVFMyU4MSVBQSVFOCVBMSVBOCVFNyU4RiVCRSVFMyU4MSVCRSVFMyU4MSU5RiVFMyU4MSVBRiVFMyU4MiU4RiVFMyU4MSU4NCVFMyU4MSU5QiVFMyU4MSVBNCVFMyU4MSVBQSVFOCVBMSU4QyVFNyU4MiVCQSVFMyU4MSVBQiVFMyU4MSVBNCVFMyU4MSU4NCVFMyU4MSVBNgY7CFQ6CXJhbmtpBg%3D%3D--31601ee31b63bfd46663b8d5356e482aef730c00
2025-03-03 16:53:20,491 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCJmp7UcGCDoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCBm1mQYzFzoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSJ5L2hjL2phL2FydGljbGVzLzg4MjMwNjk1ODM3NjktSVJJQU0lRTUlQTQlOTYlRTMlODElQTclRTMlODElQUUlRTglQTElOEMlRTclODIlQkElRTMlODElQUIlRTMlODElQTQlRTMlODElODQlRTMlODElQTYGOwhUOglyYW5raQc%3D--dcb932198741170787b5d6aba8a7e3565e34bb78
2025-03-03 16:53:23,150 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCBnKiEYGCDoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCBm1mQYzFzoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIB2y9oYy9qYS9hcnRpY2xlcy84ODIzMDQ2MTk1NzM3LSVFNSU5NiVCNiVFNSU4OCVBOSVFNyU5QiVBRSVFNyU5QSU4NCVFMyU4MSVBNyVFMyU4MSVBRSVFMyU4MSU5NCVFNSU4OCVBOSVFNyU5NCVBOCVFMyU4MiU4NCVFMyU4MSU5RCVFMyU4MSVBRSVFNCVCQiU5NiVFNSU4QiVBNyVFOCVBQSU5OCVFOCVBMSU4QyVFNyU4MiVCQSVFMyU4MSVBQiVFMyU4MSVBNCVFMyU4MSU4NCVFMyU4MSVBNgY7CFQ6CXJhbmtpCA%3D%3D--f9d07ad14058d4d2ab8f8df028449e0c2e03b3e1
2025-03-03 16:53:25,783 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCJm3vz6GEToYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCBm1mQYzFzoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIBgi9oYy9qYS9hcnRpY2xlcy8xOTI2ODI3NjA0MTYyNS0lRTYlQjAlQjQlRTclOUQlODAlRTMlODElQUUlRTUlOEYlOTYlRTMlODIlOEElRTYlODklQjElRTMlODElODQlRTMlODElQUIlRTMlODElQTQlRTMlODElODQlRTMlODElQTYGOwhUOglyYW5raQk%3D--48c1a72bd5d002a282b57e98cf6617bc68c9c55d
2025-03-03 16:53:28,476 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCBnWL7P0BzoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCBm1mQYzFzoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIBwC9oYy9qYS9hcnRpY2xlcy84NzQ3NTU5NjcxMzIxLSVFNCVCQiU5NiVFMyU4MSVBRSVFNCVCQSVCQSVFMyU4MSVBRSVFOCU5MSU5NyVFNCVCRCU5QyVFNyU4OSVBOSVFMyU4MiU5MiVFNCVCRCVCRiVFNyU5NCVBOCVFMyU4MSU5OSVFMyU4MiU4QiVFOCVBMSU4QyVFNyU4MiVCQSVFMyU4MSVBQiVFMyU4MSVBNCVFMyU4MSU4NCVFMyU4MSVBNgY7CFQ6CXJhbmtpCg%3D%3D--0885c2d25667a9a7380fe785cc89713ea5c2e8fa
2025-03-03 16:53:31,217 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037781751-配信中の楽曲使用-歌唱や演奏-BGM-について#h_01GN6RJRG0ESEXXAB398MKE4MJ
2025-03-03 16:53:33,913 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037781751-配信中の楽曲使用-歌唱や演奏-BGM-について#h_01GN6RK0G6QZ1ZMFCPDV2N9DFY
2025-03-03 16:53:36,375 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037781751-配信中の楽曲使用-歌唱や演奏-BGM-について#h_01GN6RK6CZ9ANS7EN0PQZBM32C
2025-03-03 16:53:38,782 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037781751-配信中の楽曲使用-歌唱や演奏-BGM-について#h_01GN6RKCAZ0FRENF43G2A8DM97
2025-03-03 16:53:41,192 - INFO - クローリング中: https://xxxxxxxxxx/articles/360037781751-配信中の楽曲使用-歌唱や演奏-BGM-について#article-container
2025-03-03 16:53:43,581 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCChIPNRTADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCPeQ7NNTADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSJ8L2hjL2phL2FydGljbGVzLzM2MDA0MzAwNTk5Mi0lRTMlODMlOTUlRTMlODIlQTElRTMlODMlQjMlRTMlODMlOTAlRTMlODMlODMlRTMlODIlQjglRTMlODElQUIlRTMlODElQTQlRTMlODElODQlRTMlODElQTYGOwhUOglyYW5raQY%3D--14372d24f0dbedfc60b03c332679c78697b71504
2025-03-03 16:53:46,449 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCEIlbYzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCPeQ7NNTADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSJhL2hjL2phL2FydGljbGVzLzkwMDAwNDEyODA2Ni0lRTMlODMlODAlRTMlODIlQTQlRTMlODMlQTQlRTMlODElQUIlRTMlODElQTQlRTMlODElODQlRTMlODElQTYGOwhUOglyYW5raQc%3D--6504dade4d98ad303653475edcb1d5c7075e29eb
2025-03-03 16:53:49,513 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCAKOgIzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCPeQ7NNTADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSJ8L2hjL2phL2FydGljbGVzLzkwMDAwNTQwMDA2Ni0lRTMlODIlQjklRTMlODIlQUQlRTMlODMlODMlRTMlODMlOTclRTMlODMlOTElRTMlODIlQjklRTMlODElQUIlRTMlODElQTQlRTMlODElODQlRTMlODElQTYGOwhUOglyYW5raQg%3D--ceb69dbabd3c491851ac889abfbc383d26a3e5a3
2025-03-03 16:53:52,564 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCP8noIzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCPeQ7NNTADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSJzL2hjL2phL2FydGljbGVzLzkwMDAwNzQ3MTEwMy0lRTYlOTklODIlRTklOTYlOTMlRTMlODMlODAlRTMlODIlQTQlRTMlODMlQTQlRTMlODElQUIlRTMlODElQTQlRTMlODElODQlRTMlODElQTYGOwhUOglyYW5raQk%3D--02175ad56220b713cf51359f73459ae34210cf8c
2025-03-03 16:53:55,864 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCLYIdYzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCPeQ7NNTADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIBki9oYy9qYS9hcnRpY2xlcy85MDAwMDQ2NDUwNDYtJUUzJTgyJUIzJUUzJTgzJTlGJUUzJTgzJUE1JUUzJTgzJThCJUUzJTgzJTg2JUUzJTgyJUEzJUUzJTgzJUE5JUUzJTgzJUIzJUUzJTgyJUFGJUUzJTgxJUFCJUUzJTgxJUE0JUUzJTgxJTg0JUUzJTgxJUE2BjsIVDoJcmFua2kK--636c5fb289cf135cbaaf0de5db49beb52ff43063
2025-03-03 16:53:59,497 - INFO - クローリング中: https://xxxxxxxxxx/articles/900005592143
2025-03-03 16:54:03,004 - INFO - クローリング中: https://xxxxxxxxxx/articles/900004645266
2025-03-03 16:54:05,814 - INFO - クローリング中: https://xxxxxxxxxx/articles/900004645046-コミュニティランクについて#article-container
2025-03-03 16:54:08,316 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCE98g4zRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCLYIdYzRADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIBmy9oYy9qYS9hcnRpY2xlcy85MDAwMDU1OTIxNDMtJUUzJTgzJTg3JUUzJTgyJUE0JUUzJTgzJUFBJUUzJTgzJUJDJUUzJTgzJUE5JUUzJTgzJUIzJUUzJTgyJUFGJUUzJTgyJUI5JUUzJTgyJUIzJUUzJTgyJUEyJUUzJTgxJUFCJUUzJTgxJUE0JUUzJTgxJTg0JUUzJTgxJUE2BjsIVDoJcmFua2kG--9710bccc03d50134fab778552c75313dbba7a25c
2025-03-03 16:54:10,892 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCP8noIzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCLYIdYzRADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSJzL2hjL2phL2FydGljbGVzLzkwMDAwNzQ3MTEwMy0lRTYlOTklODIlRTklOTYlOTMlRTMlODMlODAlRTMlODIlQTQlRTMlODMlQTQlRTMlODElQUIlRTMlODElQTQlRTMlODElODQlRTMlODElQTYGOwhUOglyYW5raQc%3D--01de36cfb0ef869d2f279454f2589bbc315a733a
2025-03-03 16:54:13,480 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCJIJdYzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCLYIdYzRADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIB7C9oYy9qYS9hcnRpY2xlcy85MDAwMDQ2NDUyNjYtJUUzJTgyJUIzJUUzJTgzJTlGJUUzJTgzJUE1JUUzJTgzJThCJUUzJTgzJTg2JUUzJTgyJUEzJUUzJTgzJUE5JUUzJTgzJUIzJUUzJTgyJUFGJUUzJTgxJThDJUU1JUE0JTg5JUU1JThCJTk1JUUzJTgxJTk5JUUzJTgyJThCJUUzJTgyJUJGJUUzJTgyJUE0JUUzJTgzJTlGJUUzJTgzJUIzJUUzJTgyJUIwJUUzJTgxJUFCJUUzJTgxJUE0JUUzJTgxJTg0JUUzJTgxJUE2BjsIVDoJcmFua2kI--0d25a6278592376495a52eacc9c0dfa09d695522
2025-03-03 16:54:16,606 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCAKOgIzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCLYIdYzRADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSJ8L2hjL2phL2FydGljbGVzLzkwMDAwNTQwMDA2Ni0lRTMlODIlQjklRTMlODIlQUQlRTMlODMlODMlRTMlODMlOTclRTMlODMlOTElRTMlODIlQjklRTMlODElQUIlRTMlODElQTQlRTMlODElODQlRTMlODElQTYGOwhUOglyYW5raQk%3D--af9b032476206b7a70f2a6cf4e94e6ef2a8561b1
2025-03-03 16:54:19,214 - INFO - クローリング中: https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCFYJdYzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCLYIdYzRADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIBmy9oYy9qYS9hcnRpY2xlcy85MDAwMDQ2NDUyMDYtJUUzJTgxJTgyJUUzJTgyJTkzJUUzJTgxJTk3JUUzJTgyJTkzJUUzJTgzJUE5JUUzJTgzJUIzJUUzJTgyJUFGJUUzJTgyJUI5JUUzJTgyJUIzJUUzJTgyJUEyJUUzJTgxJUFCJUUzJTgxJUE0JUUzJTgxJTg0JUUzJTgxJUE2BjsIVDoJcmFua2kK--cd13f662ba50a674ce3e1242815ed394720f2859
2025-03-03 16:54:22,449 - INFO - クローリング完了。100ページを取得しました。
2025-03-03 16:54:22,484 - INFO - クロール済みデータを保存しました。100件のドキュメントを取得。
2025-03-03 16:54:22,484 - INFO - テキスト処理を開始します...
2025-03-03 16:54:22,492 - INFO - テキスト処理完了。157件のチャンクに分割されました。
2025-03-03 16:54:22,492 - INFO - ベクトルインデックスを作成中...
2025-03-03 16:54:27,139 - INFO - HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK"
2025-03-03 16:54:27,208 - INFO - Loading faiss with AVX2 support.
2025-03-03 16:54:27,355 - INFO - Successfully loaded faiss with AVX2 support.
2025-03-03 16:54:27,363 - INFO - Failed to load GPU Faiss: name 'GpuIndexIVFFlat' is not defined. Will not load constructor refs for GPU indexes.
2025-03-03 16:54:27,383 - INFO - ベクトルインデックスを保存しました: faiss_index
2025-03-03 16:54:27,383 - INFO - RAGシステムを構築中...
/workspace/iriam-rag/claude.py:268: LangChainDeprecationWarning: The method Chain.__call__
was deprecated in langchain 0.1.0 and will be removed in 1.0. Use :meth:~invoke
instead.
response = self.qa_chain({"query": question})
2025-03-03 16:54:27,948 - INFO - HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK"
2025-03-03 16:54:30,823 - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
=== 質問 ===
〇〇〇スコアとは何ですか?
=== 回答 ===
〇〇〇ランクスコアは、(省略)
=== 情報ソース ===
- https://xxxxxxxxxx/related/click?data=BAh7CjobZGVzdGluYXRpb25fYXJ0aWNsZV9pZGwrCFYJdYzRADoYcmVmZXJyZXJfYXJ0aWNsZV9pZGwrCLYIdYzRADoLbG9jYWxlSSIHamEGOgZFVDoIdXJsSSIBmy9oYy9qYS9hcnRpY2xlcy85MDAwMDQ2NDUyMDYtJUUzJTgxJTgyJUUzJTgyJTkzJUUzJTgxJTk3JUUzJTgyJTkzJUUzJTgzJUE5JUUzJTgzJUIzJUUzJTgyJUFGJUUzJTgyJUI5JUUzJTgyJUIzJUUzJTgyJUEyJUUzJTgxJUFCJUUzJTgxJUE0JUUzJTgxJTg0JUUzJTgxJUE2BjsIVDoJcmFua2kK--cd13f662ba50a674ce3e1242815ed394720f2859
- https://xxxxxxxxxx/articles/900004645206-あんしんランクスコアについて
- https://xxxxxxxxxx/articles/900004645266
ちゃんと動きました。また、クローリング元の情報を元に回答できていますし、回答根拠とする URL も出せています(回答部分は意図して省略しています)。これは結構実用性ありそうです。
所感
実際には、プロダクションコードとして出すならクラスやファイル分割してユニットテスト書いてページ取得上限ももうちょっと上げて、(あとクローリング対象ちゃんと取れてるかとか余計なの取ってきてないかとか)
DB も CSV ファイルじゃなくて pgvector などのベクトルDB を使うべきでしょう。
また、 Dockerfile 書いて CI/CD 組んで... などなどが必要になりますがこの感じであれば最初に出してくれた工程の
- チャットボットインターフェース実装
以降も実装して!と指示すればある程度はやってくれそうです。
気が向いたら続編も書きます。
(向かなかったらアプリケーション構築は v0 使うと思います... 😇
Discussion