概览
与 Chat Completions API 类似,Assistants API 支持函数调用。函数调用允许你向 Assistants API 描述函数,并让它智能地返回需要调用的函数及其参数。
快速入门
在这个例子中,我们将创建一个天气助手并定义两个函数,
get_current_temperature and get_rain_probability,作为 Assistant 可以调用的工具。根据用户的查询,如果使用 2023 年 11 月 6 日及之后发布的最新模型,模型将调用并行函数。在我们使用并行函数调用的示例中,我们将询问 Assistant 旧金山今天的天气情况以及降雨的概率。我们还将展示如何通过流式输出生成 Assistant 的响应。
随着 Structured Outputs 的发布,你现在可以使用参数 strict: true 在使用 Assistants API 进行函数调用时。有关更多信息,请参阅 函数调用指南。请注意,在使用视觉功能时,Assistants API 不支持结构化输出。
第 1 步:定义函数
在创建助手时,你将首先在助手的 tools 参数下定义函数。
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
from openai import OpenAI
client = OpenAI()
assistant = client.beta.assistants.create(
instructions="You are a weather bot. Use the provided functions to answer questions.",
model="gpt-4o",
tools=[
{
"type": "function",
"function": {
"name": "get_current_temperature",
"description": "Get the current temperature for a specific location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g., San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["Celsius", "Fahrenheit"],
"description": "The temperature unit to use. Infer this from the user's location."
}
},
"required": ["location", "unit"]
}
}
},
{
"type": "function",
"function": {
"name": "get_rain_probability",
"description": "Get the probability of rain for a specific location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g., San Francisco, CA"
}
},
"required": ["location"]
}
}
}
]
)第 2 步:创建 Thread 并添加 Messages
当用户开始对话时创建一个 Thread,并在用户提问时将 Messages 添加到 Thread 中。
1
2
3
4
5
6
thread = client.beta.threads.create()
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="What's the weather in San Francisco today and the likelihood it'll rain?",
)第 3 步:发起 Run
当你在包含触发一个或多个函数的用户 Message 的 Thread 上发起 Run 时,Run 将进入 pending 状态。处理完毕后,Run 将进入 requires_action 状态,你可以通过检查 Run 的 status。这表示您需要运行工具并将它们的输出提交给 Assistant,以继续 Run 的执行。在我们的例子中,我们将看到两个 tool_calls,这表示用户查询导致了并行函数调用。
请注意,Run 会在创建十分钟后过期。请务必在 10 分钟标记之前提交你的工具输出。
你将看到两个 tool_calls 位于 required_action,这表示用户查询触发了并行函数调用。
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
{
"id": "run_qJL1kI9xxWlfE0z1yfL0fGg9",
...
"status": "requires_action",
"required_action": {
"submit_tool_outputs": {
"tool_calls": [
{
"id": "call_FthC9qRpsL5kBpwwyw6c7j4k",
"function": {
"arguments": "{"location": "San Francisco, CA"}",
"name": "get_rain_probability"
},
"type": "function"
},
{
"id": "call_RpEDoB8O0FTL9JoKTuCVFOyR",
"function": {
"arguments": "{"location": "San Francisco, CA", "unit": "Fahrenheit"}",
"name": "get_current_temperature"
},
"type": "function"
}
]
},
...
"type": "submit_tool_outputs"
}
}你发起 Run 并提交 tool_calls 的方式将取决于你是否使用流式传输,尽管在两种情况下,所有 tool_calls 都需要同时提交。然后,你可以通过提交所调用函数的工具输出来完成 Run。传递在 tool_call_id 对象中引用的每个 required_action 以便将输出与每个函数调用进行匹配。
对于流式传输的情况,我们创建了一个 EventHandler 类来处理响应流中的事件,并使用 Python 和 Node SDK 中的 “submit tool outputs stream” 辅助函数一次性提交所有工具输出。
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
from typing_extensions import override
from openai import AssistantEventHandler
class EventHandler(AssistantEventHandler):
@override
def on_event(self, event):
# Retrieve events that are denoted with 'requires_action'
# since these will have our tool_calls
if event.event == 'thread.run.requires_action':
run_id = event.data.id # Retrieve the run ID from the event data
self.handle_requires_action(event.data, run_id)
def handle_requires_action(self, data, run_id):
tool_outputs = []
for tool in data.required_action.submit_tool_outputs.tool_calls:
if tool.function.name == "get_current_temperature":
tool_outputs.append({"tool_call_id": tool.id, "output": "57"})
elif tool.function.name == "get_rain_probability":
tool_outputs.append({"tool_call_id": tool.id, "output": "0.06"})
# Submit all tool_outputs at the same time
self.submit_tool_outputs(tool_outputs, run_id)
def submit_tool_outputs(self, tool_outputs, run_id):
# Use the submit_tool_outputs_stream helper
with client.beta.threads.runs.submit_tool_outputs_stream(
thread_id=self.current_run.thread_id,
run_id=self.current_run.id,
tool_outputs=tool_outputs,
event_handler=EventHandler(),
) as stream:
for text in stream.text_deltas:
print(text, end="", flush=True)
print()
with client.beta.threads.runs.stream(
thread_id=thread.id,
assistant_id=assistant.id,
event_handler=EventHandler()
) as stream:
stream.until_done()Run 是异步的,这意味着你需要通过轮询 Run 对象来监视其 status 直到达到
终止状态 。为方便起见,‘create and poll’ SDK 辅助函数可帮助你创建 Run,然后轮询其完成情况。Run 完成后,你可以列出由助手添加到 Thread 中的 Messages。最后,你将检索所有 tool_outputs from
required_action 并将它们同时提交给 ‘submit tool outputs and poll’ 辅助函数。
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
run = client.beta.threads.runs.create_and_poll(
thread_id=thread.id,
assistant_id=assistant.id,
)
if run.status == 'completed':
messages = client.beta.threads.messages.list(
thread_id=thread.id
)
print(messages)
else:
print(run.status)
# Define the list to store tool outputs
tool_outputs = []
# Loop through each tool in the required action section
for tool in run.required_action.submit_tool_outputs.tool_calls:
if tool.function.name == "get_current_temperature":
tool_outputs.append({
"tool_call_id": tool.id,
"output": "57"
})
elif tool.function.name == "get_rain_probability":
tool_outputs.append({
"tool_call_id": tool.id,
"output": "0.06"
})
# Submit all tool outputs at once after collecting them in a list
if tool_outputs:
try:
run = client.beta.threads.runs.submit_tool_outputs_and_poll(
thread_id=thread.id,
run_id=run.id,
tool_outputs=tool_outputs
)
print("Tool outputs submitted successfully.")
except Exception as e:
print("Failed to submit tool outputs:", e)
else:
print("No tool outputs to submit.")
if run.status == 'completed':
messages = client.beta.threads.messages.list(
thread_id=thread.id
)
print(messages)
else:
print(run.status)使用 Structured Outputs
当你通过提供启用 结构化输出 时 strict: true,OpenAI API 将在您的首次请求时对提供的 schema 进行预处理,然后使用该产物来约束模型以符合您的 schema。
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
from openai import OpenAI
client = OpenAI()
assistant = client.beta.assistants.create(
instructions="You are a weather bot. Use the provided functions to answer questions.",
model="gpt-4o-2024-08-06",
tools=[
{
"type": "function",
"function": {
"name": "get_current_temperature",
"description": "Get the current temperature for a specific location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g., San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["Celsius", "Fahrenheit"],
"description": "The temperature unit to use. Infer this from the user's location."
}
},
"required": ["location", "unit"],
"additionalProperties": False
},
"strict": True
}
},
{
"type": "function",
"function": {
"name": "get_rain_probability",
"description": "Get the probability of rain for a specific location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g., San Francisco, CA"
}
},
"required": ["location"],
"additionalProperties": False
},
"strict": True
}
}
]
)