llm-document-extraction について
建設関連文書から LLM で構造化データを抽出するスキルです。PDF、図面、一括処理、大規模文書の検索に対応する手法を扱います。
主な機能
- 01RFI、提出書類、契約書の項目抽出
- 02LLM による PDF 処理
- 03ビジョンモデルによる図面の照会
- 04n8n によるファイルの一括処理
- 05大規模文書の検索インデックス作成
活用シーン
- 01RFI から関係者、日付、質問を抽出
- 02提出書類から製品と承認状況を抽出
- 03契約書から範囲と条項を抽出
- 04仕様書から材料要件を検索
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ datadrivenconstruction/ ddc_skills_for_ai_agents_in_construction --skill llm-document-extractionインストール後、Claude Code で/llm-document-extractionを入力して読み込まれたか確認します。
同梱ファイルclaw.jsoninstructions.md
詳細
LLM による文書データ抽出
概要
建設関連文書(RFI、提出書類、仕様書、契約書)には、構造化されていない形式に埋もれた重要なデータが含まれています。このスキルでは、LLM を使用して構造化データを自動的に抽出します。
「建設業界は新しいデータの洪水に溺れています。情報量は2015年の15ゼタバイトから2025年には181ゼタバイトに増加し、既存の全データの90%は、ここ数年のうちに作成されました。」— Artem Boiko
ユースケース
| 文書の種類 | 抽出項目 |
|---|---|
| RFI | 質問、回答、日付、関係者 |
| 提出書類 | 製品仕様、承認状況、材料 |
| 契約書 | 関係者、金額、日付、作業範囲、条項 |
| 仕様書 | 材料、規格、要件 |
| 日報 | 天候、労働力、機材、進捗 |
クイックスタート
from openai import OpenAI
import pdfplumber
import json
client = OpenAI()
def extract_from_pdf(pdf_path: str, extraction_schema: dict) -> dict:
"""Extract structured data from PDF using LLM"""
# Extract text from PDF
with pdfplumber.open(pdf_path) as pdf:
text = "\n".join(page.extract_text() for page in pdf.pages)
# Build extraction prompt
prompt = f"""
Extract the following information from this construction document.
Return ONLY valid JSON matching the schema.
Schema:
{json.dumps(extraction_schema, indent=2)}
Document:
{text[:8000]} # Truncate for context limits
JSON Output:
"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a construction document analyst. Extract data accurately."},
{"role": "user", "content": prompt}
],
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
抽出スキーマ
RFI スキーマ
rfi_schema = {
"rfi_number": "string",
"date_submitted": "YYYY-MM-DD",
"date_required": "YYYY-MM-DD",
"from_company": "string",
"to_company": "string",
"subject": "string",
"question": "string",
"response": "string or null",
"status": "open|closed|pending",
"cost_impact": "boolean",
"schedule_impact": "boolean",
"attachments": ["list of attachment names"]
}
# Extract
rfi_data = extract_from_pdf("RFI-0042.pdf", rfi_schema)
提出書類スキーマ
submittal_schema = {
"submittal_number": "string",
"spec_section": "string",
"description": "string",
"manufacturer": "string",
"product_name": "string",
"model_number": "string",
"submitted_by": "string",
"date_submitted": "YYYY-MM-DD",
"status": "approved|approved_as_noted|revise_resubmit|rejected",
"reviewer_comments": "string or null",
"materials": [
{
"name": "string",
"specification": "string",
"quantity": "string"
}
]
}
契約書スキーマ
contract_schema = {
"contract_number": "string",
"project_name": "string",
"owner": {
"name": "string",
"address": "string"
},
"contractor": {
"name": "string",
"address": "string"
},
"contract_amount": "number",
"start_date": "YYYY-MM-DD",
"completion_date": "YYYY-MM-DD",
"liquidated_damages": "number per day",
"retention_percentage": "number",
"key_clauses": [
{
"clause_number": "string",
"title": "string",
"summary": "string"
}
]
}
n8n による一括処理
{
"workflow": "Document Extraction Pipeline",
"trigger": "Watch folder for new PDFs",
"nodes": [
{
"name": "Read PDF",
"type": "Read Binary Files"
},
{
"name": "Classify Document",
"type": "AI Agent",
"prompt": "Classify this document: RFI, Submittal, Contract, Spec, or Other"
},
{
"name": "Route by Type",
"type": "Switch",
"rules": ["RFI", "Submittal", "Contract", "Spec"]
},
{
"name": "Extract RFI",
"type": "OpenAI",
"schema": "rfi_schema"
},
{
"name": "Extract Submittal",
"type": "OpenAI",
"schema": "submittal_schema"
},
{
"name": "Save to Database",
"type": "PostgreSQL",
"operation": "insert"
},
{
"name": "Update Dashboard",
"type": "HTTP Request",
"method": "POST"
}
]
}
図面向けビジョンモデル
import base64
def extract_from_drawing(image_path: str, query: str) -> str:
"""Extract information from drawings using vision model"""
with open(image_path, "rb") as f:
image_data = base64.standard_b64encode(f.read()).decode()
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": query},
{
"type": "image_url",
"image_url": {
"url": f"data:image/png;base64,{image_data}"
}
}
]
}
]
)
return response.choices[0].message.content
# Example: Extract room areas from floor plan
areas = extract_from_drawing(
"floor_plan.png",
"List all rooms with their areas in square meters. Return as JSON."
)
大規模な文書向け RAG
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Qdrant
def create_document_index(pdf_path: str):
"""Create searchable index for large documents"""
# Load and split
loader = PyPDFLoader(pdf_path)
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = splitter.split_documents(docs)
# Create vector store
embeddings = OpenAIEmbeddings()
vectorstore = Qdrant.from_documents(
chunks,
embeddings,
collection_name="contract_docs"
)
return vectorstore
def query_document(vectorstore, question: str) -> str:
"""Query document with RAG"""
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
docs = retriever.invoke(question)
context = "\n".join(doc.page_content for doc in docs)
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "Answer based on the contract excerpts provided."},
{"role": "user", "content": f"Context:\n{context}\n\nQuestion: {question}"}
]
)
return response.choices[0].message.content
# Usage
index = create_document_index("contract_100pages.pdf")
answer = query_document(index, "What are the liquidated damages terms?")
要件
pip install openai pdfplumber langchain langchain-openai qdrant-client
リソース
- OpenAI Vision:https://platform.openai.com/docs/guides/vision
- LangChain RAG:https://python.langchain.com/docs/tutorials/rag/
コメント
Markdown 対応