Langchainで文字列からDocumentオブジェクトを作成
Langchainの進化について
2024年にLangchainはモジュール構造が変更され、langchain-core
、langchain-community
、langchain-text-splitters
の3つの主要パッケージに分割されました。本記事では最新のlangchain-core
を使用した方法を中心に解説します。
核心的なエラーと解決策
最初に発生するAttributeError: 'tuple' object has no attribute 'page_content'
エラーの主な原因は2つです:
Documentオブジェクトをリストでラップしていない
Langchainの多くの関数は単一のDocumentオブジェクトではなくDocumentのリストを期待しています古いインポートパスの使用
最新バージョンではインポートパスがlangchain_core.documents
に変更されています
基本的なソリューション
文字列から単一Documentを作成
from langchain_core.documents import Document
# 文字列からDocumentを作成
text = "分析対象のテキスト内容"
doc = Document(
page_content=text,
metadata={"source": "カスタムソース", "created_at": "2023-12-01"}
)
複数Documentを処理する場合
# 複数の文字列からDocumentリストを作成
texts = ["テキスト1", "テキスト2", "テキスト3"]
documents = [
Document(page_content=text, metadata={"index": i})
for i, text in enumerate(texts)
]
重要な注意点
load_qa_chain
のinput_documents
パラメータはDocumentオブジェクトのリストを要求します。単一のDocumentでも必ずリストでラップしてください。
load_qa_chainの正しい呼び出し方
# 正しい呼び出し方 (docをリストでラップ)
chain({
"input_documents": [doc], # 単一のDocumentでもリスト内に配置
"human_input": query
})
メタデータ活用のベストプラクティス
Documentオブジェクトにはソース情報やカスタム属性を追加できます:
# メタデータの効果的な活用例
metadata = {
"source": "APIデータ",
"timestamp": "2023-12-01T14:30:00Z",
"document_type": "財務レポート",
"version": 1.2
}
doc = Document(
page_content="2023年第四四半期の決算報告...",
metadata=metadata
)
高度なユースケース
テキストスプリッターとの統合
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
# 長文を分割して複数のDocumentを作成
long_text = "非常に長い財務分析レポートの内容..."
split_docs = text_splitter.create_documents(
texts=[long_text],
metadatas=[{"source": "社内レポート"}]
)
複数ソースからのドキュメント作成
# 様々なソースからDocumentを作成する例
def create_document_from_source(content, source_type):
metadata = {
"source": source_type,
"processing_date": datetime.now().isoformat()
}
return Document(page_content=content, metadata=metadata)
# 異なるソースのデータを統合
database_doc = create_document_from_source("DBからのデータ", "database")
api_doc = create_document_from_source("APIからのレスポンス", "api")
user_input_doc = create_document_from_source("ユーザー入力テキスト", "user_input")
all_docs = [database_doc, api_doc, user_input_doc]
トラブルシューティング
ImportError
が発生する場合
古いバージョン(0.0.x系)を使用している場合は互換性のあるインポート方法:
::code-group
# 古いバージョンの場合
from langchain.docstore.document import Document
# 推奨: 最新バージョン (v0.1.x以降)
from langchain_core.documents import Document
:::
「page_content」属性エラーの解決策
# AttributeErrorに対処するために必ずチェック
if isinstance(doc, Document):
# 適切な処理
print(doc.page_content)
else:
# 型を確認し変換
print(f"期待されるDocument型ではありません: {type(doc)}")
# リストに変換
if not isinstance(doc, list):
doc = [doc]
ファイル経由の代替方法
どうしてもDocument作成で問題が解決しない場合の代替手段:
from langchain_community.document_loaders import TextLoader
import tempfile
def str_to_doc(text):
"""文字列を一時ファイル経由でDocumentに変換"""
with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as tmp:
tmp.write(text.encode('utf-8'))
tmp_path = tmp.name
loader = TextLoader(tmp_path)
docs = loader.load()
return docs
結論
Langchainで文字列からDocumentオブジェクトを作成する基本原則は次の通りです:
最新のインポートパスを使用する
from langchain_core.documents import Document
単一のDocumentであってもリストでラップする
input_documents=[doc]
形式で渡すメタデータを活用して文書の出典を管理する
検索拡張生成(RAG)のパフォーマンス向上に貢献長文は適切に分割する
テキストスプリッターを使用して処理しやすいサイズに分割