OPENAI Assistants API学习笔记
📅 2024-12-05 | 🖱️
1.OPENAI Assistants API概述 #
OPENAI Assistants API概述:构建AI助手,并提供工具和集成功能
OPENAI的Assistants API允许我们在自己的应用程序中构建AI助手。助手包含指令(instructions),并可以利用模型(models)、工具(tools)和文件(files)来响应用户查询。Assistants API 当前支持三种类型的工具:代码解释器、文件搜索和函数调用。
您可以使用 Assistants Playground 来探索 Assistants API 的功能,或者通过 Assistants API 快速入门中概述的逐步集成进行构建。
可以使用Assistants Playground了解Assistants API的功能,或者按照Assistants API快速入门中概述的逐步集成步骤进行构建。
1.1 Assistants的工作原理 #
Assistants API旨在帮助开发者构建功能强大的AI助手,支持执行多种任务。
Assistants API目前处于Beta测试阶段
- 助手可以向 OpenAI 的模型发送特定指令,从而调整其个性和能力。
- 助手可以同时访问多个工具,这些工具既可以是 OpenAI 托管的工具(如code_interpreter和file_search),也可以是我们自行构建或托管的工具(通过函数调用function calling实现)。
- 助手支持访问具有持久性的thread。thread通过存储消息历史记录并在对话超出模型上下文长度时进行截断,从而简化了AI应用程序的开发。只需创建一次thread,并在用户回复时不断向其中追加消息即可。
- 助手可以访问多种格式的文件——这些文件既可以在助手创建时提供,也可以作为助手与用户之间thread的一部分提供。在使用工具时,助手还可以创建文件(例如图像、电子表格等),并在其生成的消息中引用所使用的文件。
1.2 对象 #
Object | What it represents |
---|---|
Assistant | 为特定目的构建的AI,使用OpenAI的模型并调用工具。 |
Thread | 助手与用户之间的会话。Thread存储消息,并自动处理截断以将内容适配到模型的上下文中。 |
Message | 由助手或用户创建的消息。消息可以包含文本、图片和其他文件,并作为Thread上的列表存储。 |
Run | 在Thread上调用助手的一次运行。助手利用其配置和Thread的消息,通过调用模型和工具来执行任务。作为Run的一部分,助手会将消息附加到Thread中。 |
Run Step | 助手在一次Run中执行的详细步骤列表。助手可以在运行过程中调用工具或创建消息。检查运行步骤可深入了解助手如何得出最终结果。 |
2.Assistants API快速入门 #
Assistants API的典型集成流程如下:
- 通过定义自定义指令并选择模型来创建Assistant。如果需要,可以添加文件并启用诸如代码解释器、文件搜索和函数调用等工具。
- 当用户开始对话时,创建一个Thread。
- 用户提问时,将Message添加到该Thread。
- 在该Thread上运行Assistant,通过调用模型和工具生成响应。
本入门指南将逐步讲解如何创建和运行一个启用了代码解释器的Assistant。在本示例中,我们将创建一个个人数学辅导助手,并启用代码解释器工具。
提示
调用Assistants API时需要传递一个测试版HTTP头。如果你使用的是OpenAI官方的Python或Node.js SDK,这一操作会自动处理。
2.1 步骤1:创建一个Assistant #
Assistant代表一个实体,可以通过配置多个参数(如模型mode
、指令instructions
和工具tools
)来响应用户的消息。
1from openai import OpenAI
2client = OpenAI()
3
4assistant = client.beta.assistants.create(
5 name="Math Tutor",
6 instructions="You are a personal math tutor. Write and run code to answer math questions.",
7 tools=[{"type": "code_interpreter"}],
8 model="gpt-4o",
9)
2.2 步骤2:创建一个Thread #
Thread代表用户与一个或多个助理之间的交流。当用户(或你的AI应用)开始与Assistant交流时,可以创建一个Thread。
1thread = client.beta.threads.create()
2.3 步骤3:添加Message到Thread中 #
用户或应用程序创建的消息会作为Message对象添加Thread中。消息可以包含文本和文件。每个Thread最多可以容纳100,000 条消息,对于超出模型上下文窗口限制的部分,会自动智能截取超出的消息内容。
1message = client.beta.threads.messages.create(
2 thread_id=thread.id,
3 role="user",
4 content="I need to solve the equation `3x + 11 = 14`. Can you help me?"
5)
2.4 步骤4:创建Run #
可以使用Python和Node SDK中的“创建和流式传输”助手来创建Run并流式传输响应。
1from typing_extensions import override
2from openai import AssistantEventHandler
3
4# First, we create a EventHandler class to define
5# how we want to handle the events in the response stream.
6
7class EventHandler(AssistantEventHandler):
8 @override
9 def on_text_created(self, text) -> None:
10 print(f"\nassistant > ", end="", flush=True)
11
12 @override
13 def on_text_delta(self, delta, snapshot):
14 print(delta.value, end="", flush=True)
15
16 def on_tool_call_created(self, tool_call):
17 print(f"\nassistant > {tool_call.type}\n", flush=True)
18
19 def on_tool_call_delta(self, delta, snapshot):
20 if delta.type == 'code_interpreter':
21 if delta.code_interpreter.input:
22 print(delta.code_interpreter.input, end="", flush=True)
23 if delta.code_interpreter.outputs:
24 print(f"\n\noutput >", flush=True)
25 for output in delta.code_interpreter.outputs:
26 if output.type == "logs":
27 print(f"\n{output.logs}", flush=True)
28
29# Then, we use the `stream` SDK helper
30# with the `EventHandler` class to create the Run
31# and stream the response.
32
33with client.beta.threads.runs.stream(
34 thread_id=thread.id,
35 assistant_id=assistant.id,
36 instructions="Please address the user as Jane Doe. The user has a premium account.",
37 event_handler=EventHandler(),
38) as stream:
39 stream.until_done()
在API参考文档中可以查看完整的Assistants流式事件列表。还可以在Python和Node仓库文档中查看这些事件的SDK事件监听器列表。
Run是异步的,这意味着需要通过轮询Run对象来监控其状态status
,直到达到终止状态。为了方便,‘创建和轮询’(‘create and poll’)SDK助手可以帮助您创建Run并轮询其完成状态。
1run = client.beta.threads.runs.create_and_poll(
2 thread_id=thread.id,
3 assistant_id=assistant.id,
4 instructions="Please address the user as Jane Doe. The user has a premium account."
5)
一旦Run完成,就可以列出Assistant添加到Thread中的Messages。
1if run.status == 'completed':
2 messages = client.beta.threads.messages.list(
3 thread_id=thread.id
4 )
5 print(messages)
6else:
7 print(run.status)
如果想查看在此运行期间所做的任何工具调用,还可以列出此运行(Run)的步骤(Run Steps)。
3.深入Assistants API #
创建和管理Assistants的深入指南。
3.1 Creating Assistants #
为了获得最佳效果和与工具的最大兼容性,建议使用OpenAI的最新模型与Assistants API。
要开始使用,创建Assistant只需指定要使用的模型。但是,还可以进一步自定义助手的行为:
- 使用
instructions
参数来引导助手的个性并定义其目标。instructions
类似于Chat Completions API中的系统消息。 - 使用
tools
参数为助手提供最多128个工具的访问权限。可以为助手提供对OpenAI托管工具(如code_interpreter
和file_searc
)的访问权限,或者通过函数调用调用(function calling)第三方工具。 - 使用
tool_resources
参数为工具(如code_interpreter
和file_search
)提供对文件的访问权限。文件通过文件上传端点进行上传,必须将其用途设置为assistants
才能与此API一起使用。
例如,要创建一个可以基于.csv
文件生成数据可视化的助手,首先上传一个文件。
1file = client.files.create(
2 file=open("revenue-forecast.csv", "rb"),
3 purpose='assistants'
4)
然后,创建启用了code_interpreter
工具的助手,并将该文件作为工具的资源提供。
1assistant = client.beta.assistants.create(
2 name="Data visualizer",
3 description="You are great at creating beautiful data visualizations. You analyze data present in .csv files, understand trends, and come up with data visualizations relevant to those trends. You also share a brief text summary of the trends observed.",
4 model="gpt-4o",
5 tools=[{"type": "code_interpreter"}],
6 tool_resources={
7 "code_interpreter": {
8 "file_ids": [file.id]
9 }
10 }
11)
可以在 code_interpreter
中附加最多 20 个文件,在 file_search
(使用 vector_store
对象)中附加最多10000个文件。
每个文件最大可以是512MB,并且最多有5000000 个token。默认情况下,项目中上传的所有文件的总大小不能超过100GB。
3.2 管理Threads和Messages #
Thread和Messsage表示助手和用户之间的对话会话。每个Thread的消息数限制为100000条。当消息的大小超过模型的上下文窗口时,Thread将尝试智能地截断消息,然后删除它认为最不重要的消息。
可以像这样创建一个带有初始消息列表的Thread:
1thread = client.beta.threads.create(
2 messages=[
3 {
4 "role": "user",
5 "content": "Create 3 data visualizations based on the trends in this file.",
6 "attachments": [
7 {
8 "file_id": file.id,
9 "tools": [{"type": "code_interpreter"}]
10 }
11 ]
12 }
13 ]
14)
消息可以包含文本、图像或文件附件。消息的attachments
是帮助方法,用于向Thread的 tool_resources
添加文件。也可以直接将文件添加到thread.tool_resources
。
创建图像输入内容 #
消息内容可以包含外部图像URL或通过文件API上传的文件ID。只有支持视觉的模型才能接受图像输入。支持的图像内容类型包括png、jpg、gif 和webp。创建图像文件时,传递 purpose="vision"
以允许你稍后下载和显示输入内容。目前,每个项目有100GB的限制。
工具除非指定,否则无法访问图像内容。要将图像文件传递给代码解释器,请在消息 attachments
列表中添加文件 ID,以允许工具读取和分析输入。目前,代码解释器无法下载图像URL。
1file = client.files.create(
2 file=open("myimage.png", "rb"),
3 purpose="vision"
4)
5thread = client.beta.threads.create(
6 messages=[
7 {
8 "role": "user",
9 "content": [
10 {
11 "type": "text",
12 "text": "What is the difference between these images?"
13 },
14 {
15 "type": "image_url",
16 "image_url": {"url": "https://example.com/image.png"}
17 },
18 {
19 "type": "image_file",
20 "image_file": {"file_id": file.id}
21 },
22 ],
23 }
24 ]
25)
低保真度或高保真度图像理解
通过控制 detail
参数,它有三个选项:low
、high
或 auto
,可以控制模型处理图像并生成文本理解的方式。
low
将启用"低分辨率"模式。模型将接收一个低分辨率的 512px x 512px 版本的图像,并用85个token来表示图像。这使API能够更快地返回响应,并为不需要高细节的用例消耗较少的输入标记。high
将启用"高分辨率"模式,它首先允许模型查看低分辨率图像,然后根据输入图像的大小创建详细的图像裁剪。使用价格计算器可以查看不同图像大小的token数量。
1thread = client.beta.threads.create(
2 messages=[
3 {
4 "role": "user",
5 "content": [
6 {
7 "type": "text",
8 "text": "What is this an image of?"
9 },
10 {
11 "type": "image_url",
12 "image_url": {
13 "url": "https://example.com/image.png",
14 "detail": "high"
15 }
16 },
17 ],
18 }
19 ]
20)
上下文窗口管理 #
Assistants API会自动管理截断,以确保保持在模型的最大上下文长度内。可以通过指定希望运行使用的最大token数和/或希望在运行中包含的最近消息的最大数量来自定义此行为。
Max Completion and Max Prompt Tokens
要控制单个Run中的token使用量,在创建Run时设置max_prompt_tokens
和max_completion_tokens
。这些限制适用于Run生命周期内所有completion过程中使用的token总数。
例如,启动一个Run时将max_prompt_tokens
设置为500,max_completion_tokens
设置为 1000,意味着第一次completion将Thread截断至500个token,并将输出上限设为1000个token。如果第一次completion仅使用了200个提示token和300个completion token,那么第二次completion将有300个可用的提示token和700个可用的completion标记。
如果completion过程达到max_completion_tokens
限制,Run将 incomplete
状态终止,并在Run对象的incomplete_details
字段中提供详细信息。
提示
在使用文件搜索工具时,建议将
max_prompt_tokens
设置为不少于20,000。对于较长的对话或与文件搜索的多次交互,请考虑将此限制提高到50000
,或者最理想的是完全取消max_prompt_tokens
限制,以获得最高质量的结果。
截断策略
还可以指定截断策略来控制Thread如何渲染到模型的上下文窗口中。使用类型为auto
的截断策略将使用OpenAI的默认截断策略。使用类型为last_messages
的截断策略将允许指定要包含在上下文窗口中的最新消息的数量。
Message annotations #
Assistants创建的消息可能包含在对象的内容(content
)数组中的annotations
(即message.content[0].text.annotations
。annotations提供有关如何注解消息中的文本的信息。
有两种类型的Annotations:
file_citation
:文件引文由file_search
工具创建,并定义对Assistant上传并用于生成响应的特定文件的引用。file_path
:文件路径注释由code_interpreter
工具创建,并包含对工具生成的文件的引用。
当消息对象中存在annotations时,将在文本中看到不可读的模型生成的子字符串,应该用annotations中的信息替换这些子字符串。这些字符串可能看起来像【13†source】
或sandbox:/mnt/data/file.csv
。下面是一个示例 python 代码片段,它用annotations中的信息替换这些字符串。
1# Retrieve the message object
2message = client.beta.threads.messages.retrieve(
3 thread_id="...",
4 message_id="..."
5)
6# Extract the message content
7message_content = message.content[0].text
8annotations = message_content.annotations
9citations = []
10# Iterate over the annotations and add footnotes
11for index, annotation in enumerate(annotations):
12 # Replace the text with a footnote
13 message_content.value = message_content.value.replace(annotation.text, f' [{index}]')
14 # Gather citations based on annotation attributes
15 if (file_citation := getattr(annotation, 'file_citation', None)):
16 cited_file = client.files.retrieve(file_citation.file_id)
17 citations.append(f'[{index}] {file_citation.quote} from {cited_file.filename}')
18 elif (file_path := getattr(annotation, 'file_path', None)):
19 cited_file = client.files.retrieve(file_path.file_id)
20 citations.append(f'[{index}] Click <here> to download {cited_file.filename}')
21 # Note: File download functionality not implemented above for brevity
22# Add footnotes to the end of the message before displaying to user
23message_content.value += '\n' + '\n'.join(citations)
3.3 Runs and Run Steps #
当从Thread中获得所需的所有上下文后,可以使用选择的Assistant运行该Thread。
1run = client.beta.threads.runs.create(
2 thread_id=thread.id,
3 assistant_id=assistant.id
4)
默认情况下,Run将使用Assistant对象中指定的模型和工具配置,但可以在创建Run时覆盖其中大部分配置,以增加灵活性:
1run = client.beta.threads.runs.create(
2 thread_id=thread.id,
3 assistant_id=assistant.id,
4 model="gpt-4o",
5 instructions="New instructions that override the Assistant instructions",
6 tools=[{"type": "code_interpreter"}, {"type": "file_search"}]
7)
注意:
与Assistant关联的tool_resources
不能在Run创建期间被覆盖。必须使用modify Assistant端点来执行此操作。
Run的生命周期 #
Run对象可以有多种状态。
状态 | 描述 |
---|---|
queued | Run刚创建或完成required_action 后,会被移至queued 状态。它们应该几乎立即移至in_progress 。 |
in_progress | 在in_progress 状态,Assistant使用模型和工具执行步骤。可以通过检查Run Steps来查看Run的进度。 |
completed | Run成功完成!现在可以查看Assistant添加到Thread的所有消息,以及Run采取的所有步骤。还可以通过向Thread添加更多用户消息并创建另一个Run来继续对话。 |
requires_action | 使用函数调用工具时,一旦模型确定要调用的函数的名称和参数,运行将移至required_action 状态。然后,必须运行这些函数并提交输出,然后Run才能继续。如果没有在expires_at 时间戳之前提供输出(大约创建后10分钟),Run将移至过期状态。 |
expired | 当函数调用输出在expires_at 之前未提交且Run过期时,会发生这种情况。此外,如果运行执行时间过长,超过expires_at 中指定的时间,系统将使Run过期。 |
cancelling | 可以使用Cancel Run端点尝试取消正在进行的Run。一旦取消尝试成功,Run的状态将变为cancelled 。取消是尝试的,但不保证成功。 |
cancelled | Run已成功取消。 |
failed | 可以通过查看Run中的last_error 对象来查看失败的原因。失败的时间戳将在failed_at 下记录。 |
incomplete | 运行由于达到max_prompt_tokens 或max_completion_tokens 而结束。可以通过查看Run中的incomplete_details 对象来查看具体原因。 |
轮询更新 #
如果未使用流式传输,为了保持Run状态的最新,将必须定期检索Run对象。可以每次检索对象时检查Run的状态,以确定应用程序接下来应该做什么。 可以选择使用Node和Python SDK中的轮询助手。这些助手将自动轮询Run对象,并在其处于terminal状态时返回Run对象。
Thread locks #
当一个Run处于进行中且未处于terminal状态时,Thread被锁定。这意味着:
- 无法向Thread添加新消息。
- 无法在该Thread上创建新的Run。
Run steps #
Run steps的状态与Run状态具有相同的含义。
Run Step对象中的大部分有趣细节都位于step_details
字段中。可能有两种类型的步骤详细信息:
message_creation
:当Assistant在Thread上创建消息时,将创建此Run Step。tool_calls
:当助手调用工具时,将创建此Run Step。
3.4 数据访问指南 #
目前,通过API创建的Assistants, Threads, Messages和向量存储Vector Stores都属于创建它们的项目范围。因此,任何拥有该项目API 密钥访问权限的人都可以读取或写入项目中的Assistants, Threads, Messages和Runs。
强烈建议采取以下数据访问控制措施:
- 实施授权。在读取或写入Assistants, Threads, Messages和向量存储之前,请确保终端用户有权这样做。例如,在数据库中存储终端用户有权访问的对象ID,并在使用API获取对象ID之前进行检查。
- 限制API密钥访问。仔细考虑组织中哪些人应该拥有API密钥并成为项目的一部分。定期审核此列表。API密钥支持广泛的操作,包括读取和修改敏感信息,例如消息和文件。
- 创建单独的帐户。考虑为不同的应用程序创建单独的项目,以便隔离多个应用程序中的数据。
4.Assistants API tools #
探索文件搜索、代码和函数调用工具。
使用Assistants API创建的助手可以配备工具,使它们能够执行更复杂的任务或与你的应用程序交互。Assistants API为助手提供了内置工具,但也可以使用函数调用定义自己的工具来扩展其功能。 Assistants API当前支持以下工具: