---
title: 法律文書の要約
url: https://platform.claude.com/docs/ja/about-claude/use-case-guides/legal-summarization
description: このガイドでは、Claudeの高度な自然言語処理機能を活用して法律文書を効率的に要約し、重要な情報を抽出して法務リサーチを迅速化する方法を説明します。Claudeを使用することで、契約書のレビュー、訴訟準備、規制関連業務を効率化し、時間を節約しながら法務プロセスの正確性を確保できます。
---

> Claudeを使用した法律文書要約の実装例については、[要約クックブック](https://platform.claude.com/cookbook/capabilities-summarization-guide)をご覧ください。

## Claudeで構築する前に

### 法律文書の要約にClaudeを使用するかどうかを決定する

法律文書の要約に、Claudeのような「large language model」（大規模言語モデル）、すなわちLLMを採用すべきであることを示す主な指標は次のとおりです。

<AccordionGroup>
  <Accordion title="大量の文書を効率的かつ低コストでレビューしたい">
    大規模な文書レビューは、手作業で行うと時間とコストがかかります。Claudeは膨大な量の法律文書を迅速に処理・要約でき、文書レビューに伴う時間とコストを大幅に削減します。この機能は、デューデリジェンス、契約分析、訴訟におけるディスカバリーなど、効率性が重要なタスクで特に価値があります。
  </Accordion>

  <Accordion title="重要なメタデータの自動抽出が必要である">
    Claudeは、関係当事者、日付、契約条件、特定の条項など、法律文書から重要なメタデータを効率的に抽出・分類できます。この自動抽出により情報を整理でき、大規模な文書セットの検索、分析、管理が容易になります。契約管理、コンプライアンスチェック、検索可能な法律情報データベースの作成に特に役立ちます。
  </Accordion>

  <Accordion title="明確で簡潔、かつ標準化された要約を生成したい">
    Claudeは、あらかじめ定められた形式に従った構造化された要約を生成できるため、法務専門家がさまざまな文書の要点をすばやく把握しやすくなります。こうした標準化された要約は、特に複雑な法律用語や専門用語を扱う場合に、可読性を向上させ、文書間の比較を容易にし、全体的な理解を深めます。
  </Accordion>

  <Accordion title="要約に正確な引用が必要である">
    法律文書の要約を作成する際、信頼性と法的基準への準拠を確保するためには、適切な出典の明示と引用が不可欠です。Claudeには、参照したすべての法的論点について正確な引用を含めるようプロンプトで指示できるため、法務専門家が要約された情報をレビュー・検証しやすくなります。
  </Accordion>

  <Accordion title="法務リサーチのプロセスを効率化・迅速化したい">
    Claudeは、大量の判例、法令、法律解説をすばやく分析することで法務リサーチを支援できます。関連する先例を特定し、重要な法原則を抽出し、複雑な法的議論を要約できます。この機能によりリサーチプロセスが大幅にスピードアップし、法務専門家はより高度な分析や戦略立案に集中できるようになります。
  </Accordion>
</AccordionGroup>

### 要約で抽出したい詳細を決定する

どのような文書にも、唯一の正しい要約というものは存在しません。明確な指示がなければ、Claudeがどの詳細を含めるべきかを判断するのは難しい場合があります。最適な結果を得るには、要約に含めたい具体的な情報を特定してください。

たとえば、サブリース（転貸借）契約を要約する場合、次のような要点を抽出したいと考えるかもしれません。

<CodeGroup exclude="shell">
  ```python Python
  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)",
  ]
  ```

  ```typescript TypeScript
  const detailsToExtract = [
    "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)"
  ];
  ```

  ```csharp C#
  List<string> detailsToExtract =
  [
      "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)",
  ];
  ```

  ```go Go
  var detailsToExtract = []string{
  	"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)",
  }
  ```

  ```java Java
  List<String> detailsToExtract = List.of(
          "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)");
  ```

  ```php PHP
  $detailsToExtract = [
      '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)',
  ];
  ```

  ```ruby Ruby
  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)"
  ]
  ```
</CodeGroup>

### 成功基準を確立する

要約の品質評価は、難しいことで知られるタスクです。他の多くの自然言語処理タスクとは異なり、要約の評価には明確で客観的な指標が欠けていることがよくあります。このプロセスは非常に主観的になりがちで、読み手によって要約のどの側面を重視するかが異なります。Claudeが法律文書の要約をどの程度うまく行えるかを評価する際に検討するとよい基準を以下に示します。

<AccordionGroup>
  <Accordion title="事実の正確性">
    要約は、文書内の事実、法的概念、要点を正確に表現している必要があります。
  </Accordion>

  <Accordion title="法的な精確さ">
    用語や、法令、判例、規制への参照は正確で、法的基準に沿っている必要があります。
  </Accordion>

  <Accordion title="簡潔さ">
    要約は、重要な詳細を失うことなく、法律文書を本質的な要点に凝縮している必要があります。
  </Accordion>

  <Accordion title="一貫性">
    複数の文書を要約する場合、LLMは各要約に対して一貫した構造とアプローチを維持する必要があります。
  </Accordion>

  <Accordion title="可読性">
    テキストは明確で理解しやすいものである必要があります。読み手が法律の専門家でない場合、要約には読み手を混乱させる可能性のある法律用語を含めるべきではありません。
  </Accordion>

  <Accordion title="バイアスと公平性">
    要約は、法的な議論や立場を偏りなく公平に描写している必要があります。
  </Accordion>
</AccordionGroup>

詳細については、[成功基準の確立](https://platform.claude.com/docs/ja/test-and-evaluate/develop-tests)に関するガイドを参照してください。

***

## Claudeを使用して法律文書を要約する方法

### 適切なClaudeモデルを選択する

法律文書を要約する際には、モデルの精度が極めて重要です。Claude Opus 5.5は、このように高い精度が求められるユースケースに最適な選択肢です。文書のサイズや量が大きく、コストが懸念され始める場合は、Claude Haiku 5.5のような小型のモデルを試すこともできます。

これらのコストを見積もる参考として、OpusモデルとHaikuモデルを使用して1,000件のサブリース契約を要約する場合のコスト比較を以下に示します。

* **コンテンツサイズ**

  * 契約書の数：1,000
  * 契約書あたりの文字数：300,000
  * 合計文字数：3億

* **推定トークン数**

  * 入力トークン：1億800万、契約書あたり約108,000（約2.8文字あたり1トークン。これは、このガイドのサンプル契約書で両モデルが示した比率です）
  * 要約あたりの出力トークン（出力として課金される思考トークンを含む）：Claude Opus 5.5で約6,200、Claude Haiku 5.5で約3,100
  * 合計出力トークン：Claude Opus 5.5で620万、Claude Haiku 5.5で310万

* **Claude Opus 5.5の推定コスト**

  * 入力トークンのコスト：108 MTok \* $4.00/MTok = $432.00 USD
  * 出力トークンのコスト：6.2 MTok \* $20.00/MTok = $124.00 USD
  * 合計コスト：$432.00 + $124.00 = $556.00 USD

* **Claude Haiku 5.5の推定コスト**（100,000トークンを超えるプロンプトの料金で計算）

  * 入力トークンのコスト：108 MTok \* $0.50/MTok = $54.00 USD
  * 出力トークンのコスト：3.1 MTok \* $2.50/MTok = $7.75 USD
  * 合計コスト：$54.00 + $7.75 = $61.75 USD

<Tip>
  実際のコストはこれらの見積もりと異なる場合があります。トークン数は、

  [強力なプロンプトを構築する](https://platform.claude.com/docs/ja/about-claude/use-case-guides/legal-summarization#build-a-strong-prompt)

  セクションの例をこのガイドのサンプル契約書（約105,000入力トークン）で実行した結果に基づいており、出力の長さは文書によって、また実行ごとに異なります。ここでの各契約書は約108,000トークンの1つのプロンプトであり、Claude Haiku 5.5の

  [長いプロンプトの料金](https://platform.claude.com/docs/ja/about-claude/pricing#long-context-pricing)

  の閾値である100,000トークンを超えています。プロンプトが100,000トークン以下に収まる文書の場合は、代わりに入力$0.10/MTok、出力$0.50/MTokが適用されます。どの料金が適用されるかを確認するには、

  `model`

  を

  `claude-haiku-5-5`

  に設定して

  [トークンカウント](https://platform.claude.com/docs/ja/build-with-claude/token-counting)

  で文書のトークン数を数えてください。
</Tip>

### 文書をClaudeが処理できる形式に変換する

文書の要約を始める前に、データを準備する必要があります。これには、PDFからのテキスト抽出、テキストのクリーニング、そしてClaudeで処理できる状態になっていることの確認が含まれます。

サンプルPDFに対するこのプロセスのデモを以下に示します。

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    PDFからのテキスト抽出はコードで行ってください。
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    PDFからのテキスト抽出はコードで行ってください。
  </CodeGroupItem>

  ```python Python
  from io import BytesIO
  import re

  import pypdf
  import requests


  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


  # GitHubリポジトリから完全なURLを作成します
  url = "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample Sublease Agreement.pdf"
  url = url.replace(" ", "%20")

  # PDFファイルをメモリにダウンロードします
  response = requests.get(url)

  # メモリからPDFを読み込みます
  pdf_file = BytesIO(response.content)

  document_text = get_llm_text(pdf_file)
  print(document_text[:50000])
  ```

  ```typescript TypeScript
  import { PDFParse } from "pdf-parse";

  async function getLlmText(pdfData: Uint8Array): Promise<string> {
    const parser = new PDFParse({ data: pdfData });
    const result = await parser.getText();
    await parser.destroy();
    let text = result.pages.map((page) => page.text).join("\n");

    // ページ番号を削除します
    text = text.replace(/\n\s*\d+\s*\n/g, "\n");

    // 余分な空白を削除します
    text = text.replace(/\s+/g, " ");

    return text;
  }

  // 要約クックブックからサンプルの転貸借契約書をダウンロードします
  const response = await fetch(
    "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"
  );
  const pdfData = new Uint8Array(await response.arrayBuffer());

  const documentText = await getLlmText(pdfData);
  console.log(documentText.slice(0, 50000));
  ```

  ```csharp C#
  using System.Text.RegularExpressions;
  using UglyToad.PdfPig;
  using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;

  string GetLlmText(byte[] pdfData)
  {
      using var document = PdfDocument.Open(pdfData);
      var text = string.Join("\n", document.GetPages().Select(page => ContentOrderTextExtractor.GetText(page)));

      // ページ番号を削除します
      text = Regex.Replace(text, @"\n\s*\d+\s*\n", "\n");

      // 余分な空白を削除します
      text = Regex.Replace(text, @"\s+", " ");

      return text;
  }

  // 要約 cookbook からサンプルの転貸借契約書をダウンロードします
  using var httpClient = new HttpClient();
  var pdfData = await httpClient.GetByteArrayAsync(
      "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"
  );

  var documentText = GetLlmText(pdfData);
  Console.WriteLine(documentText[..Math.Min(50000, documentText.Length)]);
  ```

  ```go Go
  import (
  	"bytes"
  	"io"
  	"net/http"
  	"regexp"

  	"github.com/ledongthuc/pdf"
  )

  // サンプルの転貸借契約書のテキスト
  var documentText string

  func getLLMText(pdfData []byte) (string, error) {
  	reader, err := pdf.NewReader(bytes.NewReader(pdfData), int64(len(pdfData)))
  	if err != nil {
  		return "", err
  	}

  	var pages []string
  	for pageIndex := 1; pageIndex <= reader.NumPage(); pageIndex++ {
  		pageText, err := reader.Page(pageIndex).GetPlainText(nil)
  		if err != nil {
  			return "", err
  		}
  		pages = append(pages, pageText)
  	}
  	text := strings.Join(pages, "\n")

  	// ページ番号を削除します
  	text = regexp.MustCompile(`\n\s*\d+\s*\n`).ReplaceAllString(text, "\n")

  	// 余分な空白を削除します
  	text = regexp.MustCompile(`\s+`).ReplaceAllString(text, " ")

  	return text, nil
  }

  // extractDocumentText はサンプルの転貸借契約書をダウンロードし、テキストを抽出します。
  func extractDocumentText() {
  	// 要約クックブックからサンプルの転貸借契約書をダウンロードします
  	response, err := http.Get("https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf")
  	if err != nil {
  		log.Fatal(err)
  	}
  	defer response.Body.Close()

  	pdfData, err := io.ReadAll(response.Body)
  	if err != nil {
  		log.Fatal(err)
  	}

  	documentText, err = getLLMText(pdfData)
  	if err != nil {
  		log.Fatal(err)
  	}
  	fmt.Println(documentText[:min(50000, len(documentText))])
  }
  ```

  ```java Java
  import java.io.IOException;
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import org.apache.pdfbox.Loader;
  import org.apache.pdfbox.pdmodel.PDDocument;
  import org.apache.pdfbox.text.PDFTextStripper;

  // サンプルのサブリース契約書のテキスト
  String documentText;

  String getLlmText(byte[] pdfData) throws IOException {
      try (PDDocument document = Loader.loadPDF(pdfData)) {
          String text = new PDFTextStripper().getText(document);

          // ページ番号を削除します
          text = text.replaceAll("\\n\\s*\\d+\\s*\\n", "\n");

          // 余分な空白を削除します
          return text.replaceAll("\\s+", " ");
      }
  }

  void extractDocumentText() throws IOException, InterruptedException {
      // 要約クックブックからサンプルのサブリース契約書をダウンロードします
      HttpRequest request = HttpRequest.newBuilder(URI.create(
                      "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"))
              .build();
      byte[] pdfData;
      try (HttpClient httpClient = HttpClient.newHttpClient()) {
          pdfData = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()).body();
      }

      documentText = getLlmText(pdfData);
      IO.println(documentText.substring(0, Math.min(50000, documentText.length())));
  }
  ```

  ```php PHP
  use Smalot\PdfParser\Parser;

  $getLlmText = function (string $pdfData): string {
      $document = (new Parser())->parseContent($pdfData);
      $text = implode("\n", array_map(fn ($page) => $page->getText(), $document->getPages()));

      // ページ番号を削除します
      $text = preg_replace('/\n\s*\d+\s*\n/', "\n", $text);

      // 余分な空白を削除します
      return preg_replace('/\s+/', ' ', $text);
  };

  // 要約クックブックからサンプルの転貸借契約書をダウンロードします
  $pdfData = file_get_contents(
      'https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf'
  );

  $documentText = $getLlmText($pdfData);
  echo substr($documentText, 0, 50000) . "\n";
  ```

  ```ruby Ruby
  require "net/http"
  require "pdf-reader"
  require "stringio"

  def get_llm_text(pdf_data)
    reader = PDF::Reader.new(StringIO.new(pdf_data))
    text = reader.pages.map(&:text).join("\n")

    # ページ番号を削除します
    text = text.gsub(/\n\s*\d+\s*\n/, "\n")

    # 余分な空白を削除します
    text.gsub(/\s+/, " ")
  end

  # 要約クックブックからサンプルのサブリース契約書をダウンロードします
  pdf_data = Net::HTTP.get(
    URI("https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf")
  )

  document_text = get_llm_text(pdf_data)
  puts document_text[0, 50_000]
  ```
</CodeGroup>

この例では、まず[要約クックブック](https://platform.claude.com/cookbook/capabilities-summarization-guide)で使用されているサンプルのサブリース契約のPDFをダウンロードします。この契約書は、[sec.govのウェブサイト](https://www.sec.gov/Archives/edgar/data/1045425/000119312507044370/dex1032.htm)で公開されているサブリース契約から取得したものです。

この例では、`pypdf` (typescript: `pdf-parse`; csharp: `PdfPig`; go: `github.com/ledongthuc/pdf`; java: `org.apache.pdfbox:pdfbox`; php: `smalot/pdfparser`; ruby: `pdf-reader`)ライブラリを使用してPDFの内容を抽出し、テキストに変換します。その後、ページ番号や余分な空白を削除してテキストデータをクリーニングします。

### 強力なプロンプトを構築する

Claudeはさまざまな要約スタイルに適応できます。プロンプトの詳細を変更することで、Claudeの出力をより詳細にしたり簡潔にしたり、専門用語を増やしたり減らしたり、対象となるコンテキストについてより高レベルまたは低レベルの要約を提供させたりすることができます。

サブリース契約を分析する際に、生成される要約が一貫した構造に従うようにするプロンプトの作成例を以下に示します。

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    このワークフローには、単一のシェルコマンドでは実現できないロジックが必要です。実行するにはClaude SDKを使用してください。
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    このワークフローには、単一のシェルコマンドでは実現できないロジックが必要です。実行するにはClaude SDKを使用してください。
  </CodeGroupItem>

  ```python Python
  # Anthropicクライアントを初期化します
  client = anthropic.Anthropic()


  def summarize_document(
      text, details_to_extract, model="claude-opus-5-5", max_tokens=16000
  ):
      # 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
      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")


  sublease_summary = summarize_document(document_text, details_to_extract)
  print(sublease_summary)
  ```

  ```typescript TypeScript
  // Anthropic クライアントを初期化します
  const client = new Anthropic();

  async function summarizeDocument(
    text: string,
    detailsToExtract: string[],
    model: Anthropic.Model = "claude-opus-5-5",
    maxTokens = 16000
  ): Promise<string> {
    // 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
    const detailsToExtractStr = detailsToExtract.join("\n");

    // 転貸借契約書を要約するようモデルにプロンプトを送ります
    const prompt = `Summarize the following sublease agreement. Focus on these key aspects:

      ${detailsToExtractStr}

      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}
      `;

    const response = await client.messages.create({
      model,
      max_tokens: maxTokens,
      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 }]
    });

    const textBlock = response.content.find((block) => block.type === "text");
    if (!textBlock) {
      throw new Error("Expected a text block in the response");
    }
    return textBlock.text;
  }

  const subleaseSummary = await summarizeDocument(documentText, detailsToExtract);
  console.log(subleaseSummary);
  ```

  ```csharp C#
  // Anthropic クライアントを初期化します
  var client = new AnthropicClient();

  async Task<string> SummarizeDocument(
      string text,
      IReadOnlyList<string> detailsToExtract,
      Model model = Model.ClaudeOpus5_5,
      long maxTokens = 16000
  )
  {
      // 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
      var detailsToExtractStr = string.Join("\n", detailsToExtract);

      // 転貸借契約書を要約するようモデルにプロンプトを送ります
      var prompt = $"""
          Summarize the following sublease agreement. Focus on these key aspects:

              {detailsToExtractStr}

              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}
          """;

      var response = await client.Messages.Create(
          new()
          {
              Model = model,
              MaxTokens = maxTokens,
              System = "You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.",
              Messages = [new() { Role = Role.User, Content = prompt }],
          }
      );

      foreach (var block in response.Content)
      {
          if (block.TryPickText(out var textBlock))
          {
              return textBlock.Text;
          }
      }
      throw new InvalidOperationException("Expected a text block in the response.");
  }

  var subleaseSummary = await SummarizeDocument(documentText, detailsToExtract);
  Console.WriteLine(subleaseSummary);
  ```

  ```go Go
  // Anthropic クライアントを初期化します
  var client = anthropic.NewClient()

  func summarizeDocument(text string, detailsToExtract []string, model anthropic.Model, maxTokens int64) string {
  	// 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
  	detailsToExtractStr := strings.Join(detailsToExtract, "\n")

  	// 転貸借契約書を要約するようモデルにプロンプトを送ります
  	prompt := fmt.Sprintf(`Summarize the following sublease agreement. Focus on these key aspects:

      %s

      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:
      %s
      `, detailsToExtractStr, text)

  	response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
  		Model:     model,
  		MaxTokens: maxTokens,
  		System: []anthropic.TextBlockParam{
  			{Text: "You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements."},
  		},
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock(prompt)),
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  			return textBlock.Text
  		}
  	}
  	panic("expected a text block in the response")
  }

  // printSubleaseSummary は転貸借契約書を要約し、その要約を出力します。
  func printSubleaseSummary() {
  	subleaseSummary := summarizeDocument(documentText, detailsToExtract, anthropic.ModelClaudeOpus5_5, 16000)
  	fmt.Println(subleaseSummary)
  }
  ```

  ```java Java
  // Anthropic クライアントを初期化します
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  String summarizeDocument(String text, List<String> detailsToExtract, Model model, long maxTokens) {
      // 抽出する詳細項目をプロンプトのコンテキストに配置できるよう整形します
      String detailsToExtractStr = String.join("\n", detailsToExtract);

      // サブリース契約書を要約するようモデルにプロンプトを送ります
      String prompt = """
              Summarize the following sublease agreement. Focus on these key aspects:

                  %s

                  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:
                  %s
              """
              .formatted(detailsToExtractStr, text);

      Message response = client.messages().create(MessageCreateParams.builder()
              .model(model)
              .maxTokens(maxTokens)
              .system("You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.")
              .addUserMessage(prompt)
              .build());

      return response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst()
              .orElseThrow()
              .text();
  }

  void printSubleaseSummary() {
      String subleaseSummary = summarizeDocument(
              documentText, detailsToExtract, Model.CLAUDE_OPUS_5_5, 16000);
      IO.println(subleaseSummary);
  }
  ```

  ```php PHP
  // Anthropic クライアントを初期化します
  $client = new Client();

  $summarizeDocument = function (
      string $text,
      array $detailsToExtract,
      Model $model = Model::CLAUDE_OPUS_5_5,
      int $maxTokens = 16000,
  ) use ($client): string {
      // 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
      $detailsToExtractStr = implode("\n", $detailsToExtract);

      // 転貸借契約書を要約するようモデルにプロンプトを送ります
      $prompt = <<<PROMPT
          Summarize the following sublease agreement. Focus on these key aspects:

              {$detailsToExtractStr}

              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}
          PROMPT;

      $response = $client->messages->create(
          model: $model,
          maxTokens: $maxTokens,
          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]],
      );

      $textBlock = array_find($response->content, fn ($block) => $block instanceof \Anthropic\Messages\TextBlock)
          ?? throw new \RuntimeException('Expected a text block in the response.');

      return $textBlock->text;
  };

  $subleaseSummary = $summarizeDocument($documentText, $detailsToExtract);
  echo $subleaseSummary . "\n";
  ```

  ```ruby Ruby
  # Anthropic クライアントを初期化します
  CLIENT = Anthropic::Client.new

  def summarize_document(text, details_to_extract, model: Anthropic::Model::CLAUDE_OPUS_5_5, max_tokens: 16_000)
    # 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
    details_to_extract_str = details_to_extract.join("\n")

    # サブリース契約書を要約するようモデルにプロンプトを送ります
    prompt = <<~PROMPT
      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}
    PROMPT

    response = CLIENT.messages.create(
      model:,
      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}]
    )

    response.content.find { |block| block.is_a?(Anthropic::Models::TextBlock) }.text
  end

  sublease_summary = summarize_document(document_text, details_to_extract)
  puts sublease_summary
  ```
</CodeGroup>

このコードは、Claudeを使用してサブリース契約の内容を要約する`summarize_document` (typescript, go, java: `summarizeDocument`; php: `$summarizeDocument`; csharp: `SummarizeDocument`)関数を実装しています。この関数は、テキスト文字列と抽出する詳細のリストを入力として受け取ります。この例では、前のコードスニペットで定義した`document_text` (typescript, go, java, csharp: `documentText`; php: `$documentText`)変数と`details_to_extract` (typescript, go, java, csharp: `detailsToExtract`; php: `$detailsToExtract`)変数を使用して関数を呼び出しています。

関数内では、要約対象の文書、抽出する詳細、および文書を要約するための具体的な指示を含むClaude向けのプロンプトが生成されます。このプロンプトは、抽出する各詳細の要約をXMLヘッダー内にネストして応答するようClaudeに指示します。

コードは要約の各セクションをタグ内に出力するため、後処理ステップとして各セクションを簡単にパースできます。このアプローチにより、ユースケースに合わせて調整可能な構造化された要約が実現し、各要約が同じパターンに従うようになります。

### プロンプトを評価する

プロンプトを本番環境で使用できる状態にするには、多くの場合テストと最適化が必要です。ソリューションの準備状況を判断するには、定量的手法と定性的手法を組み合わせた体系的なプロセスを用いて要約の品質を評価してください。定義した成功基準に基づいて[強力な実証的評価](https://platform.claude.com/docs/ja/test-and-evaluate/develop-tests#build-evaluations)を作成することで、プロンプトを最適化できます。実証的評価に含めるとよい指標をいくつか以下に示します。

<AccordionGroup>
  <Accordion title="ROUGEスコア">
    生成された要約と、専門家が作成した参照要約との重複度を測定します。この指標は主に再現率（recall）に焦点を当てており、内容の網羅性を評価するのに役立ちます。
  </Accordion>

  <Accordion title="BLEUスコア">
    もともとは機械翻訳向けに開発された指標ですが、要約タスクにも応用できます。BLEUスコアは、生成された要約と参照要約との間のn-gram一致の適合率（precision）を測定します。スコアが高いほど、生成された要約が参照要約と類似したフレーズや用語を含んでいることを示します。
  </Accordion>

  <Accordion title="コンテキスト埋め込み類似度">
    この指標では、生成された要約と参照要約の両方についてベクトル表現（埋め込み）を作成します。次に、これらの埋め込み間の類似度を、多くの場合コサイン類似度を用いて計算します。類似度スコアが高いほど、正確な言い回しが異なっていても、生成された要約が参照要約の意味内容とコンテキストを捉えていることを示します。
  </Accordion>

  <Accordion title="LLMベースの採点">
    この手法では、ClaudeのようなLLMを使用して、採点ルーブリックに照らして生成された要約の品質を評価します。ルーブリックは特定のニーズに合わせて調整でき、正確性、完全性、一貫性などの重要な要素を評価できます。実装のガイダンスについては、

    [LLMベースの採点のヒント](https://platform.claude.com/docs/ja/test-and-evaluate/develop-tests#tips-for-llm-based-grading)

    を参照してください。
  </Accordion>

  <Accordion title="人間による評価">
    参照要約の作成に加えて、法律の専門家が生成された要約の品質を評価することもできます。大規模に行うにはコストと時間がかかりますが、本番環境にデプロイする前の検証チェックとして、少数の要約に対して行われることがよくあります。
  </Accordion>
</AccordionGroup>

### プロンプトをデプロイする

ソリューションを本番環境にデプロイする際に留意すべき追加の考慮事項を以下に示します。

1. **法的責任が生じないようにする：** 要約の誤りがもたらす法的影響を理解してください。誤りは組織やクライアントの法的責任につながる可能性があります。要約がAIによって生成されたものであり、法務専門家によるレビューが必要であることを明記した免責事項や法的通知を提供してください。

2. **多様な文書タイプに対応する：** このガイドでは、PDFからテキストを抽出する方法を説明しています。実際には、文書はさまざまな形式（PDF、Word文書、テキストファイルなど）で提供される可能性があります。受け取ることが想定されるすべてのファイル形式をデータ抽出パイプラインで変換できるようにしてください。

3. **ClaudeへのAPI呼び出しを並列化する：** トークン数の多い長い文書では、Claudeが要約を生成するのに最大1分程度かかる場合があります。大規模な文書コレクションの場合、妥当な時間内に要約を完了できるよう、ClaudeへのAPI呼び出しを並列で送信するとよいでしょう。並列で実行できるAPI呼び出しの最大数を判断するには、Anthropicの[レート制限](https://platform.claude.com/docs/ja/api/rate-limits#rate-limits)を参照してください。

***

## パフォーマンスを向上させる

複雑なシナリオでは、標準的な[プロンプトエンジニアリング手法](https://platform.claude.com/docs/ja/build-with-claude/prompt-engineering/overview)に加えて、パフォーマンスを向上させるための追加戦略を検討すると役立つ場合があります。高度な戦略をいくつか以下に示します。

### メタ要約を実行して長い文書を要約する

法律文書の要約では、長い文書や多数の関連文書を一度に扱うことが多く、Claudeの「context window」（コンテキストウィンドウ）を超えてしまうことがあります。このユースケースに対応するには、メタ要約（meta-summarization）と呼ばれるチャンク分割手法を使用できます。この手法では、文書をより小さく扱いやすいチャンクに分割し、各チャンクを個別に処理します。その後、各チャンクの要約を組み合わせて、文書全体のメタ要約を作成できます。

メタ要約の実行例を以下に示します。

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    このワークフローには、単一のシェルコマンドでは実現できないロジックが必要です。実行するにはClaude SDKを使用してください。
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    このワークフローには、単一のシェルコマンドでは実現できないロジックが必要です。実行するにはClaude SDKを使用してください。
  </CodeGroupItem>

  ```python Python
  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=16000
  ):
      # 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
      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. 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.
      """

      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")


  long_summary = summarize_long_document(document_text, details_to_extract)
  print(long_summary)
  ```

  ```typescript TypeScript
  function chunkText(text: string, chunkSize = 20000): string[] {
    return Array.from({ length: Math.ceil(text.length / chunkSize) }, (_, index) =>
      text.slice(index * chunkSize, (index + 1) * chunkSize)
    );
  }

  async function summarizeLongDocument(
    text: string,
    detailsToExtract: string[],
    model: Anthropic.Model = "claude-opus-5-5",
    maxTokens = 16000
  ): Promise<string> {
    // 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
    const detailsToExtractStr = detailsToExtract.join("\n");

    // チャンクを順に処理し、それぞれを要約します
    const chunkSummaries: string[] = [];
    for (const chunk of chunkText(text)) {
      chunkSummaries.push(await summarizeDocument(chunk, detailsToExtract, model, maxTokens));
    }

    const finalSummaryPrompt = `

      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>
      ${chunkSummaries.join("")}
      </chunked_summaries>

      Focus on these key aspects:
      ${detailsToExtractStr}

      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.
      `;

    const response = await client.messages.create({
      model,
      max_tokens: maxTokens,
      system: "You are a legal expert that summarizes notes on one document.",
      messages: [{ role: "user", content: finalSummaryPrompt }]
    });

    const textBlock = response.content.find((block) => block.type === "text");
    if (!textBlock) {
      throw new Error("Expected a text block in the response");
    }
    return textBlock.text;
  }

  const longSummary = await summarizeLongDocument(documentText, detailsToExtract);
  console.log(longSummary);
  ```

  ```csharp C#
  IEnumerable<string> ChunkText(string text, int chunkSize = 20000) =>
      text.Chunk(chunkSize).Select(chars => new string(chars));

  async Task<string> SummarizeLongDocument(
      string text,
      IReadOnlyList<string> detailsToExtract,
      Model model = Model.ClaudeOpus5_5,
      long maxTokens = 16000
  )
  {
      // 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
      var detailsToExtractStr = string.Join("\n", detailsToExtract);

      // チャンクを順に処理し、それぞれを要約します
      List<string> chunkSummaries = [];
      foreach (var chunk in ChunkText(text))
      {
          chunkSummaries.Add(await SummarizeDocument(chunk, detailsToExtract, model, maxTokens));
      }

      var finalSummaryPrompt = $"""

              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>
              {string.Concat(chunkSummaries)}
              </chunked_summaries>

              Focus on these key aspects:
              {detailsToExtractStr}

              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.
          """;

      var response = await client.Messages.Create(
          new()
          {
              Model = model,
              MaxTokens = maxTokens,
              System = "You are a legal expert that summarizes notes on one document.",
              Messages = [new() { Role = Role.User, Content = finalSummaryPrompt }],
          }
      );

      foreach (var block in response.Content)
      {
          if (block.TryPickText(out var textBlock))
          {
              return textBlock.Text;
          }
      }
      throw new InvalidOperationException("Expected a text block in the response.");
  }

  var longSummary = await SummarizeLongDocument(documentText, detailsToExtract);
  Console.WriteLine(longSummary);
  ```

  ```go Go
  func chunkText(text string, chunkSize int) []string {
  	var chunks []string
  	for chunk := range slices.Chunk([]rune(text), chunkSize) {
  		chunks = append(chunks, string(chunk))
  	}
  	return chunks
  }

  func summarizeLongDocument(text string, detailsToExtract []string, model anthropic.Model, maxTokens int64) string {
  	// 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
  	detailsToExtractStr := strings.Join(detailsToExtract, "\n")

  	// チャンクを順に処理し、それぞれを要約します
  	var chunkSummaries []string
  	for _, chunk := range chunkText(text, 20000) {
  		chunkSummaries = append(chunkSummaries, summarizeDocument(chunk, detailsToExtract, model, maxTokens))
  	}

  	finalSummaryPrompt := fmt.Sprintf(`

      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>
      %s
      </chunked_summaries>

      Focus on these key aspects:
      %s

      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.
      `, strings.Join(chunkSummaries, ""), detailsToExtractStr)

  	response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
  		Model:     model,
  		MaxTokens: maxTokens,
  		System: []anthropic.TextBlockParam{
  			{Text: "You are a legal expert that summarizes notes on one document."},
  		},
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock(finalSummaryPrompt)),
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  			return textBlock.Text
  		}
  	}
  	panic("expected a text block in the response")
  }

  // printLongSummary は転貸借契約書をチャンクごとに要約し、結合した要約を出力します。
  func printLongSummary() {
  	longSummary := summarizeLongDocument(documentText, detailsToExtract, anthropic.ModelClaudeOpus5_5, 16000)
  	fmt.Println(longSummary)
  }
  ```

  ```java Java
  List<String> chunkText(String text, int chunkSize) {
      return IntStream.iterate(0, start -> start < text.length(), start -> start + chunkSize)
              .mapToObj(start -> text.substring(start, Math.min(start + chunkSize, text.length())))
              .toList();
  }

  String summarizeLongDocument(String text, List<String> detailsToExtract, Model model, long maxTokens) {
      // 抽出する詳細項目をプロンプトのコンテキストに配置できるよう整形します
      String detailsToExtractStr = String.join("\n", detailsToExtract);

      // チャンクを順に処理し、それぞれを要約します
      String chunkSummaries = chunkText(text, 20000).stream()
              .map(chunk -> summarizeDocument(chunk, detailsToExtract, model, maxTokens))
              .collect(Collectors.joining());

      String finalSummaryPrompt = """

                  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>
                  %s
                  </chunked_summaries>

                  Focus on these key aspects:
                  %s

                  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.
              """
              .formatted(chunkSummaries, detailsToExtractStr);

      Message response = client.messages().create(MessageCreateParams.builder()
              .model(model)
              .maxTokens(maxTokens)
              .system("You are a legal expert that summarizes notes on one document.")
              .addUserMessage(finalSummaryPrompt)
              .build());

      return response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst()
              .orElseThrow()
              .text();
  }

  void printLongSummary() {
      String longSummary = summarizeLongDocument(
              documentText, detailsToExtract, Model.CLAUDE_OPUS_5_5, 16000);
      IO.println(longSummary);
  }
  ```

  ```php PHP
  $chunkText = fn (string $text, int $chunkSize = 20000): array => mb_str_split($text, $chunkSize);

  $summarizeLongDocument = function (
      string $text,
      array $detailsToExtract,
      Model $model = Model::CLAUDE_OPUS_5_5,
      int $maxTokens = 16000,
  ) use ($client, $chunkText, $summarizeDocument): string {
      // 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
      $detailsToExtractStr = implode("\n", $detailsToExtract);

      // チャンクを順に処理し、それぞれを要約します
      $chunkSummaries = array_map(
          fn (string $chunk) => $summarizeDocument($chunk, $detailsToExtract, $model, $maxTokens),
          $chunkText($text),
      );
      $chunkSummariesStr = implode('', $chunkSummaries);

      $finalSummaryPrompt = <<<PROMPT

              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>
              {$chunkSummariesStr}
              </chunked_summaries>

              Focus on these key aspects:
              {$detailsToExtractStr}

              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.
          PROMPT;

      $response = $client->messages->create(
          model: $model,
          maxTokens: $maxTokens,
          system: 'You are a legal expert that summarizes notes on one document.',
          messages: [['role' => 'user', 'content' => $finalSummaryPrompt]],
      );

      $textBlock = array_find($response->content, fn ($block) => $block instanceof \Anthropic\Messages\TextBlock)
          ?? throw new \RuntimeException('Expected a text block in the response.');

      return $textBlock->text;
  };

  $longSummary = $summarizeLongDocument($documentText, $detailsToExtract);
  echo $longSummary . "\n";
  ```

  ```ruby Ruby
  def chunk_text(text, chunk_size: 20_000)
    text.chars.each_slice(chunk_size).map(&:join)
  end

  def summarize_long_document(text, details_to_extract, model: Anthropic::Model::CLAUDE_OPUS_5_5, max_tokens: 16_000)
    # 抽出する項目をプロンプトのコンテキストに配置できるよう整形します
    details_to_extract_str = details_to_extract.join("\n")

    # チャンクを順に処理し、それぞれを要約します
    chunk_summaries = chunk_text(text).map do |chunk|
      summarize_document(chunk, details_to_extract, model:, max_tokens:)
    end

    final_summary_prompt = <<~PROMPT

          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>
          #{chunk_summaries.join}
          </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. 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.
    PROMPT

    response = CLIENT.messages.create(
      model:,
      max_tokens:,
      system_: "You are a legal expert that summarizes notes on one document.",
      messages: [{role: :user, content: final_summary_prompt}]
    )

    response.content.find { |block| block.is_a?(Anthropic::Models::TextBlock) }.text
  end

  long_summary = summarize_long_document(document_text, details_to_extract)
  puts long_summary
  ```
</CodeGroup>

`summarize_long_document` (typescript, go, java: `summarizeLongDocument`; php: `$summarizeLongDocument`; csharp: `SummarizeLongDocument`)関数は、前述の`summarize_document` (typescript, go, java: `summarizeDocument`; php: `$summarizeDocument`; csharp: `SummarizeDocument`)関数を基に、文書を小さなチャンクに分割して各チャンクを個別に要約します。

コードは、元の文書内の20,000文字ごとのチャンクに`summarize_document` (typescript, go, java: `summarizeDocument`; php: `$summarizeDocument`; csharp: `SummarizeDocument`)関数を適用することでこれを実現します。その後、個々の要約が結合され、これらのチャンクの要約から最終的な要約が作成されます。

なお、サンプルのPDFでは文書全体がClaudeのコンテキストウィンドウに収まるため、`summarize_long_document` (typescript, go, java: `summarizeLongDocument`; php: `$summarizeLongDocument`; csharp: `SummarizeLongDocument`)関数は厳密には必要ありません。しかし、Claudeのコンテキストウィンドウを超える文書や、複数の関連文書をまとめて要約する場合には不可欠になります。いずれにしても、このメタ要約の手法では、前述の単一要約のアプローチでは見落とされていた重要な詳細が、最終的な要約に追加で含まれることがよくあります。

### 要約インデックス付き文書を使用して大規模な文書コレクションを探索する

LLMを使用して文書コレクションを検索する場合、通常は「retrieval-augmented generation」（検索拡張生成）、すなわちRAGを使用します。しかし、大きな文書を扱うシナリオや、正確な情報検索が重要な場合には、基本的なRAGアプローチでは不十分なことがあります。要約インデックス付き文書（summary indexed documents）は、従来のRAG手法よりも少ないコンテキストで、検索対象の文書をより効率的にランク付けする方法を提供する高度なRAGアプローチです。このアプローチでは、まずClaudeを使用してコーパス内の各文書の簡潔な要約を生成し、次にClaudeを使用して、尋ねられているクエリに対する各要約の関連性をランク付けします。コードベースの例を含むこのアプローチの詳細については、[要約クックブック](https://platform.claude.com/cookbook/capabilities-summarization-guide)の要約インデックス付き文書のセクションをご覧ください。

### Claudeをファインチューニングしてデータセットから学習させる

Claudeの要約生成能力を向上させるもう1つの高度な手法が「fine-tuning」（ファインチューニング）です。ファインチューニングでは、法律文書要約のニーズに特化したカスタムデータセットでClaudeをトレーニングし、Claudeがユースケースに適応するようにします。ファインチューニングの実行方法の概要は次のとおりです。

1. **エラーを特定する：** まず、Claudeの要約が不十分だった事例を収集します。これには、重要な法的詳細の欠落、コンテキストの誤解、不適切な法律用語の使用などが含まれます。

2. **データセットをキュレーションする：** これらの問題を特定したら、問題のある例をまとめたデータセットを作成します。このデータセットには、元の法律文書と修正済みの要約を含め、Claudeが望ましい動作を学習できるようにします。

3. **ファインチューニングを実行する：** ファインチューニングでは、キュレーションしたデータセットでモデルを再トレーニングし、重みとパラメータを調整します。この再トレーニングにより、Claudeは法律分野の特定の要件により適応し、基準に沿って文書を要約する能力が向上します。

4. **反復的な改善：** ファインチューニングは一度きりのプロセスではありません。Claudeが要約を生成し続ける中で、パフォーマンスが不十分だった新しい例を反復的に追加し、能力をさらに磨くことができます。時間の経過とともに、この継続的なフィードバックループにより、法律文書要約タスクに高度に特化したモデルが得られます。

<Tip>
  ファインチューニングは現在、Amazon Bedrockを通じてのみ利用可能です。詳細については、

  [AWSのローンチブログ](https://aws.amazon.com/blogs/machine-learning/fine-tune-anthropics-claude-3-haiku-in-amazon-bedrock-to-boost-model-accuracy-and-quality/)

  をご覧ください。
</Tip>

<CardGroup cols={2}>
  <Card title="要約クックブック" icon="link" href="https://platform.claude.com/cookbook/capabilities-summarization-guide">
    Claudeを使用して契約書を要約する方法について、完全に実装されたコードベースの例をご覧ください。
  </Card>

  <Card title="引用クックブック" icon="link" href="https://platform.claude.com/cookbook/misc-using-citations">
    情報の正確性と説明可能性を確保する方法のガイダンスについては、引用クックブックのレシピをご覧ください。
  </Card>
</CardGroup>
