エンタープライズグレードのAIアプリケーションを構築する際、文書解析は避けて通れない工程です。PDF、Word文書、PowerPointプレゼンテーション——これらは業務システムの根幹をなすファイル形式でありながら、テキスト抽出には технических課題が伴います。本稿では、2026年最新の料金体系に基づいたコスト最適化と、実戦投入可能なコード例を示します。
2026年 主要LLM API 料金比較
ドキュメント解析の前処理フェーズでは、抽出したテキストをLLMで構造化・高精度化するのが常套手段です。まず、各APIのoutputトークン単価を確認しましょう:
| モデル | Output料金 (/MTok) | 1000万トークン/月 | 日本円換算(HolySheep ¥1=$1) |
|---|---|---|---|
| Claude Sonnet 4.5 | $15.00 | $150.00 | ¥150 |
| GPT-4.1 | $8.00 | $80.00 | ¥80 |
| Gemini 2.5 Flash | $2.50 | $25.00 | ¥25 |
| DeepSeek V3.2 | $0.42 | $4.20 | ¥4.20 |
注目すべきはDeepSeek V3.2の圧倒的低コストです。Claude Sonnet 4.5相比,月間1000万トークン處理で¥145.80の差が生まれます。ただし、高精度なドキュメント解析には、Gemini 2.5 Flashのバランスが良い仗と考えています。私は社内の契約書解析システムで実際に这般の比較を行い,白天処理はDeepSeek、精度が求められる部分是Geminiという風に振り分けています。
文書形式別の前処理アーキテクチャ
PDF解析:高精度テキスト抽出の実装
PDFは最も覆盖面が広いファイル形式ですが、表形式や画像内テキストの扱いが課題です。以下のコードは、PyMuPDF(fitz)を用いた基本的なテキスト抽出と、レイアウト保持の両立を実現します:
import fitz # PyMuPDF
import io
from PIL import Image
import pytesseract
def extract_pdf_with_layout(pdf_path: str, use_ocr_fallback: bool = True) -> dict:
"""
PDFからテキストを抽出し、レイアウト情報を保持する
Args:
pdf_path: PDFファイルのパス
use_ocr_fallback: テキスト抽出失敗時にOCRを使用するか
Returns:
dict: {"pages": [{"text": str, "images": [base64], "tables": []}]}
"""
doc = fitz.open(pdf_path)
result = {"pages": [], "metadata": {"page_count": len(doc)}}
for page_num in range(len(doc)):
page = doc[page_num]
page_data = {
"page_num": page_num + 1,
"text": "",
"images": [],
"tables": [],
"rects": []
}
# ブロック単位でテキスト抽出(レイアウト保持)
blocks = page.get_text("blocks")
for block in blocks:
x0, y0, x1, y1, text, block_no, block_type = block
if block_type == 0: # テキストブロック
page_data["text"] += text + "\n"
page_data["rects"].append({"x": x0, "y": y0, "w": x1-x0, "h": y1-y0})
# 画像抽出(OCR用)
image_list = page.get_images(full=True)
for img_index, img in enumerate(image_list):
xref = img[0]
base_image = doc.extract_image(xref)
image_bytes = base_image["image"]
# OCR処理(図表内のテキスト対応)
if use_ocr_fallback:
image = Image.open(io.BytesIO(image_bytes))
ocr_text = pytesseract.image_to_string(image, lang='jpn+eng')
if ocr_text.strip():
page_data["text"] += f"\n[画像内テキスト {img_index + 1}]\n{ocr_text}"
page_data["images"].append(base_image["ext"])
result["pages"].append(page_data)
doc.close()
return result
使用例
if __name__ == "__main__":
result = extract_pdf_with_layout("sample_document.pdf")
total_text = "\n".join(p["text"] for p in result["pages"])
print(f"抽出テキスト長: {len(total_text)} 文字")
print(f"ページ数: {result['metadata']['page_count']}")
Word文書解析:python-docxと構造抽出
Word文書は段落、見出し、テーブルと言った論理構造を持つため、抽出したテキストに構造情報を付与ことが重要です。以下は[HolySheep AI](https://www.holysheep.ai/register)を活用した高精度な構造化処理の実装例です:
from docx import Document
from docx.table import Table
from docx.text.paragraph import Paragraph
from typing import List, Dict
import httpx
HolySheep AI API設定
HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
class DocumentParser:
"""Word/PPT文書の構造化パーサー"""
def __init__(self, api_key: str):
self.api_key = api_key
self.client = httpx.Client(
base_url=HOLYSHEEP_BASE_URL,
headers={"Authorization": f"Bearer {api_key}"},
timeout=30.0
)
def extract_word_structure(self, docx_path: str) -> List[Dict]:
"""Word文書から構造화된テキストを抽出"""
doc = Document(docx_path)
elements = []
for element in doc.element.body:
if element.tag.endswith('p'): # 段落
para = Paragraph(element, doc)
text = para.text.strip()
style_name = para.style.name if para.style else "Normal"
elements.append({
"type": "paragraph",
"style": style_name,
"level": self._get_heading_level(style_name),
"text": text
})
elif element.tag.endswith('tbl'): # テーブル
table = Table(element, doc)
table_data = []
for row in table.rows:
row_data = [cell.text.strip() for cell in row.cells]
table_data.append(row_data)
elements.append({
"type": "table",
"data": table_data,
"rows": len(table_data),
"cols": len(table_data[0]) if table_data else 0
})
return elements
def _get_heading_level(self, style_name: str) -> int:
"""見出しレベルを判定"""
if "Heading 1" in style_name:
return 1
elif "Heading 2" in style_name:
return 2
elif "Heading 3" in style_name:
return 3
return 0
def enhance_with_ai(self, text: str, model: str = "gpt-4.1") -> str:
"""
HolySheep AI APIでテキストの構造化・校正を行う
レイテンシ <50ms の高速処理
"""
response = self.client.post(
"/chat/completions",
json={
"model": model,
"messages": [
{
"role": "system",
"content": "あなたは專業的な文書校正AIです。入力されたテキストの誤字脱字を修正し、構造を整理してください。"
},
{
"role": "user",
"content": text
}
],
"temperature": 0.3,
"max_tokens": 2000
}
)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
使用例
if __name__ == "__main__":
parser = DocumentParser(HOLYSHEEP_API_KEY)
# Word文書から構造抽出
elements = parser.extract_word_structure("contract.docx")
# 見出しのみ抽出
headings = [e for e in elements if e["level"] > 0]
print("文書構造:")
for h in headings:
print(f"{'#' * h['level']} {h['text']}")
# 全テキストを結合
full_text = "\n".join(e["text"] for e in elements if e["type"] == "paragraph")
# AIで校正(Gemini 2.5 Flashでコスト最適化)
# 註:HolySheepでは ¥1=$1 の為替レートで75%節約
corrected = parser.enhance_with_ai(full_text, model="gemini-2.5-flash")
print(f"\n校正結果:\n{corrected}")
PowerPoint解析:slide単位のテキスト抽出
from pptx import Presentation
from pptx.util import Inches, Pt
import json
from typing import List, Dict
class PPTAnalyzer:
"""PowerPointプレゼンテーションの構造化解析"""
def __init__(self):
self.slide_data = []
def extract_slides(self, pptx_path: str) -> List[Dict]:
"""PPTファイルから各スライドのテキスト・図形を抽出"""
prs = Presentation(pptx_path)
results = []
for idx, slide in enumerate(prs.slides):
slide_info = {
"slide_number": idx + 1,
"title": "",
"content": [],
"shapes_count": len(slide.shapes),
"images": 0
}
for shape in slide.shapes:
# タイトル抽出
if shape.has_text_frame:
text = shape.text_frame.text.strip()
if shape.shape_type == 14: # タイトル
slide_info["title"] = text
else:
if text:
slide_info["content"].append({
"text": text,
"font_size": shape.text_frame.paragraphs[0].font.size
})
# 画像カウント
if hasattr(shape, 'image'):
slide_info["images"] += 1
results.append(slide_info)
self.slide_data = results
return results
def generate_markdown(self) -> str:
"""Markdown形式に変換"""
md_lines = ["# PowerPoint Export\n"]
for slide in self.slide_data:
md_lines.append(f"\n## スライド {slide['slide_number']}\n")
if slide["title"]:
md_lines.append(f"### {slide['title']}\n")
for item in slide["content"]:
md_lines.append(f"- {item['text']}")
if slide["images"] > 0:
md_lines.append(f"\n*画像: {slide['images']}枚*\n")
return "\n".join(md_lines)
def extract_for_llm(self, max_chars: int = 8000) -> str:
"""LLM処理用のプロンプトFriendlyテキスト"""
output_parts = []
for slide in self.slide_data:
part = f"[スライド{slide['slide_number']}]"
if slide["title"]:
part += f"\nタイトル: {slide['title']}"
for item in slide["content"][:10]: # 最大10項目
part += f"\n・{item['text']}"
if len(part) + sum(len(p) for p in output_parts) < max_chars:
output_parts.append(part)
return "\n\n".join(output_parts)
使用例
if __name__ == "__main__":
analyzer = PPTAnalyzer()
slides = analyzer.extract_slides("presentation.pptx")
print(f"総スライド数: {len(slides)}")
# LLM用にフォーマット
llm_text = analyzer.extract_for_llm()
print(f"\nLLM入力テキスト ({len(llm_text)}文字):\n{llm_text[:500]}...")
HolySheep AI との統合:高コスト効率な実装
ドキュメント解析の核心は、前処理で抽出したテキストをLLMで高精度に構造化することです。[HolySheep AI](https://www.holysheep.ai/register)を使用すれば、公式為替レート¥1=$1的优势を活かし、APIコストを最大85%削減できます。WeChat Pay/Alipayにも対応しており、日本語ドキュメント処理に最適な環境です。
import httpx
from typing import Optional, List, Dict
import json
class HolySheepDocumentProcessor:
"""
HolySheep AI APIを活用したドキュメント解析パイプライン
特徴:
- ¥1=$1の優位な為替レート
- <50msの低レイテンシ
- 登録で無料クレジット付与
"""
MODELS = {
"high_accuracy": "gpt-4.1", # $8/MTok - 高精度
"balanced": "gemini-2.5-flash", # $2.50/MTok - バランス型
"cost_efficient": "deepseek-v3.2" # $0.42/MTok - コスト重視
}
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.holysheep.ai/v1"
def _make_request(self, model: str, messages: List[Dict],
max_tokens: int = 2000) -> str:
"""HolySheep APIへのリクエスト"""
with httpx.Client(
base_url=self.base_url,
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
},
timeout=30.0
) as client:
response = client.post(
"/chat/completions",
json={
"model": model,
"messages": messages,
"temperature": 0.3,
"max_tokens": max_tokens
}
)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
def analyze_invoice(self, extracted_text: str) -> Dict:
"""請求書の自動解析・構造化"""
prompt = f"""以下の請求書テキストから情報を抽出してください:
- 請求書番号
- 発行日
- 請求先
- 明細項目と金額
- 合計金額
テキスト:
{extracted_text[:3000]}
結果をJSON形式で出力してください。"""
result = self._make_request(
model=self.MODELS["balanced"], # Gemini 2.5 Flash
messages=[{"role": "user", "content": prompt}],
max_tokens=1500
)
# JSONパース
try:
return json.loads(result)
except json.JSONDecodeError:
return {"raw_result": result, "error": "JSON parse failed"}
def summarize_document(self, text: str, summary_length: str = "medium") -> str:
"""文書の要約生成"""
length_instruction = {
"short": "3文程度で",
"medium": "5-7文で",
"long": "10文程度で"
}.get(summary_length, "5-7文で")
prompt = f"""{length_instruction}要点を漏らさず要約してください:
{text[:5000]}
要約:"""
return self._make_request(
model=self.MODELS["cost_efficient"], # DeepSeek V3.2
messages=[{"role": "user", "content": prompt}],
max_tokens=500
)
def extract_structured_data(self, text: str, schema: Dict) -> Dict:
"""スキーマに基づいた構造化データ抽出"""
prompt = f"""以下のテキストから、指定されたスキーマに従ってデータを抽出してください。
スキーマ:
{json.dumps(schema, ensure_ascii=False, indent=2)}
テキスト:
{text[:4000]}
抽出結果(JSON):"""
return self._make_request(
model=self.MODELS["high_accuracy"], # GPT-4.1
messages=[{"role": "user", "content": prompt}],
max_tokens=2000
)
コスト計算例
def calculate_monthly_cost(token_count: int = 10_000_000) -> Dict:
"""月間コスト比較(1000万トークン)"""
prices = {
"Claude Sonnet 4.5": 15.00,
"GPT-4.1": 8.00,
"Gemini 2.5 Flash": 2.50,
"DeepSeek V3.2": 0.42
}
results = {}
for model, price_per_mtok in prices.items():
cost_usd = (token_count / 1_000_000) * price_per_mtok
cost_jpy = cost_usd # HolySheep ¥1=$1
results[model] = {
"usd": cost_usd,
"jpy": cost_jpy,
"savings_vs_claude": 150.0 - cost_usd # Claude比の節約額
}
return results
if __name__ == "__main__":
# HolySheep初期化(登録は https://www.holysheep.ai/register)
processor = HolySheepDocumentProcessor("YOUR_HOLYSHEEP_API_KEY")
# コスト比較表示
costs = calculate_monthly_cost()
print("月間1000万トークン処理コスト比較:")
print("-" * 50)
for model, data in costs.items():
print(f"{model}: ¥{data['jpy']:.2f} (Claude比 ¥{data['savings_vs_claude']:.2f}節約)")
# 例:DeepSeek使用時、Claude比 月額¥145.80節約
print("\n💡 ヒント: Gemini 2.5 Flash + DeepSeek V3.2の組み合わせが")
print(" コストパフォーマンスに優れています")
よくあるエラーと対処法
エラー1:PDFテキスト抽出で文字化けが発生する
# ❌ エラー例:日本語PDFで豆腐文字(□□□)が出力される
text = page.get_text("text")
print(text) # "□□□の□□□について"
✅ 解決方法:フォントEmbeddingの確認と代替フォント指定
import fitz
def extract_with_encoding_fallback(pdf_path: str) -> str:
"""エンコーディング問題を回避したテキスト抽出"""
doc = fitz.open(pdf_path)
all_text = []
for page in doc:
# まず標準抽出を試行
text = page.get_text("text")
# 豆腐文字率が30%を超えたら代替手法を試行
tofu_ratio = text.count('□') / max(len(text), 1)
if tofu_ratio > 0.3:
# 代替:blocks単位での抽出 + クリーンアップ
blocks = page.get_text("blocks")
cleaned_blocks = []
for block in blocks:
block_text = block[4].strip()
# 制御文字除去
block_text = ''.join(
c for c in block_text
if ord(c) >= 32 or c in '\n\r\t'
)
if block_text:
cleaned_blocks.append(block_text)
text = '\n'.join(cleaned_blocks)
all_text.append(text)
doc.close()
return '\n\n'.join(all_text)
エラー2:APIリクエストでrate limitExceededが発生する
# ❌ エラー例:一括処理でAPI制限に引っかかる
for document in documents:
result = client.post("/chat/completions", json=data) # RateLimitError
✅ 解決方法:指数バックオフ付きリトライ処理
import time
import httpx
from tenacity import retry, stop_after_attempt, wait_exponential
class RateLimitedClient:
"""レート制限を考慮したAPIクライアント"""
def __init__(self, api_key: str, base_url: str):
self.client = httpx.Client(
base_url=base_url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=60.0
)
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=2, max=60)
)
def _request_with_retry(self, method: str, endpoint: str, **kwargs):
"""指数バックオフでリトライ"""
try:
response = self.client.request(method, endpoint, **kwargs)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 5))
print(f"Rate limit. Waiting {retry_after}s...")
time.sleep(retry_after)
raise httpx.HTTPStatusError(
"Rate limited", request=response.request, response=response
)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
if e.response.status_code >= 500:
print(f"Server error. Retrying...")
raise # tenacityがリトライ
raise
使用例
client = RateLimitedClient(
"YOUR_HOLYSHEEP_API_KEY",
"https://api.holysheep.ai/v1"
)
for document in documents:
result = client._request_with_retry(
"POST", "/chat/completions",
json={"model": "deepseek-v3.2", "messages": [...]}
)
print(f"Processed: {document}")
エラー3:Word文書テーブル抽出でセルのマージ情報が欠落する
# ❌ エラー例:マージされたセル結合後のテキストが消失
for row in table.rows:
for cell in row.cells:
print(cell.text) # マージセルがNoneや空になる
✅ 解決方法:xml属性からセル結合情報を抽出
from docx import Document
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
def extract_tables_with_merge_info(docx_path: str) -> list:
"""マージ情報付きのテーブル抽出"""
doc = Document(docx_path)
tables_data = []
for table in doc.tables:
table_grid = []
# グリッド幅取得
tbl = table._tbl
grid = tbl.tblGrid
for row_idx, row in enumerate(table.rows):
row_data = []
for col_idx, cell in enumerate(row.cells):
tc = cell._tc
tcPr = tc.tcPr
# マージ情報を取得
merge_info = {
"text": cell.text,
"row_span": 1,
"col_span": 1,
"is_merged_horizontal": False,
"is_merged_vertical": False
}
# 横マージ
if tcPr is not None:
gridSpan = tcPr.find(qn('w:gridSpan'))
if gridSpan is not None:
merge_info["col_span"] = int(
gridSpan.get(qn('w:val'))
)
# 縦マージ
if tcPr is not None:
vMerge = tcPr.find(qn('w:vMerge'))
if vMerge is not None:
val = vMerge.get(qn('w:val'))
if val == "continue":
merge_info["is_merged_vertical"] = True
elif val is None: # start
pass # 開始セル
row_data.append(merge_info)
table_grid.append(row_data)
tables_data.append(table_grid)
return tables_data
HTMLテーブル出力の例
def to_html_table(table_data: list) -> str:
"""マージ情報を反映したHTMLテーブル生成"""
html = ['']
for row in table_data:
html.append('')
for cell in row:
# col_span属性
col_span_attr = (
f' colspan="{cell["col_span"]}"'
if cell["col_span"] > 1 else ''
)
row_span_attr = (
f' rowspan="{cell["row_span"]}"'
if cell["row_span"] > 1 else ''
)
html.append(
f'{cell["text"]} '
)
html.append(' ')
html.append('
')
return '\n'.join(html)
まとめ:コスト最適化のための実践的推奨
ドキュメント解析の前処理においては、以下の3点が重要です:
- 前処理の品質が結果を決める:レイアウト保持、表構造抽出、OCR適用など、下流工程の精度はここで決まります
- LLMモデルは用途に応じて使い分ける:高精度が必要な部分是GPT-4.1、コスト重視の部分はDeepSeek V3.2といった振り分けが効果的
- APIコストは積上也が 크게響く:月間1000万トークン處理でDeepSeek vs Claude比¥145.80の差,年額なら約¥1,750の節約に
[HolySheep AI](https://www.holysheep.ai/register)の¥1=$1為替レートと<50ms低レイテンシを組み合わせれば、日本語ドキュメント解析のproduction環境としても十分なコスト効率が実現可能です。WeChat Pay/Alipay対応で、チーム開発でのサブスクリプション管理も容易です。
👉 HolySheep AI に登録して無料クレジットを獲得