Claude Media
Claudeで法務文書を要約するAPI実装 — 抽出項目の定義とメタ要約

Claudeで法務文書を要約するAPI実装 — 抽出項目の定義とメタ要約

契約書など長文の法務文書をClaude APIで要約するパイプラインの作り方。抽出項目の決め方、PDFの前処理、評価指標、チャンク分割とメタ要約、条文の引用までを実装で示します。

Claude APIで法務文書を要約するときの要点は3つです。先に抽出項目を決めること、長い文書は分割して要約を束ねること、要約に根拠の位置を残すこと。プロンプトの文面より、この設計が品質を左右します。

ここではAnthropicの法務要約ガイド(サブリース契約を題材にしたガイド)の流れに沿って、API実装を組み立てます。Claudeに契約書を読ませて条項を確認するだけならClaudeに契約書を読ませてわかりにくい条項を確認する方法で足ります。ここで扱うのは、数百件の文書を同じ形式で要約し続けるパイプラインの側です。

法務要約に「唯一の正解」がない理由

「どの文書にも唯一の正しい要約はない」。ガイドは冒頭でこう述べています。何を残すか指示がなければ、Claudeは残す項目を自分で決めてしまいます。

そこで最初に、要約に含めたい項目を一覧にします。ガイドの例はサブリース契約で、次の6項目です。

details_to_extract = [
    "Parties involved (sublessor, sublessee, and original lessor)",
    "Property details (address, description, and permitted use)",
    "Term and rent (start date, end date, monthly rent, and security deposit)",
    "Responsibilities (utilities, maintenance, and repairs)",
    "Consent and notices (landlord's consent, and notice requirements)",
    "Special provisions (furniture, parking, and subletting restrictions)",
]

契約類型が変われば項目も変わります。たとえば秘密保持契約なら、秘密情報の定義・有効期間・返還義務・例外事項といった単位が候補になります(この項目立ては本記事の例で、ガイドの例ではありません)。項目は文書の種類ごとにリストとして持ち、コードに埋め込まず設定に出しておくと、評価のたびに差し替えられます。

抽出項目を決めるときの観点

ガイドが挙げる利用の目安は、大量の文書を安く回したい、メタデータを機械的に抜きたい、形式をそろえた要約がほしい、引用付きで検証したい、の4点です。項目リストを作る段階では、次の問いを立てると決めやすくなります。

  • 読み手は要約から何を判断するか(承認するか、次の工程へ回すか)
  • 数値・日付・当事者など、値そのものを抜く項目はどれか
  • 文書に書かれていないとき、空欄にするのか「記載なし」と明示するのか

3つ目はプロンプト例が明確に決めています。文書に明記されていない情報は "Not specified" と書かせる、という指示です。これがないと、Claudeが記載のない項目を補って書く余地が生まれます。

成功基準を先に置く

ガイドは要約の評価を「客観的な指標に乏しく、読み手によって重視点が違う難しい作業」と位置づけたうえで、次の6つを基準の候補に挙げています。

基準見るもの
事実の正確さ見るもの事実・法概念・要点を正しく表しているか
法的な正確さ見るもの用語や法令・判例への言及が正しいか
簡潔さ見るもの重要な細部を落とさず要点に絞れているか
一貫性見るもの複数文書で同じ構造・方針になっているか
読みやすさ見るもの法律の専門家でない読み手に通じるか
偏りのなさ見るもの主張や立場を公平に描いているか

法務の要約では、簡潔さと引き換えに重要な例外条項が落ちるのが最も痛い失敗です。基準は「全部を満点にする」より、どれを最優先にするか順位をつけて評価にかけるほうが実務的です。

モデル選択とコストの試算

高い精度が要る用途にはClaude Opus 5、文書の量が大きくコストが気になるならClaude Haiku 4.5のような小さいモデルも試す。これが公式の勧めです。試算の前提は次のとおりです。

  • 文書数は1,000件、1件あたり30万文字(合計3億文字)
  • 1トークンあたり3.5文字と仮定し、入力は約8,600万トークン
  • 要約1件あたりの出力は350トークン(合計35万トークン)

この前提で、ガイドの試算は次のようになります。

モデル入力単価出力単価合計
Claude Opus 5入力単価5ドル/MTok出力単価25ドル/MTok合計438.75ドル
Claude Haiku 4.5入力単価1ドル/MTok出力単価5ドル/MTok合計87.75ドル

内訳はOpusが入力86 × 5 = 430ドルに出力0.35 × 25 = 8.75ドル、Haikuが入力86 × 1 = 86ドルに出力0.35 × 5 = 1.75ドルです。ガイド自身が「実際の費用は異なりうる」と断っているので、目安として読んでください。

3.5文字/トークンは英語の契約書を想定した換算です。日本語の契約書は別の比率になるため、自社の文書でトークン数を数えてから試算し直してください。日本語のトークン消費の見方はClaudeで日本語要約のコンテキスト予算を分割設計するにまとめています。

件数が多いならBatches APIを検討する

急がない大量処理にはMessage Batches APIが使えます。バッチ内の利用は標準API価格の50%で、多くのバッチは1時間以内に終わります。1バッチの上限は10万リクエストまたは256MBで、24時間以内に終わらなかったバッチは期限切れになります。

上の試算を単純に半額にすると、Opusが約219ドル、Haikuが約44ドルです(本記事での計算値)。法務の日次バッチや過去文書の一括処理のように、即答が要らない用途に向きます。

PDFの前処理でテキストを整える

要約の前に、PDFからテキストを取り出して整形します。pypdf でページごとにテキストを抜き、ページ番号らしき行と余分な空白を正規表現で消す例を示す例が公式にあります。

pip install anthropic pypdf
import re
import pypdf
 
 
def get_llm_text(pdf_file):
    reader = pypdf.PdfReader(pdf_file)
    text = "\n".join([page.extract_text() for page in reader.pages])
 
    # ページ番号だけの行を除去
    text = re.sub(r"\n\s*\d+\s*\n", "\n", text)
 
    # 余分な空白を除去
    text = re.sub(r"\s+", " ", text)
 
    return text

この関数には注意点があります。最後の re.sub(r"\s+", " ", text) は改行もまとめて空白1つに潰します。条・項の区切りが改行で表されている文書では、章立ての手がかりまで消えます。後述のとおり条文単位で分割したいなら、この整形の前に条見出しで切っておくのが安全です。これは公式コードへの本記事の指摘で、公式が誤りだと言っているわけではありません。

PDFをそのままAPIに渡す方法もあります。PDFサポートの上限は、リクエスト全体で32MB、1リクエストあたり600ページです(コンテキストウィンドウが100万トークン未満のリクエストでは100ページ)。1ページの消費は、テキスト分として1,500〜3,000トークン程度が目安とされています。PDFの扱い全般はClaudeのPDF要約のコツが詳しいので、ここではテキスト抽出を前提に進めます。WordやExcelは document ブロックが受けないため、テキストに変換してから渡します。複数形式を受けるなら、前処理の段階で形式ごとの変換器を用意することになります。

抽出項目を埋め込んだ要約プロンプト

前処理したテキストと項目リストから、要約を作る関数です。ガイドのコードを、意味を変えない範囲で整えて示します。

import anthropic
 
client = anthropic.Anthropic()
 
 
def summarize_document(
    text, details_to_extract, model="claude-opus-5-5", max_tokens=1000
):
    details_to_extract_str = "\n".join(details_to_extract)
 
    prompt = f"""Summarize the following sublease agreement. Focus on these key aspects:
 
    {details_to_extract_str}
 
    Provide the summary in bullet points nested within the XML header for each section. For example:
 
    <parties involved>
    - Sublessor: [Name]
    // Add more details as needed
    </parties involved>
 
    If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
 
    Sublease agreement text:
    {text}
    """
 
    response = client.messages.create(
        model=model,
        max_tokens=max_tokens,
        system="You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.",
        messages=[{"role": "user", "content": prompt}],
    )
 
    return next(block.text for block in response.content if block.type == "text")

押さえたい設計は3つです。

  1. 項目リストをそのままプロンプトに差し込む。項目が変われば要約の型も変わる
  2. 各項目をXMLタグで囲ませる。タグ単位で後処理して構造化データにできる
  3. 記載がなければ Not specified と書かせる

タグ名に関する補足です。ガイドの例は <parties involved> のように空白を含みます。後段でXMLパーサーに通すなら、<parties_involved> のようにアンダースコアでつなぐほうが安全です。これも本記事の提案です。

長い文書はメタ要約で束ねる

契約書が1冊でコンテキストウィンドウに収まらない場合や、関連文書を複数まとめて要約したい場合は、メタ要約を使います。手順は次のとおりです。

  1. 文書をチャンクに分割する
  2. チャンクごとに同じ項目リストで要約する
  3. チャンク要約を連結し、もう一度Claudeに渡して全体の要約を作る

ガイドのコードは、20,000文字ごとの固定長で分割しています。

def chunk_text(text, chunk_size=20000):
    return [text[i : i + chunk_size] for i in range(0, len(text), chunk_size)]
 
 
def summarize_long_document(
    text, details_to_extract, model="claude-opus-5-5", max_tokens=1000
):
    details_to_extract_str = "\n".join(details_to_extract)
 
    chunk_summaries = [
        summarize_document(chunk, details_to_extract, model=model, max_tokens=max_tokens)
        for chunk in chunk_text(text)
    ]
 
    final_summary_prompt = f"""
    You are looking at the chunked summaries of multiple documents that are all related.
    Combine the following summaries of the document from different truthful sources into a coherent overall summary:
 
    <chunked_summaries>
    {"".join(chunk_summaries)}
    </chunked_summaries>
 
    Focus on these key aspects:
    {details_to_extract_str}
 
    Provide the summary in bullet points nested within the XML header for each section.
    If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
    """
 
    response = client.messages.create(
        model=model,
        max_tokens=max_tokens,
        system="You are a legal expert that summarizes notes on one document.",
        messages=[{"role": "user", "content": final_summary_prompt}],
    )
 
    return next(block.text for block in response.content if block.type == "text")

サンプルのPDFなら全体がコンテキストウィンドウに収まるのでこの関数は必須ではないと断ったうえで、それでもメタ要約は最初の単発要約が取りこぼした重要な細部を拾うことが多いと述べています。

固定長分割を条文単位に直す

ガイドのコードは動作の説明が目的なので、法務文書に使うには手を入れたい箇所があります。以下は本記事の改善案です。

  • チャンクの切れ目: 20,000文字ごとの機械的な分割は、条文や文の途中で切れることがある。第○条の見出しで分けると、1つの条が2つのチャンクに割れない
  • 連結の区切り: "".join(chunk_summaries) は要約同士を区切りなしにつなぐ。チャンク番号つきのタグで囲むと、統合の段階で出どころを追える
  • 「Not specified」の扱い: 冒頭のチャンクに当事者の記載があり、後半のチャンクには当然ない。統合のプロンプトで「どれかのチャンクに記載があればそれを採る」と一言添えないと、後半のNot specifiedが優先されるおそれがある

3つ目は挙動の保証ではなく、プロンプトの穴を先回りして塞ぐという設計上の判断です。統合結果が期待どおりになるかは、自社の文書で評価にかけて確かめます。

要約に根拠を残す — Citationsとの組み合わせ

法務の要約では、この記述は契約書のどこか、と読み手が原文に戻れることが要件になります。引用が必要な場面は、ガイドも利用判断の目安に挙げています。

APIにはCitations機能があり、応答の各主張に、根拠となる原文の位置を付けられます。位置の表現は文書の種類で決まります。

文書の種類分割単位引用の形式
プレーンテキスト分割単位文引用の形式文字位置(0始まり)
PDF分割単位文引用の形式ページ番号(1始まり)
カスタムコンテンツ分割単位追加の分割なし引用の形式ブロック番号(0始まり)

法務向けに使いやすいのはカスタムコンテンツです。渡したブロックがそのまま引用の粒度になるので、条ごとにブロックを作れば「第12条」単位で根拠を返せます。

articles = ["第1条(目的) ...", "第2条(定義) ...", "第3条(期間) ..."]  # 条見出しで分割済み
 
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "document",
                    "source": {
                        "type": "content",
                        "content": [{"type": "text", "text": a} for a in articles],
                    },
                    "title": "業務委託契約書",
                    "context": '{"contract_type": "業務委託", "version": "2026-04"}',
                    "citations": {"enabled": True},
                },
                {"type": "text", "text": "契約期間と解除条件を要約してください。"},
            ],
        }
    ],
)

title は長さに制限があるため、文書のメタデータは context に文字列またはJSON文字列で入れます。context は引用の対象にはなりません。この記事のコードはカスタムコンテンツ文書の形式に沿った例で、出力の形は自社の文書で確かめてください。

制約が2つあります。

  • 引用は、リクエスト内のすべての文書で有効にするか、どれも無効にするかのどちらかです
  • Citationsはstructured outputs(output_config.format)と併用できません。併用すると400エラーになります

つまり、要約を厳密なJSONスキーマで受けたい設計と、引用付きの設計は両立しません。JSONが要るなら、上で示したXMLタグ方式で項目を取り出し、根拠は別リクエストで引くなど、どちらを優先するかを先に決めます。有効化すると入力トークンがわずかに増えますが、cited_text は出力トークンに数えられません。

評価してから本番に出す

プロンプトは試行と調整を経て本番に出すもの、というのがガイドの立場で、評価指標として次の5つを挙げています。

指標何を測るか
ROUGE何を測るか専門家の参照要約との重なり。網羅性(再現率)の確認
BLEU何を測るか参照要約とのn-gramの一致精度
埋め込み類似度何を測るか要約同士の意味の近さ。言い回しが違っても捉える
LLMによる採点何を測るかルーブリックに沿ってClaudeが正確性・網羅性・一貫性を採点
人による評価何を測るか法律の専門家が少数の要約を検証。本番前の最終確認

日本語の法務要約では、参照要約との語の重なりを測る指標だけでは、言い換えを誤りと数えてしまう可能性があります。ここは本記事の見立てで、実測したものではありません。LLMによる採点と人のレビューを組み合わせる構成が現実的です。

LLM採点のコツは、評価ガイドが3つ挙げています。

  • 詳細で明確なルーブリックを書く(「第1文に必ず当事者名を含める。なければ不正解」のような形)
  • 出力を correct / incorrect や1〜5点のように限定して、集計できる形にする
  • 先に理由を書かせてから採点させ、理由は捨てる。複雑な判断ほど採点精度が上がるとされる

法務要約では、ルーブリックを成功基準の6つそれぞれに対応させ、たとえば「抽出項目が全部埋まっているか」「Not specifiedが本当に原文に無い項目だけか」を個別に採点させます。

本番に出す前の確認事項

運用面の注意は3つあります。

  1. 責任の所在: 要約の誤りが自社や顧客の法的責任につながりうることを理解し、AIが生成した要約であり法律の専門家がレビューすべきだと明記した注意書きを付ける
  2. 文書形式の多様性: 実際の文書はPDF、Word、テキストなどさまざま。想定する形式をすべてテキストに変換できる処理を用意する
  3. API呼び出しの並列化: 長い文書の要約は1分程度かかることがあるため、大量の文書では並列に投げる。上限はレートリミットで確認する

日本では、AIによる契約書レビューを業として行う場合の線引きが論点になります。サービスとして提供する前提なら、弁護士法72条とAIの契約書レビューも確認してください。自社の契約書を数件読ませてレビューしたいだけなら、Cowork側の契約書のバッチレビューのほうが導入は軽くなります。

発展: 要約を索引にする、ファインチューニング

性能を伸ばす方法はほかに2つあります。

要約索引付き文書(summary indexed documents)。大量の文書から探すとき、通常のRAGでは足りないことがあります。文書ごとに簡潔な要約を作り、質問に対して要約の関連度をClaudeに順位づけさせる方式で、通常のRAGより少ないコンテキストで済むと説明されています。詳しい実装はサマリゼーションのクックブックにあります。

ファインチューニング。要約が期待に届かなかった事例を集めて修正済み要約と対にしたデータセットを作り、再学習させる手順を説明しています。ただし注記によると、ファインチューニングはAmazon Bedrock経由でのみ利用できるとされています。Claude APIを直接使う構成では選べません。

まとめ

法務要約のパイプラインは、抽出項目の定義、前処理、要約、メタ要約、引用、評価の6つに分けて設計できます。中でも効くのは最初の抽出項目の定義で、ここが曖昧だと後段の評価も測れません。

長い文書は条文単位で分割してメタ要約にかけ、根拠が必要ならカスタムコンテンツ文書で条ごとに引用を返させます。structured outputsと引用が併用できない点だけは、設計の最初に決めておく分岐です。API利用者向けの法的保護の枠組みはAnthropic API利用者向け法的保護拡充の内容にまとまっています。

この記事を共有:XはてブLinkedIn