검색 결과

검색 결과 (Search results)

검색 결과 콘텐츠 블록은 Claude가 웹 검색 결과를 인용하는 것과 같은 방식으로 자체 콘텐츠를 인용하게 해줘요. 각 인용은 제공한 출처와 제목을 지니죠. Claude가 답을 사용자의 문서에 귀속시켜야 하는 RAG(Retrieval-Augmented Generation, 검색 증강 생성) 애플리케이션에서 사용할 수 있어요. 이 문서에서는 검색 결과를 제공하는 두 가지 방법과 인용이 어떻게 동작하는지 차근차근 설명해 드릴게요.

출처: 문서

본문

이 기능에 ZDR(zero data retention, 데이터 미보관)이 어떻게 적용되는지 알아보려면 [API와 데이터 보관](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention)을 참고하세요.

검색 결과 콘텐츠 블록은 Claude가 웹 검색 결과를 인용하는 것과 같은 방식으로 자체 콘텐츠를 인용하게 해줘요. 각 인용은 제공한 출처와 제목을 지녀요. Claude가 답을 사용자의 문서에 귀속시켜야 하는 RAG 애플리케이션에서 사용하세요.

모든 활성 모델은 인용과 함께 검색 결과를 지원해요. Claude Haiku 3는 예외예요. 베타 헤더는 필요 없어요. 검색 결과는 표준 Messages API의 일부예요.

동작 방식 (How it works)

검색 결과는 두 가지 방법으로 제공할 수 있어요:

  1. 도구 호출에서: 커스텀 도구가 검색 결과를 반환해 동적 RAG 애플리케이션을 가능하게 함
  2. 최상위 콘텐츠로: 사전 가져오거나 캐시된 콘텐츠를 위해 사용자 메시지에 검색 결과를 직접 제공

두 경우 모두 인용이 활성화되면 Claude가 검색 결과를 자동으로 인용해요. 특별한 프롬프팅은 필요 없어요. 질문을 하면, 사용자의 콘텐츠를 사용하는 텍스트 블록에 인용이 나타나요.

검색 결과 스키마 (Search result schema)

검색 결과는 다음 구조를 사용해요:

{
  "type": "search_result",
  "source": "https://example.com/article", // Required: Source URL or identifier
  "title": "Article Title", // Required: Title of the result
  "content": [
    // Required: Array of text blocks
    {
      "type": "text",
      "text": "The actual content of the search result..."
    }
  ],
  "citations": {
    // Optional: Citation configuration
    "enabled": true // Enable/disable citations for this result
  }
}

필수 필드 (Required fields)

필드 타입 설명
type string "search_result"여야 함
source string 콘텐츠의 출처. 임의의 안정적인 문자열이 동작해요. URL 또는 kb://article-1234 같은 내부 식별자
title string 검색 결과의 서술적 제목
content array 실제 콘텐츠를 담은 텍스트 블록 배열

선택 필드 (Optional fields)

필드 타입 설명
citations object enabled Boolean 필드가 있는 인용 구성. 인용은 기본으로 비활성화되고, 이 페이지의 모든 예시는 "enabled": true를 명시적으로 설정해요. 요청의 모든 검색 결과는 같은 설정을 사용해야 해요(인용 제어 참조)
cache_control object 캐시 제어 설정(예: {"type": "ephemeral"})

content 배열의 각 항목은 다음을 가진 텍스트 블록이어야 해요:

  • type: "text"여야 함
  • text: 실제 텍스트 콘텐츠(비어 있지 않은 문자열)

검색 결과는 텍스트만 담아요. content 배열 안에서 이미지와 다른 미디어는 지원되지 않아요.

방법 1: 도구 호출에서의 검색 결과 (Method 1: Search results from tool calls)

커스텀 도구에서 검색 결과를 반환하면 동적 RAG 애플리케이션이 가능해져요. 도구가 런타임에 콘텐츠를 가져오고 Claude가 응답에서 그것을 인용하죠. 다음 예시는 tool_choice로 도구 호출을 강제해서, 검색 단계가 매번 실행되게 해요.

예시: 지식 베이스 도구 (Example: Knowledge base tool)

```bash cURL # The tool-calling flow needs application-side search logic that doesn't # translate to a one-off shell command. See the SDK tabs for the full flow. # The raw shape of a tool conversation with search results is shown in the # Combining both methods cURL tab; Method 2 shows the top-level shape. ```
# The tool-calling flow needs application-side search logic that doesn't
# translate to a one-off shell command. See the SDK tabs for the full flow.
# The raw shape of a tool conversation with search results is shown in the
# Combining both methods cURL tab; Method 2 shows the top-level shape.
from anthropic.types import (
    MessageParam,
    TextBlockParam,
    SearchResultBlockParam,
    ToolResultBlockParam,
)

client = Anthropic()

# Define a knowledge base search tool
knowledge_base_tool = {
    "name": "search_knowledge_base",
    "description": "Search the company knowledge base for information",
    "input_schema": {
        "type": "object",
        "properties": {"query": {"type": "string", "description": "The search query"}},
        "required": ["query"],
    },
}


# Function to handle the tool call
def search_knowledge_base(query):
    # Your search logic here
    # Returns search results in the correct format
    return [
        SearchResultBlockParam(
            type="search_result",
            source="https://docs.company.com/product-guide",
            title="Product Configuration Guide",
            content=[
                TextBlockParam(
                    type="text",
                    text="To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs.",
                )
            ],
            citations={"enabled": True},
        ),
        SearchResultBlockParam(
            type="search_result",
            source="https://docs.company.com/troubleshooting",
            title="Troubleshooting Guide",
            content=[
                TextBlockParam(
                    type="text",
                    text="If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values.",
                )
            ],
            citations={"enabled": True},
        ),
    ]


# Build up the conversation in a list, starting with the user's question
messages = [
    MessageParam(role="user", content="How do I configure the timeout settings?")
]

# Create a message with the tool
response = client.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    tools=[knowledge_base_tool],
    tool_choice={"type": "tool", "name": "search_knowledge_base"},
    messages=messages,
)

# When Claude calls the tool, provide the search results.
# The tool_use block is not always first: iterate to find it.
tool_use = next((block for block in response.content if block.type == "tool_use"), None)
if tool_use is not None:
    tool_result = search_knowledge_base(tool_use.input["query"])

    # Append Claude's turn, then the tool result, to the running conversation
    messages.append(MessageParam(role="assistant", content=response.content))
    messages.append(
        MessageParam(
            role="user",
            content=[
                ToolResultBlockParam(
                    type="tool_result",
                    tool_use_id=tool_use.id,
                    content=tool_result,  # Search results go here
                )
            ],
        )
    )

    # Send the tool result back
    final_response = client.messages.create(
        model="claude-opus-5",
        max_tokens=1024,
        messages=messages,
    )
    print(final_response)
const client = new Anthropic();

// Define a knowledge base search tool
const knowledgeBaseTool: Anthropic.Tool = {
  name: "search_knowledge_base",
  description: "Search the company knowledge base for information",
  input_schema: {
    type: "object" as const,
    properties: {
      query: {
        type: "string",
        description: "The search query"
      }
    },
    required: ["query"]
  }
};

// Function to handle the tool call
function searchKnowledgeBase(query: string) {
  // Your search logic here
  // Returns search results in the correct format
  return [
    {
      type: "search_result" as const,
      source: "https://docs.company.com/product-guide",
      title: "Product Configuration Guide",
      content: [
        {
          type: "text" as const,
          text: "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs."
        }
      ],
      citations: { enabled: true }
    },
    {
      type: "search_result" as const,
      source: "https://docs.company.com/troubleshooting",
      title: "Troubleshooting Guide",
      content: [
        {
          type: "text" as const,
          text: "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values."
        }
      ],
      citations: { enabled: true }
    }
  ];
}

// Build up the conversation in a list, starting with the user's question
const messages: Anthropic.MessageParam[] = [
  { role: "user", content: "How do I configure the timeout settings?" }
];

// Create a message with the tool
const response = await client.messages.create({
  model: "claude-opus-5",
  max_tokens: 1024,
  tools: [knowledgeBaseTool],
  tool_choice: { type: "tool", name: "search_knowledge_base" },
  messages
});

// Handle tool use and provide results.
// The tool_use block is not always first: find it in the content array.
const toolUse = response.content.find(
  (block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
);
if (toolUse) {
  const input = toolUse.input as { query: string };
  const toolResult = searchKnowledgeBase(input.query);

  // Append Claude's turn, then the tool result, to the running conversation
  messages.push({ role: "assistant", content: response.content });
  messages.push({
    role: "user",
    content: [
      {
        type: "tool_result" as const,
        tool_use_id: toolUse.id,
        content: toolResult // Search results go here
      }
    ]
  });

  // Send the tool result back
  const finalResponse = await client.messages.create({
    model: "claude-opus-5",
    max_tokens: 1024,
    messages
  });
  console.log(finalResponse);
}
AnthropicClient client = new();

var tools = new List<ToolUnion>
{
    new ToolUnion(new Tool()
    {
        Name = "search_knowledge_base",
        Description = "Search the company knowledge base for information",
        InputSchema = new InputSchema()
        {
            Properties = new Dictionary<string, JsonElement>
            {
                ["query"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The search query" }),
            },
            Required = ["query"],
        },
    }),
};

// Function to handle the tool call
static List<Block> SearchKnowledgeBase(string query)
{
    // Your search logic here
    // Returns search results in the correct format
    return
    [
        new SearchResultBlockParam
        {
            Source = "https://docs.company.com/product-guide",
            Title = "Product Configuration Guide",
            Content = [new() { Text = "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs." }],
            Citations = new() { Enabled = true },
        },
        new SearchResultBlockParam
        {
            Source = "https://docs.company.com/troubleshooting",
            Title = "Troubleshooting Guide",
            Content = [new() { Text = "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values." }],
            Citations = new() { Enabled = true },
        },
    ];
}

// Build up the conversation in a list, starting with the user's question
List<MessageParam> messages = [new() { Role = Role.User, Content = "How do I configure the timeout settings?" }];

// Create a message with the tool
var response = await client.Messages.Create(new()
{
    Model = Model.ClaudeOpus5,
    MaxTokens = 1024,
    Tools = tools,
    ToolChoice = new ToolChoiceTool { Name = "search_knowledge_base" },
    Messages = messages,
});

// When Claude calls the tool, provide the search results.
// The tool_use block is not always first: find the first one.
foreach (var block in response.Content)
{
    if (block.TryPickToolUse(out var toolUse))
    {
        var query = toolUse.Input["query"].GetString() ?? "";
        var toolResults = SearchKnowledgeBase(query);

        // Append Claude's turn, then the tool result, to the running conversation
        messages.Add(new() { Role = Role.Assistant, Content = response.Content.Select(contentBlock => new ContentBlockParam(contentBlock.Json)).ToList() });
        messages.Add(new()
        {
            Role = Role.User,
            Content = new MessageParamContent(
                [new ContentBlockParam(new ToolResultBlockParam() { ToolUseID = toolUse.ID, Content = new ToolResultBlockParamContent(toolResults) })]
            ),
        });

        // Send the tool result back
        var finalResponse = await client.Messages.Create(new()
        {
            Model = Model.ClaudeOpus5,
            MaxTokens = 1024,
            Messages = messages,
        });
        Console.WriteLine(finalResponse);
        break;
    }
}
	client := anthropic.NewClient()

	knowledgeBaseTool := anthropic.ToolUnionParam{
		OfTool: &anthropic.ToolParam{
			Name:        "search_knowledge_base",
			Description: anthropic.String("Search the company knowledge base for information"),
			InputSchema: anthropic.ToolInputSchemaParam{
				Properties: map[string]any{
					"query": map[string]any{
						"type":        "string",
						"description": "The search query",
					},
				},
				Required: []string{"query"},
			},
		},
	}

	// Build up the conversation in a slice, starting with the user's question
	messages := []anthropic.MessageParam{
		anthropic.NewUserMessage(anthropic.NewTextBlock("How do I configure the timeout settings?")),
	}

	response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
		Model:      anthropic.ModelClaudeOpus5,
		MaxTokens:  1024,
		Tools:      []anthropic.ToolUnionParam{knowledgeBaseTool},
		ToolChoice: anthropic.ToolChoiceUnionParam{OfTool: &anthropic.ToolChoiceToolParam{Name: "search_knowledge_base"}},
		Messages:   messages,
	})
	if err != nil {
		log.Fatal(err)
	}

	// The tool_use block is not always first: find it in the content list
	var toolUse *anthropic.ToolUseBlock
	for _, block := range response.Content {
		if variant, ok := block.AsAny().(anthropic.ToolUseBlock); ok {
			toolUse = &variant
			break
		}
	}

	if toolUse != nil {
		var input struct {
			Query string `json:"query"`
		}
		if err := json.Unmarshal(toolUse.Input, &input); err != nil {
			log.Fatal(err)
		}
		toolResults := searchKnowledgeBase(input.Query)

		// Append Claude's turn, then the tool result, to the running conversation
		messages = append(messages, response.ToParam())
		messages = append(messages, anthropic.NewUserMessage(anthropic.ContentBlockParamUnion{
			OfToolResult: &anthropic.ToolResultBlockParam{
				ToolUseID: toolUse.ID,
				Content:   toolResults,
			},
		}))

		// Send the tool result back
		finalResponse, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
			Model:     anthropic.ModelClaudeOpus5,
			MaxTokens: 1024,
			Messages:  messages,
		})
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(finalResponse)
	}
// ...
func searchKnowledgeBase(query string) []anthropic.ToolResultBlockParamContentUnion {
	return []anthropic.ToolResultBlockParamContentUnion{
		{OfSearchResult: &anthropic.SearchResultBlockParam{
			Content: []anthropic.TextBlockParam{
				{Text: "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs."},
			},
			Source:    "https://docs.company.com/product-guide",
			Title:     "Product Configuration Guide",
			Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},
		}},
		{OfSearchResult: &anthropic.SearchResultBlockParam{
			Content: []anthropic.TextBlockParam{
				{Text: "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values."},
			},
			Source:    "https://docs.company.com/troubleshooting",
			Title:     "Troubleshooting Guide",
			Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},
		}},
	}
}
import com.anthropic.models.messages.ContentBlockParam;
import com.anthropic.models.messages.CitationsConfigParam;
// ...
import com.anthropic.models.messages.MessageParam;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.SearchResultBlockParam;
import com.anthropic.models.messages.TextBlockParam;
import com.anthropic.models.messages.Tool;
import com.anthropic.models.messages.ToolChoice;
import com.anthropic.models.messages.ToolChoiceTool;
import com.anthropic.models.messages.ToolResultBlockParam;
import com.anthropic.core.JsonValue;
// ...

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    Tool knowledgeBaseTool = Tool.builder()
        .name("search_knowledge_base")
        .description("Search the company knowledge base for information")
        .inputSchema(Tool.InputSchema.builder()
            .properties(JsonValue.from(Map.of(
                "query", Map.of(
                    "type", "string",
                    "description", "The search query"
                )
            )))
            .putAdditionalProperty("required", JsonValue.from(List.of("query")))
            .build())
        .build();

    // Build up the conversation in a list, starting with the user's question
    List<MessageParam> messages = new ArrayList<>();
    messages.add(MessageParam.builder()
        .role(MessageParam.Role.USER)
        .content("How do I configure the timeout settings?")
        .build());

    MessageCreateParams params = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_5)
        .maxTokens(1024L)
        .addTool(knowledgeBaseTool)
        .toolChoice(ToolChoice.ofTool(ToolChoiceTool.builder()
            .name("search_knowledge_base")
            .build()))
        .messages(messages)
        .build();

    Message response = client.messages().create(params);

    // The tool_use block is not always first: find it in the content list
    response.content().stream()
        .flatMap(contentBlock -> contentBlock.toolUse().stream())
        .findFirst()
        .ifPresent(toolUse -> {
            Map<String, JsonValue> input =
                (Map<String, JsonValue>) toolUse._input().asObject().get();
            List<ToolResultBlockParam.Content.Block> toolResult = searchKnowledgeBase(
                input.get("query").asStringOrThrow()
            );

            // Append Claude's entire turn to the running conversation, then the tool result.
            // Rebuilding only the tool_use block would drop any other content blocks Claude
            // returned (e.g. leading text when the tool call is not forced) — append the
            // full turn, as the other language tabs do.
            messages.add(MessageParam.builder()
                .role(MessageParam.Role.ASSISTANT)
                .contentOfBlockParams(
                    response.content().stream()
                        .map(block -> block.toParam())
                        .toList()
                )
                .build());
            messages.add(MessageParam.builder()
                .role(MessageParam.Role.USER)
                .contentOfBlockParams(List.of(
                    ContentBlockParam.ofToolResult(
                        ToolResultBlockParam.builder()
                            .toolUseId(toolUse.id())
                            .contentOfBlocks(toolResult)
                            .build()
                    )
                ))
                .build());

            // Send the tool result back
            MessageCreateParams finalParams = MessageCreateParams.builder()
                .model(Model.CLAUDE_OPUS_5)
                .maxTokens(1024L)
                .messages(messages)
                .build();

            Message finalResponse = client.messages().create(finalParams);
            System.out.println(finalResponse);
        });
}

static List<ToolResultBlockParam.Content.Block> searchKnowledgeBase(String query) {
    return List.of(
        ToolResultBlockParam.Content.Block.ofSearchResult(
            SearchResultBlockParam.builder()
                .source("https://docs.company.com/product-guide")
                .title("Product Configuration Guide")
                .content(List.of(
                    TextBlockParam.builder()
                        .text("To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs.")
                        .build()
                ))
                .citations(CitationsConfigParam.builder().enabled(true).build())
                .build()
        ),
        ToolResultBlockParam.Content.Block.ofSearchResult(
            SearchResultBlockParam.builder()
                .source("https://docs.company.com/troubleshooting")
                .title("Troubleshooting Guide")
                .content(List.of(
                    TextBlockParam.builder()
                        .text("If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values.")
                        .build()
                ))
                .citations(CitationsConfigParam.builder().enabled(true).build())
                .build()
        )
    );
}
$client = new Client();

$knowledgeBaseTool = [
    'name' => 'search_knowledge_base',
    'description' => 'Search the company knowledge base for information',
    'input_schema' => [
        'type' => 'object',
        'properties' => [
            'query' => [
                'type' => 'string',
                'description' => 'The search query'
            ]
        ],
        'required' => ['query']
    ]
];

function searchKnowledgeBase($query) {
    return [
        [
            'type' => 'search_result',
            'source' => 'https://docs.company.com/product-guide',
            'title' => 'Product Configuration Guide',
            'content' => [
                [
                    'type' => 'text',
                    'text' => 'To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs.'
                ]
            ],
            'citations' => ['enabled' => true]
        ],
        [
            'type' => 'search_result',
            'source' => 'https://docs.company.com/troubleshooting',
            'title' => 'Troubleshooting Guide',
            'content' => [
                [
                    'type' => 'text',
                    'text' => 'If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values.'
                ]
            ],
            'citations' => ['enabled' => true]
        ]
    ];
}

// Build up the conversation in a list, starting with the user's question
$messages = [
    ['role' => 'user', 'content' => 'How do I configure the timeout settings?']
];

$response = $client->messages->create(
    maxTokens: 1024,
    messages: $messages,
    model: 'claude-opus-5',
    toolChoice: ['type' => 'tool', 'name' => 'search_knowledge_base'],
    tools: [$knowledgeBaseTool],
);

$toolUseBlock = null;
foreach ($response->content as $block) {
    if ($block->type === 'tool_use') {
        $toolUseBlock = $block;
        break;
    }
}

if ($toolUseBlock !== null) {
    $toolResult = searchKnowledgeBase($toolUseBlock->input['query']);

    // Append Claude's turn, then the tool result, to the running conversation
    $messages[] = ['role' => 'assistant', 'content' => $response->content];
    $messages[] = [
        'role' => 'user',
        'content' => [
            [
                'type' => 'tool_result',
                'tool_use_id' => $toolUseBlock->id,
                'content' => $toolResult
            ]
        ]
    ];

    // Send the tool result back
    $finalResponse = $client->messages->create(
        maxTokens: 1024,
        messages: $messages,
        model: 'claude-opus-5',
    );
    echo $finalResponse;
} else {
    echo $response;
}
client = Anthropic::Client.new

knowledge_base_tool = {
  name: "search_knowledge_base",
  description: "Search the company knowledge base for information",
  input_schema: {
    type: "object",
    properties: {
      query: { type: "string", description: "The search query" }
    },
    required: ["query"]
  }
}

def search_knowledge_base(query)
  [
    {
      type: "search_result",
      source: "https://docs.company.com/product-guide",
      title: "Product Configuration Guide",
      content: [
        {
          type: "text",
          text: "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs."
        }
      ],
      citations: { enabled: true }
    },
    {
      type: "search_result",
      source: "https://docs.company.com/troubleshooting",
      title: "Troubleshooting Guide",
      content: [
        {
          type: "text",
          text: "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values."
        }
      ],
      citations: { enabled: true }
    }
  ]
end

# Build up the conversation in a list, starting with the user's question
messages = [
  { role: "user", content: "How do I configure the timeout settings?" }
]

response = client.messages.create(
  model: "claude-opus-5",
  max_tokens: 1024,
  tools: [knowledge_base_tool],
  tool_choice: { type: "tool", name: "search_knowledge_base" },
  messages: messages
)

# The tool_use block is not always first: find it in the content array
tool_use = response.content.find { |block| block.type == :tool_use }

if tool_use
  tool_result = search_knowledge_base(tool_use.input[:query])

  # Append Claude's turn, then the tool result, to the running conversation
  messages << { role: "assistant", content: response.content }
  messages << {
    role: "user",
    content: [
      {
        type: "tool_result",
        tool_use_id: tool_use.id,
        content: tool_result
      }
    ]
  }

  # Send the tool result back
  final_response = client.messages.create(
    model: "claude-opus-5",
    max_tokens: 1024,
    messages: messages
  )
  puts final_response
end

방법 2: 최상위 콘텐츠로의 검색 결과 (Method 2: Search results as top-level content)

검색 결과를 사용자 메시지에 직접 제공할 수도 있어요. 다음에 유용해요:

  • 검색 인프라에서 사전 가져온 콘텐츠
  • 이전 질의에서 캐시된 검색 결과
  • 외부 검색 서비스의 콘텐츠
  • 테스트와 개발

예시: 직접 검색 결과 (Example: Direct search results)

```bash cURL curl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHR...KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-opus-5-5", "max_tokens": 1024, "messages": [ { "role": "user", "content": [ { "type": "search_result", "source": "https://docs.company.com/api-reference", "title": "API Reference - Authentication", "content": [ { "type": "text", "text": "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium." } ], "citations": { "enabled": true } }, { "type": "search_result", "source": "https://docs.company.com/quickstart", "title": "Getting Started Guide", "content": [ { "type": "text", "text": "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key." } ], "citations": { "enabled": true } }, { "type": "text", "text": "Based on these search results, how do I authenticate API requests and what are the rate limits?" } ] } ] }' ```
ant messages create <<'YAML'
model: claude-opus-5-5
max_tokens: 1024
messages:
  - role: user
    content:
      - type: search_result
        source: https://docs.company.com/api-reference
        title: API Reference - Authentication
        content:
          - type: text
            text: >-
              All API requests must include an API key in the Authorization
              header. Keys can be generated from the dashboard. Rate limits:
              1000 requests per hour for standard tier, 10000 for premium.
        citations:
          enabled: true
      - type: search_result
        source: https://docs.company.com/quickstart
        title: Getting Started Guide
        content:
          - type: text
            text: >-
              To get started: 1) Sign up for an account, 2) Generate an API
              key from the dashboard, 3) Install our SDK using pip install
              company-sdk, 4) Initialize the client with your API key.
        citations:
          enabled: true
      - type: text
        text: >-
          Based on these search results, how do I authenticate API requests
          and what are the rate limits?
YAML
from anthropic.types import MessageParam, TextBlockParam, SearchResultBlockParam

client = Anthropic()

# Provide search results directly in the user message
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[
        MessageParam(
            role="user",
            content=[
                SearchResultBlockParam(
                    type="search_result",
                    source="https://docs.company.com/api-reference",
                    title="API Reference - Authentication",
                    content=[
                        TextBlockParam(
                            type="text",
                            text="All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium.",
                        )
                    ],
                    citations={"enabled": True},
                ),
                SearchResultBlockParam(
                    type="search_result",
                    source="https://docs.company.com/quickstart",
                    title="Getting Started Guide",
                    content=[
                        TextBlockParam(
                            type="text",
                            text="To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key.",
                        )
                    ],
                    citations={"enabled": True},
                ),
                TextBlockParam(
                    type="text",
                    text="Based on these search results, how do I authenticate API requests and what are the rate limits?",
                ),
            ],
        )
    ],
)

print(response)
const client = new Anthropic();

// Provide search results directly in the user message
const response = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: [
        {
          type: "search_result" as const,
          source: "https://docs.company.com/api-reference",
          title: "API Reference - Authentication",
          content: [
            {
              type: "text" as const,
              text: "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."
            }
          ],
          citations: { enabled: true }
        },
        {
          type: "search_result" as const,
          source: "https://docs.company.com/quickstart",
          title: "Getting Started Guide",
          content: [
            {
              type: "text" as const,
              text: "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."
            }
          ],
          citations: { enabled: true }
        },
        {
          type: "text" as const,
          text: "Based on these search results, how do I authenticate API requests and what are the rate limits?"
        }
      ]
    }
  ]
});

console.log(response);
AnthropicClient client = new();

// Provide search results directly in the user message
var response = await client.Messages.Create(new()
{
    Model = Model.ClaudeOpus5_5,
    MaxTokens = 1024,
    Messages =
    [
        new()
        {
            Role = Role.User,
            Content = new MessageParamContent(
            [
                new ContentBlockParam(new SearchResultBlockParam
                {
                    Source = "https://docs.company.com/api-reference",
                    Title = "API Reference - Authentication",
                    Content = [new() { Text = "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium." }],
                    Citations = new() { Enabled = true },
                }),
                new ContentBlockParam(new SearchResultBlockParam
                {
                    Source = "https://docs.company.com/quickstart",
                    Title = "Getting Started Guide",
                    Content = [new() { Text = "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key." }],
                    Citations = new() { Enabled = true },
                }),
                new ContentBlockParam(new TextBlockParam { Text = "Based on these search results, how do I authenticate API requests and what are the rate limits?" }),
            ]),
        },
    ],
});

Console.WriteLine(response);
client := anthropic.NewClient()

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
	Model:     anthropic.ModelClaudeOpus5_5,
	MaxTokens: 1024,
	Messages: []anthropic.MessageParam{
		anthropic.NewUserMessage(
			anthropic.ContentBlockParamUnion{OfSearchResult: &anthropic.SearchResultBlockParam{
				Content: []anthropic.TextBlockParam{
					{Text: "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."},
				},
				Source:    "https://docs.company.com/api-reference",
				Title:     "API Reference - Authentication",
				Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},
			}},
			anthropic.ContentBlockParamUnion{OfSearchResult: &anthropic.SearchResultBlockParam{
				Content: []anthropic.TextBlockParam{
					{Text: "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."},
				},
				Source:    "https://docs.company.com/quickstart",
				Title:     "Getting Started Guide",
				Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},
			}},
			anthropic.NewTextBlock("Based on these search results, how do I authenticate API requests and what are the rate limits?"),
		),
	},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(response)
import com.anthropic.models.messages.ContentBlockParam;
import com.anthropic.models.messages.CitationsConfigParam;
// ...
import com.anthropic.models.messages.SearchResultBlockParam;
import com.anthropic.models.messages.TextBlockParam;
// ...

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    MessageCreateParams params = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_5_5)
        .maxTokens(1024L)
        .addUserMessageOfBlockParams(List.of(
            ContentBlockParam.ofSearchResult(
                SearchResultBlockParam.builder()
                    .source("https://docs.company.com/api-reference")
                    .title("API Reference - Authentication")
                    .content(List.of(
                        TextBlockParam.builder()
                            .text("All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium.")
                            .build()
                    ))
                    .citations(CitationsConfigParam.builder().enabled(true).build())
                    .build()
            ),
            ContentBlockParam.ofSearchResult(
                SearchResultBlockParam.builder()
                    .source("https://docs.company.com/quickstart")
                    .title("Getting Started Guide")
                    .content(List.of(
                        TextBlockParam.builder()
                            .text("To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key.")
                            .build()
                    ))
                    .citations(CitationsConfigParam.builder().enabled(true).build())
                    .build()
            ),
            ContentBlockParam.ofText(
                TextBlockParam.builder()
                    .text("Based on these search results, how do I authenticate API requests and what are the rate limits?")
                    .build()
            )
        ))
        .build();

    Message response = client.messages().create(params);
    System.out.println(response);
}
$client = new Client();

$message = $client->messages->create(
    maxTokens: 1024,
    messages: [
        [
            'role' => 'user',
            'content' => [
                [
                    'type' => 'search_result',
                    'source' => 'https://docs.company.com/api-reference',
                    'title' => 'API Reference - Authentication',
                    'content' => [
                        [
                            'type' => 'text',
                            'text' => 'All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium.'
                        ]
                    ],
                    'citations' => ['enabled' => true]
                ],
                [
                    'type' => 'search_result',
                    'source' => 'https://docs.company.com/quickstart',
                    'title' => 'Getting Started Guide',
                    'content' => [
                        [
                            'type' => 'text',
                            'text' => 'To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key.'
                        ]
                    ],
                    'citations' => ['enabled' => true]
                ],
                [
                    'type' => 'text',
                    'text' => 'Based on these search results, how do I authenticate API requests and what are the rate limits?'
                ]
            ]
        ]
    ],
    model: 'claude-opus-5-5',
);

echo json_encode($message, JSON_PRETTY_PRINT);
client = Anthropic::Client.new

message = client.messages.create(
  model: "claude-opus-5-5",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: [
        {
          type: "search_result",
          source: "https://docs.company.com/api-reference",
          title: "API Reference - Authentication",
          content: [
            {
              type: "text",
              text: "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."
            }
          ],
          citations: { enabled: true }
        },
        {
          type: "search_result",
          source: "https://docs.company.com/quickstart",
          title: "Getting Started Guide",
          content: [
            {
              type: "text",
              text: "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."
            }
          ],
          citations: { enabled: true }
        },
        {
          type: "text",
          text: "Based on these search results, how do I authenticate API requests and what are the rate limits?"
        }
      ]
    }
  ]
)

puts message

인용이 있는 Claude 응답 (Claude's response with citations)

검색 결과가 어떻게 제공되든, Claude는 검색 결과의 정보를 사용할 때 인용을 자동으로 포함해요:

{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard.",
      "citations": [
        {
          "type": "search_result_location",
          "cited_text": "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium.",
          "source": "https://docs.company.com/api-reference",
          "title": "API Reference - Authentication",
          "search_result_index": 0,
          "start_block_index": 0,
          "end_block_index": 1
        }
      ]
    },
    {
      "type": "text",
      "text": "\n\nTo set this up from scratch, you'll need to "
    },
    {
      "type": "text",
      "text": "sign up for an account, generate an API key from the dashboard, install the SDK using `pip install company-sdk`, and initialize the client with your API key.",
      "citations": [
        {
          "type": "search_result_location",
          "cited_text": "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key.",
          "source": "https://docs.company.com/quickstart",
          "title": "Getting Started Guide",
          "search_result_index": 1,
          "start_block_index": 0,
          "end_block_index": 1
        }
      ]
    }
  ]
}

인용 필드 (Citation fields)

각 인용은 다음을 포함해요:

필드 타입 설명
type string 검색 결과 인용에서 항상 "search_result_location"
source string 원래 검색 결과의 출처
title string 또는 null 원래 검색 결과의 제목
cited_text string 인용된 블록(들)의 전체 텍스트를 이어붙인 것. content[start_block_index:end_block_index]의 내용을 이어붙인 것과 같아요. 출력 토큰에 집계되지 않아요.
search_result_index integer 요청의 모든 search_result 블록 중 인용된 검색 결과를 나타나는 순서(모든 메시지와 도구 결과에 걸쳐) 기준 0 기반 인덱스.
start_block_index integer 검색 결과 content 배열에서 첫 번째 인용 블록의 0 기반 인덱스.
end_block_index integer 검색 결과 content 배열에서 인용된 블록 범위의 배타적 끝 인덱스. 항상 start_block_index보다 큼.

블록 인덱스는 검색 결과 content 배열의 조각을 식별하고, cited_text는 그 조각의 전체 텍스트예요. 텍스트 블록이 최소 인용 단위예요. Claude는 블록 안의 하위 문자열이 아니라 전체 블록을 인용해요. 더 세분화된 인용을 얻으려면 검색 결과 콘텐츠를 더 작은 블록으로 나누세요(여러 콘텐츠 블록 참조).

여러 콘텐츠 블록 (Multiple content blocks)

검색 결과는 content 배열에 여러 텍스트 블록을 담을 수 있어요:

{
  "type": "search_result",
  "source": "https://docs.company.com/api-guide",
  "title": "API Documentation",
  "content": [
    {
      "type": "text",
      "text": "Authentication: All API requests require an API key."
    },
    {
      "type": "text",
      "text": "Rate Limits: The API allows 1000 requests per hour per key."
    },
    {
      "type": "text",
      "text": "Error Handling: The API returns standard HTTP status codes."
    }
  ],
  "citations": { "enabled": true }
}

레이트 한계 블록을 참조하는 인용은 이렇게 생겨요:

{
  "type": "search_result_location",
  "cited_text": "Rate Limits: The API allows 1000 requests per hour per key.",
  "source": "https://docs.company.com/api-guide",
  "title": "API Documentation",
  "search_result_index": 0,
  "start_block_index": 1,
  "end_block_index": 2
}

이 검색 결과가 인용되면 start_block_indexend_block_index는 이 블록들 중 어느 것을 인용이 덮는지 식별하고, cited_text는 정확히 그 블록들의 텍스트를 담아요. 콘텐츠를 더 작고 집중된 블록으로 나누면 Claude가 더 세밀한 인용 경계를 얻어요. 콘텐츠를 하나의 블록으로 합치면 모든 인용이 전체 텍스트를 반환해요. 이는 커스텀 콘텐츠 문서가 Citations 기능에서 사용하는 것과 같은 모델이에요.

고급 사용법 (Advanced usage)

두 방법 결합하기 (Combining both methods)

같은 대화에서 두 방법을 섞을 수 있어요. Claude는 어느 쪽이든 인용하고, search_result_index는 출처와 무관하게 요청 순서의 모든 search_result 블록을 세어요.

다음 예시는 완전한 대화를 재생해요. 첫 사용자 메시지가 사전 가져온 검색 결과를 지니고, 어시스턴트 턴이 지식 베이스 도구를 호출하며, 도구 결과가 두 번째 검색 결과를 반환해요. Claude의 답은 두 출처를 모두 인용해요:

```bash cURL curl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHR...KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-opus-5-5", "max_tokens": 1024, "tools": [ { "name": "search_knowledge_base", "description": "Search the company knowledge base for information", "input_schema": { "type": "object", "properties": { "query": {"type": "string", "description": "The search query"} }, "required": ["query"] } } ], "messages": [ { "role": "user", "content": [ { "type": "search_result", "source": "https://docs.company.com/overview", "title": "Product Overview", "content": [ { "type": "text", "text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards." } ], "citations": {"enabled": true} }, { "type": "text", "text": "What does Acme Dashboard do, and what plans is it available on?" } ] }, { "role": "assistant", "content": [ { "type": "text", "text": "Let me check the pricing information." }, { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "search_knowledge_base", "input": {"query": "Acme Dashboard pricing plans"} } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": [ { "type": "search_result", "source": "https://docs.company.com/pricing", "title": "Pricing Plans", "content": [ { "type": "text", "text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing." } ], "citations": {"enabled": true} } ] } ] } ] }' ```
ant messages create <<'YAML'
model: claude-opus-5-5
max_tokens: 1024
tools:
  - name: search_knowledge_base
    description: Search the company knowledge base for information
    input_schema:
      type: object
      properties:
        query:
          type: string
          description: The search query
      required: [query]
messages:
  - role: user
    content:
      - type: search_result
        source: https://docs.company.com/overview
        title: Product Overview
        content:
          - type: text
            text: >-
              Acme Dashboard is a monitoring tool for distributed systems.
              It supports real-time alerting and custom metric dashboards.
        citations:
          enabled: true
      - type: text
        text: What does Acme Dashboard do, and what plans is it available on?
  - role: assistant
    content:
      - type: text
        text: Let me check the pricing information.
      - type: tool_use
        id: toolu_01A09q90qw90lq917835lq9
        name: search_knowledge_base
        input:
          query: Acme Dashboard pricing plans
  - role: user
    content:
      - type: tool_result
        tool_use_id: toolu_01A09q90qw90lq917835lq9
        content:
          - type: search_result
            source: https://docs.company.com/pricing
            title: Pricing Plans
            content:
              - type: text
                text: >-
                  Acme Dashboard is available on the Starter plan at $10 per
                  user per month and the Enterprise plan with custom pricing.
            citations:
              enabled: true
YAML
from anthropic.types import (
    MessageParam,
    SearchResultBlockParam,
    TextBlockParam,
    ToolResultBlockParam,
    ToolUseBlockParam,
)

client = Anthropic()

knowledge_base_tool = {
    "name": "search_knowledge_base",
    "description": "Search the company knowledge base for information",
    "input_schema": {
        "type": "object",
        "properties": {"query": {"type": "string", "description": "The search query"}},
        "required": ["query"],
    },
}

# Replay a conversation that provides search results both ways: the first
# user message carries a pre-fetched result, the tool result returns another
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[knowledge_base_tool],
    messages=[
        MessageParam(
            role="user",
            content=[
                SearchResultBlockParam(
                    type="search_result",
                    source="https://docs.company.com/overview",
                    title="Product Overview",
                    content=[
                        TextBlockParam(
                            type="text",
                            text="Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",
                        )
                    ],
                    citations={"enabled": True},
                ),
                TextBlockParam(
                    type="text",
                    text="What does Acme Dashboard do, and what plans is it available on?",
                ),
            ],
        ),
        MessageParam(
            role="assistant",
            content=[
                TextBlockParam(
                    type="text", text="Let me check the pricing information."
                ),
                ToolUseBlockParam(
                    type="tool_use",
                    id="toolu_01A09q90qw90lq917835lq9",
                    name="search_knowledge_base",
                    input={"query": "Acme Dashboard pricing plans"},
                ),
            ],
        ),
        MessageParam(
            role="user",
            content=[
                ToolResultBlockParam(
                    type="tool_result",
                    tool_use_id="toolu_01A09q90qw90lq917835lq9",
                    content=[
                        SearchResultBlockParam(
                            type="search_result",
                            source="https://docs.company.com/pricing",
                            title="Pricing Plans",
                            content=[
                                TextBlockParam(
                                    type="text",
                                    text="Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",
                                )
                            ],
                            citations={"enabled": True},
                        )
                    ],
                )
            ],
        ),
    ],
)

print(response)
const client = new Anthropic();

const knowledgeBaseTool: Anthropic.Tool = {
  name: "search_knowledge_base",
  description: "Search the company knowledge base for information",
  input_schema: {
    type: "object" as const,
    properties: {
      query: { type: "string", description: "The search query" }
    },
    required: ["query"]
  }
};

// Replay a conversation that provides search results both ways: the first
// user message carries a pre-fetched result, the tool result returns another
const response = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 1024,
  tools: [knowledgeBaseTool],
  messages: [
    {
      role: "user",
      content: [
        {
          type: "search_result" as const,
          source: "https://docs.company.com/overview",
          title: "Product Overview",
          content: [
            {
              type: "text" as const,
              text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."
            }
          ],
          citations: { enabled: true }
        },
        {
          type: "text" as const,
          text: "What does Acme Dashboard do, and what plans is it available on?"
        }
      ]
    },
    {
      role: "assistant",
      content: [
        { type: "text" as const, text: "Let me check the pricing information." },
        {
          type: "tool_use" as const,
          id: "toolu_01A09q90qw90lq917835lq9",
          name: "search_knowledge_base",
          input: { query: "Acme Dashboard pricing plans" }
        }
      ]
    },
    {
      role: "user",
      content: [
        {
          type: "tool_result" as const,
          tool_use_id: "toolu_01A09q90qw90lq917835lq9",
          content: [
            {
              type: "search_result" as const,
              source: "https://docs.company.com/pricing",
              title: "Pricing Plans",
              content: [
                {
                  type: "text" as const,
                  text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."
                }
              ],
              citations: { enabled: true }
            }
          ]
        }
      ]
    }
  ]
});

console.log(response);
AnthropicClient client = new();

// Replay a conversation that provides search results both ways: the first
// user message carries a pre-fetched result, the tool result returns another
var response = await client.Messages.Create(new()
{
    Model = Model.ClaudeOpus5_5,
    MaxTokens = 1024,
    Tools =
    [
        new ToolUnion(new Tool()
        {
            Name = "search_knowledge_base",
            Description = "Search the company knowledge base for information",
            InputSchema = new InputSchema()
            {
                Properties = new Dictionary<string, JsonElement>
                {
                    ["query"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The search query" }),
                },
                Required = ["query"],
            },
        }),
    ],
    Messages =
    [
        new()
        {
            Role = Role.User,
            Content = new MessageParamContent(
            [
                new ContentBlockParam(new SearchResultBlockParam
                {
                    Source = "https://docs.company.com/overview",
                    Title = "Product Overview",
                    Content = [new() { Text = "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards." }],
                    Citations = new() { Enabled = true },
                }),
                new ContentBlockParam(new TextBlockParam { Text = "What does Acme Dashboard do, and what plans is it available on?" }),
            ]),
        },
        new()
        {
            Role = Role.Assistant,
            Content = new MessageParamContent(
            [
                new ContentBlockParam(new TextBlockParam { Text = "Let me check the pricing information." }),
                new ContentBlockParam(new ToolUseBlockParam
                {
                    ID = "toolu_01A09q90qw90lq917835lq9",
                    Name = "search_knowledge_base",
                    Input = new Dictionary<string, JsonElement>
                    {
                        ["query"] = JsonSerializer.SerializeToElement("Acme Dashboard pricing plans"),
                    },
                }),
            ]),
        },
        new()
        {
            Role = Role.User,
            Content = new MessageParamContent(
            [
                new ContentBlockParam(new ToolResultBlockParam()
                {
                    ToolUseID = "toolu_01A09q90qw90lq917835lq9",
                    Content = new ToolResultBlockParamContent(
                    [
                        new SearchResultBlockParam
                        {
                            Source = "https://docs.company.com/pricing",
                            Title = "Pricing Plans",
                            Content = [new() { Text = "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing." }],
                            Citations = new() { Enabled = true },
                        },
                    ]),
                }),
            ]),
        },
    ],
});

Console.WriteLine(response);
client := anthropic.NewClient()

knowledgeBaseTool := anthropic.ToolUnionParam{
	OfTool: &anthropic.ToolParam{
		Name:        "search_knowledge_base",
		Description: anthropic.String("Search the company knowledge base for information"),
		InputSchema: anthropic.ToolInputSchemaParam{
			Properties: map[string]any{
				"query": map[string]any{"type": "string", "description": "The search query"},
			},
			Required: []string{"query"},
		},
	},
}

// Replay a conversation that provides search results both ways: the first
// user message carries a pre-fetched result, the tool result returns another
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
	Model:     anthropic.ModelClaudeOpus5_5,
	MaxTokens: 1024,
	Tools:     []anthropic.ToolUnionParam{knowledgeBaseTool},
	Messages: []anthropic.MessageParam{
		anthropic.NewUserMessage(
			anthropic.ContentBlockParamUnion{OfSearchResult: &anthropic.SearchResultBlockParam{
				Content: []anthropic.TextBlockParam{
					{Text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."},
				},
				Source:    "https://docs.company.com/overview",
				Title:     "Product Overview",
				Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},
			}},
			anthropic.NewTextBlock("What does Acme Dashboard do, and what plans is it available on?"),
		),
		anthropic.NewAssistantMessage(
			anthropic.NewTextBlock("Let me check the pricing information."),
			anthropic.ContentBlockParamUnion{OfToolUse: &anthropic.ToolUseBlockParam{
				ID:    "toolu_01A09q90qw90lq917835lq9",
				Name:  "search_knowledge_base",
				Input: map[string]any{"query": "Acme Dashboard pricing plans"},
			}},
		),
		anthropic.NewUserMessage(
			anthropic.ContentBlockParamUnion{OfToolResult: &anthropic.ToolResultBlockParam{
				ToolUseID: "toolu_01A09q90qw90lq917835lq9",
				Content: []anthropic.ToolResultBlockParamContentUnion{
					{OfSearchResult: &anthropic.SearchResultBlockParam{
						Content: []anthropic.TextBlockParam{
							{Text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."},
						},
						Source:    "https://docs.company.com/pricing",
						Title:     "Pricing Plans",
						Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},
					}},
				},
			}},
		),
	},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(response)
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.CitationsConfigParam;
import com.anthropic.models.messages.ContentBlockParam;
// ...
import com.anthropic.models.messages.SearchResultBlockParam;
import com.anthropic.models.messages.TextBlockParam;
import com.anthropic.models.messages.Tool;
import com.anthropic.models.messages.ToolResultBlockParam;
import com.anthropic.models.messages.ToolUseBlockParam;
// ...

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    Tool knowledgeBaseTool = Tool.builder()
        .name("search_knowledge_base")
        .description("Search the company knowledge base for information")
        .inputSchema(Tool.InputSchema.builder()
            .properties(JsonValue.from(Map.of(
                "query", Map.of("type", "string", "description", "The search query")
            )))
            .putAdditionalProperty("required", JsonValue.from(List.of("query")))
            .build())
        .build();

    // Replay a conversation that provides search results both ways: the first
    // user message carries a pre-fetched result, the tool result returns another
    MessageCreateParams params = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_5_5)
        .maxTokens(1024L)
        .addTool(knowledgeBaseTool)
        .addUserMessageOfBlockParams(List.of(
            ContentBlockParam.ofSearchResult(SearchResultBlockParam.builder()
                .source("https://docs.company.com/overview")
                .title("Product Overview")
                .content(List.of(TextBlockParam.builder()
                    .text("Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.")
                    .build()))
                .citations(CitationsConfigParam.builder().enabled(true).build())
                .build()),
            ContentBlockParam.ofText(TextBlockParam.builder()
                .text("What does Acme Dashboard do, and what plans is it available on?")
                .build())
        ))
        .addAssistantMessageOfBlockParams(List.of(
            ContentBlockParam.ofText(TextBlockParam.builder()
                .text("Let me check the pricing information.")
                .build()),
            ContentBlockParam.ofToolUse(ToolUseBlockParam.builder()
                .id("toolu_01A09q90qw90lq917835lq9")
                .name("search_knowledge_base")
                .input(JsonValue.from(Map.of("query", "Acme Dashboard pricing plans")))
                .build())
        ))
        .addUserMessageOfBlockParams(List.of(
            ContentBlockParam.ofToolResult(ToolResultBlockParam.builder()
                .toolUseId("toolu_01A09q90qw90lq917835lq9")
                .contentOfBlocks(List.of(
                    ToolResultBlockParam.Content.Block.ofSearchResult(SearchResultBlockParam.builder()
                        .source("https://docs.company.com/pricing")
                        .title("Pricing Plans")
                        .content(List.of(TextBlockParam.builder()
                            .text("Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.")
                            .build()))
                        .citations(CitationsConfigParam.builder().enabled(true).build())
                        .build())
                ))
                .build())
        ))
        .build();

    Message response = client.messages().create(params);
    System.out.println(response);
}
$client = new Client();

$knowledgeBaseTool = [
    'name' => 'search_knowledge_base',
    'description' => 'Search the company knowledge base for information',
    'input_schema' => [
        'type' => 'object',
        'properties' => [
            'query' => ['type' => 'string', 'description' => 'The search query']
        ],
        'required' => ['query']
    ]
];

// Replay a conversation that provides search results both ways: the first
// user message carries a pre-fetched result, the tool result returns another
$response = $client->messages->create(
    maxTokens: 1024,
    tools: [$knowledgeBaseTool],
    messages: [
        [
            'role' => 'user',
            'content' => [
                [
                    'type' => 'search_result',
                    'source' => 'https://docs.company.com/overview',
                    'title' => 'Product Overview',
                    'content' => [
                        [
                            'type' => 'text',
                            'text' => 'Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.'
                        ]
                    ],
                    'citations' => ['enabled' => true]
                ],
                [
                    'type' => 'text',
                    'text' => 'What does Acme Dashboard do, and what plans is it available on?'
                ]
            ]
        ],
        [
            'role' => 'assistant',
            'content' => [
                ['type' => 'text', 'text' => 'Let me check the pricing information.'],
                [
                    'type' => 'tool_use',
                    'id' => 'toolu_01A09q90qw90lq917835lq9',
                    'name' => 'search_knowledge_base',
                    'input' => ['query' => 'Acme Dashboard pricing plans']
                ]
            ]
        ],
        [
            'role' => 'user',
            'content' => [
                [
                    'type' => 'tool_result',
                    'tool_use_id' => 'toolu_01A09q90qw90lq917835lq9',
                    'content' => [
                        [
                            'type' => 'search_result',
                            'source' => 'https://docs.company.com/pricing',
                            'title' => 'Pricing Plans',
                            'content' => [
                                [
                                    'type' => 'text',
                                    'text' => 'Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.'
                                ]
                            ],
                            'citations' => ['enabled' => true]
                        ]
                    ]
                ]
            ]
        ]
    ],
    model: 'claude-opus-5-5',
);

echo json_encode($response, JSON_PRETTY_PRINT);
client = Anthropic::Client.new

knowledge_base_tool = {
  name: "search_knowledge_base",
  description: "Search the company knowledge base for information",
  input_schema: {
    type: "object",
    properties: {
      query: { type: "string", description: "The search query" }
    },
    required: ["query"]
  }
}

# Replay a conversation that provides search results both ways: the first
# user message carries a pre-fetched result, the tool result returns another
response = client.messages.create(
  model: "claude-opus-5-5",
  max_tokens: 1024,
  tools: [knowledge_base_tool],
  messages: [
    {
      role: "user",
      content: [
        {
          type: "search_result",
          source: "https://docs.company.com/overview",
          title: "Product Overview",
          content: [
            {
              type: "text",
              text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."
            }
          ],
          citations: { enabled: true }
        },
        {
          type: "text",
          text: "What does Acme Dashboard do, and what plans is it available on?"
        }
      ]
    },
    {
      role: "assistant",
      content: [
        { type: "text", text: "Let me check the pricing information." },
        {
          type: "tool_use",
          id: "toolu_01A09q90qw90lq917835lq9",
          name: "search_knowledge_base",
          input: { query: "Acme Dashboard pricing plans" }
        }
      ]
    },
    {
      role: "user",
      content: [
        {
          type: "tool_result",
          tool_use_id: "toolu_01A09q90qw90lq917835lq9",
          content: [
            {
              type: "search_result",
              source: "https://docs.company.com/pricing",
              title: "Pricing Plans",
              content: [
                {
                  type: "text",
                  text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."
                }
              ],
              citations: { enabled: true }
            }
          ]
        }
      ]
    }
  ]
)

puts response

응답은 두 출처를 모두 인용해요. 사전 가져온 결과는 search_result_index: 0, 도구가 반환한 결과는 search_result_index: 1로, search_result 블록이 대화에 나타나는 순서와 일치해요:

{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Here's what I found about Acme Dashboard:\n\n**What it does:** "
    },
    {
      "type": "text",
      "text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",
      "citations": [
        {
          "type": "search_result_location",
          "cited_text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",
          "source": "https://docs.company.com/overview",
          "title": "Product Overview",
          "search_result_index": 0,
          "start_block_index": 0,
          "end_block_index": 1
        }
      ]
    },
    {
      "type": "text",
      "text": "\n\n**Available plans:** "
    },
    {
      "type": "text",
      "text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",
      "citations": [
        {
          "type": "search_result_location",
          "cited_text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",
          "source": "https://docs.company.com/pricing",
          "title": "Pricing Plans",
          "search_result_index": 1,
          "start_block_index": 0,
          "end_block_index": 1
        }
      ]
    }
  ]
}

다른 콘텐츠 유형과 섞기 (Mixing with other content types)

사용자 메시지에서 search_result 블록은 다른 어떤 콘텐츠 블록과도 나란히 놓일 수 있어요. 방법 2 예시는 검색 결과와 text 질문을 짝으로 하고, 이미지나 문서 블록도 같은 방식으로 합류할 수 있어요.

도구 결과는 더 엄격해요. tool_result 콘텐츠 배열의 어떤 블록이 search_result라면, 모든 블록이 search_result여야 해요. 같은 도구 결과에서 검색 결과를 다른 블록 유형과 섞으면 검증 오류가 나요. 도구에서 온 검색 결과와 함께 설명 텍스트를 반환하려면 검색 결과 중 하나의 content 배열 안에 텍스트 블록으로 포함하면, 거기서도 인용 가능해져요.

캐시 제어 (Cache control)

검색 결과 블록에 cache_control을 추가해 재사용을 위해 요청 간 캐시할 수 있어요. 같은 블록에서 citations와 나란히 놓여요:

{
  "type": "search_result",
  "source": "https://docs.company.com/guide",
  "title": "User Guide",
  "content": [{ "type": "text", "text": "..." }],
  "citations": { "enabled": true },
  "cache_control": { "type": "ephemeral" }
}

최소 캐시 가능 길이와 다른 요구사항은 프롬프트 캐싱을 참고하세요.

인용 제어 (Citation control)

기본적으로 검색 결과의 인용은 비활성화돼요. citations 구성을 명시적으로 설정해 인용을 활성화할 수 있어요:

{
  "type": "search_result",
  "source": "https://docs.company.com/guide",
  "title": "User Guide",
  "content": [{ "type": "text", "text": "Important documentation..." }],
  "citations": {
    "enabled": true // Enable citations for this result
  }
}

citations.enabledtrue로 설정하면 Claude가 검색 결과를 사용하는 텍스트 블록에 인용 참조를 붙여요.

인용은 전부 아니면 전무예요. 요청의 모든 검색 결과가 인용을 활성화했거나, 모두 비활성화해야 해요. 다른 인용 설정으로 검색 결과를 섞으면 오류가 나요.

모범 사례 (Best practices)

도구 기반 검색(방법 1)에서 (For tool-based search (Method 1))

  • 동적 콘텐츠: 실시간 검색과 동적 RAG 애플리케이션에 사용
  • 오류 처리: 검색이 실패하면 적절한 메시지 반환
  • 결과 한계: 컨텍스트 오버플로를 피하기 위해 가장 관련성 높은 결과만 반환

최상위 검색(방법 2)에서 (For top-level search (Method 2))

  • 사전 가져온 콘텐츠: 이미 검색 결과가 있을 때 사용
  • 배치 처리: 여러 검색 결과를 한 번에 처리하기에 이상적
  • 테스트: 알려진 콘텐츠로 인용 동작을 테스트하기에 좋음

일반 모범 사례 (General best practices)

  1. 결과를 효과적으로 구조화하세요:

    • 명확하고 영구적인 출처 URL 사용
    • 서술적 제목 제공
    • 긴 콘텐츠를 논리적 텍스트 블록으로 나눠 Claude에게 더 세밀한 인용 경계 제공
  2. 일관성을 유지하세요:

    • 애플리케이션 전반에 걸쳐 일관된 출처 형식 사용
    • 제목이 콘텐츠를 정확히 반영하게 보장
    • 서식 일관성 유지
  3. 오류를 우아하게 처리하세요: 검색이 실패하거나 아무것도 반환하지 않으면 오류를 일으키는 대신 결과를 설명하는 평문 텍스트 블록(예: {"type": "text", "text": "No results found."})을 반환하세요. Claude가 빈 결과를 사용자에게 설명하고, 대화가 계속돼요.

제한 사항 (Limitations)

  • 검색 결과 콘텐츠 블록은 Claude API, Amazon Bedrock, Google Cloud에서 사용할 수 있어요.
  • 검색 결과 안에서 텍스트 콘텐츠만 지원돼요(이미지나 다른 미디어 없음).
  • search_result 블록은 사용자 메시지(도구 결과 안 포함)에만 나타날 수 있어요. 검색 결과가 있는 어시스턴트 메시지는 거부돼요.
  • 같은 요청에서 웹 검색 도구가 활성화되면 모든 search_result 블록에 인용이 활성화되어야 해요.

다음 단계 (Next steps)

스트리밍 응답에서 거부 중지 이유를 감지·처리하고, 거부된 요청을 폴백 모델에서 재시도하기. Claude의 응답을 원본 문서에 근거시키기. 인용은 각 주장을 지지하는 정확한 구절을 반환하므로, 답을 검증하고 출처를 사용자에게 노출할 수 있어요. 인용된 출처, 선택적 동적 필터링, 도메인 제어와 함께 현재 웹 콘텐츠에 대한 Claude의 접근 제공. 콘텐츠 블록 유형을 포함한 완전한 Messages API 문서 보기. `cache_control`로 검색 결과를 캐시해 반복 요청에서 비용과 지연 감소.

더 알아보기 (Learn more)