工具搜索让模型能够按需动态搜索工具,并将其加载到模型的上下文中。这样,您就无需预先将所有工具定义加载到模型的上下文中, 有助于减少总体 Token 用量和成本。为了优化成本和延迟,工具搜索在设计上会 保留模型的缓存。模型发现新工具时,这些工具会被注入到上下文窗口的末尾。
在 Responses API 中,只有 gpt-5.4 及更新的模型支持 tool_search。
以下配置和示例使用 Responses API。有关基于会话的函数加载和 MCP 自动发现,请参阅 Agents API。
要在 Responses API 中启用工具搜索,您必须完成以下两项操作:
- 将
tool_search 作为工具添加到您的 tools 数组中。
- 如果您使用函数,请用
defer_loading: true 标记要延迟加载的函数。如果您使用 MCP 服务器,请在 MCP 服务器的工具定义中设置 defer_loading: true。
您可以将工具搜索用于延迟加载的函数、命名空间或 MCP 服务器,但我们建议尽可能使用命名空间或 MCP 服务器。我们的模型主要针对这两类工具集合的搜索进行了训练,使用它们通常能更显著地节省 Token。
对于命名空间,defer_loading 适用于命名空间内的函数,而不是命名空间对象本身。
请求开始时,模型仍然可以看到所有可搜索项的名称和描述。对于命名空间或 MCP 服务器,这意味着模型最初只能看到命名空间或服务器的名称和描述;其中各个函数的详细信息要等到工具搜索工具加载后才会显示。对于单个延迟加载的函数,模型仍然可以看到函数名称和描述,因此在实际使用中,工具搜索主要延迟加载的是参数模式。
为了最大限度地节省 Token,我们建议将延迟加载的函数分组到命名空间或 MCP 服务器中,并提供清晰的概括性描述,让模型充分了解其中的内容,以便有效地搜索并仅加载相关函数。作为最佳实践,建议将每个命名空间的函数数量控制在 10 个以下,以提高 Token 使用效率和模型性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28{
"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": "tool_search"
}
]
}
命名空间可以同时包含延迟加载和非延迟加载的工具。未设置 defer_loading: true 的工具可以立即调用,同一命名空间中延迟加载的工具则通过工具搜索加载。
您可以在以下两种工具搜索类型中进行选择:
- 托管式工具搜索: OpenAI 会在您于请求中声明的延迟加载工具中进行搜索,并在同一响应中返回已加载的工具子集。
- 客户端执行的工具搜索: 模型发出
tool_search_call,您的应用执行查找,然后由您返回对应的 tool_search_output。
如果您在创建请求时就已知候选工具,请优先使用托管式工具搜索。如果工具发现依赖于项目状态、租户状态或您的应用控制的其他系统,请使用客户端执行的工具搜索。
如果您已经知道希望模型搜索的全部函数、命名空间或 MCP 服务器,托管式工具搜索就是最简单的方式。您只需预先声明这些内容,添加 {"type": "tool_search"},然后让 API 决定加载哪些工具。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47import 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.",
defer_loading: true,
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.",
tools: [crmNamespace, { type: "tool_search" }],
parallel_tool_calls: false,
});
console.log(response.output);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50from 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.",
"defer_loading": True,
"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,
{"type": "tool_search"},
],
parallel_tool_calls=False,
)
print(response.output)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41package 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)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58import 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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39require "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,包含已加载且可供调用的工具子集。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47[
{
"type": "tool_search_call",
"execution": "server",
"call_id": null,
"status": "completed",
"arguments": {
"paths": ["crm"]
}
},
{
"type": "tool_search_output",
"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 列表中声明的信息时,这种方式很有用。
为 tool_search 工具配置 execution: "client",并提供用于定义应用所需搜索参数的模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70import 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",
execution: "client",
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),
searchOutput,
],
});
console.log(secondResponse.output);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61from 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",
"execution": "client",
"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,
{
"type": "tool_search_output",
"execution": "client",
"call_id": search_call.call_id,
"status": "completed",
"tools": loaded_tools,
},
],
)
print(second_response.output)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65package 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)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81import 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() + ")"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64require "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 后便停止:
1
2
3
4
5
6
7
8
9
10
11[
{
"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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24[
{
"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
}
}
]
}
]
在下一轮中,已加载的工具就可以像普通函数一样被调用:
1
2
3
4
5
6
7
8
9[
{
"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_output 中原样返回 tool_search_call 中的 call_id。
请在命名空间描述中清晰地说明使用场景,因为模型依赖这段描述来决定何时加载该命名空间中的部分函数。描述不宜过长。更丰富的细节应放在延迟加载的函数描述中,这些描述只会在需要时加载。
tool_search_output.tools 包含模型动态加载的工具列表。模型可以在后续轮次中调用其中的任何工具,因此在客户端模式下,您无需跨轮次重复加载同一个工具。未列入该数组的工具将无法供模型使用。如果您想禁用某个已加载的工具,可以从定义已加载工具集的 tool_search_output 项中将其移除,但请注意,更改已加载的工具集会使模型从该位置起的缓存失效。
大多数集成会在请求的 tools 参数中声明工具。客户端执行的工具搜索还支持更高级的方式:您的应用可以返回原始请求中未包含的工具。请将此视为高级工作流程:仔细验证返回的模式,并且只提供可信的工具定义。
所有工具都会加载到模型上下文窗口的末尾。托管式工具搜索和客户端执行的工具搜索均如此。这使模型的缓存能够在不同请求之间保留,从而降低总体成本并提高速度。
在高级工作流程中,您可以使用 additional_tools 输入项,让工具从对话中的特定位置开始可用。当您的应用在常规工具搜索流程之外加载工具,或者需要保留先前响应中所添加工具的顺序时,这种方式很有用。
将 role 设置为 developer,并在该项的 tools 数组中包含要添加的工具:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19{
"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 项中的工具只有在该项出现在输入中之后才可用。当您手动将对话项传回模型时,请保留该项的位置,确保模型在对话的同一位置看到相同的工具。
Agents API 默认会预先加载函数定义。要延迟加载指定函数,请在 agent.tools 中添加 { "type": "tool_search" },并为您希望智能体按需发现的每个函数设置 defer_loading: true。添加 tool_search 并不会让所有函数都延迟加载。
您的会话请求仍需提供完整的函数定义,包括名称、描述和参数模式。工具搜索改变的是该定义传入模型的时机。发现函数后,您的应用仍会照常处理函数调用并返回结果。有关结果处理,请参阅函数。
运行此示例前,请先设置 OPENAI_API_KEY:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44import 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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32from 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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45import (
"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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43import 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());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36require "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
| 策略 | 配置 | 适用场景 | 权衡因素 |
|---|
| 预先加载 | 省略 defer_loading,或将其设为 false。 | 函数数量较少,或大多数任务都需要用到这些函数。 | 未使用的定义会占用上下文。更改定义可能导致已缓存的前缀失效。 |
| 延迟加载 | 设置 defer_loading: true 并添加 tool_search。 | 函数目录庞大,但每项任务只需用到少量函数。 | 发现工具会增加一个步骤,且依赖于能否找到相关工具。 |
Agents API 支持在同一会话中混用预先加载和延迟加载的函数,但通常不建议这样做。请为延迟加载的函数提供清晰的名称和描述。在选择默认策略前,请使用有代表性的请求来比较任务完成情况、输入 Token 用量和延迟。
当模型和提供方支持工具搜索时,MCP 工具会在 Agents API 中使用自动发现机制。运行时会延迟加载 MCP 工具,并在有可搜索的延迟加载工具时添加工具搜索。这适用于远程 MCP、执行器 MCP 以及插件提供的 MCP 工具。
您无需仅为 MCP 工具添加 { "type": "tool_search" },也无需在 MCP 服务器上设置函数级别的 defer_loading 标志。请通过 MCP 连接配置服务器。本指南前面介绍的 Responses API 配置不适用于 Agents API 的 MCP 服务器。
- 使用函数调用来定义可调用的函数和自定义工具。
- 参阅使用工具,全面了解 Responses 中的各类工具。