Follow Discord
Sweep 22 Oct 2026 · 04:32Z Build v2.1.308 527 read Stable v2.1.308 Latest v2.1.308 Next v2.1.308 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · api

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
/
lines

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 
Feedback