工具搜尋可讓模型視需要動態搜尋工具,並將工具載入模型的上下文。這樣就不必一開始就將所有工具定義載入模型的上下文,且 可能有助於減少整體 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 用量與延遲。
當模型與供應商支援工具搜尋時,Agents API 中的 MCP 工具會使用自動探索功能。執行階段會延後載入 MCP 工具,並在有可搜尋的延後載入工具時加入工具搜尋。這適用於遠端 MCP、執行器 MCP,以及外掛程式提供的 MCP 工具。
你不需要專為 MCP 工具加入 { "type": "tool_search" },也不需要在 MCP 伺服器上設定函式層級的 defer_loading 旗標。請使用 MCP 連線來設定伺服器。本指南前面介紹的 Responses API 組態不適用於 Agents API 的 MCP 伺服器。
- 使用函式呼叫來定義可呼叫的函式和自訂工具。
- 請參閱使用工具,全面瞭解 Responses 中可用的工具。