legal-summarization changedabout-claude/use-case-guides/legal-summarization
Nearest release: v2.1.301, published 5 hours before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Recorded here
Lines+1,107added
Lines−138removed
From line
40
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits9to this page, all time
The whole hunk
from line 40, old and new numbered
/
The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.
from line 40
4040
4141For instance, when summarizing a sublease agreement, you might want to extract the following key points:
4242
43```python
44details_to_extract = [
43<CodeGroup exclude="shell">
44 ```python Python
45 details_to_extract = [
46 "Parties involved (sublessor, sublessee, and original lessor)",
47 "Property details (address, description, and permitted use)",
48 "Term and rent (start date, end date, monthly rent, and security deposit)",
49 "Responsibilities (utilities, maintenance, and repairs)",
50 "Consent and notices (landlord's consent, and notice requirements)",
51 "Special provisions (furniture, parking, and subletting restrictions)",
52 ]
53 ```
54
55 ```typescript TypeScript
56 const detailsToExtract = [
4557 "Parties involved (sublessor, sublessee, and original lessor)",
4658 "Property details (address, description, and permitted use)",
4759 "Term and rent (start date, end date, monthly rent, and security deposit)",
4860 "Responsibilities (utilities, maintenance, and repairs)",
4961 "Consent and notices (landlord's consent, and notice requirements)",
50 "Special provisions (furniture, parking, and subletting restrictions)",
51]
52```
62 "Special provisions (furniture, parking, and subletting restrictions)"
63 ];
64 ```
65
66 ```csharp C#
67 List<string> detailsToExtract =
68 [
69 "Parties involved (sublessor, sublessee, and original lessor)",
70 "Property details (address, description, and permitted use)",
71 "Term and rent (start date, end date, monthly rent, and security deposit)",
72 "Responsibilities (utilities, maintenance, and repairs)",
73 "Consent and notices (landlord's consent, and notice requirements)",
74 "Special provisions (furniture, parking, and subletting restrictions)",
75 ];
76 ```
77
78 ```go Go
79 var detailsToExtract = []string{
80 "Parties involved (sublessor, sublessee, and original lessor)",
81 "Property details (address, description, and permitted use)",
82 "Term and rent (start date, end date, monthly rent, and security deposit)",
83 "Responsibilities (utilities, maintenance, and repairs)",
84 "Consent and notices (landlord's consent, and notice requirements)",
85 "Special provisions (furniture, parking, and subletting restrictions)",
86 }
87
88 ```
89
90 ```java Java
91 List<String> detailsToExtract = List.of(
92 "Parties involved (sublessor, sublessee, and original lessor)",
93 "Property details (address, description, and permitted use)",
94 "Term and rent (start date, end date, monthly rent, and security deposit)",
95 "Responsibilities (utilities, maintenance, and repairs)",
96 "Consent and notices (landlord's consent, and notice requirements)",
97 "Special provisions (furniture, parking, and subletting restrictions)");
98 ```
99
100 ```php PHP
101 $detailsToExtract = [
102 'Parties involved (sublessor, sublessee, and original lessor)',
103 'Property details (address, description, and permitted use)',
104 'Term and rent (start date, end date, monthly rent, and security deposit)',
105 'Responsibilities (utilities, maintenance, and repairs)',
106 "Consent and notices (landlord's consent, and notice requirements)",
107 'Special provisions (furniture, parking, and subletting restrictions)',
108 ];
109 ```
110
111 ```ruby Ruby
112 details_to_extract = [
113 "Parties involved (sublessor, sublessee, and original lessor)",
114 "Property details (address, description, and permitted use)",
115 "Term and rent (start date, end date, monthly rent, and security deposit)",
116 "Responsibilities (utilities, maintenance, and repairs)",
117 "Consent and notices (landlord's consent, and notice requirements)",
118 "Special provisions (furniture, parking, and subletting restrictions)"
119 ]
120 ```
121</CodeGroup>
53122
54123### Establish success criteria
55124
from line 216
147216
148217Here is a demonstration of this process on a sample PDF:
149218
150```python
151from io import BytesIO
152import re
153
154import pypdf
155import requests
156
157
158def get_llm_text(pdf_file):
159 reader = pypdf.PdfReader(pdf_file)
160 text = "\n".join([page.extract_text() for page in reader.pages])
219<CodeGroup exclude="shell">
220 <CodeGroupItem language="cURL">
221 Extract the text from the PDF in code.
222 </CodeGroupItem>
223
224 <CodeGroupItem language="CLI">
225 Extract the text from the PDF in code.
226 </CodeGroupItem>
227
228 ```python Python
229 from io import BytesIO
230 import re
231
232 import pypdf
233 import requests
234
235
236 def get_llm_text(pdf_file):
237 reader = pypdf.PdfReader(pdf_file)
238 text = "\n".join([page.extract_text() for page in reader.pages])
239
240 # Remove page numbers
241 text = re.sub(r"\n\s*\d+\s*\n", "\n", text)
242
243 # Remove extra whitespace
244 text = re.sub(r"\s+", " ", text)
245
246 return text
247
248
249 # Create the full URL from the GitHub repository
250 url = "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample Sublease Agreement.pdf"
251 url = url.replace(" ", "%20")
252
253 # Download the PDF file into memory
254 response = requests.get(url)
255
256 # Load the PDF from memory
257 pdf_file = BytesIO(response.content)
258
259 document_text = get_llm_text(pdf_file)
260 print(document_text[:50000])
261 ```
262
263 ```typescript TypeScript
264 import { PDFParse } from "pdf-parse";
265
266 async function getLlmText(pdfData: Uint8Array): Promise<string> {
267 const parser = new PDFParse({ data: pdfData });
268 const result = await parser.getText();
269 await parser.destroy();
270 let text = result.pages.map((page) => page.text).join("\n");
271
272 // Remove page numbers
273 text = text.replace(/\n\s*\d+\s*\n/g, "\n");
274
275 // Remove extra whitespace
276 text = text.replace(/\s+/g, " ");
277
278 return text;
279 }
280
281 // Download the sample sublease agreement from the summarization cookbook
282 const response = await fetch(
283 "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"
284 );
285 const pdfData = new Uint8Array(await response.arrayBuffer());
286
287 const documentText = await getLlmText(pdfData);
288 console.log(documentText.slice(0, 50000));
289 ```
290
291 ```csharp C#
292 using System.Text.RegularExpressions;
293 using UglyToad.PdfPig;
294 using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;
295
296 string GetLlmText(byte[] pdfData)
297 {
298 using var document = PdfDocument.Open(pdfData);
299 var text = string.Join("\n", document.GetPages().Select(page => ContentOrderTextExtractor.GetText(page)));
300
301 // Remove page numbers
302 text = Regex.Replace(text, @"\n\s*\d+\s*\n", "\n");
303
304 // Remove extra whitespace
305 text = Regex.Replace(text, @"\s+", " ");
306
307 return text;
308 }
309
310 // Download the sample sublease agreement from the summarization cookbook
311 using var httpClient = new HttpClient();
312 var pdfData = await httpClient.GetByteArrayAsync(
313 "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"
314 );
315
316 var documentText = GetLlmText(pdfData);
317 Console.WriteLine(documentText[..Math.Min(50000, documentText.Length)]);
318 ```
319
320 ```go Go
321 import (
322 "bytes"
323 "io"
324 "net/http"
325 "regexp"
326
327 "github.com/ledongthuc/pdf"
328 )
329
330 // The text of the sample sublease agreement
331 var documentText string
332
333 func getLLMText(pdfData []byte) (string, error) {
334 reader, err := pdf.NewReader(bytes.NewReader(pdfData), int64(len(pdfData)))
335 if err != nil {
336 return "", err
337 }
338
339 var pages []string
340 for pageIndex := 1; pageIndex <= reader.NumPage(); pageIndex++ {
341 pageText, err := reader.Page(pageIndex).GetPlainText(nil)
342 if err != nil {
343 return "", err
344 }
345 pages = append(pages, pageText)
346 }
347 text := strings.Join(pages, "\n")
348
349 // Remove page numbers
350 text = regexp.MustCompile(`\n\s*\d+\s*\n`).ReplaceAllString(text, "\n")
351
352 // Remove extra whitespace
353 text = regexp.MustCompile(`\s+`).ReplaceAllString(text, " ")
354
355 return text, nil
356 }
357
358 // extractDocumentText downloads the sample sublease agreement and extracts its text.
359 func extractDocumentText() {
360 // Download the sample sublease agreement from the summarization cookbook
361 response, err := http.Get("https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf")
362 if err != nil {
363 log.Fatal(err)
364 }
365 defer response.Body.Close()
366
367 pdfData, err := io.ReadAll(response.Body)
368 if err != nil {
369 log.Fatal(err)
370 }
371
372 documentText, err = getLLMText(pdfData)
373 if err != nil {
374 log.Fatal(err)
375 }
376 fmt.Println(documentText[:min(50000, len(documentText))])
377 }
378
379 ```
380
381 ```java Java
382 import java.io.IOException;
383 import java.net.URI;
384 import java.net.http.HttpClient;
385 import java.net.http.HttpRequest;
386 import java.net.http.HttpResponse;
387 import org.apache.pdfbox.Loader;
388 import org.apache.pdfbox.pdmodel.PDDocument;
389 import org.apache.pdfbox.text.PDFTextStripper;
390
391 // The text of the sample sublease agreement
392 String documentText;
393
394 String getLlmText(byte[] pdfData) throws IOException {
395 try (PDDocument document = Loader.loadPDF(pdfData)) {
396 String text = new PDFTextStripper().getText(document);
397
398 // Remove page numbers
399 text = text.replaceAll("\\n\\s*\\d+\\s*\\n", "\n");
400
401 // Remove extra whitespace
402 return text.replaceAll("\\s+", " ");
403 }
404 }
405
406 void extractDocumentText() throws IOException, InterruptedException {
407 // Download the sample sublease agreement from the summarization cookbook
408 HttpRequest request = HttpRequest.newBuilder(URI.create(
409 "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf"))
410 .build();
411 byte[] pdfData;
412 try (HttpClient httpClient = HttpClient.newHttpClient()) {
413 pdfData = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()).body();
414 }
415
416 documentText = getLlmText(pdfData);
417 IO.println(documentText.substring(0, Math.min(50000, documentText.length())));
418 }
419 ```
420
421 ```php PHP
422 $getLlmText = function (string $pdfData): string {
423 $document = (new \Smalot\PdfParser\Parser())->parseContent($pdfData);
424 $text = implode("\n", array_map(fn ($page) => $page->getText(), $document->getPages()));
425
426 // Remove page numbers
427 $text = preg_replace('/\n\s*\d+\s*\n/', "\n", $text);
428
429 // Remove extra whitespace
430 return preg_replace('/\s+/', ' ', $text);
431 };
432
433 // Download the sample sublease agreement from the summarization cookbook
434 $pdfData = file_get_contents(
435 'https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf'
436 );
437
438 $documentText = $getLlmText($pdfData);
439 echo substr($documentText, 0, 50000) . "\n";
440 ```
441
442 ```ruby Ruby
443 require "net/http"
444 require "pdf-reader"
445 require "stringio"
446
447 def get_llm_text(pdf_data)
448 reader = PDF::Reader.new(StringIO.new(pdf_data))
449 text = reader.pages.map(&:text).join("\n")
161450
162451 # Remove page numbers
163 text = re.sub(r"\n\s*\d+\s*\n", "\n", text)
452 text = text.gsub(/\n\s*\d+\s*\n/, "\n")
164453
165454 # Remove extra whitespace
166 text = re.sub(r"\s+", " ", text)
167
168 return text
169
170
171# Create the full URL from the GitHub repository
172url = "https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample Sublease Agreement.pdf"
173url = url.replace(" ", "%20")
174
175# Download the PDF file into memory
176response = requests.get(url)
177
178# Load the PDF from memory
179pdf_file = BytesIO(response.content)
180
181document_text = get_llm_text(pdf_file)
182print(document_text[:50000])
183```
455 text.gsub(/\s+/, " ")
456 end
457
458 # Download the sample sublease agreement from the summarization cookbook
459 pdf_data = Net::HTTP.get(
460 URI("https://raw.githubusercontent.com/anthropics/claude-cookbooks/main/capabilities/summarization/data/Sample%20Sublease%20Agreement.pdf")
461 )
462
463 document_text = get_llm_text(pdf_data)
464 puts document_text[0, 50_000]
465 ```
466</CodeGroup>
184467
185468In this example, you first download a PDF of a sample sublease agreement used in the [summarization cookbook](https://platform.claude.com/cookbook/capabilities-summarization-guide). This agreement was sourced from a publicly available sublease agreement from the [sec.gov website](https://www.sec.gov/Archives/edgar/data/1045425/000119312507044370/dex1032.htm).
186469
187The example uses the pypdf library to extract the contents of the PDF and convert it to text. The text data is then cleaned by removing page numbers and extra whitespace.
470The example uses the `pypdf` (typescript: `pdf-parse`; csharp: `PdfPig`; go: `github.com/ledongthuc/pdf`; java: `org.apache.pdfbox:pdfbox`; php: `smalot/pdfparser`; ruby: `pdf-reader`) library to extract the contents of the PDF and convert it to text. The text data is then cleaned by removing page numbers and extra whitespace.
188471
189472### Build a strong prompt
190473
from line 475
192475
193476Here’s an example of how to create a prompt that ensures the generated summaries follow a consistent structure when analyzing sublease agreements:
194477
195```python Python
196# Initialize the Anthropic client
197client = anthropic.Anthropic()
198
199
200def summarize_document(
201 text, details_to_extract, model="claude-opus-5-5", max_tokens=16000
202):
478<CodeGroup exclude="shell">
479 <CodeGroupItem language="cURL">
480 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
481 </CodeGroupItem>
482
483 <CodeGroupItem language="CLI">
484 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
485 </CodeGroupItem>
486
487 ```python Python
488 # Initialize the Anthropic client
489 client = anthropic.Anthropic()
490
491
492 def summarize_document(
493 text, details_to_extract, model="claude-opus-5-5", max_tokens=16000
494 ):
495 # Format the details to extract to be placed within the prompt's context
496 details_to_extract_str = "\n".join(details_to_extract)
497
498 # Prompt the model to summarize the sublease agreement
499 prompt = f"""Summarize the following sublease agreement. Focus on these key aspects:
500
501 {details_to_extract_str}
502
503 Provide the summary in bullet points nested within the XML header for each section. For example:
504
505 <parties involved>
506 - Sublessor: [Name]
507 // Add more details as needed
508 </parties involved>
509
510 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
511
512 Sublease agreement text:
513 {text}
514 """
515
516 response = client.messages.create(
517 model=model,
518 max_tokens=max_tokens,
519 system="You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.",
520 messages=[
521 {"role": "user", "content": prompt},
522 ],
523 )
524
525 return next(block.text for block in response.content if block.type == "text")
526
527
528 sublease_summary = summarize_document(document_text, details_to_extract)
529 print(sublease_summary)
530 ```
531
532 ```typescript TypeScript
533 // Initialize the Anthropic client
534 const client = new Anthropic();
535
536 async function summarizeDocument(
537 text: string,
538 detailsToExtract: string[],
539 model: Anthropic.Model = "claude-opus-5-5",
540 maxTokens = 16000
541 ): Promise<string> {
542 // Format the details to extract to be placed within the prompt's context
543 const detailsToExtractStr = detailsToExtract.join("\n");
544
545 // Prompt the model to summarize the sublease agreement
546 const prompt = `Summarize the following sublease agreement. Focus on these key aspects:
547
548 ${detailsToExtractStr}
549
550 Provide the summary in bullet points nested within the XML header for each section. For example:
551
552 <parties involved>
553 - Sublessor: [Name]
554 // Add more details as needed
555 </parties involved>
556
557 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
558
559 Sublease agreement text:
560 ${text}
561 `;
562
563 const response = await client.messages.create({
564 model,
565 max_tokens: maxTokens,
566 system:
567 "You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.",
568 messages: [{ role: "user", content: prompt }]
569 });
570
571 const textBlock = response.content.find((block) => block.type === "text");
572 if (!textBlock) {
573 throw new Error("Expected a text block in the response");
574 }
575 return textBlock.text;
576 }
577
578 const subleaseSummary = await summarizeDocument(documentText, detailsToExtract);
579 console.log(subleaseSummary);
580 ```
581
582 ```csharp C#
583 // Initialize the Anthropic client
584 var client = new AnthropicClient();
585
586 async Task<string> SummarizeDocument(
587 string text,
588 IReadOnlyList<string> detailsToExtract,
589 Model model = Model.ClaudeOpus5_5,
590 long maxTokens = 16000
591 )
592 {
593 // Format the details to extract to be placed within the prompt's context
594 var detailsToExtractStr = string.Join("\n", detailsToExtract);
595
596 // Prompt the model to summarize the sublease agreement
597 var prompt = $"""
598 Summarize the following sublease agreement. Focus on these key aspects:
599
600 {detailsToExtractStr}
601
602 Provide the summary in bullet points nested within the XML header for each section. For example:
603
604 <parties involved>
605 - Sublessor: [Name]
606 // Add more details as needed
607 </parties involved>
608
609 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
610
611 Sublease agreement text:
612 {text}
613 """;
614
615 var response = await client.Messages.Create(
616 new()
617 {
618 Model = model,
619 MaxTokens = maxTokens,
620 System = "You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.",
621 Messages = [new() { Role = Role.User, Content = prompt }],
622 }
623 );
624
625 foreach (var block in response.Content)
626 {
627 if (block.TryPickText(out var textBlock))
628 {
629 return textBlock.Text;
630 }
631 }
632 throw new InvalidOperationException("Expected a text block in the response.");
633 }
634
635 var subleaseSummary = await SummarizeDocument(documentText, detailsToExtract);
636 Console.WriteLine(subleaseSummary);
637 ```
638
639 ```go Go
640 // Initialize the Anthropic client
641 var client = anthropic.NewClient()
642
643 func summarizeDocument(text string, detailsToExtract []string, model anthropic.Model, maxTokens int64) string {
644 // Format the details to extract to be placed within the prompt's context
645 detailsToExtractStr := strings.Join(detailsToExtract, "\n")
646
647 // Prompt the model to summarize the sublease agreement
648 prompt := fmt.Sprintf(`Summarize the following sublease agreement. Focus on these key aspects:
649
650 %s
651
652 Provide the summary in bullet points nested within the XML header for each section. For example:
653
654 <parties involved>
655 - Sublessor: [Name]
656 // Add more details as needed
657 </parties involved>
658
659 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
660
661 Sublease agreement text:
662 %s
663 `, detailsToExtractStr, text)
664
665 response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
666 Model: model,
667 MaxTokens: maxTokens,
668 System: []anthropic.TextBlockParam{
669 {Text: "You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements."},
670 },
671 Messages: []anthropic.MessageParam{
672 anthropic.NewUserMessage(anthropic.NewTextBlock(prompt)),
673 },
674 })
675 if err != nil {
676 log.Fatal(err)
677 }
678
679 for _, block := range response.Content {
680 if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
681 return textBlock.Text
682 }
683 }
684 panic("expected a text block in the response")
685 }
686
687 // printSubleaseSummary summarizes the sublease agreement and prints the summary.
688 func printSubleaseSummary() {
689 subleaseSummary := summarizeDocument(documentText, detailsToExtract, anthropic.ModelClaudeOpus5_5, 16000)
690 fmt.Println(subleaseSummary)
691 }
692
693 ```
694
695 ```java Java
696 // Initialize the Anthropic client
697 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
698
699 String summarizeDocument(String text, List<String> detailsToExtract, Model model, long maxTokens) {
700 // Format the details to extract to be placed within the prompt's context
701 String detailsToExtractStr = String.join("\n", detailsToExtract);
702
703 // Prompt the model to summarize the sublease agreement
704 String prompt = """
705 Summarize the following sublease agreement. Focus on these key aspects:
706
707 %s
708
709 Provide the summary in bullet points nested within the XML header for each section. For example:
710
711 <parties involved>
712 - Sublessor: [Name]
713 // Add more details as needed
714 </parties involved>
715
716 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
717
718 Sublease agreement text:
719 %s
720 """
721 .formatted(detailsToExtractStr, text);
722
723 Message response = client.messages().create(MessageCreateParams.builder()
724 .model(model)
725 .maxTokens(maxTokens)
726 .system("You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.")
727 .addUserMessage(prompt)
728 .build());
729
730 return response.content().stream()
731 .flatMap(block -> block.text().stream())
732 .findFirst()
733 .orElseThrow()
734 .text();
735 }
736
737 void printSubleaseSummary() {
738 String subleaseSummary = summarizeDocument(
739 documentText, detailsToExtract, Model.CLAUDE_OPUS_5_5, 16000);
740 IO.println(subleaseSummary);
741 }
742 ```
743
744 ```php PHP
745 // Initialize the Anthropic client
746 $client = new \Anthropic\Client();
747
748 $summarizeDocument = function (
749 string $text,
750 array $detailsToExtract,
751 \Anthropic\Messages\Model $model = \Anthropic\Messages\Model::CLAUDE_OPUS_5_5,
752 int $maxTokens = 16000,
753 ) use ($client): string {
754 // Format the details to extract to be placed within the prompt's context
755 $detailsToExtractStr = implode("\n", $detailsToExtract);
756
757 // Prompt the model to summarize the sublease agreement
758 $prompt = <<<PROMPT
759 Summarize the following sublease agreement. Focus on these key aspects:
760
761 {$detailsToExtractStr}
762
763 Provide the summary in bullet points nested within the XML header for each section. For example:
764
765 <parties involved>
766 - Sublessor: [Name]
767 // Add more details as needed
768 </parties involved>
769
770 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
771
772 Sublease agreement text:
773 {$text}
774 PROMPT;
775
776 $response = $client->messages->create(
777 model: $model,
778 maxTokens: $maxTokens,
779 system: 'You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.',
780 messages: [['role' => 'user', 'content' => $prompt]],
781 );
782
783 $textBlock = array_find($response->content, fn ($block) => $block instanceof \Anthropic\Messages\TextBlock)
784 ?? throw new \RuntimeException('Expected a text block in the response.');
785
786 return $textBlock->text;
787 };
788
789 $subleaseSummary = $summarizeDocument($documentText, $detailsToExtract);
790 echo $subleaseSummary . "\n";
791 ```
792
793 ```ruby Ruby
794 # Initialize the Anthropic client
795 CLIENT = Anthropic::Client.new
796
797 def summarize_document(text, details_to_extract, model: Anthropic::Model::CLAUDE_OPUS_5_5, max_tokens: 16_000)
203798 # Format the details to extract to be placed within the prompt's context
204 details_to_extract_str = "\n".join(details_to_extract)
799 details_to_extract_str = details_to_extract.join("\n")
205800
206801 # Prompt the model to summarize the sublease agreement
207 prompt = f"""Summarize the following sublease agreement. Focus on these key aspects:
208
209 {details_to_extract_str}
210
211 Provide the summary in bullet points nested within the XML header for each section. For example:
212
213 <parties involved>
214 - Sublessor: [Name]
215 // Add more details as needed
216 </parties involved>
217
218 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
219
220 Sublease agreement text:
221 {text}
222 """
223
224 response = client.messages.create(
225 model=model,
226 max_tokens=max_tokens,
227 system="You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.",
228 messages=[
229 {"role": "user", "content": prompt},
230 ],
802 prompt = <<~PROMPT
803 Summarize the following sublease agreement. Focus on these key aspects:
804
805 #{details_to_extract_str}
806
807 Provide the summary in bullet points nested within the XML header for each section. For example:
808
809 <parties involved>
810 - Sublessor: [Name]
811 // Add more details as needed
812 </parties involved>
813
814 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
815
816 Sublease agreement text:
817 #{text}
818 PROMPT
819
820 response = CLIENT.messages.create(
821 model:,
822 max_tokens:,
823 system_: "You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.",
824 messages: [{role: :user, content: prompt}]
231825 )
232826
233 return next(block.text for block in response.content if block.type == "text")
234
235
236sublease_summary = summarize_document(document_text, details_to_extract)
237print(sublease_summary)
238```
239
240This code implements a `summarize_document` function that uses Claude to summarize the contents of a sublease agreement. The function accepts a text string and a list of details to extract as inputs. In this example, the code calls the function with the `document_text` and `details_to_extract` variables that were defined in the previous code snippets.
827 response.content.find { |block| block.is_a?(Anthropic::Models::TextBlock) }.text
828 end
829
830 sublease_summary = summarize_document(document_text, details_to_extract)
831 puts sublease_summary
832 ```
833</CodeGroup>
834
835This code implements a `summarize_document` (typescript, go, java: `summarizeDocument`; php: `$summarizeDocument`; csharp: `SummarizeDocument`) function that uses Claude to summarize the contents of a sublease agreement. The function accepts a text string and a list of details to extract as inputs. In this example, the code calls the function with the `document_text` (typescript, go, java, csharp: `documentText`; php: `$documentText`) and `details_to_extract` (typescript, go, java, csharp: `detailsToExtract`; php: `$detailsToExtract`) variables that were defined in the previous code snippets.
241836
242837Within the function, a prompt is generated for Claude, including the document to be summarized, the details to extract, and specific instructions for summarizing the document. The prompt instructs Claude to respond with a summary of each detail to extract nested within XML headers.
243838
from line 890
295890
296891Here's an example of how to perform meta-summarization:
297892
298```python Python
299# Initialize the Anthropic client
300client = anthropic.Anthropic()
301
302
303def chunk_text(text, chunk_size=20000):
304 return [text[i : i + chunk_size] for i in range(0, len(text), chunk_size)]
305
306
307def summarize_long_document(
308 text, details_to_extract, model="claude-opus-5-5", max_tokens=16000
309):
893<CodeGroup exclude="shell">
894 <CodeGroupItem language="cURL">
895 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
896 </CodeGroupItem>
897
898 <CodeGroupItem language="CLI">
899 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
900 </CodeGroupItem>
901
902 ```python Python
903 def chunk_text(text, chunk_size=20000):
904 return [text[i : i + chunk_size] for i in range(0, len(text), chunk_size)]
905
906
907 def summarize_long_document(
908 text, details_to_extract, model="claude-opus-5-5", max_tokens=16000
909 ):
910 # Format the details to extract to be placed within the prompt's context
911 details_to_extract_str = "\n".join(details_to_extract)
912
913 # Iterate over chunks and summarize each one
914 chunk_summaries = [
915 summarize_document(
916 chunk, details_to_extract, model=model, max_tokens=max_tokens
917 )
918 for chunk in chunk_text(text)
919 ]
920
921 final_summary_prompt = f"""
922
923 You are looking at the chunked summaries of multiple documents that are all related.
924 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
925
926 <chunked_summaries>
927 {"".join(chunk_summaries)}
928 </chunked_summaries>
929
930 Focus on these key aspects:
931 {details_to_extract_str}
932
933 Provide the summary in bullet points nested within the XML header for each section. For example:
934
935 <parties involved>
936 - Sublessor: [Name]
937 // Add more details as needed
938 </parties involved>
939
940 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
941 """
942
943 response = client.messages.create(
944 model=model,
945 max_tokens=max_tokens,
946 system="You are a legal expert that summarizes notes on one document.",
947 messages=[
948 {"role": "user", "content": final_summary_prompt},
949 ],
950 )
951
952 return next(block.text for block in response.content if block.type == "text")
953
954
955 long_summary = summarize_long_document(document_text, details_to_extract)
956 print(long_summary)
957 ```
958
959 ```typescript TypeScript
960 function chunkText(text: string, chunkSize = 20000): string[] {
961 return Array.from({ length: Math.ceil(text.length / chunkSize) }, (_, index) =>
962 text.slice(index * chunkSize, (index + 1) * chunkSize)
963 );
964 }
965
966 async function summarizeLongDocument(
967 text: string,
968 detailsToExtract: string[],
969 model: Anthropic.Model = "claude-opus-5-5",
970 maxTokens = 16000
971 ): Promise<string> {
972 // Format the details to extract to be placed within the prompt's context
973 const detailsToExtractStr = detailsToExtract.join("\n");
974
975 // Iterate over chunks and summarize each one
976 const chunkSummaries: string[] = [];
977 for (const chunk of chunkText(text)) {
978 chunkSummaries.push(await summarizeDocument(chunk, detailsToExtract, model, maxTokens));
979 }
980
981 const finalSummaryPrompt = `
982
983 You are looking at the chunked summaries of multiple documents that are all related.
984 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
985
986 <chunked_summaries>
987 ${chunkSummaries.join("")}
988 </chunked_summaries>
989
990 Focus on these key aspects:
991 ${detailsToExtractStr}
992
993 Provide the summary in bullet points nested within the XML header for each section. For example:
994
995 <parties involved>
996 - Sublessor: [Name]
997 // Add more details as needed
998 </parties involved>
999
1000 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
1001 `;
1002
1003 const response = await client.messages.create({
1004 model,
1005 max_tokens: maxTokens,
1006 system: "You are a legal expert that summarizes notes on one document.",
1007 messages: [{ role: "user", content: finalSummaryPrompt }]
1008 });
1009
1010 const textBlock = response.content.find((block) => block.type === "text");
1011 if (!textBlock) {
1012 throw new Error("Expected a text block in the response");
1013 }
1014 return textBlock.text;
1015 }
1016
1017 const longSummary = await summarizeLongDocument(documentText, detailsToExtract);
1018 console.log(longSummary);
1019 ```
1020
1021 ```csharp C#
1022 IEnumerable<string> ChunkText(string text, int chunkSize = 20000) =>
1023 text.Chunk(chunkSize).Select(chars => new string(chars));
1024
1025 async Task<string> SummarizeLongDocument(
1026 string text,
1027 IReadOnlyList<string> detailsToExtract,
1028 Model model = Model.ClaudeOpus5_5,
1029 long maxTokens = 16000
1030 )
1031 {
1032 // Format the details to extract to be placed within the prompt's context
1033 var detailsToExtractStr = string.Join("\n", detailsToExtract);
1034
1035 // Iterate over chunks and summarize each one
1036 List<string> chunkSummaries = [];
1037 foreach (var chunk in ChunkText(text))
1038 {
1039 chunkSummaries.Add(await SummarizeDocument(chunk, detailsToExtract, model, maxTokens));
1040 }
1041
1042 var finalSummaryPrompt = $"""
1043
1044 You are looking at the chunked summaries of multiple documents that are all related.
1045 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
1046
1047 <chunked_summaries>
1048 {string.Concat(chunkSummaries)}
1049 </chunked_summaries>
1050
1051 Focus on these key aspects:
1052 {detailsToExtractStr}
1053
1054 Provide the summary in bullet points nested within the XML header for each section. For example:
1055
1056 <parties involved>
1057 - Sublessor: [Name]
1058 // Add more details as needed
1059 </parties involved>
1060
1061 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
1062 """;
1063
1064 var response = await client.Messages.Create(
1065 new()
1066 {
1067 Model = model,
1068 MaxTokens = maxTokens,
1069 System = "You are a legal expert that summarizes notes on one document.",
1070 Messages = [new() { Role = Role.User, Content = finalSummaryPrompt }],
1071 }
1072 );
1073
1074 foreach (var block in response.Content)
1075 {
1076 if (block.TryPickText(out var textBlock))
1077 {
1078 return textBlock.Text;
1079 }
1080 }
1081 throw new InvalidOperationException("Expected a text block in the response.");
1082 }
1083
1084 var longSummary = await SummarizeLongDocument(documentText, detailsToExtract);
1085 Console.WriteLine(longSummary);
1086 ```
1087
1088 ```go Go
1089 func chunkText(text string, chunkSize int) []string {
1090 var chunks []string
1091 for chunk := range slices.Chunk([]rune(text), chunkSize) {
1092 chunks = append(chunks, string(chunk))
1093 }
1094 return chunks
1095 }
1096
1097 func summarizeLongDocument(text string, detailsToExtract []string, model anthropic.Model, maxTokens int64) string {
1098 // Format the details to extract to be placed within the prompt's context
1099 detailsToExtractStr := strings.Join(detailsToExtract, "\n")
1100
1101 // Iterate over chunks and summarize each one
1102 var chunkSummaries []string
1103 for _, chunk := range chunkText(text, 20000) {
1104 chunkSummaries = append(chunkSummaries, summarizeDocument(chunk, detailsToExtract, model, maxTokens))
1105 }
1106
1107 finalSummaryPrompt := fmt.Sprintf(`
1108
1109 You are looking at the chunked summaries of multiple documents that are all related.
1110 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
1111
1112 <chunked_summaries>
1113 %s
1114 </chunked_summaries>
1115
1116 Focus on these key aspects:
1117 %s
1118
1119 Provide the summary in bullet points nested within the XML header for each section. For example:
1120
1121 <parties involved>
1122 - Sublessor: [Name]
1123 // Add more details as needed
1124 </parties involved>
1125
1126 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
1127 `, strings.Join(chunkSummaries, ""), detailsToExtractStr)
1128
1129 response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
1130 Model: model,
1131 MaxTokens: maxTokens,
1132 System: []anthropic.TextBlockParam{
1133 {Text: "You are a legal expert that summarizes notes on one document."},
1134 },
1135 Messages: []anthropic.MessageParam{
1136 anthropic.NewUserMessage(anthropic.NewTextBlock(finalSummaryPrompt)),
1137 },
1138 })
1139 if err != nil {
1140 log.Fatal(err)
1141 }
1142
1143 for _, block := range response.Content {
1144 if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
1145 return textBlock.Text
1146 }
1147 }
1148 panic("expected a text block in the response")
1149 }
1150
1151 // printLongSummary summarizes the sublease agreement chunk by chunk and prints the combined summary.
1152 func printLongSummary() {
1153 longSummary := summarizeLongDocument(documentText, detailsToExtract, anthropic.ModelClaudeOpus5_5, 16000)
1154 fmt.Println(longSummary)
1155 }
1156
1157 ```
1158
1159 ```java Java
1160 List<String> chunkText(String text, int chunkSize) {
1161 return IntStream.iterate(0, start -> start < text.length(), start -> start + chunkSize)
1162 .mapToObj(start -> text.substring(start, Math.min(start + chunkSize, text.length())))
1163 .toList();
1164 }
1165
1166 String summarizeLongDocument(String text, List<String> detailsToExtract, Model model, long maxTokens) {
1167 // Format the details to extract to be placed within the prompt's context
1168 String detailsToExtractStr = String.join("\n", detailsToExtract);
1169
1170 // Iterate over chunks and summarize each one
1171 String chunkSummaries = chunkText(text, 20000).stream()
1172 .map(chunk -> summarizeDocument(chunk, detailsToExtract, model, maxTokens))
1173 .collect(Collectors.joining());
1174
1175 String finalSummaryPrompt = """
1176
1177 You are looking at the chunked summaries of multiple documents that are all related.
1178 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
1179
1180 <chunked_summaries>
1181 %s
1182 </chunked_summaries>
1183
1184 Focus on these key aspects:
1185 %s
1186
1187 Provide the summary in bullet points nested within the XML header for each section. For example:
1188
1189 <parties involved>
1190 - Sublessor: [Name]
1191 // Add more details as needed
1192 </parties involved>
1193
1194 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
1195 """
1196 .formatted(chunkSummaries, detailsToExtractStr);
1197
1198 Message response = client.messages().create(MessageCreateParams.builder()
1199 .model(model)
1200 .maxTokens(maxTokens)
1201 .system("You are a legal expert that summarizes notes on one document.")
1202 .addUserMessage(finalSummaryPrompt)
1203 .build());
1204
1205 return response.content().stream()
1206 .flatMap(block -> block.text().stream())
1207 .findFirst()
1208 .orElseThrow()
1209 .text();
1210 }
1211
1212 void printLongSummary() {
1213 String longSummary = summarizeLongDocument(
1214 documentText, detailsToExtract, Model.CLAUDE_OPUS_5_5, 16000);
1215 IO.println(longSummary);
1216 }
1217 ```
1218
1219 ```php PHP
1220 $chunkText = fn (string $text, int $chunkSize = 20000): array => mb_str_split($text, $chunkSize);
1221
1222 $summarizeLongDocument = function (
1223 string $text,
1224 array $detailsToExtract,
1225 \Anthropic\Messages\Model $model = \Anthropic\Messages\Model::CLAUDE_OPUS_5_5,
1226 int $maxTokens = 16000,
1227 ) use ($client, $chunkText, $summarizeDocument): string {
1228 // Format the details to extract to be placed within the prompt's context
1229 $detailsToExtractStr = implode("\n", $detailsToExtract);
1230
1231 // Iterate over chunks and summarize each one
1232 $chunkSummaries = array_map(
1233 fn (string $chunk) => $summarizeDocument($chunk, $detailsToExtract, $model, $maxTokens),
1234 $chunkText($text),
1235 );
1236 $chunkSummariesStr = implode('', $chunkSummaries);
1237
1238 $finalSummaryPrompt = <<<PROMPT
1239
1240 You are looking at the chunked summaries of multiple documents that are all related.
1241 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
1242
1243 <chunked_summaries>
1244 {$chunkSummariesStr}
1245 </chunked_summaries>
1246
1247 Focus on these key aspects:
1248 {$detailsToExtractStr}
1249
1250 Provide the summary in bullet points nested within the XML header for each section. For example:
1251
1252 <parties involved>
1253 - Sublessor: [Name]
1254 // Add more details as needed
1255 </parties involved>
1256
1257 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
1258 PROMPT;
1259
1260 $response = $client->messages->create(
1261 model: $model,
1262 maxTokens: $maxTokens,
1263 system: 'You are a legal expert that summarizes notes on one document.',
1264 messages: [['role' => 'user', 'content' => $finalSummaryPrompt]],
1265 );
1266
1267 $textBlock = array_find($response->content, fn ($block) => $block instanceof \Anthropic\Messages\TextBlock)
1268 ?? throw new \RuntimeException('Expected a text block in the response.');
1269
1270 return $textBlock->text;
1271 };
1272
1273 $longSummary = $summarizeLongDocument($documentText, $detailsToExtract);
1274 echo $longSummary . "\n";
1275 ```
1276
1277 ```ruby Ruby
1278 def chunk_text(text, chunk_size: 20_000)
1279 text.chars.each_slice(chunk_size).map(&:join)
1280 end
1281
1282 def summarize_long_document(text, details_to_extract, model: Anthropic::Model::CLAUDE_OPUS_5_5, max_tokens: 16_000)
3101283 # Format the details to extract to be placed within the prompt's context
311 details_to_extract_str = "\n".join(details_to_extract)
1284 details_to_extract_str = details_to_extract.join("\n")
3121285
3131286 # Iterate over chunks and summarize each one
314 chunk_summaries = [
315 summarize_document(
316 chunk, details_to_extract, model=model, max_tokens=max_tokens
317 )
318 for chunk in chunk_text(text)
319 ]
320
321 final_summary_prompt = f"""
322
323 You are looking at the chunked summaries of multiple documents that are all related.
324 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
325
326 <chunked_summaries>
327 {"".join(chunk_summaries)}
328 </chunked_summaries>
329
330 Focus on these key aspects:
331 {details_to_extract_str}
332
333 Provide the summary in bullet points nested within the XML header for each section. For example:
334
335 <parties involved>
336 - Sublessor: [Name]
337 // Add more details as needed
338 </parties involved>
339
340 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
341 """
342
343 response = client.messages.create(
344 model=model,
345 max_tokens=max_tokens,
346 system="You are a legal expert that summarizes notes on one document.",
347 messages=[
348 {"role": "user", "content": final_summary_prompt},
349 ],
1287 chunk_summaries = chunk_text(text).map do |chunk|
1288 summarize_document(chunk, details_to_extract, model:, max_tokens:)
1289 end
1290
1291 final_summary_prompt = <<~PROMPT
1292
1293 You are looking at the chunked summaries of multiple documents that are all related.
1294 Combine the following summaries of the document from different truthful sources into a coherent overall summary:
1295
1296 <chunked_summaries>
1297 #{chunk_summaries.join}
1298 </chunked_summaries>
1299
1300 Focus on these key aspects:
1301 #{details_to_extract_str}
1302
1303 Provide the summary in bullet points nested within the XML header for each section. For example:
1304
1305 <parties involved>
1306 - Sublessor: [Name]
1307 // Add more details as needed
1308 </parties involved>
1309
1310 If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
1311 PROMPT
1312
1313 response = CLIENT.messages.create(
1314 model:,
1315 max_tokens:,
1316 system_: "You are a legal expert that summarizes notes on one document.",
1317 messages: [{role: :user, content: final_summary_prompt}]
3501318 )
3511319
352 return next(block.text for block in response.content if block.type == "text")
353
354
355long_summary = summarize_long_document(document_text, details_to_extract)
356print(long_summary)
357```
358
359The `summarize_long_document` function builds upon the earlier `summarize_document` function by splitting the document into smaller chunks and summarizing each chunk individually.
360
361The code achieves this by applying the `summarize_document` function to each chunk of 20,000 characters within the original document. The individual summaries are then combined, and a final summary is created from these chunk summaries.
362
363Note that the `summarize_long_document` function isn't strictly necessary for the example PDF, as the entire document fits within Claude's context window. However, it becomes essential for documents exceeding Claude's context window or when summarizing multiple related documents together. Regardless, this meta-summarization technique often captures additional important details in the final summary that were missed in the earlier single-summary approach.
1320 response.content.find { |block| block.is_a?(Anthropic::Models::TextBlock) }.text
1321 end
1322
1323 long_summary = summarize_long_document(document_text, details_to_extract)
1324 puts long_summary
1325 ```
1326</CodeGroup>
1327
1328The `summarize_long_document` (typescript, go, java: `summarizeLongDocument`; php: `$summarizeLongDocument`; csharp: `SummarizeLongDocument`) function builds upon the earlier `summarize_document` (typescript, go, java: `summarizeDocument`; php: `$summarizeDocument`; csharp: `SummarizeDocument`) function by splitting the document into smaller chunks and summarizing each chunk individually.
1329
1330The code achieves this by applying the `summarize_document` (typescript, go, java: `summarizeDocument`; php: `$summarizeDocument`; csharp: `SummarizeDocument`) function to each chunk of 20,000 characters within the original document. The individual summaries are then combined, and a final summary is created from these chunk summaries.
1331
1332Note that the `summarize_long_document` (typescript, go, java: `summarizeLongDocument`; php: `$summarizeLongDocument`; csharp: `SummarizeLongDocument`) function isn't strictly necessary for the example PDF, as the entire document fits within Claude's context window. However, it becomes essential for documents exceeding Claude's context window or when summarizing multiple related documents together. Regardless, this meta-summarization technique often captures additional important details in the final summary that were missed in the earlier single-summary approach.
3641333
3651334### Use summary indexed documents to explore a large collection of documents
3661335
3671336
No line in this hunk matches that.