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 capture · api

One read of Claude Developer Platformapi-20261015T200735Z

78 pages moved out of 781 read.

Pages moved 78 significant first
Pages read 781 in this capture
Captured 20:07 UTC
Corpus hash 28c59a41a592 corpus-hash

What this read moved

1-25 of 78, page 1 of 4

This capture is too large to show at once. Changes 1-25 of 78 are below, significant first; the rest are on the following screens.

about-claude/use-case-guides/legal-summarization Changed · +1107 / -138 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 

about-claude/use-case-guides/ticket-routing Changed · +1092 / -133 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 6
66 
77## Prerequisites
88 
9* A Claude API key and the Python SDK installed
9* A Claude API key and the Claude SDK installed
1010* Access to, and familiarity with, your existing support ticketing system
1111* A sample set of historical support tickets for testing
1212 
from line 249
249249 
250250Here's an example ticket routing classification prompt:
251251 
252```python
253def classify_support_request(ticket_contents):
252<CodeGroup exclude="shell">
253 <CodeGroupItem language="cURL">
254 Build the prompt in code.
255 </CodeGroupItem>
256 
257 <CodeGroupItem language="CLI">
258 Build the prompt in code.
259 </CodeGroupItem>
260 
261 ```python Python
262 def classify_support_request(ticket_contents):
263 # Define the prompt for the classification task
264 classification_prompt = f"""You will be acting as a customer support ticket classification system. Your task is to analyze customer support requests and output the appropriate classification intent for each request, along with your reasoning.
265 
266 Here is the customer support request you need to classify:
267 
268 <request>{ticket_contents}</request>
269 
270 Please carefully analyze the above request to determine the customer's core intent and needs. Consider what the customer is asking for has concerns about.
271 
272 First, write out your reasoning and analysis of how to classify this request inside <reasoning> tags.
273 
274 Then, output the appropriate classification label for the request inside a <intent> tag. The valid intents are:
275 <intents>
276 <intent>Support, Feedback, Complaint</intent>
277 <intent>Order Tracking</intent>
278 <intent>Refund/Exchange</intent>
279 </intents>
280 
281 A request may have ONLY ONE applicable intent. Only include the intent that is most applicable to the request.
282 
283 As an example, consider the following request:
284 <request>Hello! I had high-speed fiber internet installed on Saturday and my installer, Kevin, was absolutely fantastic! Where can I send my positive review? Thanks for your help!</request>
285 
286 Here is an example of how your output should be formatted (for the above example request):
287 <reasoning>The user seeks information in order to leave positive feedback.</reasoning>
288 <intent>Support, Feedback, Complaint</intent>
289 
290 Here are a few more examples:
291 <examples>
292 <example 2>
293 Example 2 Input:
294 <request>I wanted to write and personally thank you for the compassion you showed towards my family during my father's funeral this past weekend. Your staff was so considerate and helpful throughout this whole process; it really took a load off our shoulders. The visitation brochures were beautiful. We'll never forget the kindness you showed us and we are so appreciative of how smoothly the proceedings went. Thank you, again, Amarantha Hill on behalf of the Hill Family.</request>
295 
296 Example 2 Output:
297 <reasoning>User leaves a positive review of their experience.</reasoning>
298 <intent>Support, Feedback, Complaint</intent>
299 </example 2>
300 <example 3>
301 
302 […]
303 
304 </example 8>
305 <example 9>
306 Example 9 Input:
307 <request>Your website keeps sending ad-popups that block the entire screen. It took me twenty minutes just to finally find the phone number to call and complain. How can I possibly access my account information with all of these popups? Can you access my account for me, since your website is broken? I need to know what the address is on file.</request>
308 
309 Example 9 Output:
310 <reasoning>The user requests help accessing their web account information.</reasoning>
311 <intent>Support, Feedback, Complaint</intent>
312 </example 9>
313 
314 Remember to always include your classification reasoning before your actual intent output. The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
315 """
316 ```
317 
318 ```typescript TypeScript
319 function classifySupportRequest(ticketContents: string) {
320 // Define the prompt for the classification task
321 const classificationPrompt = `You will be acting as a customer support ticket classification system. Your task is to analyze customer support requests and output the appropriate classification intent for each request, along with your reasoning.
322 
323 Here is the customer support request you need to classify:
324 
325 <request>${ticketContents}</request>
326 
327 Please carefully analyze the above request to determine the customer's core intent and needs. Consider what the customer is asking for has concerns about.
328 
329 First, write out your reasoning and analysis of how to classify this request inside <reasoning> tags.
330 
331 Then, output the appropriate classification label for the request inside a <intent> tag. The valid intents are:
332 <intents>
333 <intent>Support, Feedback, Complaint</intent>
334 <intent>Order Tracking</intent>
335 <intent>Refund/Exchange</intent>
336 </intents>
337 
338 A request may have ONLY ONE applicable intent. Only include the intent that is most applicable to the request.
339 
340 As an example, consider the following request:
341 <request>Hello! I had high-speed fiber internet installed on Saturday and my installer, Kevin, was absolutely fantastic! Where can I send my positive review? Thanks for your help!</request>
342 
343 Here is an example of how your output should be formatted (for the above example request):
344 <reasoning>The user seeks information in order to leave positive feedback.</reasoning>
345 <intent>Support, Feedback, Complaint</intent>
346 
347 Here are a few more examples:
348 <examples>
349 <example 2>
350 Example 2 Input:
351 <request>I wanted to write and personally thank you for the compassion you showed towards my family during my father's funeral this past weekend. Your staff was so considerate and helpful throughout this whole process; it really took a load off our shoulders. The visitation brochures were beautiful. We'll never forget the kindness you showed us and we are so appreciative of how smoothly the proceedings went. Thank you, again, Amarantha Hill on behalf of the Hill Family.</request>
352 
353 Example 2 Output:
354 <reasoning>User leaves a positive review of their experience.</reasoning>
355 <intent>Support, Feedback, Complaint</intent>
356 </example 2>
357 <example 3>
358 
359 […]
360 
361 </example 8>
362 <example 9>
363 Example 9 Input:
364 <request>Your website keeps sending ad-popups that block the entire screen. It took me twenty minutes just to finally find the phone number to call and complain. How can I possibly access my account information with all of these popups? Can you access my account for me, since your website is broken? I need to know what the address is on file.</request>
365 
366 Example 9 Output:
367 <reasoning>The user requests help accessing their web account information.</reasoning>
368 <intent>Support, Feedback, Complaint</intent>
369 </example 9>
370 
371 Remember to always include your classification reasoning before your actual intent output. The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
372 `;
373 }
374 ```
375 
376 ```csharp C#
377 void ClassifySupportRequest(string ticketContents)
378 {
379 // Define the prompt for the classification task
380 var classificationPrompt = $"""
381 You will be acting as a customer support ticket classification system. Your task is to analyze customer support requests and output the appropriate classification intent for each request, along with your reasoning.
382 
383 Here is the customer support request you need to classify:
384 
385 <request>{ticketContents}</request>
386 
387 Please carefully analyze the above request to determine the customer's core intent and needs. Consider what the customer is asking for has concerns about.
388 
389 First, write out your reasoning and analysis of how to classify this request inside <reasoning> tags.
390 
391 Then, output the appropriate classification label for the request inside a <intent> tag. The valid intents are:
392 <intents>
393 <intent>Support, Feedback, Complaint</intent>
394 <intent>Order Tracking</intent>
395 <intent>Refund/Exchange</intent>
396 </intents>
397 
398 A request may have ONLY ONE applicable intent. Only include the intent that is most applicable to the request.
399 
400 As an example, consider the following request:
401 <request>Hello! I had high-speed fiber internet installed on Saturday and my installer, Kevin, was absolutely fantastic! Where can I send my positive review? Thanks for your help!</request>
402 
403 Here is an example of how your output should be formatted (for the above example request):
404 <reasoning>The user seeks information in order to leave positive feedback.</reasoning>
405 <intent>Support, Feedback, Complaint</intent>
406 
407 Here are a few more examples:
408 <examples>
409 <example 2>
410 Example 2 Input:
411 <request>I wanted to write and personally thank you for the compassion you showed towards my family during my father's funeral this past weekend. Your staff was so considerate and helpful throughout this whole process; it really took a load off our shoulders. The visitation brochures were beautiful. We'll never forget the kindness you showed us and we are so appreciative of how smoothly the proceedings went. Thank you, again, Amarantha Hill on behalf of the Hill Family.</request>
412 
413 Example 2 Output:
414 <reasoning>User leaves a positive review of their experience.</reasoning>
415 <intent>Support, Feedback, Complaint</intent>
416 </example 2>
417 <example 3>
418 
419 […]
420 
421 </example 8>
422 <example 9>
423 Example 9 Input:
424 <request>Your website keeps sending ad-popups that block the entire screen. It took me twenty minutes just to finally find the phone number to call and complain. How can I possibly access my account information with all of these popups? Can you access my account for me, since your website is broken? I need to know what the address is on file.</request>
425 
426 Example 9 Output:
427 <reasoning>The user requests help accessing their web account information.</reasoning>
428 <intent>Support, Feedback, Complaint</intent>
429 </example 9>
430 
431 Remember to always include your classification reasoning before your actual intent output. The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
432 """;
433 }
434 ```
435 
436 ```go Go
437 func classifySupportRequest(ticketContents string) {
438 // Define the prompt for the classification task
439 classificationPrompt := `You will be acting as a customer support ticket classification system. Your task is to analyze customer support requests and output the appropriate classification intent for each request, along with your reasoning.
440 
441 Here is the customer support request you need to classify:
442 
443 <request>` + ticketContents + `</request>
444 
445 Please carefully analyze the above request to determine the customer's core intent and needs. Consider what the customer is asking for has concerns about.
446 
447 First, write out your reasoning and analysis of how to classify this request inside <reasoning> tags.
448 
449 Then, output the appropriate classification label for the request inside a <intent> tag. The valid intents are:
450 <intents>
451 <intent>Support, Feedback, Complaint</intent>
452 <intent>Order Tracking</intent>
453 <intent>Refund/Exchange</intent>
454 </intents>
455 
456 A request may have ONLY ONE applicable intent. Only include the intent that is most applicable to the request.
457 
458 As an example, consider the following request:
459 <request>Hello! I had high-speed fiber internet installed on Saturday and my installer, Kevin, was absolutely fantastic! Where can I send my positive review? Thanks for your help!</request>
460 
461 Here is an example of how your output should be formatted (for the above example request):
462 <reasoning>The user seeks information in order to leave positive feedback.</reasoning>
463 <intent>Support, Feedback, Complaint</intent>
464 
465 Here are a few more examples:
466 <examples>
467 <example 2>
468 Example 2 Input:
469 <request>I wanted to write and personally thank you for the compassion you showed towards my family during my father's funeral this past weekend. Your staff was so considerate and helpful throughout this whole process; it really took a load off our shoulders. The visitation brochures were beautiful. We'll never forget the kindness you showed us and we are so appreciative of how smoothly the proceedings went. Thank you, again, Amarantha Hill on behalf of the Hill Family.</request>
470 
471 Example 2 Output:
472 <reasoning>User leaves a positive review of their experience.</reasoning>
473 <intent>Support, Feedback, Complaint</intent>
474 </example 2>
475 <example 3>
476 
477 […]
478 
479 </example 8>
480 <example 9>
481 Example 9 Input:
482 <request>Your website keeps sending ad-popups that block the entire screen. It took me twenty minutes just to finally find the phone number to call and complain. How can I possibly access my account information with all of these popups? Can you access my account for me, since your website is broken? I need to know what the address is on file.</request>
483 
484 Example 9 Output:
485 <reasoning>The user requests help accessing their web account information.</reasoning>
486 <intent>Support, Feedback, Complaint</intent>
487 </example 9>
488 
489 Remember to always include your classification reasoning before your actual intent output. The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
490 `
491 // ...
492 }
493 ```
494 
495 ```java Java
496 void classifySupportRequest(String ticketContents) {
497 // Define the prompt for the classification task
498 String classificationPrompt = """
499 You will be acting as a customer support ticket classification system. Your task is to analyze customer support requests and output the appropriate classification intent for each request, along with your reasoning.
500 
501 Here is the customer support request you need to classify:
502 
503 <request>%s</request>
504 
505 Please carefully analyze the above request to determine the customer's core intent and needs. Consider what the customer is asking for has concerns about.
506 
507 First, write out your reasoning and analysis of how to classify this request inside <reasoning> tags.
508 
509 Then, output the appropriate classification label for the request inside a <intent> tag. The valid intents are:
510 <intents>
511 <intent>Support, Feedback, Complaint</intent>
512 <intent>Order Tracking</intent>
513 <intent>Refund/Exchange</intent>
514 </intents>
515 
516 A request may have ONLY ONE applicable intent. Only include the intent that is most applicable to the request.
517 
518 As an example, consider the following request:
519 <request>Hello! I had high-speed fiber internet installed on Saturday and my installer, Kevin, was absolutely fantastic! Where can I send my positive review? Thanks for your help!</request>
520 
521 Here is an example of how your output should be formatted (for the above example request):
522 <reasoning>The user seeks information in order to leave positive feedback.</reasoning>
523 <intent>Support, Feedback, Complaint</intent>
524 
525 Here are a few more examples:
526 <examples>
527 <example 2>
528 Example 2 Input:
529 <request>I wanted to write and personally thank you for the compassion you showed towards my family during my father's funeral this past weekend. Your staff was so considerate and helpful throughout this whole process; it really took a load off our shoulders. The visitation brochures were beautiful. We'll never forget the kindness you showed us and we are so appreciative of how smoothly the proceedings went. Thank you, again, Amarantha Hill on behalf of the Hill Family.</request>
530 
531 Example 2 Output:
532 <reasoning>User leaves a positive review of their experience.</reasoning>
533 <intent>Support, Feedback, Complaint</intent>
534 </example 2>
535 <example 3>
536 
537 […]
538 
539 </example 8>
540 <example 9>
541 Example 9 Input:
542 <request>Your website keeps sending ad-popups that block the entire screen. It took me twenty minutes just to finally find the phone number to call and complain. How can I possibly access my account information with all of these popups? Can you access my account for me, since your website is broken? I need to know what the address is on file.</request>
543 
544 Example 9 Output:
545 <reasoning>The user requests help accessing their web account information.</reasoning>
546 <intent>Support, Feedback, Complaint</intent>
547 </example 9>
548 
549 Remember to always include your classification reasoning before your actual intent output. The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
550 """.formatted(ticketContents);
551 }
552 ```
553 
554 ```php PHP
555 function classifySupportRequest(string $ticketContents): void
556 {
557 // Define the prompt for the classification task
558 $classificationPrompt = <<<PROMPT
559 You will be acting as a customer support ticket classification system. Your task is to analyze customer support requests and output the appropriate classification intent for each request, along with your reasoning.
560 
561 Here is the customer support request you need to classify:
562 
563 <request>{$ticketContents}</request>
564 
565 Please carefully analyze the above request to determine the customer's core intent and needs. Consider what the customer is asking for has concerns about.
566 
567 First, write out your reasoning and analysis of how to classify this request inside <reasoning> tags.
568 
569 Then, output the appropriate classification label for the request inside a <intent> tag. The valid intents are:
570 <intents>
571 <intent>Support, Feedback, Complaint</intent>
572 <intent>Order Tracking</intent>
573 <intent>Refund/Exchange</intent>
574 </intents>
575 
576 A request may have ONLY ONE applicable intent. Only include the intent that is most applicable to the request.
577 
578 As an example, consider the following request:
579 <request>Hello! I had high-speed fiber internet installed on Saturday and my installer, Kevin, was absolutely fantastic! Where can I send my positive review? Thanks for your help!</request>
580 
581 Here is an example of how your output should be formatted (for the above example request):
582 <reasoning>The user seeks information in order to leave positive feedback.</reasoning>
583 <intent>Support, Feedback, Complaint</intent>
584 
585 Here are a few more examples:
586 <examples>
587 <example 2>
588 Example 2 Input:
589 <request>I wanted to write and personally thank you for the compassion you showed towards my family during my father's funeral this past weekend. Your staff was so considerate and helpful throughout this whole process; it really took a load off our shoulders. The visitation brochures were beautiful. We'll never forget the kindness you showed us and we are so appreciative of how smoothly the proceedings went. Thank you, again, Amarantha Hill on behalf of the Hill Family.</request>
590 
591 Example 2 Output:
592 <reasoning>User leaves a positive review of their experience.</reasoning>
593 <intent>Support, Feedback, Complaint</intent>
594 </example 2>
595 <example 3>
596 
597 […]
598 
599 </example 8>
600 <example 9>
601 Example 9 Input:
602 <request>Your website keeps sending ad-popups that block the entire screen. It took me twenty minutes just to finally find the phone number to call and complain. How can I possibly access my account information with all of these popups? Can you access my account for me, since your website is broken? I need to know what the address is on file.</request>
603 
604 Example 9 Output:
605 <reasoning>The user requests help accessing their web account information.</reasoning>
606 <intent>Support, Feedback, Complaint</intent>
607 </example 9>
608 
609 Remember to always include your classification reasoning before your actual intent output. The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
610 PROMPT;
611 }
612 ```
613 
614 ```ruby Ruby
615 def classify_support_request(ticket_contents)
254616 # Define the prompt for the classification task
255 classification_prompt = f"""You will be acting as a customer support ticket classification system. Your task is to analyze customer support requests and output the appropriate classification intent for each request, along with your reasoning.
256 
257 Here is the customer support request you need to classify:
258 
259 <request>{ticket_contents}</request>
260 
261 Please carefully analyze the above request to determine the customer's core intent and needs. Consider what the customer is asking for has concerns about.
262 
263 First, write out your reasoning and analysis of how to classify this request inside <reasoning> tags.
264 
265 Then, output the appropriate classification label for the request inside a <intent> tag. The valid intents are:
266 <intents>
267 <intent>Support, Feedback, Complaint</intent>
268 <intent>Order Tracking</intent>
269 <intent>Refund/Exchange</intent>
270 </intents>
271 
272 A request may have ONLY ONE applicable intent. Only include the intent that is most applicable to the request.
273 
274 As an example, consider the following request:
275 <request>Hello! I had high-speed fiber internet installed on Saturday and my installer, Kevin, was absolutely fantastic! Where can I send my positive review? Thanks for your help!</request>
276 
277 Here is an example of how your output should be formatted (for the above example request):
278 <reasoning>The user seeks information in order to leave positive feedback.</reasoning>
279 <intent>Support, Feedback, Complaint</intent>
280 
281 Here are a few more examples:
282 <examples>
283 <example 2>
284 Example 2 Input:
285 <request>I wanted to write and personally thank you for the compassion you showed towards my family during my father's funeral this past weekend. Your staff was so considerate and helpful throughout this whole process; it really took a load off our shoulders. The visitation brochures were beautiful. We'll never forget the kindness you showed us and we are so appreciative of how smoothly the proceedings went. Thank you, again, Amarantha Hill on behalf of the Hill Family.</request>
286 
287 Example 2 Output:
288 <reasoning>User leaves a positive review of their experience.</reasoning>
289 <intent>Support, Feedback, Complaint</intent>
290 </example 2>
291 <example 3>
292 
293 ...
294 
295 </example 8>
296 <example 9>
297 Example 9 Input:
298 <request>Your website keeps sending ad-popups that block the entire screen. It took me twenty minutes just to finally find the phone number to call and complain. How can I possibly access my account information with all of these popups? Can you access my account for me, since your website is broken? I need to know what the address is on file.</request>
299 
300 Example 9 Output:
301 <reasoning>The user requests help accessing their web account information.</reasoning>
302 <intent>Support, Feedback, Complaint</intent>
303 </example 9>
304 
305 Remember to always include your classification reasoning before your actual intent output. The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
306 """
307```
617 classification_prompt = <<~PROMPT
618 You will be acting as a customer support ticket classification system. Your task is to analyze customer support requests and output the appropriate classification intent for each request, along with your reasoning.
619 
620 Here is the customer support request you need to classify:
621 
622 <request>#{ticket_contents}</request>
623 
624 Please carefully analyze the above request to determine the customer's core intent and needs. Consider what the customer is asking for has concerns about.
625 
626 First, write out your reasoning and analysis of how to classify this request inside <reasoning> tags.
627 
628 Then, output the appropriate classification label for the request inside a <intent> tag. The valid intents are:
629 <intents>
630 <intent>Support, Feedback, Complaint</intent>
631 <intent>Order Tracking</intent>
632 <intent>Refund/Exchange</intent>
633 </intents>
634 
635 A request may have ONLY ONE applicable intent. Only include the intent that is most applicable to the request.
636 
637 As an example, consider the following request:
638 <request>Hello! I had high-speed fiber internet installed on Saturday and my installer, Kevin, was absolutely fantastic! Where can I send my positive review? Thanks for your help!</request>
639 
640 Here is an example of how your output should be formatted (for the above example request):
641 <reasoning>The user seeks information in order to leave positive feedback.</reasoning>
642 <intent>Support, Feedback, Complaint</intent>
643 
644 Here are a few more examples:
645 <examples>
646 <example 2>
647 Example 2 Input:
648 <request>I wanted to write and personally thank you for the compassion you showed towards my family during my father's funeral this past weekend. Your staff was so considerate and helpful throughout this whole process; it really took a load off our shoulders. The visitation brochures were beautiful. We'll never forget the kindness you showed us and we are so appreciative of how smoothly the proceedings went. Thank you, again, Amarantha Hill on behalf of the Hill Family.</request>
649 
650 Example 2 Output:
651 <reasoning>User leaves a positive review of their experience.</reasoning>
652 <intent>Support, Feedback, Complaint</intent>
653 </example 2>
654 <example 3>
655 
656 […]
657 
658 </example 8>
659 <example 9>
660 Example 9 Input:
661 <request>Your website keeps sending ad-popups that block the entire screen. It took me twenty minutes just to finally find the phone number to call and complain. How can I possibly access my account information with all of these popups? Can you access my account for me, since your website is broken? I need to know what the address is on file.</request>
662 
663 Example 9 Output:
664 <reasoning>The user requests help accessing their web account information.</reasoning>
665 <intent>Support, Feedback, Complaint</intent>
666 </example 9>
667 
668 Remember to always include your classification reasoning before your actual intent output. The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
669 PROMPT
670 end
671 ```
672</CodeGroup>
308673 
309674<Note>
310675 This prompt is written for Claude Haiku 5.5, which runs here at `low` effort. On Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, and Claude Sonnet 5.5, ask for the intent and a one-sentence summary of the request instead. See [Keep reasoning in thinking blocks](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#keep-reasoning-in-thinking-blocks).
from line 677
312677 
313678Here are the key components of this prompt:
314679 
315* The prompt template is a Python f-string, allowing the `ticket_contents` to be inserted into the `<request>` tags.
680* The prompt template inserts `ticket_contents` (typescript, csharp, go, java: `ticketContents`; php: `$ticketContents`) into the `<request>` tags.
316681* The prompt gives Claude a clearly defined role as a classification system that carefully analyzes the ticket content to determine the customer's core intent and needs.
317682* The prompt instructs Claude on proper output formatting, in this case to provide its reasoning and analysis inside `<reasoning>` tags, followed by the appropriate classification label inside `<intent>` tags.
318683* The prompt specifies the valid intent categories: "Support, Feedback, Complaint", "Order Tracking", and "Refund/Exchange".
from line 689
324689 
325690It’s hard to know how well your prompt works without deploying it in a test production setting and [running evaluations](https://platform.claude.com/docs/en/test-and-evaluate/develop-tests).
326691 
327Build the deployment structure. Start by defining the method signature for wrapping the call to Claude. Extend the method you began writing earlier, which takes `ticket_contents` as input, so that it now returns a tuple of `reasoning` and `intent` as output. If you have an existing automation using traditional ML, you'll want to follow that method signature instead.
328 
329```python Python
330import re
331 
332# Create an instance of the Claude API client
333client = anthropic.Anthropic()
334 
335# Set the default model
336DEFAULT_MODEL = "claude-haiku-5-5"
337 
338 
339def classify_support_request(ticket_contents):
692Build the deployment structure. Start by defining the method signature for wrapping the call to Claude. Extend the method you began writing earlier, which takes `ticket_contents` (typescript, csharp, go, java: `ticketContents`; php: `$ticketContents`) as input, so that it now returns the reasoning and the intent as output. If you have an existing automation using traditional ML, you'll want to follow that method signature instead.
693 
694<CodeGroup exclude="shell">
695 <CodeGroupItem language="cURL">
696 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
697 </CodeGroupItem>
698 
699 <CodeGroupItem language="CLI">
700 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
701 </CodeGroupItem>
702 
703 ```python Python
704 import re
705 
706 # Create an instance of the Claude API client
707 client = anthropic.Anthropic()
708 
709 # Set the default model
710 DEFAULT_MODEL = "claude-haiku-5-5"
711 
712 
713 def classify_support_request(ticket_contents):
714 # Define the prompt for the classification task
715 classification_prompt = f"""You will be acting as a customer support ticket classification system.
716 […]
717 ... The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
718 """
719 # Send the prompt to the API to classify the support request.
720 message = client.messages.create(
721 model=DEFAULT_MODEL,
722 max_tokens=2048,
723 output_config={"effort": "low"},
724 messages=[{"role": "user", "content": classification_prompt}],
725 stream=False,
726 )
727 reasoning_and_intent = next(
728 (block.text for block in message.content if block.type == "text"), ""
729 )
730 
731 # Use Python's regular expressions library to extract `reasoning`.
732 reasoning_match = re.search(
733 r"<reasoning>(.*?)</reasoning>", reasoning_and_intent, re.DOTALL
734 )
735 reasoning = reasoning_match.group(1).strip() if reasoning_match else ""
736 
737 # Similarly, also extract the `intent`.
738 intent_match = re.search(r"<intent>(.*?)</intent>", reasoning_and_intent, re.DOTALL)
739 intent = intent_match.group(1).strip() if intent_match else ""
740 
741 return reasoning, intent
742 ```
743 
744 ```typescript TypeScript
745 // Create an instance of the Claude API client
746 const client = new Anthropic();
747 
748 // Set the default model
749 const DEFAULT_MODEL = "claude-haiku-5-5";
750 
751 async function classifySupportRequest(
752 ticketContents: string
753 ): Promise<{ reasoning: string; intent: string }> {
754 // Define the prompt for the classification task
755 const classificationPrompt = `You will be acting as a customer support ticket classification system.
756 […]
757 ... The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
758 `;
759 // Send the prompt to the API to classify the support request.
760 const message = await client.messages.create({
761 model: DEFAULT_MODEL,
762 max_tokens: 2048,
763 output_config: { effort: "low" },
764 messages: [{ role: "user", content: classificationPrompt }]
765 });
766 const textBlock = message.content.find(
767 (block): block is Anthropic.TextBlock => block.type === "text"
768 );
769 const reasoningAndIntent = textBlock?.text ?? "";
770 
771 // Use a regular expression to extract `reasoning`.
772 const reasoningMatch = /<reasoning>(.*?)<\/reasoning>/s.exec(reasoningAndIntent);
773 const reasoning = reasoningMatch?.[1].trim() ?? "";
774 
775 // Similarly, also extract the `intent`.
776 const intentMatch = /<intent>(.*?)<\/intent>/s.exec(reasoningAndIntent);
777 const intent = intentMatch?.[1].trim() ?? "";
778 
779 return { reasoning, intent };
780 }
781 ```
782 
783 ```csharp C#
784 using System.Text.RegularExpressions;
785 // ...
786 
787 // Create an instance of the Claude API client
788 AnthropicClient client = new();
789 
790 // Set the default model
791 const Model DefaultModel = Model.ClaudeHaiku5_5;
792 
793 async Task<(string Reasoning, string Intent)> ClassifySupportRequest(string ticketContents)
794 {
795 // Define the prompt for the classification task
796 var classificationPrompt = """
797 You will be acting as a customer support ticket classification system.
798 […]
799 ... The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
800 """;
801 // Send the prompt to the API to classify the support request.
802 var message = await client.Messages.Create(new MessageCreateParams
803 {
804 Model = DefaultModel,
805 MaxTokens = 2048,
806 OutputConfig = new OutputConfig { Effort = Effort.Low },
807 Messages = [new() { Role = Role.User, Content = classificationPrompt }],
808 });
809 var reasoningAndIntent = "";
810 foreach (var block in message.Content)
811 {
812 if (block.TryPickText(out var textBlock))
813 {
814 reasoningAndIntent = textBlock.Text;
815 break;
816 }
817 }
818 
819 // Use a regular expression to extract `reasoning`.
820 var reasoningMatch = Regex.Match(reasoningAndIntent, "<reasoning>(.*?)</reasoning>", RegexOptions.Singleline);
821 var reasoning = reasoningMatch.Success ? reasoningMatch.Groups[1].Value.Trim() : "";
822 
823 // Similarly, also extract the `intent`.
824 var intentMatch = Regex.Match(reasoningAndIntent, "<intent>(.*?)</intent>", RegexOptions.Singleline);
825 var intent = intentMatch.Success ? intentMatch.Groups[1].Value.Trim() : "";
826 
827 return (reasoning, intent);
828 }
829 ```
830 
831 ```go Go
832 import (
833 "context"
834 "regexp"
835 "strings"
836 // ...
837 )
838 
839 // Create an instance of the Claude API client
840 var client = anthropic.NewClient()
841 
842 // Set the default model
843 const defaultModel = anthropic.ModelClaudeHaiku5_5
844 
845 func classifySupportRequest(ctx context.Context, ticketContents string) (reasoning, intent string, err error) {
846 // Define the prompt for the classification task
847 classificationPrompt := `You will be acting as a customer support ticket classification system.
848 […]
849 ... The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
850 `
851 // Send the prompt to the API to classify the support request.
852 message, err := client.Messages.New(ctx, anthropic.MessageNewParams{
853 Model: defaultModel,
854 MaxTokens: 2048,
855 OutputConfig: anthropic.OutputConfigParam{Effort: anthropic.OutputConfigEffortLow},
856 Messages: []anthropic.MessageParam{
857 anthropic.NewUserMessage(anthropic.NewTextBlock(classificationPrompt)),
858 },
859 })
860 if err != nil {
861 return "", "", err
862 }
863 var reasoningAndIntent string
864 for _, block := range message.Content {
865 if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
866 reasoningAndIntent = textBlock.Text
867 break
868 }
869 }
870 
871 // Use a regular expression to extract `reasoning`.
872 reasoningMatch := regexp.MustCompile(`(?s)<reasoning>(.*?)</reasoning>`).FindStringSubmatch(reasoningAndIntent)
873 if reasoningMatch != nil {
874 reasoning = strings.TrimSpace(reasoningMatch[1])
875 }
876 
877 // Similarly, also extract the `intent`.
878 intentMatch := regexp.MustCompile(`(?s)<intent>(.*?)</intent>`).FindStringSubmatch(reasoningAndIntent)
879 if intentMatch != nil {
880 intent = strings.TrimSpace(intentMatch[1])
881 }
882 
883 return reasoning, intent, nil
884 }
885 ```
886 
887 ```java Java
888 import java.util.regex.Matcher;
889 import java.util.regex.Pattern;
890 
891 // Create an instance of the Claude API client
892 final AnthropicClient client = AnthropicOkHttpClient.fromEnv();
893 
894 // Set the default model
895 static final Model DEFAULT_MODEL = Model.CLAUDE_HAIKU_5_5;
896 
897 record Classification(String reasoning, String intent) {}
898 
899 Classification classifySupportRequest(String ticketContents) {
900 // Define the prompt for the classification task
901 String classificationPrompt = """
902 You will be acting as a customer support ticket classification system.
903 […]
904 ... The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
905 """;
906 // Send the prompt to the API to classify the support request.
907 Message message = client.messages().create(MessageCreateParams.builder()
908 .model(DEFAULT_MODEL)
909 .maxTokens(2048L)
910 .outputConfig(OutputConfig.builder().effort(OutputConfig.Effort.LOW).build())
911 .addUserMessage(classificationPrompt)
912 .build());
913 String reasoningAndIntent = message.content().stream()
914 .flatMap(block -> block.text().stream())
915 .map(textBlock -> textBlock.text())
916 .findFirst()
917 .orElse("");
918 
919 // Use a regular expression to extract `reasoning`.
920 Matcher reasoningMatch = Pattern.compile("<reasoning>(.*?)</reasoning>", Pattern.DOTALL)
921 .matcher(reasoningAndIntent);
922 String reasoning = reasoningMatch.find() ? reasoningMatch.group(1).strip() : "";
923 
924 // Similarly, also extract the `intent`.
925 Matcher intentMatch = Pattern.compile("<intent>(.*?)</intent>", Pattern.DOTALL)
926 .matcher(reasoningAndIntent);
927 String intent = intentMatch.find() ? intentMatch.group(1).strip() : "";
928 
929 return new Classification(reasoning, intent);
930 }
931 ```
932 
933 ```php PHP
934 // Create an instance of the Claude API client
935 $client = new Client();
936 
937 // Set the default model
938 const DEFAULT_MODEL = Model::CLAUDE_HAIKU_5_5;
939 
940 /**
941 * @return array{string, string} the reasoning and the intent
942 */
943 function classifySupportRequest(Client $client, string $ticketContents): array
944 {
945 // Define the prompt for the classification task
946 $classificationPrompt = <<<PROMPT
947 You will be acting as a customer support ticket classification system.
948 […]
949 ... The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
950 PROMPT;
951 // Send the prompt to the API to classify the support request.
952 $message = $client->messages->create(
953 model: DEFAULT_MODEL,
954 maxTokens: 2048,
955 outputConfig: ['effort' => 'low'],
956 messages: [['role' => 'user', 'content' => $classificationPrompt]],
957 );
958 $reasoningAndIntent = '';
959 foreach ($message->content as $block) {
960 if ($block instanceof \Anthropic\Messages\TextBlock) {
961 $reasoningAndIntent = $block->text;
962 break;
963 }
964 }
965 
966 // Use a regular expression to extract `reasoning`.
967 $reasoning = preg_match('/<reasoning>(.*?)<\/reasoning>/s', $reasoningAndIntent, $reasoningMatch)
968 ? trim($reasoningMatch[1])
969 : '';
970 
971 // Similarly, also extract the `intent`.
972 $intent = preg_match('/<intent>(.*?)<\/intent>/s', $reasoningAndIntent, $intentMatch)
973 ? trim($intentMatch[1])
974 : '';
975 
976 return [$reasoning, $intent];
977 }
978 ```
979 
980 ```ruby Ruby
981 # Create an instance of the Claude API client
982 client = Anthropic::Client.new
983 
984 # Set the default model
985 DEFAULT_MODEL = Anthropic::Model::CLAUDE_HAIKU_5_5
986 
987 def classify_support_request(client, ticket_contents)
340988 # Define the prompt for the classification task
341 classification_prompt = f"""You will be acting as a customer support ticket classification system.
342 ...
343 ... The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
344 """
989 classification_prompt = <<~PROMPT
990 You will be acting as a customer support ticket classification system.
991 […]
992 ... The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
993 PROMPT
345994 # Send the prompt to the API to classify the support request.
346995 message = client.messages.create(
347 model=DEFAULT_MODEL,
348 max_tokens=2048,
349 output_config={"effort": "low"},
350 messages=[{"role": "user", "content": classification_prompt}],
351 stream=False,
996 model: DEFAULT_MODEL,
997 max_tokens: 2048,
998 output_config: { effort: "low" },
999 messages: [{ role: "user", content: classification_prompt }]
3521000 )
353 reasoning_and_intent = next(
354 (block.text for block in message.content if block.type == "text"), ""
355 )
356 
357 # Use Python's regular expressions library to extract `reasoning`.
358 reasoning_match = re.search(
359 r"<reasoning>(.*?)</reasoning>", reasoning_and_intent, re.DOTALL
360 )
361 reasoning = reasoning_match.group(1).strip() if reasoning_match else ""
1001 reasoning_and_intent = message.content.grep(Anthropic::Models::TextBlock).first&.text || ""
1002 
1003 # Use a regular expression to extract `reasoning`.
1004 reasoning = reasoning_and_intent[%r{<reasoning>(.*?)</reasoning>}m, 1]&.strip || ""
3621005 
3631006 # Similarly, also extract the `intent`.
364 intent_match = re.search(r"<intent>(.*?)</intent>", reasoning_and_intent, re.DOTALL)
365 intent = intent_match.group(1).strip() if intent_match else ""
366 
367 return reasoning, intent
368```
1007 intent = reasoning_and_intent[%r{<intent>(.*?)</intent>}m, 1]&.strip || ""
1008 
1009 [reasoning, intent]
1010 end
1011 ```
1012</CodeGroup>
3691013 
3701014This code:
3711015 
3721016* Creates a client instance using your API key.
373* Defines a `classify_support_request` function that takes a `ticket_contents` string.
374* Sends the `ticket_contents` to Claude for classification using the `classification_prompt`.
375* Returns the model's `reasoning` and `intent` extracted from the response.
376 
377Because the entire reasoning and intent text must be generated before parsing, the example sets `stream=False` (the default).
1017* Defines a `classify_support_request` (typescript, go, java, php: `classifySupportRequest`; csharp: `ClassifySupportRequest`) function that takes a `ticket_contents` (typescript, csharp, go, java: `ticketContents`; php: `$ticketContents`) string.
1018* Sends the `ticket_contents` (typescript, csharp, go, java: `ticketContents`; php: `$ticketContents`) to Claude for classification using the `classification_prompt` (typescript, csharp, go, java: `classificationPrompt`; php: `$classificationPrompt`).
1019* Returns the model's reasoning and intent extracted from the response.
1020 
1021Because the entire reasoning and intent text must be generated before parsing, the example does not stream the response.
3781022 
3791023***
3801024 
from line 1039
3951039 
3961040To assess this, first modify the script to add a function that compares the predicted intent with the actual intent and calculates the percentage of correct predictions. Then add cost calculation and time measurement functionality.
3971041 
398```python Python
399import re
400 
401# Create an instance of the Claude API client
402client = anthropic.Anthropic()
403 
404# Set the default model
405DEFAULT_MODEL = "claude-haiku-5-5"
406 
407 
408def classify_support_request(request, actual_intent):
1042<CodeGroup exclude="shell">
1043 <CodeGroupItem language="cURL">
1044 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
1045 </CodeGroupItem>
1046 
1047 <CodeGroupItem language="CLI">
1048 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
1049 </CodeGroupItem>
1050 
1051 ```python Python
1052 import re
1053 
1054 # Create an instance of the Claude API client
1055 client = anthropic.Anthropic()
1056 
1057 # Set the default model
1058 DEFAULT_MODEL = "claude-haiku-5-5"
1059 
1060 
1061 def classify_support_request(request, actual_intent):
1062 # Define the prompt for the classification task
1063 classification_prompt = f"""You will be acting as a customer support ticket classification system.
1064 […]
1065 ...The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
1066 """
1067 
1068 message = client.messages.create(
1069 model=DEFAULT_MODEL,
1070 max_tokens=2048,
1071 output_config={"effort": "low"},
1072 messages=[{"role": "user", "content": classification_prompt}],
1073 )
1074 usage = message.usage # Get the usage statistics for the API call for how many input and output tokens were used.
1075 reasoning_and_intent = next(
1076 (block.text for block in message.content if block.type == "text"), ""
1077 )
1078 
1079 # Use Python's regular expressions library to extract `reasoning`.
1080 reasoning_match = re.search(
1081 r"<reasoning>(.*?)</reasoning>", reasoning_and_intent, re.DOTALL
1082 )
1083 reasoning = reasoning_match.group(1).strip() if reasoning_match else ""
1084 
1085 # Similarly, also extract the `intent`.
1086 intent_match = re.search(r"<intent>(.*?)</intent>", reasoning_and_intent, re.DOTALL)
1087 intent = intent_match.group(1).strip() if intent_match else ""
1088 
1089 # Check if the model's prediction is correct.
1090 correct = actual_intent.strip() == intent.strip()
1091 
1092 # Return the reasoning, intent, correct, and usage.
1093 return reasoning, intent, correct, usage
1094 ```
1095 
1096 ```typescript TypeScript
1097 // Create an instance of the Claude API client
1098 const client = new Anthropic();
1099 
1100 // Set the default model
1101 const DEFAULT_MODEL = "claude-haiku-5-5";
1102 
1103 async function classifySupportRequest(
1104 request: string,
1105 actualIntent: string
1106 ): Promise<{ reasoning: string; intent: string; correct: boolean; usage: Anthropic.Usage }> {
1107 // Define the prompt for the classification task
1108 const classificationPrompt = `You will be acting as a customer support ticket classification system.
1109 […]
1110 ...The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
1111 `;
1112 
1113 const message = await client.messages.create({
1114 model: DEFAULT_MODEL,
1115 max_tokens: 2048,
1116 output_config: { effort: "low" },
1117 messages: [{ role: "user", content: classificationPrompt }]
1118 });
1119 // Get the usage statistics for the API call for how many input and output tokens were used.
1120 const usage = message.usage;
1121 const textBlock = message.content.find(
1122 (block): block is Anthropic.TextBlock => block.type === "text"
1123 );
1124 const reasoningAndIntent = textBlock?.text ?? "";
1125 
1126 // Use a regular expression to extract `reasoning`.
1127 const reasoningMatch = /<reasoning>(.*?)<\/reasoning>/s.exec(reasoningAndIntent);
1128 const reasoning = reasoningMatch?.[1].trim() ?? "";
1129 
1130 // Similarly, also extract the `intent`.
1131 const intentMatch = /<intent>(.*?)<\/intent>/s.exec(reasoningAndIntent);
1132 const intent = intentMatch?.[1].trim() ?? "";
1133 
1134 // Check if the model's prediction is correct.
1135 const correct = actualIntent.trim() === intent.trim();
1136 
1137 // Return the reasoning, intent, correct, and usage.
1138 return { reasoning, intent, correct, usage };
1139 }
1140 ```
1141 
1142 ```csharp C#
1143 using System.Text.RegularExpressions;
1144 // ...
1145 
1146 // Create an instance of the Claude API client
1147 AnthropicClient client = new();
1148 
1149 // Set the default model
1150 const Model DefaultModel = Model.ClaudeHaiku5_5;
1151 
1152 async Task<(string Reasoning, string Intent, bool Correct, Usage Usage)> ClassifySupportRequest(string request, string actualIntent)
1153 {
1154 // Define the prompt for the classification task
1155 var classificationPrompt = """
1156 You will be acting as a customer support ticket classification system.
1157 […]
1158 ...The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
1159 """;
1160 
1161 var message = await client.Messages.Create(new MessageCreateParams
1162 {
1163 Model = DefaultModel,
1164 MaxTokens = 2048,
1165 OutputConfig = new OutputConfig { Effort = Effort.Low },
1166 Messages = [new() { Role = Role.User, Content = classificationPrompt }],
1167 });
1168 // Get the usage statistics for the API call for how many input and output tokens were used.
1169 var usage = message.Usage;
1170 var reasoningAndIntent = "";
1171 foreach (var block in message.Content)
1172 {
1173 if (block.TryPickText(out var textBlock))
1174 {
1175 reasoningAndIntent = textBlock.Text;
1176 break;
1177 }
1178 }
1179 
1180 // Use a regular expression to extract `reasoning`.
1181 var reasoningMatch = Regex.Match(reasoningAndIntent, "<reasoning>(.*?)</reasoning>", RegexOptions.Singleline);
1182 var reasoning = reasoningMatch.Success ? reasoningMatch.Groups[1].Value.Trim() : "";
1183 
1184 // Similarly, also extract the `intent`.
1185 var intentMatch = Regex.Match(reasoningAndIntent, "<intent>(.*?)</intent>", RegexOptions.Singleline);
1186 var intent = intentMatch.Success ? intentMatch.Groups[1].Value.Trim() : "";
1187 
1188 // Check if the model's prediction is correct.
1189 var correct = actualIntent.Trim() == intent.Trim();
1190 
1191 // Return the reasoning, intent, correct, and usage.
1192 return (reasoning, intent, correct, usage);
1193 }
1194 ```
1195 
1196 ```go Go
1197 import (
1198 "context"
1199 "regexp"
1200 "strings"
1201 // ...
1202 )
1203 
1204 // Create an instance of the Claude API client
1205 var client = anthropic.NewClient()
1206 
1207 // Set the default model
1208 const defaultModel = anthropic.ModelClaudeHaiku5_5
1209 
1210 func classifySupportRequest(ctx context.Context, request, actualIntent string) (reasoning, intent string, correct bool, usage anthropic.Usage, err error) {
1211 // Define the prompt for the classification task
1212 classificationPrompt := `You will be acting as a customer support ticket classification system.
1213 […]
1214 ...The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
1215 `
1216 message, err := client.Messages.New(ctx, anthropic.MessageNewParams{
1217 Model: defaultModel,
1218 MaxTokens: 2048,
1219 OutputConfig: anthropic.OutputConfigParam{Effort: anthropic.OutputConfigEffortLow},
1220 Messages: []anthropic.MessageParam{
1221 anthropic.NewUserMessage(anthropic.NewTextBlock(classificationPrompt)),
1222 },
1223 })
1224 if err != nil {
1225 return "", "", false, anthropic.Usage{}, err
1226 }
1227 // Get the usage statistics for the API call for how many input and output tokens were used.
1228 usage = message.Usage
1229 var reasoningAndIntent string
1230 for _, block := range message.Content {
1231 if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
1232 reasoningAndIntent = textBlock.Text
1233 break
1234 }
1235 }
1236 
1237 // Use a regular expression to extract `reasoning`.
1238 reasoningMatch := regexp.MustCompile(`(?s)<reasoning>(.*?)</reasoning>`).FindStringSubmatch(reasoningAndIntent)
1239 if reasoningMatch != nil {
1240 reasoning = strings.TrimSpace(reasoningMatch[1])
1241 }
1242 
1243 // Similarly, also extract the `intent`.
1244 intentMatch := regexp.MustCompile(`(?s)<intent>(.*?)</intent>`).FindStringSubmatch(reasoningAndIntent)
1245 if intentMatch != nil {
1246 intent = strings.TrimSpace(intentMatch[1])
1247 }
1248 
1249 // Check if the model's prediction is correct.
1250 correct = strings.TrimSpace(actualIntent) == strings.TrimSpace(intent)
1251 
1252 // Return the reasoning, intent, correct, and usage.
1253 return reasoning, intent, correct, usage, nil
1254 }
1255 ```
1256 
1257 ```java Java
1258 import java.util.regex.Matcher;
1259 import java.util.regex.Pattern;
1260 
1261 // Create an instance of the Claude API client
1262 final AnthropicClient client = AnthropicOkHttpClient.fromEnv();
1263 
1264 // Set the default model
1265 static final Model DEFAULT_MODEL = Model.CLAUDE_HAIKU_5_5;
1266 
1267 record Classification(String reasoning, String intent, boolean correct, Usage usage) {}
1268 
1269 Classification classifySupportRequest(String request, String actualIntent) {
1270 // Define the prompt for the classification task
1271 String classificationPrompt = """
1272 You will be acting as a customer support ticket classification system.
1273 […]
1274 ...The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
1275 """;
1276 
1277 Message message = client.messages().create(MessageCreateParams.builder()
1278 .model(DEFAULT_MODEL)
1279 .maxTokens(2048L)
1280 .outputConfig(OutputConfig.builder().effort(OutputConfig.Effort.LOW).build())
1281 .addUserMessage(classificationPrompt)
1282 .build());
1283 // Get the usage statistics for the API call for how many input and output tokens were used.
1284 Usage usage = message.usage();
1285 String reasoningAndIntent = message.content().stream()
1286 .flatMap(block -> block.text().stream())
1287 .map(textBlock -> textBlock.text())
1288 .findFirst()
1289 .orElse("");
1290 
1291 // Use a regular expression to extract `reasoning`.
1292 Matcher reasoningMatch = Pattern.compile("<reasoning>(.*?)</reasoning>", Pattern.DOTALL)
1293 .matcher(reasoningAndIntent);
1294 String reasoning = reasoningMatch.find() ? reasoningMatch.group(1).strip() : "";
1295 
1296 // Similarly, also extract the `intent`.
1297 Matcher intentMatch = Pattern.compile("<intent>(.*?)</intent>", Pattern.DOTALL)
1298 .matcher(reasoningAndIntent);
1299 String intent = intentMatch.find() ? intentMatch.group(1).strip() : "";
1300 
1301 // Check if the model's prediction is correct.
1302 boolean correct = actualIntent.strip().equals(intent.strip());
1303 
1304 // Return the reasoning, intent, correct, and usage.
1305 return new Classification(reasoning, intent, correct, usage);
1306 }
1307 ```
1308 
1309 ```php PHP
1310 // Create an instance of the Claude API client
1311 $client = new Client();
1312 
1313 // Set the default model
1314 const DEFAULT_MODEL = Model::CLAUDE_HAIKU_5_5;
1315 
1316 /**
1317 * @return array{string, string, bool, Usage} the reasoning, the intent, whether the intent is correct, and the usage
1318 */
1319 function classifySupportRequest(Client $client, string $request, string $actualIntent): array
1320 {
1321 // Define the prompt for the classification task
1322 $classificationPrompt = <<<PROMPT
1323 You will be acting as a customer support ticket classification system.
1324 […]
1325 ...The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
1326 PROMPT;
1327 
1328 $message = $client->messages->create(
1329 model: DEFAULT_MODEL,
1330 maxTokens: 2048,
1331 outputConfig: ['effort' => 'low'],
1332 messages: [['role' => 'user', 'content' => $classificationPrompt]],
1333 );
1334 // Get the usage statistics for the API call for how many input and output tokens were used.
1335 $usage = $message->usage;
1336 $reasoningAndIntent = '';
1337 foreach ($message->content as $block) {
1338 if ($block instanceof \Anthropic\Messages\TextBlock) {
1339 $reasoningAndIntent = $block->text;
1340 break;
1341 }
1342 }
1343 
1344 // Use a regular expression to extract `reasoning`.
1345 $reasoning = preg_match('/<reasoning>(.*?)<\/reasoning>/s', $reasoningAndIntent, $reasoningMatch)
1346 ? trim($reasoningMatch[1])
1347 : '';
1348 
1349 // Similarly, also extract the `intent`.
1350 $intent = preg_match('/<intent>(.*?)<\/intent>/s', $reasoningAndIntent, $intentMatch)
1351 ? trim($intentMatch[1])
1352 : '';
1353 
1354 // Check if the model's prediction is correct.
1355 $correct = trim($actualIntent) === trim($intent);
1356 
1357 // Return the reasoning, intent, correct, and usage.
1358 return [$reasoning, $intent, $correct, $usage];
1359 }
1360 ```
1361 
1362 ```ruby Ruby
1363 # Create an instance of the Claude API client
1364 client = Anthropic::Client.new
1365 
1366 # Set the default model
1367 DEFAULT_MODEL = Anthropic::Model::CLAUDE_HAIKU_5_5
1368 
1369 def classify_support_request(client, request, actual_intent)
4091370 # Define the prompt for the classification task
410 classification_prompt = f"""You will be acting as a customer support ticket classification system.
411 ...
412 ...The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
413 """
1371 classification_prompt = <<~PROMPT
1372 You will be acting as a customer support ticket classification system.
1373 […]
1374 ...The reasoning should be enclosed in <reasoning> tags and the intent in <intent> tags. Return only the reasoning and the intent.
1375 PROMPT
4141376 
4151377 message = client.messages.create(
416 model=DEFAULT_MODEL,
417 max_tokens=2048,
418 output_config={"effort": "low"},
419 messages=[{"role": "user", "content": classification_prompt}],
1378 model: DEFAULT_MODEL,
1379 max_tokens: 2048,
1380 output_config: { effort: "low" },
1381 messages: [{ role: "user", content: classification_prompt }]
4201382 )
421 usage = message.usage # Get the usage statistics for the API call for how many input and output tokens were used.
422 reasoning_and_intent = next(
423 (block.text for block in message.content if block.type == "text"), ""
424 )
425 
426 # Use Python's regular expressions library to extract `reasoning`.
427 reasoning_match = re.search(
428 r"<reasoning>(.*?)</reasoning>", reasoning_and_intent, re.DOTALL
429 )
430 reasoning = reasoning_match.group(1).strip() if reasoning_match else ""
1383 # Get the usage statistics for the API call for how many input and output tokens were used.
1384 usage = message.usage
1385 reasoning_and_intent = message.content.grep(Anthropic::Models::TextBlock).first&.text || ""
1386 
1387 # Use a regular expression to extract `reasoning`.
1388 reasoning = reasoning_and_intent[%r{<reasoning>(.*?)</reasoning>}m, 1]&.strip || ""
4311389 
4321390 # Similarly, also extract the `intent`.
433 intent_match = re.search(r"<intent>(.*?)</intent>", reasoning_and_intent, re.DOTALL)
434 intent = intent_match.group(1).strip() if intent_match else ""
1391 intent = reasoning_and_intent[%r{<intent>(.*?)</intent>}m, 1]&.strip || ""
4351392 
4361393 # Check if the model's prediction is correct.
437 correct = actual_intent.strip() == intent.strip()
1394 correct = actual_intent.strip == intent.strip
4381395 
4391396 # Return the reasoning, intent, correct, and usage.
440 return reasoning, intent, correct, usage
441```
1397 [reasoning, intent, correct, usage]
1398 end
1399 ```
1400</CodeGroup>
4421401 
4431402Here is a breakdown of the edits:
4441403 
445* The `classify_support_request` method now takes the `actual_intent` from the test cases and compares it against Claude’s intent classification to assess whether they match.
1404* The `classify_support_request` (typescript, go, java, php: `classifySupportRequest`; csharp: `ClassifySupportRequest`) method now takes the `actual_intent` (typescript, csharp, go, java: `actualIntent`; php: `$actualIntent`) from the test cases and compares it against Claude’s intent classification to assess whether they match.
4461405* The method extracts usage statistics for the API call to calculate cost based on input and output tokens used.
4471406 
4481407### Run your evaluation
4491408 

agents-and-tools/agent-skills/quickstart Changed · +11 / -13 lines

from line 129
129129 -H "content-type: application/json" \
130130 -H "x-api-key: $ANTHROPIC_API_KEY" \
131131 -H "anthropic-version: 2023-06-01" \
132 -d @- <<'EOF'
133 {
134 "model": "claude-opus-5-5",
135 "max_tokens": 16000,
136 "container": {
137 "skills": [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}]
138 },
139 "messages": [
140 {"role": "user", "content": "Create a presentation about renewable energy with 5 slides"}
141 ],
142 "tools": [{"type": "code_execution_20260521", "name": "code_execution"}]
143 }
144 EOF
132 -d '{
133 "model": "claude-opus-5-5",
134 "max_tokens": 16000,
135 "container": {
136 "skills": [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}]
137 },
138 "messages": [
139 {"role": "user", "content": "Create a presentation about renewable energy with 5 slides"}
140 ],
141 "tools": [{"type": "code_execution_20260521", "name": "code_execution"}]
142 }'
145143 )
146144 ```
147145 

agents-and-tools/tool-use/browser-use-sdk New page · 1357 lines, new page

## Use a driver from a provider ## Partner integrations ## Customize a driver ### Enable or disable members ### Add before and after hooks ## Implement a driver ### Return results ### Report browser state ### Handle errors ### Start calls while the response streams ### Run without the tool runner ## Advanced example: write your own driver ## Run the toolset safely ### Write a URL policy ### Intercept requests in the driver ### Put egress policy on the container ### Confine uploads and downloads ### Gate consequential members ### Isolate the browser host ### Remote and hosted browsers ## Computer toolset ### Write a desktop driver ### Scale coordinates and size images ### Input checks and results ### Run the computer toolset safely ### Use both toolsets in one request ## Reference ## Limitations ## Next steps

A whole new page. There's nothing to diff it against, so here is what it says.

---
title: Browser and computer use with the SDK toolsets
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk
description: Run the browser use tool or the computer use tool from the Python or TypeScript SDK. The SDK runs the loop and the checks you configure, and you supply the browser or the desktop.
---

With the browser and computer toolsets in the Python and TypeScript SDKs, you can build an agent that works in a web browser or on a desktop. It can use applications with no programmatic interface. Through the [browser use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool), Claude navigates, reads, and interacts with webpages. Through the [computer use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool), Claude sees a desktop in screenshots and uses the mouse and keyboard.

The SDK connects Claude to your browser or desktop. It runs the checks you configure, such as a URL policy for `navigate` or an approval callback. It sends each call that passes to your driver and builds the `tool_result` from what the driver returns. The [tool runner](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner) can run the agentic loop for you.

You supply the driver, which operates the browser or desktop. Start from a [provider's driver](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#use-a-driver-from-a-provider). With one, an agent takes a few lines of code. If none fits, write your own with a browser automation library such as Playwright.

Most of this page is about the browser toolset. [Computer toolset](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#computer-toolset) covers what differs.

Writing your own driver is an advanced case, covered in [Implement a driver](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver). The claude-quickstarts repository also has an [example browser driver](https://github.com/anthropics/claude-quickstarts/tree/main/browser-toolset) in Python and TypeScript. It controls Chromium through the Chrome DevTools Protocol (CDP), and it isn't production code.

<Note>
  The browser toolset class is in beta and available in the [Python SDK](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md) and the [TypeScript SDK](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md). The computer toolset class is in beta in the same SDKs, which have a [Python computer toolset guide](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) and a [TypeScript computer toolset guide](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md).
</Note>

## Use a driver from a provider

With a driver from a provider, such as one under [Partner integrations](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), you can build an agent in a few lines of code. The provider in this example is made up. Replace `example_browser_provider` (typescript: `example-browser-provider`) and `ExampleBrowser` with [a real provider's](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#partner-integrations) package and driver class, and set them up as the provider's docs describe.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  from anthropic import Anthropic
  from example_browser_provider import ExampleBrowser  # made-up provider

  client = Anthropic()
  with ExampleBrowser() as browser:
      runner = client.beta.messages.tool_runner(
          model="claude-opus-5-5",
          max_tokens=1024,
          tools=[browser],
          messages=[
              {"role": "user", "content": "Open example.com and tell me its heading."}
          ],
      )
      for message in runner:
          print(message)
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import { ExampleBrowser } from "example-browser-provider"; // made-up provider

  const client = new Anthropic();
  const browser = new ExampleBrowser();
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [browser],
      messages: [{ role: "user", content: "Open example.com and tell me its heading." }]
    });
    for await (const message of runner) {
      console.log(message);
    }
  } finally {
    await browser.close();
  }
  ```
</CodeGroup>

Pass the driver instance itself as the `tools` entry. The runner never closes the toolset, so one instance can serve several runs. Close it when you're done.

To write your own driver, see [Implement a driver](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver).

## Partner integrations

Browser Use, Browserbase, Daytona, and E2B publish their own drivers for the SDK toolsets.

<CardGroup cols={2}>
  <Card id="browser-use" title="Browser Use" image="https://platform.claude.com/docs/images/partner-logos/browser-use.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browser-use-dark.svg" href="https://docs.browser-use.com/open-source/customize/integrations/toolsets-for-claude">
    Browser Use driver for the browser toolset.
  </Card>

  <Card id="browserbase" title="Browserbase" image="https://platform.claude.com/docs/images/partner-logos/browserbase.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browserbase-dark.svg" href="https://docs.stagehand.dev/v4/integrations/agent-frameworks/claude-cua-toolset-quickstart">
    Browserbase Stagehand driver for the browser toolset.
  </Card>

  <Card id="daytona" title="Daytona" image="https://platform.claude.com/docs/images/partner-logos/daytona.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/daytona-dark.svg" href="https://www.daytona.io/docs/en/guides/claude/claude-draws-daytona-sandbox/">
    Daytona driver for the computer toolset.
  </Card>

  <Card id="e2-b" title="E2B" image="https://platform.claude.com/docs/images/partner-logos/e2b.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/e2b-dark.svg" href="https://docs.e2b.dev/agents/claude-toolsets">
    E2B drivers for the browser and computer toolsets.
  </Card>
</CardGroup>

Our partners also publish code on GitHub: the [Browser Use quickstart](https://github.com/browser-use/browser-use/tree/main/examples/integrations/toolsets-for-claude), the [Browserbase toolset](https://github.com/browserbase/claude-cua-toolset), and the [E2B example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-computer-use-orangehrm-js).

<Warning>
  Do these two things before you run a driver against anything but a throwaway browser:

  * [Write a URL policy](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy). Without one, the SDK checks no URL. For a browser that a provider hosts, also read [Remote and hosted browsers](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).
  * Use a browser profile that isn't signed in to any account whose data or actions you wouldn't hand to Claude. See [Isolate the browser host](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host).

  [Run the toolset safely](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#run-the-toolset-safely) covers the rest.
</Warning>

## Customize a driver

The examples in this section use `MyBrowser`, the driver from the [advanced example](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#quick-start) later on this page.

### Enable or disable members

`configs` takes the per-member settings described under [Configure the toolset](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset). List only the members you change:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # A MyBrowser that also implements read_console
  browser = MyBrowser(
      backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
  )
  ```

  ```typescript TypeScript
  // A MyBrowser that also implements read_console
  const browser = new MyBrowser(backend, {
    configs: { read_console: { enabled: true }, navigate: { enabled: false } }
  });
  ```
</CodeGroup>

The SDK refuses a call to a disabled member before your code runs. Enabling a member your class doesn't implement is a configuration error, unless the class overrides `execute`.

### Add before and after hooks

Override `execute` and call the parent's `execute`. Code before that call runs after the [URL policy](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), the [file policy](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads), and [`confirm`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members), and it can change the input. The SDK doesn't check the changed input again. Code after the call receives the result, and it can change the result. Raise `ToolError` (throw it in TypeScript) to refuse the call.

Overriding `execute` changes which members Claude is offered. The SDK counts every member as implemented, so Claude is offered every member that's on by default. The `MyBrowser` in the [advanced example](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#quick-start) serves three members, so the following `TracedBrowser` offers Claude members it can't serve. Turn those members off with `configs` before you use it.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  import time


  class TracedBrowser(MyBrowser):
      def execute(self, context, name, input):
          started = time.monotonic()
          result = super().execute(context, name, input)
          elapsed_ms = (time.monotonic() - started) * 1000
          call_id = context.tool_use.id if context.tool_use else "-"
          log.info("%s %s %.0fms", call_id, name, elapsed_ms)
          return redact(result) if name == "get_page_text" else result
  ```

  ```typescript TypeScript
  import type { BetaToolsetCallContext } from "@anthropic-ai/sdk/helpers/beta/toolsets";
  import type {
    BetaBrowserMemberInput,
    BetaBrowserMemberName
  } from "@anthropic-ai/sdk/resources/beta";

  class TracedBrowser extends MyBrowser {
    protected override async execute(
      ctx: BetaToolsetCallContext,
      name: BetaBrowserMemberName,
      input: BetaBrowserMemberInput
    ) {
      const started = performance.now();
      const result = await super.execute(ctx, name, input);
      log.info(`${ctx.toolUse?.id ?? "-"} ${name} ${Math.round(performance.now() - started)}ms`);
      return name === "get_page_text" ? redact(result as string) : result;
    }
  }
  ```
</CodeGroup>

## Implement a driver

A driver is your subclass of `BetaAbstractBrowserToolset20260801`. In it, override the members your browser supports. A member you don't implement is sent to the API as disabled. If Claude calls it anyway, the SDK returns an error, and the run continues. Overriding `execute` changes which members are sent as disabled ([Add before and after hooks](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#add-before-and-after-hooks)). Each member receives the call context and the member's input as a typed object, such as `BetaBrowserNavigateInput`. The input types come from `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`). [Member tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#member-tools) lists each input's fields.

In TypeScript, write members as methods, not arrow-function fields, because the SDK finds them on the prototype. Spell the `type` member `type_` in TypeScript. In Python, it's `type`.

### Return results

What a member returns determines what Claude reads. A successful result ends with a `browser_state` block built from your state report. An error result carries no block.

| Member                                                                                  | Returns                                                                                              | Claude reads                                                                                |
| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom`                                                                    | `BetaScreenshotResult`                                                                               | One image block                                                                             |
| `navigate`                                                                              | `BetaBrowserNavigateResult`                                                                          | `Navigated to {url} — {title} (HTTP {status})`                                              |
| `new_tab`, `switch_tab`, `list_tabs`, `close_tab`                                       | A tab entry (`new_tab`, `switch_tab`), a list of tab entries (`list_tabs`), or nothing (`close_tab`) | The `browser_state` block alone                                                             |
| `read_page`, `get_page_text`, `find`, `read_console`, `read_network`, `javascript_exec` | A string                                                                                             | The string, or `(empty)` for an empty string                                                |
| Every other member                                                                      | Nothing, or one line of text                                                                         | A short confirmation, such as `Clicked.`, then the returned line in a text block of its own |

### Report browser state

The SDK calls `_browser_state` (typescript: `browserState`) after each call that returns a result, including refused and failed calls. Return every open tab, and what changed since the last report:

* Opened tabs and download events.
* A `BetaNavigationRefused` for each navigation your [request hook](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver) blocked.
* A `BetaDialogDismissed` for each native dialog your driver dismissed.

Put all of these in `state_changes`. In Python, `BetaNavigationRefused()` and `BetaDialogDismissed(kind=..., message=...)` come from `anthropic.tools.browser`. In TypeScript, they're `{ type: "navigation_refused" }` and `{ type: "dialog_dismissed", kind, message }`.

The last two aren't API state changes. The SDK reports them to Claude as text outside the `browser_state` block: one line for all refused navigations, which doesn't include a URL, and a line for each of the first three dismissed dialogs, then a count of any others.

When any tab is open, exactly one must be active. Every member that takes a `tab_id` must act on the tab it names. The API's limits on the report are listed under [Track tabs with `browser_state`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-state).

Claude reads each tab's URL, each download's URL, and the URL that your `navigate` returns, as your driver wrote them. The SDK doesn't parse these URLs. It turns line breaks and other control characters into spaces, trims the ends, and cuts each URL at 4,096 characters. So a `data:` URL's contents, a `file:` URL's path, and a user name or password in a URL all reach Claude.

### Handle errors

| Raised by a member or the SDK | Claude reads                                                                         | The run   |
| ----------------------------- | ------------------------------------------------------------------------------------ | --------- |
| `ToolError`                   | Its message, as an error result                                                      | Continues |
| `ToolsetUsageError`           | Nothing                                                                              | Stops     |
| Any other exception           | `ClassName: message` in Python or `Error: message` in TypeScript, as an error result | Continues |

The SDK raises `ToolsetUsageError` for a configuration error, a misuse of the SDK during a call, or a call after `close`. It also raises one when `_browser_state` (typescript: `browserState`) raises any exception, even a `ToolError`. The tool runner and `tool_result` (typescript: `toolResult`) don't catch it, so it reaches their caller.

The SDK doesn't hide local paths in a member's error text, in the line an action such as `left_click` returns, in a failed download's error, or in a dismissed dialog's message.

A `ToolError` from your URL policy, file policy, or `confirm` callable reaches Claude as written, so leave local paths out of its text. Catch exceptions in your members, and raise `ToolError` (throw it in TypeScript) with your own text.

### Start calls while the response streams

By default, the tool runner runs a turn's calls after Claude's response ends. With early start, it can start a browser or computer call while the response is still streaming.

To turn it on, pass `stream=True` (typescript: `stream: true`) and `run_tools_eagerly=True` (typescript: `runToolsEagerly: true`) to the tool runner, as the [advanced example](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#quick-start) does. With `stream`, each pass through your loop over the runner gives you a stream instead of a message.

Early start changes when a call can start, not how the toolset runs it:

* **The checks still run first:** The SDK calls your URL policy, your file policy, and `confirm` before a call runs. With early start, it can call `confirm` while the response is still streaming.
* **Calls still run one at a time:** A toolset runs its calls in the order Claude wrote them. A failed call still stops that toolset's later calls in the turn.
* **A call that has started can't be taken back:** If the response is then cut off, for example at `max_tokens`, or your loop stops early, the action still happens, and Claude never reads its result.

### Run without the tool runner

Pass the instance in `tools` (`browser.toJSON()` in TypeScript), and answer each member call with `tool_result` (typescript: `toolResult`). The browser use tool requires you to stop at the first failed call ([Batch actions](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#batch-actions)).

This loop uses `MyBrowser` and the URL policy from the [advanced example](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-sdk#quick-start). After a failed call, it answers the turn's later calls without running them:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  from anthropic.types.beta import BetaMessageParam, BetaToolResultBlockParam

  NOT_EXECUTED = "Not executed: an earlier action in this turn failed."
  MAX_TURNS = 10

  with MyBrowser(backend, url_policy=url_policy) as browser:
      messages: list[BetaMessageParam] = [{"role": "user", "content": "Open example.com"}]
      for _ in range(MAX_TURNS):
          response = client.beta.messages.create(
              model="claude-opus-5-5",
              max_tokens=1024,
              tools=[browser],
              messages=messages,
          )
          messages.append({"role": "assistant", "content": response.content})
          calls = [
              block
              for block in response.content
              if block.type == "tool_use" and block.toolset_name == browser.toolset_name
          ]
          if not calls:
              break
          results: list[BetaToolResultBlockParam] = []
          failed = False
          for call in calls:
              if failed:
                  # After a failed call, the rest of the turn is answered, not run.
                  results.append(
                      {
                          "type": "tool_result",
                          "tool_use_id": call.id,
                          "toolset_name": call.toolset_name,
                          "content": NOT_EXECUTED,
                          "is_error": True,
                      }
                  )
                  continue
              result = browser.tool_result(call)
              failed = bool(result.get("is_error"))
              results.append(result)
          messages.append({"role": "user", "content": results})
  ```

  ```typescript TypeScript
  const NOT_EXECUTED = "Not executed: an earlier action in this turn failed.";
  const MAX_TURNS = 10;

  const browser = new MyBrowser(backend, { urlPolicy });
  try {
    const messages: Anthropic.Beta.BetaMessageParam[] = [
      { role: "user", content: "Open example.com" }
    ];
    for (let turn = 0; turn < MAX_TURNS; turn++) {
      const response = await client.beta.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        tools: [browser.toJSON()],
        messages
      });
      messages.push({ role: "assistant", content: response.content });
      const calls = response.content.filter(

Cut at 300 lines. The page has the rest.

agents-and-tools/tool-use/build-a-tool-using-agent Changed · +8 / -21 lines

from line 4019
40194019 
40204020Each SDK provides a helper that turns an ordinary function into a runnable tool and derives the input schema from its signature; the tabs below show the idiomatic form for each language.
40214021 
4022<CodeGroup>
4023 ```bash cURL
4024 #!/bin/bash
4025 # Ring 5: The Tool Runner SDK abstraction.
4022<CodeGroup exclude="shell">
4023 <CodeGroupItem language="cURL">
4024 The Tool Runner is a helper in the Claude SDK. Raw HTTP requests have no equivalent. To keep using curl, use the loop from [Ring 4](https://platform.claude.com/docs/en/agents-and-tools/tool-use/build-a-tool-using-agent#ring-4-error-handling).
4025 </CodeGroupItem>
40264026 
4027 # The Tool Runner SDK abstraction is available in all seven SDKs: Python,
4028 # TypeScript, C#, Go, Java, PHP, and Ruby. There is no equivalent for raw
4029 # curl requests. Switch to any SDK tab to see Ring 5, or keep the Ring 4
4030 # loop as your shell implementation.
4031 ```
4032 
4033 ```bash CLI
4034 #!/usr/bin/env bash
4035 # Ring 5: The Tool Runner SDK abstraction.
4036 set -euo pipefail
4037 
4038 # The Tool Runner SDK abstraction is available in all seven SDKs: Python,
4039 # TypeScript, C#, Go, Java, PHP, and Ruby. The ant CLI exposes the Messages
4040 # API directly and has no equivalent helper. Switch to any SDK tab to see
4041 # Ring 5, or keep the Ring 4 loop as your CLI implementation.
4042 ```
4027 <CodeGroupItem language="CLI">
4028 The Tool Runner is a helper in the Claude SDK. The `ant` CLI has no equivalent. To keep using the CLI, use the loop from [Ring 4](https://platform.claude.com/docs/en/agents-and-tools/tool-use/build-a-tool-using-agent#ring-4-error-handling).
4029 </CodeGroupItem>
40434030 
40444031 ```python Python
40454032 # Ring 5: The Tool Runner SDK abstraction.

agents-and-tools/tool-use/code-execution-tool Changed · +8 / -12 lines

from line 558
558558 
559559When Claude saves files to its output directory during code execution (see [How generated files are captured](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#how-generated-files-are-captured)), each file's ID appears in the code execution tool result, and you can download it with the [Files API](https://platform.claude.com/docs/en/build-with-claude/files):
560560 
561<CodeGroup>
562 ```bash cURL
563 # Downloading every generated file means looping over the file IDs in the tool
564 # result, which doesn't translate to a one-off shell command. Use one of the
565 # SDK examples instead.
566 ```
561<CodeGroup exclude="shell">
562 <CodeGroupItem language="cURL">
563 Downloading every generated file takes a loop over the file IDs in the tool result, which needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) for this.
564 </CodeGroupItem>
567565 
568 ```bash CLI
569 # Extracting every file ID from the tool results and downloading each one
570 # requires a loop, which doesn't translate well to a one-off CLI command.
571 # Use one of the SDK examples instead.
572 ```
566 <CodeGroupItem language="CLI">
567 Downloading every generated file takes a loop over the file IDs in the tool result, which needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) for this.
568 </CodeGroupItem>
573569 
574570 ```python Python
575571 client = Anthropic()

agents-and-tools/tool-use/define-tools Changed · +54 / -58 lines

from line 146
146146 -H "content-type: application/json" \
147147 -H "x-api-key: $ANTHROPIC_API_KEY" \
148148 -H "anthropic-version: 2023-06-01" \
149 -d @- <<'EOF'
150 {
151 "model": "claude-opus-5-5",
152 "max_tokens": 1024,
153 "tools": [
154 {
155 "name": "get_weather",
156 "description": "Get the current weather in a given location",
157 "input_schema": {
158 "type": "object",
159 "properties": {
160 "location": {
161 "type": "string",
162 "description": "The city and state, e.g. San Francisco, CA"
149 -d '{
150 "model": "claude-opus-5-5",
151 "max_tokens": 1024,
152 "tools": [
153 {
154 "name": "get_weather",
155 "description": "Get the current weather in a given location",
156 "input_schema": {
157 "type": "object",
158 "properties": {
159 "location": {
160 "type": "string",
161 "description": "The city and state, e.g. San Francisco, CA"
162 },
163 "unit": {
164 "type": "string",
165 "enum": ["celsius", "fahrenheit"],
166 "description": "The unit of temperature"
167 }
163168 },
164 "unit": {
165 "type": "string",
166 "enum": ["celsius", "fahrenheit"],
167 "description": "The unit of temperature"
168 }
169 "required": ["location"]
169170 },
170 "required": ["location"]
171 },
172 "input_examples": [
173 {"location": "San Francisco, CA", "unit": "fahrenheit"},
174 {"location": "Tokyo, Japan", "unit": "celsius"},
175 {"location": "New York, NY"}
176 ]
177 }
178 ],
179 "messages": [
180 {"role": "user", "content": "What's the weather like in San Francisco?"}
181 ]
182 }
183 EOF
171 "input_examples": [
172 {"location": "San Francisco, CA", "unit": "fahrenheit"},
173 {"location": "Tokyo, Japan", "unit": "celsius"},
174 {"location": "New York, NY"}
175 ]
176 }
177 ],
178 "messages": [
179 {"role": "user", "content": "What is the weather like in San Francisco?"}
180 ]
181 }'
184182 ```
185183 
186184 ```bash CLI
from line 569
571569 -H "content-type: application/json" \
572570 -H "x-api-key: $ANTHROPIC_API_KEY" \
573571 -H "anthropic-version: 2023-06-01" \
574 -d @- <<'EOF'
575 {
576 "model": "claude-opus-5",
577 "max_tokens": 1024,
578 "tools": [
579 {
580 "name": "get_weather",
581 "description": "Get the current weather in a given location",
582 "input_schema": {
583 "type": "object",
584 "properties": {
585 "location": {
586 "type": "string",
587 "description": "The city and state, e.g. San Francisco, CA"
588 }
589 },
590 "required": ["location"]
572 -d '{
573 "model": "claude-opus-5",
574 "max_tokens": 1024,
575 "tools": [
576 {
577 "name": "get_weather",
578 "description": "Get the current weather in a given location",
579 "input_schema": {
580 "type": "object",
581 "properties": {
582 "location": {
583 "type": "string",
584 "description": "The city and state, e.g. San Francisco, CA"
585 }
586 },
587 "required": ["location"]
588 }
591589 }
592 }
593 ],
594 "tool_choice": {"type": "tool", "name": "get_weather"},
595 "messages": [
596 {"role": "user", "content": "What's the weather like in San Francisco?"}
597 ]
598 }
599 EOF
590 ],
591 "tool_choice": {"type": "tool", "name": "get_weather"},
592 "messages": [
593 {"role": "user", "content": "What is the weather like in San Francisco?"}
594 ]
595 }'
600596 ```
601597 
602598 ```bash CLI

agents-and-tools/tool-use/fine-grained-tool-streaming Changed · +8 / -10 lines

from line 496
496496 
497497The type mismatch between the initial `input: {}` (object) and `partial_json` (string) is by design. The empty object marks the slot in the content array. The delta strings build the real value.
498498 
499<CodeGroup>
500 ```bash cURL
501 # Accumulating per-block input deltas needs a programming language; the first
502 # example's CLI tab shows the raw fragments with jq. See the SDK tabs.
503 ```
499<CodeGroup exclude="shell">
500 <CodeGroupItem language="cURL">
501 Accumulating the input fragments of each `tool_use` block needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) for this. To see the raw fragments as they stream, run the request in [How to use fine-grained tool streaming](https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming#how-to-use-fine-grained-tool-streaming).
502 </CodeGroupItem>
504503 
505 ```bash CLI
506 # Accumulating per-block input deltas needs a programming language; the first
507 # example's CLI tab shows the raw fragments with jq. See the SDK tabs.
508 ```
504 <CodeGroupItem language="CLI">
505 Accumulating the input fragments of each `tool_use` block needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) for this. To see the raw fragments as they stream, run the request in [How to use fine-grained tool streaming](https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming#how-to-use-fine-grained-tool-streaming).
506 </CodeGroupItem>
509507 
510508 ```python Python
511509 client = anthropic.Anthropic()

agents-and-tools/tool-use/parallel-tool-use Changed · +16 / -21 lines

from line 33
3333 
3434The following script sends a request that should trigger parallel tool calls, verifies the response contains them, and formats the tool results so parallelism keeps working. Run it with `ANTHROPIC_API_KEY` set in your environment:
3535 
36<CodeGroup>
37 ```bash cURL
38 # This end-to-end test flow doesn't translate well to a one-off shell command.
39 # See the SDK tabs for the full flow. The underlying HTTP request is a standard
40 # tool use request with multiple tools defined.
41 ```
36<CodeGroup exclude="shell">
37 <CodeGroupItem language="cURL">
38 This end-to-end test needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it. The request it sends is a standard tool use request with two tools defined.
39 </CodeGroupItem>
4240 
43 ```bash CLI
44 # This end-to-end test flow doesn't translate well to a one-off shell command.
45 # See the SDK tabs for the full flow.
46 ```
41 <CodeGroupItem language="CLI">
42 This end-to-end test needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
43 </CodeGroupItem>
4744 
4845 ```python Python
4946 client = Anthropic()
from line 1445
14481445 
14491446To verify parallel tool calls are working:
14501447 
1451<CodeGroup>
1452 ```bash cURL
1453 # Measuring parallel tool use is client-side analysis of responses you've already
1454 # collected, so it doesn't translate to a one-off shell command. See the SDK tabs.
1455 ```
1448<CodeGroup exclude="shell">
1449 <CodeGroupItem language="cURL">
1450 To measure parallel tool use, your code analyzes the responses that it already collected. This needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) for this.
1451 </CodeGroupItem>
14561452 
1457 ```bash CLI
1458 # Measuring parallel tool use is client-side analysis of responses you've already
1459 # collected, so it doesn't translate to a one-off shell command. See the SDK tabs.
1460 ```
1453 <CodeGroupItem language="CLI">
1454 To measure parallel tool use, your code analyzes the responses that it already collected. This needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) for this.
1455 </CodeGroupItem>
14611456 
14621457 ```python Python
14631458 messages = [] # Message objects returned by client.messages.create across your run

api/errors Changed · +4 / -5 lines

from line 269
269269 
270270If you don't need to process events incrementally, the SDK can consume the stream for you and return the complete `Message` object, identical to what a non-streaming call returns:
271271 
272<CodeGroup>
273 ```bash cURL
274 # Raw SSE output requires handling events; there is no single-command way
275 # to accumulate the final message with curl. Use the SDK examples instead.
276 ```
272<CodeGroup exclude="shell:cURL">
273 <CodeGroupItem language="cURL">
274 A curl request prints the raw stream events and can't combine them into the final message. To get the complete `Message` object from a stream, use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview).
275 </CodeGroupItem>
277276 
278277 ```bash CLI
279278 # The CLI streams events; --format jsonl emits one event per line

build-with-claude/cache-diagnostics Changed · +25 / -29 lines

from line 50
5050 -H "x-api-key: $ANTHROPIC_API_KEY" \
5151 -H "anthropic-version: 2023-06-01" \
5252 -H "content-type: application/json" \
53 -d @- <<EOF | jq '{id, diagnostics}' # diagnostics: null means no divergence was found
54 {
55 "model": "claude-opus-5-5",
56 "max_tokens": 1024,
57 "cache_control": {"type": "ephemeral"},
58 "system": "You are an AI assistant analyzing a large document. <document>...</document>",
59 "messages": [
60 {"role": "user", "content": "Summarize section 1."},
61 {"role": "assistant", "content": "Section 1 covers..."},
62 {"role": "user", "content": "Now summarize section 2."}
63 ],
64 "diagnostics": {"previous_message_id": "$message_id"}
65 }
66 EOF
53 -d '{
54 "model": "claude-opus-5-5",
55 "max_tokens": 1024,
56 "cache_control": {"type": "ephemeral"},
57 "system": "You are an AI assistant analyzing a large document. <document>...</document>",
58 "messages": [
59 {"role": "user", "content": "Summarize section 1."},
60 {"role": "assistant", "content": "Section 1 covers..."},
61 {"role": "user", "content": "Now summarize section 2."}
62 ],
63 "diagnostics": {"previous_message_id": "'"$message_id"'"}
64 }' | jq '{id, diagnostics}' # diagnostics: null means no divergence was found
6765 ```
6866 
6967 ```bash CLI
from line 411
413411 -H "x-api-key: $ANTHROPIC_API_KEY" \
414412 -H "anthropic-version: 2023-06-01" \
415413 -H "content-type: application/json" \
416 -d @- <<EOF | jq -R 'select(startswith("data: ")) | ltrimstr("data: ") | fromjson | select(.type == "message_start") | .message.diagnostics'
417 {
418 "model": "claude-opus-5-5",
419 "max_tokens": 1024,
420 "stream": true,
421 "cache_control": {"type": "ephemeral"},
422 "system": "You are an AI assistant analyzing a large document. <document>...</document>",
423 "messages": [
424 {"role": "user", "content": "Summarize section 1."},
425 {"role": "assistant", "content": "Section 1 covers..."},
426 {"role": "user", "content": "Now summarize section 2."}
427 ],
428 "diagnostics": {"previous_message_id": "$message_id"}
429 }
430 EOF
414 -d '{
415 "model": "claude-opus-5-5",
416 "max_tokens": 1024,
417 "stream": true,
418 "cache_control": {"type": "ephemeral"},
419 "system": "You are an AI assistant analyzing a large document. <document>...</document>",
420 "messages": [
421 {"role": "user", "content": "Summarize section 1."},
422 {"role": "assistant", "content": "Section 1 covers..."},
423 {"role": "user", "content": "Now summarize section 2."}
424 ],
425 "diagnostics": {"previous_message_id": "'"$message_id"'"}
426 }' | jq -R 'select(startswith("data: ")) | ltrimstr("data: ") | fromjson | select(.type == "message_start") | .message.diagnostics'
431427 ```
432428 
433429 ```bash CLI

build-with-claude/citations Changed · +44 / -48 lines

from line 752
752752 -H "x-api-key: $ANTHROPIC_API_KEY" \
753753 -H "anthropic-version: 2023-06-01" \
754754 -H "content-type: application/json" \
755 -d @- <<EOF
756 {
757 "model": "claude-opus-5-5",
758 "max_tokens": 1024,
759 "messages": [
760 {
761 "role": "user",
762 "content": [
763 {
764 "type": "document",
765 "source": {"type": "file", "file_id": "$FILE_ID"},
766 "title": "Document Title",
767 "context": "Context about the document that will not be cited from",
768 "citations": {"enabled": true}
769 },
770 {
771 "type": "text",
772 "text": "Summarize this document."
773 }
774 ]
775 }
776 ]
777 }
778 EOF
755 -d '{
756 "model": "claude-opus-5-5",
757 "max_tokens": 1024,
758 "messages": [
759 {
760 "role": "user",
761 "content": [
762 {
763 "type": "document",
764 "source": {"type": "file", "file_id": "'"$FILE_ID"'"},
765 "title": "Document Title",
766 "context": "Context about the document that will not be cited from",
767 "citations": {"enabled": true}
768 },
769 {
770 "type": "text",
771 "text": "Summarize this document."
772 }
773 ]
774 }
775 ]
776 }'
779777 ```
780778 
781779 ```bash CLI
from line 1571
15731571 -H "x-api-key: $ANTHROPIC_API_KEY" \
15741572 -H "anthropic-version: 2023-06-01" \
15751573 -H "content-type: application/json" \
1576 -d @- <<EOF
1577 {
1578 "model": "claude-opus-5-5",
1579 "max_tokens": 1024,
1580 "messages": [
1581 {
1582 "role": "user",
1583 "content": [
1584 {
1585 "type": "document",
1586 "source": {"type": "file", "file_id": "$FILE_ID"},
1587 "title": "Document Title",
1588 "context": "Context about the document that will not be cited from",
1589 "citations": {"enabled": true}
1590 },
1591 {
1592 "type": "text",
1593 "text": "Summarize this document."
1594 }
1595 ]
1596 }
1597 ]
1598 }
1599 EOF
1574 -d '{
1575 "model": "claude-opus-5-5",
1576 "max_tokens": 1024,
1577 "messages": [
1578 {
1579 "role": "user",
1580 "content": [
1581 {
1582 "type": "document",
1583 "source": {"type": "file", "file_id": "'"$FILE_ID"'"},
1584 "title": "Document Title",
1585 "context": "Context about the document that will not be cited from",
1586 "citations": {"enabled": true}
1587 },
1588 {
1589 "type": "text",
1590 "text": "Summarize this document."
1591 }
1592 ]
1593 }
1594 ]
1595 }'
16001596 ```
16011597 
16021598 ```bash CLI

build-with-claude/claude-in-microsoft-foundry Changed · +11 / -6 lines

from line 31
3131 
3232* An active Azure subscription
3333* Access to the [Foundry portal](https://ai.azure.com/)
34* The [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) installed (required for the Entra ID cURL example, optional otherwise)
34* The [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) installed (required for the Entra ID cURL and CLI examples, optional otherwise)
3535* An Azure RBAC role that allows you to use the resource, such as **Foundry User** (formerly Azure AI User) or **Cognitive Services User**
3636 
3737## Install an SDK
from line 419
419419 ```
420420 
421421 ```bash CLI
422 # The ant CLI can send a bearer token with --auth-token, but a set
423 # ANTHROPIC_API_KEY environment variable takes precedence over it (the CLI
424 # prints only a console notice), so your request could authenticate with
425 # the wrong credential. For the Entra ID flow, use the cURL example or one
426 # of the SDK examples instead.
422 # Get Microsoft Entra ID token
423 ACCESS_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
424 
425 # Make request with token. Replace {resource} with your resource name
426 # env -u unsets ANTHROPIC_API_KEY, which the CLI would send instead of the token
427 env -u ANTHROPIC_API_KEY ANTHROPIC_AUTH_TOKEN="$ACCESS_TOKEN" ant messages create \
428 --base-url https://{resource}.services.ai.azure.com/anthropic \
429 --model claude-opus-5-5 \
430 --max-tokens 1024 \
431 --message '{role: user, content: "Hello!"}'
427432 ```
428433 
429434 ```python Python

build-with-claude/claude-on-vertex-ai Changed · +16 / -16 lines

from line 155
155155 
156156The following examples show how to generate text from Claude on Agent Platform:
157157 
158<CodeGroup>
158<CodeGroup exclude="shell:CLI">
159159 ```bash cURL
160160 MODEL_ID=claude-opus-5-5
161161 PROJECT_ID=MY_PROJECT_ID
from line 170
170170 }'
171171 ```
172172 
173 ```bash CLI
174 # The ant CLI does not support Agent Platform.
175 ```
173 <CodeGroupItem language="CLI">
174 The `ant` CLI doesn't support Agent Platform.
175 </CodeGroupItem>
176176 
177177 ```python Python
178178 from anthropic import AnthropicVertex
from line 431
431431 
432432Set the `region` parameter to `"global"` when initializing the client:
433433 
434<CodeGroup>
434<CodeGroup exclude="shell:CLI">
435435 ```bash cURL
436436 MODEL_ID=claude-opus-5-5
437437 PROJECT_ID=MY_PROJECT_ID
from line 446
446446 }'
447447 ```
448448 
449 ```bash CLI
450 # The ant CLI does not support Agent Platform.
451 ```
449 <CodeGroupItem language="CLI">
450 The `ant` CLI doesn't support Agent Platform.
451 </CodeGroupItem>
452452 
453453 ```python Python
454454 from anthropic import AnthropicVertex
from line 619
619619 
620620Set the `region` parameter to a multi-region identifier: `"us"` for the United States or `"eu"` for the European Union. The SDK routes requests to the corresponding multi-region endpoint (`https://aiplatform.us.rep.googleapis.com` or `https://aiplatform.eu.rep.googleapis.com`), which dynamically balances traffic across regions within that geography.
621621 
622<CodeGroup>
622<CodeGroup exclude="shell:CLI">
623623 ```bash cURL
624624 MODEL_ID=claude-opus-5-5
625625 LOCATION=us # Multi-region identifier: "us" or "eu"
from line 635
635635 }'
636636 ```
637637 
638 ```bash CLI
639 # The ant CLI does not support Agent Platform.
640 ```
638 <CodeGroupItem language="CLI">
639 The `ant` CLI doesn't support Agent Platform.
640 </CodeGroupItem>
641641 
642642 ```python Python
643643 from anthropic import AnthropicVertex
from line 808
808808 
809809Specify a specific region such as `"us-east5"` or `"europe-west1"`:
810810 
811<CodeGroup>
811<CodeGroup exclude="shell:CLI">
812812 ```bash cURL
813813 # Specific regional endpoints support Claude Sonnet 4.6 and earlier; newer models use the global or multi-region endpoints
814814 MODEL_ID=claude-sonnet-4-6
from line 825
825825 }'
826826 ```
827827 
828 ```bash CLI
829 # The ant CLI does not support Agent Platform.
830 ```
828 <CodeGroupItem language="CLI">
829 The `ant` CLI doesn't support Agent Platform.
830 </CodeGroupItem>
831831 
832832 ```python Python
833833 from anthropic import AnthropicVertex

build-with-claude/claude-platform-on-aws Changed · +41 / -5 lines

from line 561
561561 
562562[Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) is available on Claude Platform on AWS, including [agents](https://platform.claude.com/docs/en/managed-agents/agent-setup), [environments](https://platform.claude.com/docs/en/managed-agents/environments), [sessions](https://platform.claude.com/docs/en/managed-agents/sessions), [scheduled deployments](https://platform.claude.com/docs/en/managed-agents/scheduled-deployments), [credential vaults](https://platform.claude.com/docs/en/managed-agents/vaults), [memory stores](https://platform.claude.com/docs/en/managed-agents/memory), [webhooks](https://platform.claude.com/docs/en/managed-agents/webhooks), [multiagent orchestration](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration), and [self-hosted sandboxes](https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes).
563563 
564Session behavior on Claude Platform on AWS differs from first-party Claude Managed Agents in two ways:
564Sessions and self-hosted workers on Claude Platform on AWS differ from first-party Claude Managed Agents in three ways:
565565 
566566* **Autonomous-session reauthentication:** A session can run autonomously, without any [user events](https://platform.claude.com/docs/en/managed-agents/reference#event-types), for up to 6 hours. After 6 hours, the session requires reauthentication before it continues. To reauthenticate, send any user-role event to the session (see [Events and streaming](https://platform.claude.com/docs/en/managed-agents/events-and-streaming)). First-party Claude Managed Agents has no autonomous-session runtime limit.
567567* **[Memory stores on self-hosted environments](https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes-memory):** A session that runs on a self-hosted environment cannot attach memory stores; a session that includes one is rejected at creation. Sessions on cloud environments attach memory stores as usual. On first-party Claude Managed Agents, sessions on both cloud and self-hosted environments can attach memory stores.
568* **[Worker authentication on self-hosted environments](https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes#prerequisites):** A worker authenticates with AWS IAM (SigV4) or an [API key generated in the AWS Console](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws#api-key-authentication), not an environment key. Environment keys generated in the Claude Console don't work with the Claude Platform on AWS endpoint.
568569 
569570### Features not supported
570571 
from line 896
895896 ```
896897 
897898 ```bash CLI
898 # The ant CLI's output formats print the response body, not response headers.
899 # To read x-amzn-requestid, use the cURL example (-i) or an SDK example.
899 # Replace us-west-2 with your AWS region
900 # ant reads ANTHROPIC_API_KEY and sends it as x-api-key. Generate a key in the
901 # AWS Console (see API key authentication).
902 export ANTHROPIC_API_KEY="YOUR_AWS_API_KEY"
903 
904 # --debug prints the HTTP response, including the X-Amzn-Requestid and
905 # Request-Id headers, to stderr
906 ant --debug messages create \
907 --base-url https://aws-external-anthropic.us-west-2.api.aws \
908 --workspace-id "$ANTHROPIC_AWS_WORKSPACE_ID" \
909 --model claude-sonnet-5-5 \
910 --max-tokens 1024 \
911 --message '{role: user, content: "Hello!"}'
900912 ```
901913 
902914 ```python Python
from line 1037
10251037 ```
10261038 
10271039 ```ruby Ruby
1028 # Accessing raw response headers is not currently supported in the Ruby SDK.
1029 # To inspect the x-amzn-requestid header, use one of the other SDK examples.
1040 require "anthropic"
1041 
1042 client = Anthropic::AWSClient.new
1043 
1044 # Read response headers in per-request middleware, which receives the
1045 # raw HTTP response before the SDK parses it
1046 aws_request_id = nil
1047 anthropic_request_id = nil
1048 read_request_ids = lambda do |request, call_next|
1049 response = call_next.call(request)
1050 # Keys in response.headers are lowercase
1051 aws_request_id = response.headers["x-amzn-requestid"]
1052 anthropic_request_id = response.headers["request-id"]
1053 response
1054 end
1055 
1056 message = client.messages.create(
1057 model: "claude-sonnet-5-5",
1058 max_tokens: 1024,
1059 messages: [{ role: "user", content: "Hello!" }],
1060 request_options: { middleware: [read_request_ids] }
1061 )
1062 
1063 puts aws_request_id # AWS request ID
1064 puts anthropic_request_id # Anthropic request ID
1065 puts message.content
10301066 ```
10311067</CodeGroup>
10321068 

build-with-claude/fast-mode Changed · +56 / -81 lines

from line 1
11---
2title: Fast mode (research preview)
2title: Fast mode
33url: https://platform.claude.com/docs/en/build-with-claude/fast-mode
4description: Get up to 2.5x higher output tokens per second from supported Claude Opus models.
4description: Get up to 2.5x higher output tokens per second from supported Claude models.
55---
66 
7Fast mode delivers up to 2.5x higher output tokens per second from Claude Opus 5.5, Claude Opus 5, and Claude Opus 4.8 at premium pricing. Set `speed: "fast"` with the `fast-mode-2026-02-01` beta header on your request to opt in.
7Fast mode delivers up to 2.5x higher output tokens per second from Claude Opus 5.5, Claude Opus 5, and Claude Opus 4.8 at premium pricing. Set `speed: "fast"` on your request to opt in.
88 
99<Note>
10 Fast mode is in research preview. Contact your account manager to request access. If you do not have an account manager, [join the waitlist](https://claude.com/fast-mode) for fast mode.
10 Fast mode for Claude Opus 5.5 doesn't need the `fast-mode-2026-02-01` beta header or an access request. Requests that still send the header keep working. Fast mode for Claude Opus 5 and Claude Opus 4.8 is in research preview: contact your account manager or [join the waitlist](https://claude.com/fast-mode) to request access, and send the [beta header](https://platform.claude.com/docs/en/api/beta-headers) on your requests.
1111</Note>
1212 
1313<Note>
from line 23
2323* Claude Opus 4.8 (claude-opus-4-8)
2424 
2525<Note>
26 Fast mode for Claude Opus 5.5, Claude Opus 5, and Claude Opus 4.8 is available as a research preview on the Claude API, including [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview), only. It is not available on Amazon Bedrock, Claude Platform on AWS, Google Cloud, or Microsoft Foundry.
26 Fast mode is available on the Claude API, including [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview), only. It is not available on Amazon Bedrock, Claude Platform on AWS, Google Cloud, or Microsoft Foundry.
2727</Note>
2828 
2929<Note>
from line 31
3131</Note>
3232 
3333<Note>
34 Fast mode is not available on Claude Opus 4.6. Requests to `claude-opus-4-6` with `speed: "fast"` do not return an error: they run at standard speed and are billed at [standard rates](https://platform.claude.com/docs/en/about-claude/pricing) rather than fast mode's premium rates, and the response reports [`usage.speed: "standard"`](https://platform.claude.com/docs/en/build-with-claude/fast-mode#checking-which-speed-was-used). To continue using fast mode, see [Migrating to Claude Opus 5.5 from Claude Opus 4.6 and earlier Opus models](https://platform.claude.com/docs/en/models/opus-5-5/migration-guide#migrating-from-claude-opus-46).
34 Fast mode is not available on Claude Opus 4.6. Requests to `claude-opus-4-6` that set `speed` need the `fast-mode-2026-02-01` beta header; without it, they return a `400` error. With the header, requests with `speed: "fast"` run at standard speed and are billed at [standard rates](https://platform.claude.com/docs/en/about-claude/pricing) rather than fast mode's premium rates, and the response reports [`usage.speed: "standard"`](https://platform.claude.com/docs/en/build-with-claude/fast-mode#checking-which-speed-was-used). To continue using fast mode, see [Migrating to Claude Opus 5.5 from Claude Opus 4.6 and earlier Opus models](https://platform.claude.com/docs/en/models/opus-5-5/migration-guide#migrating-from-claude-opus-46).
3535</Note>
3636 
3737## How fast mode works
from line 45
4545 
4646## Basic usage
4747 
48Models in research preview also need the `fast-mode-2026-02-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers).
49 
4850<CodeGroup>
4951 ```bash cURL
5052 curl https://api.anthropic.com/v1/messages \
5153 -H "x-api-key: $ANTHROPIC_API_KEY" \
5254 -H "anthropic-version: 2023-06-01" \
53 -H "anthropic-beta: fast-mode-2026-02-01" \
5455 -H "content-type: application/json" \
5556 -d '{
5657 "model": "claude-opus-5-5",
from line 65
6465 ```
6566 
6667 ```bash CLI
67 ant beta:messages create \
68 --beta fast-mode-2026-02-01 \
69 --transform 'content.#(type=="text").text' \
70 --raw-output <<'YAML'
68 ant messages create <<'YAML'
7169 model: claude-opus-5-5
7270 max_tokens: 4096
7371 speed: fast
from line 78
8078 ```python Python
8179 client = anthropic.Anthropic()
8280 
83 response = client.beta.messages.create(
81 response = client.messages.create(
8482 model="claude-opus-5-5",
8583 max_tokens=4096,
8684 speed="fast",
87 betas=["fast-mode-2026-02-01"],
8885 messages=[
8986 {"role": "user", "content": "Refactor this module to use dependency injection"}
9087 ],
from line 95
9895 ```typescript TypeScript
9996 const client = new Anthropic();
10097 
101 const response = await client.beta.messages.create({
98 const response = await client.messages.create({
10299 model: "claude-opus-5-5",
103100 max_tokens: 4096,
104101 speed: "fast",
105 betas: ["fast-mode-2026-02-01"],
106102 messages: [
107103 {
108104 role: "user",
from line 108
112108 });
113109 
114110 const textBlock = response.content.find(
115 (block): block is Anthropic.Beta.Messages.BetaTextBlock => block.type === "text"
111 (block): block is Anthropic.TextBlock => block.type === "text"
116112 );
117113 console.log(textBlock?.text);
118114 ```
from line 116
120116 ```csharp C#
121117 AnthropicClient client = new();
122118 
123 var response = await client.Beta.Messages.Create(new MessageCreateParams
119 var response = await client.Messages.Create(new MessageCreateParams
124120 {
125121 Model = "claude-opus-5-5",
126122 MaxTokens = 4096,
127123 Speed = Speed.Fast,
128 Betas = ["fast-mode-2026-02-01"],
129124 Messages = [
130125 new() { Role = Role.User, Content = "Refactor this module to use dependency injection" }
131126 ],
from line 138
143138 ```go Go
144139 client := anthropic.NewClient()
145140 
146 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
141 response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
147142 Model: anthropic.ModelClaudeOpus5_5,
148143 MaxTokens: 4096,
149 Speed: anthropic.BetaMessageNewParamsSpeedFast,
150 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaFastMode2026_02_01},
151 Messages: []anthropic.BetaMessageParam{
152 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Refactor this module to use dependency injection")),
144 Speed: anthropic.MessageNewParamsSpeedFast,
145 Messages: []anthropic.MessageParam{
146 anthropic.NewUserMessage(anthropic.NewTextBlock("Refactor this module to use dependency injection")),
153147 },
154148 })
155149 if err != nil {
from line 150
156150 log.Fatal(err)
157151 }
158152 for _, block := range response.Content {
159 if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok {
153 if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
160154 fmt.Println(textBlock.Text)
161155 }
162156 }
from line 159
165159 ```java Java
166160 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
167161 
168 BetaMessage response = client.beta().messages().create(
162 Message response = client.messages().create(
169163 MessageCreateParams.builder()
170164 .model(Model.CLAUDE_OPUS_5_5)
171165 .maxTokens(4096L)
172166 .speed(MessageCreateParams.Speed.FAST)
173 .addBeta(AnthropicBeta.FAST_MODE_2026_02_01)
174167 .addUserMessage("Refactor this module to use dependency injection")
175168 .build());
176169 
from line 175
182175 ```php PHP
183176 $client = new Client();
184177 
185 $response = $client->beta->messages->create(
178 $response = $client->messages->create(
186179 model: 'claude-opus-5-5',
187180 maxTokens: 4096,
188181 speed: 'fast',
189 betas: ['fast-mode-2026-02-01'],
190182 messages: [
191183 ['role' => 'user', 'content' => 'Refactor this module to use dependency injection'],
192184 ],
from line 194
202194 ```ruby Ruby
203195 client = Anthropic::Client.new
204196 
205 response = client.beta.messages.create(
197 response = client.messages.create(
206198 model: "claude-opus-5-5",
207199 max_tokens: 4096,
208200 speed: "fast",
209 betas: ["fast-mode-2026-02-01"],
210201 messages: [{role: "user", content: "Refactor this module to use dependency injection"}]
211202 )
212203 
from line 227
236227 
237228Fast mode has a dedicated rate limit that is separate from standard Opus rate limits. When your fast mode rate limit is exceeded, the API returns a `429` error with a `retry-after` header indicating when capacity will be available.
238229 
230Claude Opus 5.5 has a default fast mode rate limit in every [usage tier](https://platform.claude.com/docs/en/api/rate-limits), so you don't need to request access to use fast mode with it.
231 
239232The response includes headers that indicate your fast mode rate limit status:
240233 
241234| Header | Description |
from line 240
247240| `anthropic-fast-output-tokens-remaining` | Remaining fast mode output tokens |
248241| `anthropic-fast-output-tokens-reset` | Time when the fast mode output token limit resets |
249242 
250For tier-specific rate limits, see the [Rate limits](https://platform.claude.com/docs/en/api/rate-limits) page.
243To see your fast mode rate limits, go to the [Rate limits](https://platform.claude.com/usage/limits) page in the Claude Console.
251244 
252245## Checking which speed was used
253246 
254The response `usage` object includes a `speed` field that indicates which speed was used, either `"fast"` or `"standard"`. Requesting `speed: "fast"` on a [model that doesn't support fast mode](https://platform.claude.com/docs/en/build-with-claude/fast-mode#supported-models) returns an error, and so does exceeding fast mode's rate limits or capacity (a `429` or `529`). When a request with `speed: "fast"` succeeds, `usage.speed` is `"fast"`. If you are using Claude Opus 4.6 and request fast mode, its behavior is unique. Instead of returning an error like other models that don't support fast mode, it silently switches to standard speed. Though there is no error with Opus 4.6, the `speed` field accurately shows `"standard"`.
247The response `usage` object includes a `speed` field that indicates which speed was used, either `"fast"` or `"standard"`. Requesting `speed: "fast"` on a [model that doesn't support fast mode](https://platform.claude.com/docs/en/build-with-claude/fast-mode#supported-models) returns an error, and so does exceeding fast mode's rate limits or capacity (a `429` or `529`). When a request with `speed: "fast"` succeeds, `usage.speed` is `"fast"`. Claude Opus 4.6 is an exception: a request with `speed: "fast"` and the `fast-mode-2026-02-01` beta header does not return an error but runs at standard speed, and `usage.speed` shows `"standard"`. Without the header, the request returns a `400` error.
255248 
256249<CodeGroup>
257250 ```bash cURL
from line 251
258251 curl https://api.anthropic.com/v1/messages \
259252 -H "x-api-key: $ANTHROPIC_API_KEY" \
260253 -H "anthropic-version: 2023-06-01" \
261 -H "anthropic-beta: fast-mode-2026-02-01" \
262254 -H "content-type: application/json" \
263255 -d '{
264256 "model": "claude-opus-5-5",
from line 261
269261 ```
270262 
271263 ```bash CLI
272 ant beta:messages create \
273 --beta fast-mode-2026-02-01 \
264 ant messages create \
274265 --transform usage.speed \
275266 --raw-output <<'YAML'
276267 model: claude-opus-5-5
from line 276
285276 ```python Python
286277 client = anthropic.Anthropic()
287278 
288 response = client.beta.messages.create(
279 response = client.messages.create(
289280 model="claude-opus-5-5",
290281 max_tokens=1024,
291282 speed="fast",
292 betas=["fast-mode-2026-02-01"],
293283 messages=[{"role": "user", "content": "Hello"}],
294284 )
295285 
from line 289
299289 ```typescript TypeScript
300290 const client = new Anthropic();
301291 
302 const response = await client.beta.messages.create({
292 const response = await client.messages.create({
303293 model: "claude-opus-5-5",
304294 max_tokens: 1024,
305295 speed: "fast",
306 betas: ["fast-mode-2026-02-01"],
307296 messages: [{ role: "user", content: "Hello" }]
308297 });
309298 
from line 302
313302 ```csharp C#
314303 AnthropicClient client = new();
315304 
316 var response = await client.Beta.Messages.Create(new MessageCreateParams
305 var response = await client.Messages.Create(new MessageCreateParams
317306 {
318307 Model = "claude-opus-5-5",
319308 MaxTokens = 1024,
320309 Speed = Speed.Fast,
321 Betas = ["fast-mode-2026-02-01"],
322310 Messages = [new() { Role = Role.User, Content = "Hello" }],
323311 });
324312 
from line 316
328316 ```go Go
329317 client := anthropic.NewClient()
330318 
331 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
319 response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
332320 Model: anthropic.ModelClaudeOpus5_5,
333321 MaxTokens: 1024,
334 Speed: anthropic.BetaMessageNewParamsSpeedFast,
335 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaFastMode2026_02_01},
336 Messages: []anthropic.BetaMessageParam{
337 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello")),
322 Speed: anthropic.MessageNewParamsSpeedFast,
323 Messages: []anthropic.MessageParam{
324 anthropic.NewUserMessage(anthropic.NewTextBlock("Hello")),
338325 },
339326 })
340327 if err != nil {
from line 337
350337 .model(Model.CLAUDE_OPUS_5_5)
351338 .maxTokens(1024L)
352339 .speed(MessageCreateParams.Speed.FAST)
353 .addBeta(AnthropicBeta.FAST_MODE_2026_02_01)
354340 .addUserMessage("Hello")
355341 .build();
356342 
357 BetaMessage response = client.beta().messages().create(params);
343 Message response = client.messages().create(params);
358344 IO.println(response.usage().speed().orElseThrow()); // "fast" or "standard"
359345 ```
360346 
from line 347
361347 ```php PHP
362348 $client = new Client();
363349 
364 $response = $client->beta->messages->create(
350 $response = $client->messages->create(
365351 model: 'claude-opus-5-5',
366352 maxTokens: 1024,
367353 speed: 'fast',
368 betas: ['fast-mode-2026-02-01'],
369354 messages: [['role' => 'user', 'content' => 'Hello']],
370355 );
371356 
from line 360
375360 ```ruby Ruby
376361 client = Anthropic::Client.new
377362 
378 response = client.beta.messages.create(
363 response = client.messages.create(
379364 model: "claude-opus-5-5",
380365 max_tokens: 1024,
381366 speed: "fast",
382 betas: ["fast-mode-2026-02-01"],
383367 messages: [{ role: "user", content: "Hello" }]
384368 )
385369 
from line 396
412396### Falling back to standard speed
413397 
414398<Note>
415 This section covers an opt-in client-side fallback when fast mode is rate limited. It is separate from the behavior on [Claude Opus 4.6](https://platform.claude.com/docs/en/build-with-claude/fast-mode#supported-models), where fast mode is not available and requests run at standard speed automatically.
399 This section covers an opt-in client-side fallback when fast mode is rate limited. It is separate from the behavior on [Claude Opus 4.6](https://platform.claude.com/docs/en/build-with-claude/fast-mode#supported-models), where fast mode is not available.
416400</Note>
417401 
418402If you'd prefer to fall back to standard speed rather than wait for fast mode capacity, catch the rate limit error and retry without `speed: "fast"`. Set `max_retries` (typescript, java, php: `maxRetries`; csharp: `MaxRetries`; go: `option.WithMaxRetries`) to `0` on the initial fast request to skip automatic retries and fail immediately on rate limit errors.
from line 416
432416 local speed="$1" max_attempts="${2:-3}" body out
433417 body=${3:-$(cat)}
434418 out=$(
435 ant beta:messages create --beta fast-mode-2026-02-01 \
419 ant messages create \
436420 ${speed:+--speed "$speed"} \
437421 --transform-error error.type --format-error yaml <<<"$body" 2>/dev/null
438422 ) && { printf '%s\n' "$out"; return; }
from line 453
469453 
470454 def create_message_with_fast_fallback(max_retries=0, max_attempts=3, **params):
471455 try:
472 return client.with_options(max_retries=max_retries).beta.messages.create(
473 **params
474 )
456 return client.with_options(max_retries=max_retries).messages.create(**params)
475457 except anthropic.RateLimitError:
476458 if params.get("speed") == "fast":
477459 del params["speed"]
from line 476
494476 model="claude-opus-5-5",
495477 max_tokens=1024,
496478 messages=[{"role": "user", "content": "Hello"}],
497 betas=["fast-mode-2026-02-01"],
498479 speed="fast",
499480 max_retries=0,
500481 )
from line 485
504485 const client = new Anthropic();
505486 
506487 async function createMessageWithFastFallback(
507 params: Anthropic.Beta.MessageCreateParamsNonStreaming,
488 params: Anthropic.MessageCreateParamsNonStreaming,
508489 requestOptions?: Anthropic.RequestOptions,
509490 maxAttempts: number = 3
510 ): Promise<Anthropic.Beta.Messages.BetaMessage> {
491 ): Promise<Anthropic.Message> {
511492 try {
512 return await client.beta.messages.create(params, requestOptions);
493 return await client.messages.create(params, requestOptions);
513494 } catch (e) {
514495 if (e instanceof Anthropic.RateLimitError && params.speed === "fast") {
515496 const { speed, ...rest } = params;
from line 513
532513 model: "claude-opus-5-5",
533514 max_tokens: 1024,
534515 messages: [{ role: "user", content: "Hello" }],
535 betas: ["fast-mode-2026-02-01"],
536516 speed: "fast"
537517 },
538518 { maxRetries: 0 }
from line 522
542522 ```csharp C#
543523 AnthropicClient client = new();
544524 
545 async Task<BetaMessage> CreateMessageWithFastFallback(
525 async Task<Message> CreateMessageWithFastFallback(
546526 MessageCreateParams parameters,
547527 int? maxRetries = null,
548528 int maxAttempts = 3)
from line 532
552532 var requestClient = maxRetries is int retries
553533 ? client.WithOptions(options => options with { MaxRetries = retries })
554534 : client;
555 return await requestClient.Beta.Messages.Create(parameters);
535 return await requestClient.Messages.Create(parameters);
556536 }
557537 catch (AnthropicRateLimitException)
558538 {
from line 560
580560 Model = "claude-opus-5-5",
581561 MaxTokens = 1024,
582562 Messages = [new() { Role = Role.User, Content = "Hello" }],
583 Betas = ["fast-mode-2026-02-01"],
584563 Speed = Speed.Fast,
585564 },
586565 maxRetries: 0);
from line 569
590569 func createMessageWithFastFallback(
591570 ctx context.Context,
592571 client *anthropic.Client,
593 params anthropic.BetaMessageNewParams,
572 params anthropic.MessageNewParams,
594573 maxAttempts int,
595574 opts ...option.RequestOption,
596 ) (*anthropic.BetaMessage, error) {
597 message, err := client.Beta.Messages.New(ctx, params, opts...)
575 ) (*anthropic.Message, error) {
576 message, err := client.Messages.New(ctx, params, opts...)
598577 if err != nil {
599578 var apierr *anthropic.Error
600579 if errors.As(err, &apierr) && apierr.StatusCode == 429 && params.Speed != "" {
from line 595
616595 message, err := createMessageWithFastFallback(
617596 context.TODO(),
618597 &client,
619 anthropic.BetaMessageNewParams{
598 anthropic.MessageNewParams{
620599 Model: anthropic.ModelClaudeOpus5_5,
621600 MaxTokens: 1024,
622 Messages: []anthropic.BetaMessageParam{
623 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hello")),
601 Messages: []anthropic.MessageParam{
602 anthropic.NewUserMessage(anthropic.NewTextBlock("Hello")),
624603 },
625 Speed: anthropic.BetaMessageNewParamsSpeedFast,
626 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaFastMode2026_02_01},
604 Speed: anthropic.MessageNewParamsSpeedFast,
627605 },
628606 3,
629607 option.WithMaxRetries(0),
from line 621
643621 AnthropicClient client =
644622 AnthropicOkHttpClient.builder().fromEnv().maxRetries(0).build();
645623 
646 BetaMessage createMessageWithFastFallback(
624 Message createMessageWithFastFallback(
647625 MessageCreateParams params, int maxAttempts) {
648626 try {
649 return client.beta().messages().create(params);
627 return client.messages().create(params);
650628 } catch (RateLimitException e) {
651629 if (params.speed().isPresent()) {
652630 MessageCreateParams retryParams = params.toBuilder()
from line 642
664642 }
665643 
666644 void main() {
667 BetaMessage message = createMessageWithFastFallback(
645 Message message = createMessageWithFastFallback(
668646 MessageCreateParams.builder()
669647 .model(Model.CLAUDE_OPUS_5_5)
670648 .maxTokens(1024L)
671649 .addUserMessage("Hello")
672 .addBeta(AnthropicBeta.FAST_MODE_2026_02_01)
673650 .speed(MessageCreateParams.Speed.FAST)
674651 .build(),
675652 3);
from line 671
694671 int $maxAttempts = 3,
695672 ) {
696673 try {
697 return $client->beta->messages->create(
674 return $client->messages->create(
698675 ...$params,
699676 requestOptions: $requestOptions,
700677 );
from line 697
720697 'model' => 'claude-opus-5-5',
721698 'maxTokens' => 1024,
722699 'messages' => [['role' => 'user', 'content' => 'Hello']],
723 'betas' => ['fast-mode-2026-02-01'],
724700 'speed' => 'fast',
725701 ],
726702 RequestOptions::with(maxRetries: 0),
from line 707
731707 client = Anthropic::Client.new
732708 
733709 def create_message_with_fast_fallback(client, request_options: {}, max_attempts: 3, **params)
734 client.beta.messages.create(**params, request_options: request_options)
710 client.messages.create(**params, request_options: request_options)
735711 rescue Anthropic::Errors::RateLimitError
736712 raise unless params[:speed] == "fast"
737713 params.delete(:speed)
from line 722
746722 model: "claude-opus-5-5",
747723 max_tokens: 1024,
748724 messages: [{ role: "user", content: "Hello" }],
749 betas: ["fast-mode-2026-02-01"],
750725 speed: "fast",
751726 request_options: { max_retries: 0 }
752727 )

build-with-claude/files Changed · +21 / -23 lines

from line 175
175175 -H "x-api-key: $ANTHROPIC_API_KEY" \
176176 -H "anthropic-version: 2023-06-01" \
177177 -H "content-type: application/json" \
178 -d @- <<EOF
179 {
180 "model": "claude-opus-5-5",
181 "max_tokens": 1024,
182 "messages": [
183 {
184 "role": "user",
185 "content": [
186 {
187 "type": "text",
188 "text": "Please summarize this document for me."
189 },
190 {
191 "type": "document",
192 "source": {
193 "type": "file",
194 "file_id": "$FILE_ID"
178 -d '{
179 "model": "claude-opus-5-5",
180 "max_tokens": 1024,
181 "messages": [
182 {
183 "role": "user",
184 "content": [
185 {
186 "type": "text",
187 "text": "Please summarize this document for me."
188 },
189 {
190 "type": "document",
191 "source": {
192 "type": "file",
193 "file_id": "'"$FILE_ID"'"
194 }
195195 }
196 }
197 ]
198 }
199 ]
200 }
201 EOF
196 ]
197 }
198 ]
199 }'
202200 ```
203201 
204202 ```bash CLI

build-with-claude/mid-conversation-effort-example Changed · +33 / -35 lines

from line 2872
28722872 -H "x-api-key: $ANTHROPIC_API_KEY" \
28732873 -H "anthropic-version: 2023-06-01" \
28742874 -H "content-type: application/json" \
2875 -d @- <<'EOF'
2876 {
2877 "model": "claude-opus-5-5",
2878 "max_tokens": 64000,
2879 "system": "You are a helpful general-purpose agent. Answer the user's request directly.",
2880 "output_config": {"effort": "xhigh"},
2881 "tools": [
2882 {
2883 "name": "Workflow",
2884 "description": "Orchestrate a multiagent workflow: split a large task into independent subtasks and run them as parallel agents, then collect their results. Opt-in: only use this tool when the user explicitly asks for a workflow, or when a system message confirms that orchestration mode is on. Granularity: scope each subtask to a distinct concern rather than per line or file section; a review of a small module rarely needs more than about ten. Standing consent: while a system message confirms orchestration mode is on, author and run a workflow for every substantive task by default; work solo only on conversational turns or trivial mechanical edits.",
2885 "input_schema": {
2886 "type": "object",
2887 "properties": {
2888 "subtasks": {
2889 "type": "array",
2890 "items": {"type": "string"},
2891 "description": "Independent subtask prompts to run as parallel agents"
2892 }
2893 },
2894 "required": ["subtasks"]
2875 -d '{
2876 "model": "claude-opus-5-5",
2877 "max_tokens": 64000,
2878 "system": "You are a helpful general-purpose agent. Answer the request from the user directly.",
2879 "output_config": {"effort": "xhigh"},
2880 "tools": [
2881 {
2882 "name": "Workflow",
2883 "description": "Orchestrate a multiagent workflow: split a large task into independent subtasks and run them as parallel agents, then collect their results. Opt-in: only use this tool when the user explicitly asks for a workflow, or when a system message confirms that orchestration mode is on. Granularity: scope each subtask to a distinct concern rather than per line or file section; a review of a small module rarely needs more than about ten. Standing consent: while a system message confirms orchestration mode is on, author and run a workflow for every substantive task by default; work solo only on conversational turns or trivial mechanical edits.",
2884 "input_schema": {
2885 "type": "object",
2886 "properties": {
2887 "subtasks": {
2888 "type": "array",
2889 "items": {"type": "string"},
2890 "description": "Independent subtask prompts to run as parallel agents"
2891 }
2892 },
2893 "required": ["subtasks"]
2894 }
2895 },
2896 {"type": "bash_20250124", "name": "bash"}
2897 ],
2898 "messages": [
2899 {
2900 "role": "user",
2901 "content": "Explore the current directory, then give a thorough review: what it does, code-quality issues, and concrete improvements."
2902 },
2903 {
2904 "role": "system",
2905 "content": "Orchestration mode is on: optimize for the most exhaustive, correct answer rather than the fastest one. Use the Workflow tool on every substantive task, sized to the natural decomposition of the problem rather than the maximum the tool allows. See the description of the Workflow tool for standing consent, granularity guidance, and quality patterns. Work solo only on conversational or trivial turns."
28952906 }
2896 },
2897 {"type": "bash_20250124", "name": "bash"}
2898 ],
2899 "messages": [
2900 {
2901 "role": "user",
2902 "content": "Explore the current directory, then give a thorough review: what it does, code-quality issues, and concrete improvements."
2903 },
2904 {
2905 "role": "system",
2906 "content": "Orchestration mode is on: optimize for the most exhaustive, correct answer rather than the fastest one. Use the Workflow tool on every substantive task, sized to the problem's natural decomposition rather than the maximum the tool allows. See the Workflow tool's description for standing consent, granularity guidance, and quality patterns. Work solo only on conversational or trivial turns."
2907 }
2908 ]
2909 }
2910 EOF
2907 ]
2908 }'
29112909 ```
29122910 
29132911 ```bash CLI

build-with-claude/pdf-support Changed · +17 / -19 lines

from line 726
726726 -H "content-type: application/json" \
727727 -H "x-api-key: $ANTHROPIC_API_KEY" \
728728 -H "anthropic-version: 2023-06-01" \
729 -d @- <<EOF
730 {
731 "model": "claude-opus-5-5",
732 "max_tokens": 1024,
733 "messages": [{
734 "role": "user",
735 "content": [{
736 "type": "document",
737 "source": {
738 "type": "file",
739 "file_id": "$FILE_ID"
740 }
741 },
742 {
743 "type": "text",
744 "text": "What are the key findings in this document?"
729 -d '{
730 "model": "claude-opus-5-5",
731 "max_tokens": 1024,
732 "messages": [{
733 "role": "user",
734 "content": [{
735 "type": "document",
736 "source": {
737 "type": "file",
738 "file_id": "'"$FILE_ID"'"
739 }
740 },
741 {
742 "type": "text",
743 "text": "What are the key findings in this document?"
744 }]
745745 }]
746 }]
747 }
748 EOF
746 }'
749747 ```
750748 
751749 ```bash CLI

build-with-claude/prompt-engineering/claude-prompting-best-practices Changed · +45 / -0 lines

### Medication dosing

from line 1067
10671067 
10681068You can also refer to the [full skill definition](https://github.com/anthropics/claude-code/blob/main/plugins/frontend-design/skills/frontend-design/SKILL.md).
10691069 
1070### Medication dosing
1071 
1072If people might ask your application about medicine doses, add the block below for the model your application calls to your system prompt, exactly as written. The block has Claude give a child's dose of an over-the-counter medicine from the product's label, not from memory. With web search, such as the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool), Claude checks the label before answering, which adds latency and search costs. Without web search, Claude points the person to the label's dosing chart, a pharmacist, or the child's doctor instead of giving a dose.
1073 
1074```text Medication dosing block for Claude Sonnet 5.5 wrap
1075<medical_guidance>
1076This applies only when the person explicitly says the medicine is for a child, and isn't a healthcare professional asking for work.
1077Dosing for children's over-the-counter medicines depends on the child's weight and the specific product. For infants and toddlers, a small error can cause real harm. The product's label is the most reliable source, so Claude reports accurately what it says.
1078If neither the child's age or weight is given, Claude asks for both. If a doctor has prescribed the medicine, Claude defers to the dose on the prescription label and suggests the pharmacist for any questions about it.
1079If web search is available, Claude uses it to find the product's label and confirms it against an official copy of the same label, such as DailyMed. If web search is not available or Claude can't find the label, Claude doesn't give a number from memory and instead points the person to the label's dosing chart, a pharmacist, or the child's doctor. Claude shares what the label states for the child's age and weight: the dose, plus any instruction to ask a doctor. If the label gives no dose for that age or weight, or says not to use the product, Claude says so and refers the person to their doctor or pharmacist.
1080</medical_guidance>
1081```
1082 
1083```text Medication dosing block for Claude Haiku 5.5 wrap
1084<medical_guidance>
1085This applies only when the person explicitly says the medicine is for a child, and isn't a healthcare professional asking for work.
1086Dosing for children's over-the-counter medicines depends on the child's age or weight and the specific product. For infants and toddlers, a small error can cause real harm. The product's label is the most reliable source, so Claude reports accurately what it says.
1087If neither the child's age or weight is given, Claude asks for both. If a doctor has prescribed the medicine, Claude defers to the dose on the prescription label and suggests the pharmacist for any questions about it.
1088If web search is available, Claude uses it to find the product's label and confirms it against an official copy of the same label, such as DailyMed. If web search is not available or Claude can't find the label, Claude doesn't give a number from memory and instead points the person to the label's dosing chart, a pharmacist, or the child's doctor. Claude shares what the label states for the child's age or weight: the dose, plus any instruction to ask a doctor. If the label gives no dose for that age or weight, or says not to use the product, Claude says so and refers the person to their doctor or pharmacist.
1089If someone mentions emotional distress or a difficult experience and asks about a medication's dose or limit, Claude should not provide the requested information and should instead address the underlying emotional distress.
1090</medical_guidance>
1091```
1092 
1093```text Medication dosing block for Claude Sonnet 4.6 wrap
1094<medical_guidance>
1095If web search is available, Claude uses it to find the children's medicine label and confirms it against an official copy of the same label, such as DailyMed. Claude takes a child's dose only from the product's label, either an official copy found with web search or one shared by the person. Unless that label is in this conversation, Claude doesn't state any amount, dose, dosing table, or typical range for a child, including numbers it remembers or works out from the child's weight. Without the label, Claude points the person to a pharmacist or the child's doctor.
1096This applies only to medicine the person explicitly says will be given to a child and not to healthcare professionals asking for work.
1097If a doctor has prescribed the medicine, Claude defers to the dose on the prescription label and suggests the pharmacist for any questions about it.
1098If neither the child's age nor weight is given, Claude asks for both.
1099Claude shares what the label states for the child's age or weight: the dose, plus any instruction to ask a doctor. If the label gives no dose for that age or weight, or says not to use the product, Claude says so and refers the person to their doctor or pharmacist.
1100</medical_guidance>
1101```
1102 
1103```text Medication dosing block for Claude Haiku 4.5 wrap
1104<medical_guidance>
1105When someone asks about a medicine for a child and web search is available, Claude uses it to find that medicine's label and confirms it against an official copy, such as DailyMed.
1106When a medicine is for a child, Claude takes the dose only from the product's label, either an official copy found with web search or one shared by the person. Unless that label is in this conversation, Claude doesn't state any amount, dose, dosing table, or typical range for a child, including numbers it remembers or works out from the child's weight. Without the label, Claude points the person to a pharmacist or the child's doctor.
1107Claude gives adults asking about their own medicine the standard dose as usual.
1108If no child is mentioned, Claude answers as it would for an adult.
1109If a doctor has prescribed the medicine for a child, Claude doesn't look up or work out a dose, even from the product's label or the child's weight; it defers to the prescription label and suggests the pharmacist for any questions about it.
1110If neither the child's age nor weight is given, Claude asks for both.
1111Claude shares what the label states for the child's age or weight: the dose, plus any instruction to ask a doctor. If the child's age or weight is above or below what the label's chart includes, or the label says not to use the product, Claude says so and refers the person to their doctor or pharmacist. Claude doesn't give doses from charts other than the product's label.
1112</medical_guidance>
1113```
1114 
10701115## Migration considerations
10711116 
10721117When migrating to current Claude models from earlier generations:

build-with-claude/refusals-and-fallback Changed · +8 / -12 lines

from line 782
782782 
783783Pass `BetaRefusalFallbackMiddleware` (typescript: `betaRefusalFallbackMiddleware`; go: `betafallback.BetaRefusalFallbackMiddleware`; csharp: `BetaRefusalFallbackHandler`; java: `BetaRefusalFallbackInterceptor`; php: `RefusalFallbackMiddleware`) to the client constructor, and share one `BetaFallbackState` instance across the requests of a conversation.
784784 
785<CodeGroup>
786 ```bash cURL
787 # The refusal-fallback middleware is an SDK feature. See the
788 # server-side fallback section for the equivalent single-request approach,
789 # or the fallback credit page for the raw HTTP retry pattern.
790 ```
785<CodeGroup exclude="shell">
786 <CodeGroupItem language="cURL">
787 The refusal-fallback middleware is part of the Claude SDK, so a cURL request can't use it. To fall back within a single request, use [server-side fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#server-side-fallback). To write the retry yourself, see [Fallback credit](https://platform.claude.com/docs/en/build-with-claude/fallback-credit).
788 </CodeGroupItem>
791789 
792 ```bash CLI
793 # The refusal-fallback middleware is an SDK feature. See the
794 # server-side fallback section for the equivalent single-request approach,
795 # or the fallback credit page for the raw HTTP retry pattern.
796 ```
790 <CodeGroupItem language="CLI">
791 The `ant` CLI doesn't include the refusal-fallback middleware. To fall back within a single request, use [server-side fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#server-side-fallback). To write the retry yourself, see [Fallback credit](https://platform.claude.com/docs/en/build-with-claude/fallback-credit).
792 </CodeGroupItem>
797793 
798794 ```python Python
799795 from anthropic import Anthropic, BetaFallbackState, BetaRefusalFallbackMiddleware

build-with-claude/search-results Changed · +8 / -14 lines

from line 73
7373 
7474### Example: Knowledge base tool
7575 
76<CodeGroup>
77 ```bash cURL
78 # The tool-calling flow needs application-side search logic that doesn't
79 # translate to a one-off shell command. See the SDK tabs for the full flow.
80 # The raw shape of a tool conversation with search results is shown in the
81 # Combining both methods cURL tab; Method 2 shows the top-level shape.
82 ```
76<CodeGroup exclude="shell">
77 <CodeGroupItem language="cURL">
78 The tool-calling flow needs search logic in your application, which needs more than a single shell command. Use the Claude SDK for the full flow. For the raw request shape of a tool conversation with search results, see [Combining both methods](https://platform.claude.com/docs/en/build-with-claude/search-results#combining-both-methods). For search results as top-level content, see [Method 2](https://platform.claude.com/docs/en/build-with-claude/search-results#method-2-search-results-as-top-level-content).
79 </CodeGroupItem>
8380 
84 ```bash CLI
85 # The tool-calling flow needs application-side search logic that doesn't
86 # translate to a one-off shell command. See the SDK tabs for the full flow.
87 # The raw shape of a tool conversation with search results is shown in the
88 # Combining both methods cURL tab; Method 2 shows the top-level shape.
89 ```
81 <CodeGroupItem language="CLI">
82 The tool-calling flow needs search logic in your application, which needs more than a single shell command. Use the Claude SDK for the full flow. For the raw request shape of a tool conversation with search results, see [Combining both methods](https://platform.claude.com/docs/en/build-with-claude/search-results#combining-both-methods). For search results as top-level content, see [Method 2](https://platform.claude.com/docs/en/build-with-claude/search-results#method-2-search-results-as-top-level-content).
83 </CodeGroupItem>
9084 
9185 ```python Python
9286 from anthropic.types import (

build-with-claude/skills-guide Changed · +8 / -14 lines

from line 936
936936 
937937The response's `container` object carries the container's `id` and `expires_at` timestamp (see [Container reuse](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#container-reuse) for lifetime details). Reuse the same container across multiple messages by specifying the container ID:
938938 
939<CodeGroup>
940 ```bash cURL
941 # Multi-turn container reuse doesn't translate well to a one-off shell
942 # command; one of the SDK options would be a better fit. Capture
943 # container.id from the first response, then pass it in the next request as
944 # "container": {"id": "...", "skills": [...]} with the conversation history.
945 ```
939<CodeGroup exclude="shell:cURL">
940 <CodeGroupItem language="cURL">
941 Multi-turn container reuse needs more logic than a single shell command, so use the Claude SDK for it. To build the requests yourself, capture `container.id` from the first response. Then pass it in the next request as `"container": {"id": "...", "skills": [...]}`, together with the conversation history.
942 </CodeGroupItem>
946943 
947944 ```bash CLI
948945 # First request creates container
from line 4466
44694466 
44704467Handle Skill-related errors gracefully:
44714468 
4472<CodeGroup>
4473 ```bash cURL
4474 # This error-handling flow doesn't translate well to a one-off shell
4475 # command; one of the SDK options would be a better fit. A failing request
4476 # returns HTTP 400 with an error JSON whose .error.message names the
4477 # Skill problem.
4478 ```
4469<CodeGroup exclude="shell:cURL">
4470 <CodeGroupItem language="cURL">
4471 This error-handling flow needs more logic than a single shell command, so use the Claude SDK for it. A request that fails because of a Skill problem returns HTTP 400. The `error.message` field in the JSON response body describes the problem.
4472 </CodeGroupItem>
44794473 
44804474 ```bash CLI
44814475 if ! RESULT=$(ant messages create \

build-with-claude/task-budgets Changed · +17 / -2 lines

from line 514
514514 
515515Run a representative sample of tasks **without** `task_budget` set and record the total tokens Claude spends per task. For an agentic loop, sum `usage.output_tokens` across every request in the loop, plus the tokens of the tool results you append between requests:
516516 
517<CodeGroup exclude="shell:cURL">
517<CodeGroup>
518 ```bash cURL
519 curl https://api.anthropic.com/v1/messages \
520 -H "x-api-key: $ANTHROPIC_API_KEY" \
521 -H "anthropic-version: 2023-06-01" \
522 -H "content-type: application/json" \
523 -d '{
524 "model": "claude-opus-5-5",
525 "max_tokens": 4096,
526 "messages": [{
527 "role": "user",
528 "content": "Review the codebase and propose a refactor plan."
529 }]
530 }'
531 ```
532 
518533 ```bash CLI
519 ant messages create --transform 'usage.output_tokens' <<'YAML'
534 ant messages create <<'YAML'
520535 model: claude-opus-5-5
521536 max_tokens: 4096
522537 messages:

build-with-claude/thinking-tool-workflows Changed · +21 / -24 lines

from line 35
3535 -H "anthropic-version: 2023-06-01" \
3636 -H "content-type: application/json" \
3737 -H "x-api-key: $ANTHROPIC_API_KEY" \
38 -d @- <<'EOF'
39 {
40 "model": "claude-opus-5-5",
41 "max_tokens": 16000,
42 "thinking": {"type": "adaptive"},
43 "tools": [{
44 "name": "get_weather",
45 "description": "Get current weather for a location",
46 "input_schema": {
47 "type": "object",
48 "properties": {
49 "location": {"type": "string", "description": "City name"}
50 },
51 "required": ["location"]
52 }
53 }],
54 "messages": [{"role": "user", "content": "What's the weather in Paris?"}]
55 }
56 EOF
38 -d '{
39 "model": "claude-opus-5-5",
40 "max_tokens": 16000,
41 "thinking": {"type": "adaptive"},
42 "tools": [{
43 "name": "get_weather",
44 "description": "Get current weather for a location",
45 "input_schema": {
46 "type": "object",
47 "properties": {
48 "location": {"type": "string", "description": "City name"}
49 },
50 "required": ["location"]
51 }
52 }],
53 "messages": [{"role": "user", "content": "What is the weather in Paris?"}]
54 }'
5755 ```
5856 
5957 ```bash CLI
from line 313
315313 
316314 Each sample is a self-contained script: it repeats the first request, then immediately sends the follow-up using the response it just received.
317315 
318 <CodeGroup>
319 ```bash cURL
320 # This workflow does not translate well to a one-off shell command.
321 # Use one of the SDK examples in this code group instead.
322 ```
316 <CodeGroup exclude="shell:cURL">
317 <CodeGroupItem language="cURL">
318 This workflow needs more logic than a single shell command. Use the Claude SDK to run it.
319 </CodeGroupItem>
323320 
324321 ```bash CLI
325322 # First turn: write the assistant content array (thinking and tool_use
Feedback