Tool search
Tool search (도구 검색)
출처: 문서
도구 검색은 모델이 필요에 따라 도구를 동적으로 검색하고 모델 컨텍스트로 로드하게 해줘요. 이를 통해 모든 도구 정의를 처음부터 모델 컨텍스트에 로드하지 않을 수 있으며 전체 토큰 사용량과 비용을 줄이는 데 도움이 될 수 있어요. 최적의 비용과 지연 시간을 위해 도구 검색은 모델의 캐시를 보존하도록 설계됐어요. 모델이 새 도구를 발견하면 컨텍스트 창 끝에 주입돼요.
Responses API에서 tool_search는 gpt-5.4 이상 모델만 지원해요.
아래 구성과 예시는 Responses API를 사용해요. 세션 기반 함수 로드와 자동 MCP 발견은 Agents API를 참고하세요.
Responses API에서 도구 검색을 활성화하려면 두 가지를 해야 해요.
tools배열에 도구로tool_search를 추가하세요.- 함수를 사용한다면 지연시키려는 함수에
defer_loading: true를 표시하세요. MCP 서버를 사용한다면 MCP 서버 도구 정의에defer_loading: true를 설정하세요.
가능하면 네임스페이스 사용
지연된 함수, 네임스페이스, MCP 서버와 함께 도구 검색을 사용할 수 있지만, 가능하면 네임스페이스나 MCP 서버를 권장해요. 우리 모델은 주로 그러한 표면을 검색하도록 훈련됐고, 토큰 절약도 보통 그쪽이 더 실질적이에요.
네임스페이스의 경우 defer_loading은 네임스페이스 객체 자체가 아니라 네임스페이스 안의 함수에 적용돼요.
요청 시작 시 모델은 검색 가능한 것의 이름과 설명을 여전히 봐요. 네임스페이스나 MCP 서버의 경우 모델은 시작 시 서버·네임스페이스 이름과 설명만 보고, 도구 검색 도구가 로드할 때까지 안에 포함된 개별 함수의 세부 사항은 표시되지 않아요. 개별 지연 함수의 경우 모델은 여전히 함수 이름과 설명을 보므로, 실제로 도구 검색은 주로 파라미터 스키마를 지연시켜요.
최대 토큰 절약을 위해 지연 함수를 명확하고 높은 수준의 설명이 있는 네임스페이스나 MCP 서버로 그룹화해서, 모델이 그 안에 무엇이 있는지 강력한 개요를 얻고 관련 함수만 효과적으로 검색·로드할 수 있게 하세요. 모범 사례로, 더 나은 토큰 효율성과 모델 성능을 위해 각 네임스페이스는 10개 미만의 함수로 유지하세요.
{
"tools": [
{
// highlight-start:subtle
"type": "namespace",
// highlight-end
"name": "crm",
"description": "CRM tools for customer lookup and order management.",
"tools": [
{
"type": "function",
"name": "list_open_orders",
"description": "List open orders for a customer ID.",
// highlight-start:subtle
"defer_loading": true,
// highlight-end
"parameters": {
"type": "object",
"properties": {
"customer_id": { "type": "string" }
},
"required": ["customer_id"],
"additionalProperties": false
}
}
]
},
{
"type": "tool_search"
}
]
}
네임스페이스는 지연·비지연 도구를 섞어 가질 수 있어요. defer_loading: true가 없는 도구는 즉시 호출 가능하고, 같은 네임스페이스의 지연 도구는 도구 검색을 통해 로드돼요.
도구 검색 타입
두 가지 도구 검색 타입 중에 고르세요.
- 호스티드 도구 검색: OpenAI가 요청에서 선언한 지연 도구들을 검색하고 로드된 부분집합을 같은 응답으로 반환해요.
- 클라이언트 실행 도구 검색: 모델이
tool_search_call을 방출하고, 애플리케이션이 조회를 수행하며, 일치하는tool_search_output을 반환해요.
요청을 만들 때 후보 도구가 이미 알려져 있다면 호스티드 도구 검색부터 시작하세요. 도구 발견이 프로젝트 상태, 테넌트 상태, 또는 애플리케이션이 통제하는 다른 시스템에 달려 있다면 클라이언트 실행 도구 검색을 사용하세요.
호스티드 도구 검색
모델이 검색하길 원하는 함수, 네임스페이스, MCP 서버의 전체 인벤토리를 이미 알 때 호스티드 도구 검색이 가장 간단한 경로예요. 그것들을 먼저 선언하고 {"type": "tool_search"}를 추가하고 API가 무엇을 로드할지 결정하게 두세요.
호스티드 도구 검색 구성
import OpenAI from "openai";
const client = new OpenAI();
const crmNamespace = {
type: "namespace",
name: "crm",
description: "CRM tools for customer lookup and order management.",
tools: [
{
type: "function",
name: "get_customer_profile",
description: "Fetch a customer profile by customer ID.",
parameters: {
type: "object",
properties: {
customer_id: { type: "string" },
},
required: ["customer_id"],
additionalProperties: false,
},
},
{
type: "function",
name: "list_open_orders",
description: "List open orders for a customer ID.",
// highlight-start:subtle
defer_loading: true,
// highlight-end
parameters: {
type: "object",
properties: {
customer_id: { type: "string" },
},
required: ["customer_id"],
additionalProperties: false,
},
},
],
};
const response = await client.responses.create({
model: "gpt-6-astra",
input: "List open orders for customer CUST-12345.",
// highlight-start:subtle
tools: [crmNamespace, { type: "tool_search" }],
// highlight-end
parallel_tool_calls: false,
});
console.log(response.output);
from openai import OpenAI
client = OpenAI()
crm_namespace = {
"type": "namespace",
"name": "crm",
"description": "CRM tools for customer lookup and order management.",
"tools": [
{
"type": "function",
"name": "get_customer_profile",
"description": "Fetch a customer profile by customer ID.",
"parameters": {
"type": "object",
"properties": {
"customer_id": {"type": "string"},
},
"required": ["customer_id"],
"additionalProperties": False,
},
},
{
"type": "function",
"name": "list_open_orders",
"description": "List open orders for a customer ID.",
# highlight-start:subtle
"defer_loading": True,
# highlight-end
"parameters": {
"type": "object",
"properties": {
"customer_id": {"type": "string"},
},
"required": ["customer_id"],
"additionalProperties": False,
},
},
],
}
response = client.responses.create(
model="gpt-6-astra",
input="List open orders for customer CUST-12345.",
tools=[
crm_namespace,
# highlight-start:subtle
{"type": "tool_search"},
# highlight-end
],
parallel_tool_calls=False,
)
print(response.output)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
parameters := map[string]any{
"type": "object",
"properties": map[string]any{"customer_id": map[string]any{"type": "string"}},
"required": []string{"customer_id"},
"additionalProperties": false,
}
namespace := responses.ToolParamOfNamespace(
"CRM tools for customer lookup and order management.",
"crm",
[]responses.NamespaceToolToolUnionParam{
{OfFunction: &responses.NamespaceToolToolFunctionParam{
Name: "get_customer_profile", Description: openai.String("Fetch a customer profile by customer ID."), Parameters: parameters,
}},
{OfFunction: &responses.NamespaceToolToolFunctionParam{
Name: "list_open_orders", Description: openai.String("List open orders for a customer ID."), DeferLoading: openai.Bool(true), Parameters: parameters,
}},
},
)
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("List open orders for customer CUST-12345.")},
Tools: []responses.ToolUnionParam{namespace, {OfToolSearch: &responses.ToolSearchToolParam{}}},
ParallelToolCalls: openai.Bool(false),
})
if err != nil {
panic(err)
}
fmt.Println(response.Output)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.NamespaceTool;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ToolSearchTool;
import java.util.List;
import java.util.Map;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("List open orders for customer CUST-12345.")
.parallelToolCalls(false)
.addTool(
NamespaceTool.builder()
.name("crm")
.description("CRM tools for customer lookup and order management.")
.addTool(
NamespaceTool.Tool.Function.builder()
.name("get_customer_profile")
.description("Fetch a customer profile by customer ID.")
.strict(true)
.parameters(
JsonValue.from(
Map.of(
"type",
"object",
"properties",
Map.of("customer_id", Map.of("type", "string")),
"required",
List.of("customer_id"),
"additionalProperties",
false)))
.build())
.addTool(
NamespaceTool.Tool.Function.builder()
.name("list_open_orders")
.description("List open orders for a customer ID.")
.deferLoading(true)
.strict(true)
.parameters(
JsonValue.from(
Map.of(
"type",
"object",
"properties",
Map.of("customer_id", Map.of("type", "string")),
"required",
List.of("customer_id"),
"additionalProperties",
false)))
.build())
.build())
.addTool(ToolSearchTool.builder().execution(ToolSearchTool.Execution.SERVER).build())
.build();
client.responses().create(params).output().forEach(System.out::println);
require "openai"
client = OpenAI::Client.new
parameters = {
type: :object,
properties: { customer_id: { type: :string } },
required: ["customer_id"],
additionalProperties: false
}
response = client.responses.create(
model: "gpt-6-astra",
input: "List open orders for customer CUST-12345.",
parallel_tool_calls: false,
tools: [
{
type: :namespace,
name: "crm",
description: "CRM tools for customer lookup and order management.",
tools: [
{
type: :function,
name: "get_customer_profile",
description: "Fetch a customer profile by customer ID.",
parameters: parameters
},
{
type: :function,
name: "list_open_orders",
description: "List open orders for a customer ID.",
defer_loading: true,
parameters: parameters
}
]
},
{ type: :tool_search }
]
)
puts(response.output)
모델이 지연 도구가 필요하다고 결정하면, 응답에는 최종 함수 호출 전에 두 개의 추가 출력 항목이 포함돼요.
tool_search_call— 호스티드 검색 단계를 기록해요.tool_search_output— 호출 가능해지는 로드된 부분집합을 담고 있어요.
호스티드 도구 검색 응답
[
{
// highlight-start:subtle
"type": "tool_search_call",
// highlight-end
"execution": "server",
"call_id": null,
"status": "completed",
"arguments": {
"paths": ["crm"]
}
},
{
// highlight-start:subtle
"type": "tool_search_output",
// highlight-end
"execution": "server",
"call_id": null,
"status": "completed",
"tools": [
{
"type": "namespace",
"name": "crm",
"description": "CRM tools for customer lookup and order management.",
"tools": [
{
"type": "function",
"name": "list_open_orders",
"description": "List open orders for a customer ID.",
"defer_loading": true,
"parameters": {
"type": "object",
"properties": {
"customer_id": { "type": "string" }
},
"required": ["customer_id"],
"additionalProperties": false
}
}
]
}
]
},
{
"type": "function_call",
"name": "list_open_orders",
"namespace": "crm",
"call_id": "call_abc123",
"arguments": "{\"customer_id\":\"CUST-12345\"}"
}
]
호스티드 모드에서 execution은 server로, call_id는 null로 설정돼요.
더 복잡한 작업에서는 모델이 같은 tool_search_call에서 여러 네임스페이스나 MCP 서버를 로드할 수도 있어요. 예를 들어 한 작업을 완료하기 위해 다른 네임스페이스의 함수가 필요하면, 후속 함수 호출 전에 그 표면들을 함께 검색·로드하기로 선택할 수 있어요.
클라이언트 실행 도구 검색
클라이언트 실행 도구 검색은 도구 발견이 어떻게 동작하는지 애플리케이션에 완전한 통제권을 줘요. 사용 가능한 도구가 초기 tools 목록에 선언하기 비현실적인 정보에 의존할 때 유용해요.
execution: "client"와 애플리케이션이 기대하는 검색 인자용 스키마로 tool_search 도구를 구성하세요.
클라이언트 실행 도구 검색 구성
import OpenAI from "openai";
import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";
const client = new OpenAI();
const firstResponse = await client.responses.create({
model: "gpt-6-astra",
input: "Find the shipping ETA tool first, then use it for order_42.",
tools: [
{
type: "tool_search",
// highlight-start:subtle
execution: "client",
// highlight-end
description:
"Find the project-specific tools needed to continue the task.",
parameters: {
type: "object",
properties: {
goal: { type: "string" },
},
required: ["goal"],
additionalProperties: false,
},
},
],
parallel_tool_calls: false,
});
const searchCall = firstResponse.output.find(
(item) => item.type === "tool_search_call"
);
if (!searchCall) {
throw new Error("The response did not include a tool search call.");
}
const loadedTools = [
{
type: "function",
name: "get_shipping_eta",
description: "Look up shipping ETA details for an order.",
defer_loading: true,
parameters: {
type: "object",
properties: {
order_id: { type: "string" },
},
required: ["order_id"],
additionalProperties: false,
},
strict: true,
},
];
const searchOutput = {
type: "tool_search_output",
execution: "client",
call_id: searchCall.call_id,
status: "completed",
tools: loadedTools,
};
const secondResponse = await client.responses.create({
model: "gpt-6-astra",
input: [
...toResponseInputItems(firstResponse.output),
// highlight-start:subtle
searchOutput,
// highlight-end
],
});
console.log(secondResponse.output);
from openai import OpenAI
client = OpenAI()
first_response = client.responses.create(
model="gpt-6-astra",
input="Find the shipping ETA tool first, then use it for order_42.",
tools=[
{
"type": "tool_search",
# highlight-start:subtle
"execution": "client",
# highlight-end
"description": "Find the project-specific tools needed to continue the task.",
"parameters": {
"type": "object",
"properties": {
"goal": {"type": "string"},
},
"required": ["goal"],
"additionalProperties": False,
},
}
],
parallel_tool_calls=False,
)
search_call = next(
item for item in first_response.output if item.type == "tool_search_call"
)
loaded_tools = [
{
"type": "function",
"name": "get_shipping_eta",
"description": "Look up shipping ETA details for an order.",
"defer_loading": True,
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
},
"required": ["order_id"],
"additionalProperties": False,
},
}
]
second_response = client.responses.create(
model="gpt-6-astra",
input=[
*first_response.output,
{
# highlight-start:subtle
"type": "tool_search_output",
# highlight-end
"execution": "client",
"call_id": search_call.call_id,
"status": "completed",
# highlight-start:subtle
"tools": loaded_tools,
# highlight-end
},
],
)
print(second_response.output)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
searchTool := responses.ToolUnionParam{OfToolSearch: &responses.ToolSearchToolParam{
Execution: responses.ToolSearchToolExecutionClient,
Description: openai.String("Find the project-specific tools needed to continue the task."),
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{"goal": map[string]any{"type": "string"}},
"required": []string{"goal"},
"additionalProperties": false,
},
}}
first, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Find the shipping ETA tool first, then use it for order_42.")},
Tools: []responses.ToolUnionParam{searchTool},
ParallelToolCalls: openai.Bool(false),
})
if err != nil {
panic(err)
}
callID := ""
for _, item := range first.Output {
if item.Type == "tool_search_call" {
callID = item.CallID
break
}
}
if callID == "" {
panic("the response did not include a tool search call")
}
loadedTool := responses.ToolParamOfFunction("get_shipping_eta", map[string]any{
"type": "object",
"properties": map[string]any{"order_id": map[string]any{"type": "string"}},
"required": []string{"order_id"},
"additionalProperties": false,
}, true)
loadedTool.OfFunction.Description = openai.String("Look up shipping ETA details for an order.")
loadedTool.OfFunction.DeferLoading = openai.Bool(true)
searchOutput := responses.ResponseInputItemParamOfToolSearchOutput([]responses.ToolUnionParam{loadedTool})
searchOutput.OfToolSearchOutput.CallID = openai.String(callID)
searchOutput.OfToolSearchOutput.Execution = responses.ResponseToolSearchOutputItemParamExecutionClient
searchOutput.OfToolSearchOutput.Status = responses.ResponseToolSearchOutputItemParamStatusCompleted
second, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
PreviousResponseID: openai.String(first.ID),
Input: responses.ResponseNewParamsInputUnion{OfInputItemList: responses.ResponseInputParam{searchOutput}},
})
if err != nil {
panic(err)
}
fmt.Println(second.Output)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.FunctionTool;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputItem;
import com.openai.models.responses.ResponseToolSearchOutputItemParam;
import com.openai.models.responses.ToolSearchTool;
import java.util.List;
import java.util.Map;
ResponseCreateParams searchRequest =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Find the shipping ETA tool, then use it for order_42.")
.parallelToolCalls(false)
.addTool(
ToolSearchTool.builder()
.execution(ToolSearchTool.Execution.CLIENT)
.description("Find the project tools needed to continue the task.")
.parameters(
JsonValue.from(
Map.of(
"type",
"object",
"properties",
Map.of("goal", Map.of("type", "string")),
"required",
List.of("goal"),
"additionalProperties",
false)))
.build())
.build();
var search = client.responses().create(searchRequest);
var searchCall =
search.output().stream()
.flatMap(item -> item.toolSearchCall().stream())
.findFirst()
.orElseThrow(() -> new IllegalStateException("No tool search call returned"));
FunctionTool shippingTool =
FunctionTool.builder()
.name("get_shipping_eta")
.description("Look up shipping details for an order.")
.deferLoading(true)
.strict(true)
.parameters(
FunctionTool.Parameters.builder()
.putAdditionalProperty("type", JsonValue.from("object"))
.putAdditionalProperty(
"properties", JsonValue.from(Map.of("order_id", Map.of("type", "string"))))
.putAdditionalProperty("required", JsonValue.from(List.of("order_id")))
.putAdditionalProperty("additionalProperties", JsonValue.from(false))
.build())
.build();
var searchOutput =
ResponseToolSearchOutputItemParam.builder()
.callId(searchCall.callId().orElseThrow())
.execution(ResponseToolSearchOutputItemParam.Execution.CLIENT)
.status(ResponseToolSearchOutputItemParam.Status.COMPLETED)
.addTool(shippingTool)
.build();
var response =
client
.responses()
.create(
ResponseCreateParams.builder()
.model("gpt-6-astra")
.previousResponseId(search.id())
.inputOfResponse(List.of(ResponseInputItem.ofToolSearchOutput(searchOutput)))
.build());
var loadedCalls =
response.output().stream().flatMap(item -> item.functionCall().stream()).toList();
if (loadedCalls.isEmpty()) {
throw new IllegalStateException("No loaded function call returned");
}
loadedCalls.forEach(call -> System.out.println(call.name() + "(" + call.arguments() + ")"));
require "openai"
client = OpenAI::Client.new
search = client.responses.create(
model: "gpt-6-astra",
input: "Find the shipping ETA tool, then use it for order_42.",
parallel_tool_calls: false,
tools: [
{
type: :tool_search,
execution: :client,
description: "Find the project tools needed to continue the task.",
parameters: {
type: :object,
properties: { goal: { type: :string } },
required: ["goal"],
additionalProperties: false
}
}
]
)
call = search.output.find do |item|
item.is_a?(OpenAI::Models::Responses::ResponseToolSearchCall)
end
unless call.is_a?(OpenAI::Models::Responses::ResponseToolSearchCall)
raise "No tool search call returned"
end
response = client.responses.create(
model: "gpt-6-astra",
previous_response_id: search.id,
input: [
{
type: :tool_search_output,
call_id: call.call_id,
execution: :client,
status: :completed,
tools: [
{
type: :function,
name: "get_shipping_eta",
description: "Look up shipping details for an order.",
defer_loading: true,
strict: true,
parameters: {
type: :object,
properties: { order_id: { type: :string } },
required: ["order_id"],
additionalProperties: false
}
}
]
}
]
)
function_calls = response.output.grep(
OpenAI::Models::Responses::ResponseFunctionToolCall
)
raise "No loaded function call returned" if function_calls.empty?
function_calls.each do |function_call|
puts("#{function_call.name}(#{function_call.arguments})")
end
첫 번째 턴에서 모델은 tool_search_call을 방출하고 거기서 멈춰요.
클라이언트 도구 검색 호출
[
{
"type": "tool_search_call",
"execution": "client",
"call_id": "call_abc123",
"status": "completed",
"arguments": {
"goal": "Find the shipping ETA tool for order_42."
}
}
]
그런 다음 애플리케이션이 검색을 수행하고 로드할 도구를 담은 tool_search_output을 반환해요.
tool_search_output 반환
[
{
"type": "tool_search_output",
"execution": "client",
"call_id": "call_abc123",
"status": "completed",
"tools": [
{
"type": "function",
"name": "get_shipping_eta",
"description": "Look up shipping ETA details for an order.",
"defer_loading": true,
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string" }
},
"required": ["order_id"],
"additionalProperties": false
}
}
]
}
]
다음 턴에서 로드된 도구는 일반 함수처럼 호출 가능해요.
로드된 함수 호출
[
{
"type": "function_call",
"name": "get_shipping_eta",
"namespace": "get_shipping_eta",
"call_id": "call_xyz456",
"arguments": "{\"order_id\":\"order_42\"}"
}
]
클라이언트 모드에서 execution은 client로 설정되고 call_id가 정의돼요. tool_search_call의 같은 call_id를 tool_search_output에 그대로 넣어 반향(echo)하세요.
고급 사용법
네임스페이스 설명을 명확히 유지
모델이 그 네임스페이스에서 함수 부분집합을 언제 로드할지 결정할 때 설명에 의존하므로, 네임스페이스 설명을 사용 사례에 대해 명확하고 서술적으로 작성하세요. 지나치게 긴 설명은 피하세요. 대신 필요할 때만 로드되는 지연 함수 설명에 더 풍부한 세부 사항을 넣으세요.
무엇이 로드되는지 이해
tool_search_output.tools는 모델이 동적으로 로드한 도구 목록을 담고 있어요. 모델은 이후 턴에서 이 도구들 중 어느 것이든 호출할 수 있으므로, 클라이언트 모드에서 같은 도구를 턴마다 다시 로드할 필요는 없어요. 이 배열에 포함되지 않은 도구는 모델이 사용할 수 없어요. 로드된 도구를 비활성화하려면 로드 도구 세트를 정의하는 tool_search_output 항목에서 그것을 제거할 수 있지만, 로드 도구 세트를 바꾸면 그 시점부터 모델의 캐시가 깨진다는 점에 유의하세요.
고급 주입 패턴
대부분의 통합은 요청의 tools 파라미터에 도구를 선언해요. 클라이언트 실행 도구 검색은 원래 요청에 없던 도구를 애플리케이션이 반환하는 더 고급 패턴도 지원해요. 고급 워크플로로 취급하세요: 반환된 스키마를 신중히 검증하고 신뢰하는 도구 정의만 노출하세요.
도구 검색과 캐싱
모든 도구는 모델 컨텍스트 창 끝에 로드돼요. 이는 호스티드 도구 검색과 클라이언트 실행 도구 검색 둘 다에 해당돼요. 이를 통해 모델의 캐시가 요청 간에 보존되어 전체 비용을 낮추고 속도를 높여요.
입력의 특정 지점에 도구 추가
고급 워크플로에서는 additional_tools 입력 항목을 사용해 대화의 특정 지점에서 도구를 사용 가능하게 만들 수 있어요. 이는 애플리케이션이 일반 도구 검색 흐름 밖에서 도구를 로드하거나, 이전 응답 중 추가된 도구의 순서를 보존해야 할 때 유용해요.
role을 developer로 설정하고 추가할 도구를 항목의 tools 배열에 포함하세요.
{
"type": "additional_tools",
"role": "developer",
"tools": [
{
"type": "function",
"name": "get_customer",
"description": "Look up a customer by ID.",
"parameters": {
"type": "object",
"properties": {
"customer_id": { "type": "string" }
},
"required": ["customer_id"],
"additionalProperties": false
}
}
]
}
additional_tools 항목의 도구는 그 항목이 입력에 나타난 후에만 사용 가능해져요. 대화 항목을 수동으로 왕복(round-trip)시킬 때는 항목의 위치를 보존해서 모델이 대화의 같은 지점에서 같은 도구를 보게 하세요.
Agents API
Agents API는 기본적으로 함수 정의를 즉시(eagerly) 로드해요. 선택한 함수를 지연시키려면 agent.tools에 { "type": "tool_search" }를 포함하고, 에이전트가 필요할 때 발견하길 원하는 각 함수에 defer_loading: true를 설정하세요. tool_search를 추가한다고 모든 함수가 지연되지는 않아요.
세션 요청은 여전히 함수의 이름·설명·인자 스키마를 포함한 전체 함수 정의를 제공해요. 도구 검색은 그 정의가 모델에 도달하는 시점을 바꿔요. 발견 후 애플리케이션은 함수 호출을 처리하고 평소처럼 결과를 반환해요. 결과 처리는 Functions를 참고하세요.
이 예제를 실행하기 전에 OPENAI_API_KEY를 설정하세요.
필요할 때만 함수 도구 로드
import OpenAI from "openai";
const client = new OpenAI();
const result = await client.beta.agents.sessions.create({
agent: {
model: "gpt-6-astra",
tools: [
{
type: "tool_search",
},
{
type: "function",
name: "lookup_account",
description: "Find an account by its account number.",
parameters: {
type: "object",
properties: {
account_id: {
type: "string",
},
},
required: ["account_id"],
additionalProperties: false,
},
defer_loading: true,
},
],
},
environment: {
type: "none",
},
input: [
{
role: "user",
content: [
{
type: "input_text",
text: "Look up account 42.",
},
],
},
],
});
console.log(result.id);
from openai import OpenAI
client = OpenAI()
result = client.beta.agents.sessions.create(
agent={
"model": "gpt-6-astra",
"tools": [
{"type": "tool_search"},
{
"type": "function",
"name": "lookup_account",
"description": "Find an account by its account number.",
"parameters": {
"type": "object",
"properties": {"account_id": {"type": "string"}},
"required": ["account_id"],
"additionalProperties": False,
},
"defer_loading": True,
},
],
},
environment={"type": "none"},
input=[
{
"role": "user",
"content": [{"type": "input_text", "text": "Look up account 42."}],
}
],
)
print(result.id)
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
ctx := context.Background()
client := openai.NewClient()
result, err := client.Beta.Agents.Sessions.New(ctx,
openai.BetaAgentSessionNewParams{
Agent: openai.BetaAgentSessionNewParamsAgent{
Model: openai.String("gpt-6-astra"),
Tools: []openai.AgentToolParamUnion{
{OfParamToolSearch: &openai.AgentToolParamToolSearch{}},
{
OfParamFunction: &openai.AgentToolParamFunction{
Name: "lookup_account",
Description: "Find an account by its account number.",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{"account_id": map[string]any{"type": "string"}},
"required": []any{"account_id"},
"additionalProperties": false,
},
DeferLoading: openai.Bool(true),
},
},
},
},
Environment: openai.EnvironmentParamUnion{OfParamNone: &openai.EnvironmentParamNone{}},
Input: openai.BetaAgentSessionNewParamsInputUnion{
OfArrayOfInputMessages: []openai.AgentSessionInputMessageParam{
{
Content: []openai.InputContentParamUnion{
{OfParamInputText: &openai.InputContentParamInputText{Text: "Look up account 42."}},
},
},
},
},
})
if err != nil {
panic(err)
}
fmt.Println(result.ID)
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.beta.agents.AgentToolParam;
import com.openai.models.beta.agents.sessions.SessionCreateParams;
import java.util.List;
import java.util.Map;
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
var result =
client
.beta()
.agents()
.sessions()
.create(
SessionCreateParams.builder()
.agent(
SessionCreateParams.Agent.builder()
.model("gpt-6-astra")
.addToolToolSearch()
.addTool(
AgentToolParam.Function.builder()
.name("lookup_account")
.description("Find an account by its account number.")
.parameters(
AgentToolParam.Function.Parameters.builder()
.putAdditionalProperty("type", JsonValue.from("object"))
.putAdditionalProperty(
"properties",
JsonValue.from(
Map.of("account_id", Map.of("type", "string"))))
.putAdditionalProperty(
"required", JsonValue.from(List.of("account_id")))
.putAdditionalProperty(
"additionalProperties", JsonValue.from(false))
.build())
.deferLoading(true)
.build())
.build())
.environmentNone()
.input("Look up account 42.")
.build());
System.out.println(result.id());
require "openai"
client = OpenAI::Client.new
result = client.beta.agents.sessions.create(
agent: {
model: "gpt-6-astra",
tools: [
{ type: "tool_search" },
{
type: "function",
name: "lookup_account",
description: "Find an account by its account number.",
parameters: {
type: "object",
properties: { account_id: { type: "string" } },
required: ["account_id"],
additionalProperties: false
},
defer_loading: true
}
]
},
environment: { type: "none" },
input: [
{
role: "user",
content: [
{
type: "input_text",
text: "Look up account 42."
}
]
}
]
)
puts result.id
함수 로딩 전략 선택
| 전략 | 구성 | 유용한 경우 | 트레이드오프 |
|---|---|---|---|
| 즉시 로딩 (Eager loading) | defer_loading을 생략하거나 false로 설정 |
소수의 함수, 또는 대부분의 작업에 필요한 함수 | 사용되지 않는 정의가 컨텍스트를 차지해요. 정의 변경은 캐시된 접두사를 무효화할 수 있어요. |
| 지연 로딩 (Deferred loading) | defer_loading: true 설정 및 tool_search 포함 |
각 작업이 소수의 함수만 필요로 하는 큰 카탈로그 | 발견이 한 단계를 추가하고 관련 도구를 찾는 데 의존해요. |
Agents API 세션에서 즉시 로딩 함수와 지연 함수를 섞는 것은 지원되지만 일반적으로 권장되지 않아요. 지연 함수에 명확한 이름과 설명을 주세요. 기본값을 고르기 전에 대표 요청으로 작업 완료, 입력 토큰 사용량, 지연 시간을 비교하세요.
MCP 및 플러그인 도구
Agents API에서 모델과 프로바이더가 도구 검색을 지원하면 MCP 도구는 자동 발견을 사용해요. 런타임은 MCP 도구를 지연시키고, 검색 가능한 지연 도구가 있을 때 도구 검색을 추가해요. 이는 원격 MCP, executor MCP, 그리고 플러그인이 제공하는 MCP 도구에 적용돼요.
MCP 도구만을 위해 { "type": "tool_search" }를 추가하거나 MCP 서버에 함수 수준 defer_loading 플래그를 설정할 필요는 없어요. MCP 연결로 서버를 구성하세요. 이 가이드 앞부분의 Responses API 구성은 Agents API MCP 서버에는 적용되지 않아요.