概览
文件搜索通过引入模型外部的知识(例如专有产品信息或用户提供的文档)来增强助手能力。OpenAI 会自动解析并对您的文档进行分块,创建并存储嵌入,同时结合向量搜索和关键词搜索来检索相关内容,以回答用户的查询。
快速入门
在本示例中,我们将创建一个助手,用于帮助解答有关公司财务报表的问题。
步骤 1:创建启用文件搜索的新助手
创建一个新助手,并在助手的 file_search 参数中启用 tools Assistant 的参数。
1
2
3
4
5
6
7
8
9
10
from openai import OpenAI
client = OpenAI()
assistant = client.beta.assistants.create(
name="Financial Analyst Assistant",
instructions="You are an expert financial analyst. Use you knowledge base to answer questions about audited financial statements.",
model="gpt-4o",
tools=[{"type": "file_search"}],
)一旦启用了 file_search 工具,模型将根据用户消息自行决定何时检索内容。
步骤 2:上传文件并将其添加到向量存储
为了访问您的文件, file_search 工具使用了向量存储对象。上传您的文件并创建一个向量存储来容纳它们。创建向量存储后,您应轮询其状态,直到所有文件均脱离 in_progress 状态,以确保所有内容均已处理完毕。SDK 提供了可一次性完成上传和轮询的辅助工具。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Create a vector store called "Financial Statements"
vector_store = client.vector_stores.create(name="Financial Statements")
# Ready the files for upload to OpenAI
file_paths = ["edgar/goog-10k.pdf", "edgar/brka-10k.txt"]
file_streams = [open(path, "rb") for path in file_paths]
# Use the upload and poll SDK helper to upload the files, add them to the vector store,
# and poll the status of the file batch for completion.
file_batch = client.vector_stores.file_batches.upload_and_poll(
vector_store_id=vector_store.id, files=file_streams
)
# You can print the status and the file counts of the batch to see the result of this operation.
print(file_batch.status)
print(file_batch.file_counts)步骤 3:更新助手以使用新的向量存储
要使助手能够访问这些文件,请更新助手的 tool_resources with the new vector_store id.
1
2
3
4
assistant = client.beta.assistants.update(
assistant_id=assistant.id,
tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}},
)第 4 步:创建 Thread
您也可以在 Thread 上将文件作为 Message 附件添加。这样做将创建另一个 vector_store 与该 Thread 关联的向量存储,或者,如果该 Thread 已经关联了一个向量存储,则会将新文件附加到现有的 Thread 向量存储中。当您在此 Thread 上创建 Run 时,文件搜索工具将同时查询来自助手的 vector_store 和 Thread 上的 vector_store 在该线程上。
在这个示例中,用户附加了一份 Apple 最新的 10-K 文件副本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Upload the user provided file to OpenAI
message_file = client.files.create(
file=open("edgar/aapl-10k.pdf", "rb"), purpose="assistants"
)
# Create a thread and attach the file to the message
thread = client.beta.threads.create(
messages=[
{
"role": "user",
"content": "How many shares of AAPL were outstanding at the end of of October 2023?", # Attach the new file to the message.
"attachments": [
{ "file_id": message_file.id, "tools": [{"type": "file_search"}] }
],
}
]
)
# The thread now has a vector store with that file in its tool resources.
print(thread.tool_resources.file_search)使用消息附件创建的向量存储具有默认的过期策略,即在最后一次处于活动状态(定义为该向量存储最后一次作为 Run 的一部分)的 7 天后过期。此默认设置旨在帮助您管理向量存储成本。您可以随时覆盖这些过期策略。了解更多信息 此处.
第 5 步:创建 Run 并检查输出
现在,创建一个 Run,并观察模型如何使用文件搜索工具来回答用户的问题。
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
from typing_extensions import override
from openai import AssistantEventHandler, OpenAI
client = OpenAI()
class EventHandler(AssistantEventHandler):
@override
def on_text_created(self, text) -> None:
print(f"\nassistant > ", end="", flush=True)
@override
def on_tool_call_created(self, tool_call):
print(f"\nassistant > {tool_call.type}\n", flush=True)
@override
def on_message_done(self, message) -> None:
# print a citation to the file searched
message_content = message.content[0].text
annotations = message_content.annotations
citations = []
for index, annotation in enumerate(annotations):
message_content.value = message_content.value.replace(
annotation.text, f"[{index}]"
)
if file_citation := getattr(annotation, "file_citation", None):
cited_file = client.files.retrieve(file_citation.file_id)
citations.append(f"[{index}] {cited_file.filename}")
print(message_content.value)
print("\n".join(citations))
# Then, we use the stream SDK helper
# with the EventHandler class to create the Run
# and stream the response.
with client.beta.threads.runs.stream(
thread_id=thread.id,
assistant_id=assistant.id,
instructions="Please address the user as Jane Doe. The user has a premium account.",
event_handler=EventHandler(),
) as stream:
stream.until_done()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Use the create and poll SDK helper to create a run and poll the status of
# the run until it's in a terminal state.
run = client.beta.threads.runs.create_and_poll(
thread_id=thread.id, assistant_id=assistant.id
)
messages = list(client.beta.threads.messages.list(thread_id=thread.id, run_id=run.id))
message_content = messages[0].content[0].text
annotations = message_content.annotations
citations = []
for index, annotation in enumerate(annotations):
message_content.value = message_content.value.replace(annotation.text, f"[{index}]")
if file_citation := getattr(annotation, "file_citation", None):
cited_file = client.files.retrieve(file_citation.file_id)
citations.append(f"[{index}] {cited_file.filename}")
print(message_content.value)
print("\n".join(citations))您的新助手将查询两个附加的向量存储(一个包含 goog-10k.pdf and brka-10k.txt,另一个包含 aapl-10k.pdf),并从 aapl-10k.pdf.
检索模型所使用的文件搜索结果的内容,请使用 include 查询参数并提供 step_details.tool_calls[*].file_search.results[*].content in the format ?include[]=step_details.tool_calls[*].file_search.results[*].content.
工作原理
The file_search 工具开箱即实现了多种检索最佳实践,可帮助您从文件中提取正确的数据,并增强模型的回复。 file_search tool:
- 重写用户查询以优化其搜索效果。
- 将复杂的用户查询分解为多个可并行运行的搜索。
- 在助手和线程向量存储中同时运行关键词搜索和语义搜索。
- 对搜索结果进行重排序,以便在生成最终回复之前挑选出最相关的结果。
默认情况下, file_search 工具使用以下设置,但可以 进行配置 to suit your needs:
- 块大小:800 个 token
- 块重叠:400 个 token
- 嵌入模型:
text-embedding-3-large维度为 256 - 添加到上下文的最大块数:20(可能会更少)
- Ranker:
auto(OpenAI 将选择要使用的排序器) - 分数阈值:最低排序分数为 0
已知限制
我们有一些已知限制,正在努力在未来几个月内添加相关支持:
- 支持使用自定义元数据进行确定性的搜索前过滤。
- 支持解析文档中的图像(包括图表、图形、表格等的图像)
- 支持对结构化文件格式(如
csvorjsonl). - 更好地支持摘要生成——该工具目前针对搜索查询进行了优化。
向量存储
向量存储对象赋予了文件搜索工具搜索文件的能力。将文件添加到 vector_store 会自动解析、分块、嵌入并将其存储在支持关键词和语义搜索的向量数据库中。每个 vector_store 最多可容纳 10,000 个文件。对于 2025 年 11 月及之后创建的向量存储,此限制为 100,000,000 个文件。向量存储可以附加到助手和线程上。目前,您最多可以将一个向量存储附加到助手,并将一个向量存储附加到线程。
创建向量存储并添加文件
您可以通过单个 API 调用创建向量存储并向其添加文件:
1
2
3
4
vector_store = client.vector_stores.create(
name="Product Documentation",
file_ids=['file_1', 'file_2', 'file_3', 'file_4', 'file_5']
)将文件添加到向量存储是一个异步操作。为确保操作完成,我们建议您使用我们官方 SDK 中的“create and poll”辅助方法。如果您没有使用 SDK,可以检索 vector_store 对象并监控其 file_counts 属性以查看文件摄取操作的结果。
在创建向量存储之后,也可以通过 创建向量存储文件.
来向其中添加文件。添加文件的操作受每个向量存储 ID 的速率限制。向 /vector_stores/{vector_store_id}/files and /vector_stores/{vector_store_id}/file_batches 共享每个向量存储每分钟 300 次请求的限制。
1
2
3
4
file = client.vector_stores.files.create_and_poll(
vector_store_id="vs_abc123",
file_id="file-abc123"
)或者,你可以通过 创建批次 将多个文件添加到向量存储中,每个批次最多包含 500 个文件。
批量创建接受一个简单的列表,或者由带有 file_ids or a files 加上可选 file_id 的对象组成的数组 attributes and chunking_strategy值。使用 files (当需要针对每个文件的元数据或分块设置时使用),请注意, file_ids and files 在单个请求中是互斥的。
为了实现向一个向量存储的高吞吐量写入,请尽可能使用文件批次,以减少请求量并改善延迟。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
batch = client.vector_stores.file_batches.create_and_poll(
vector_store_id="vs_abc123",
files=[
{
"file_id": "file_1",
"attributes": {"category": "finance"}
},
{
"file_id": "file_2",
"chunking_strategy": {
"type": "static",
"max_chunk_size_tokens": 1000,
"chunk_overlap_tokens": 200
}
}
]
)类似地,可以通过以下任一方式将这些文件从向量存储中删除:
文件大小上限为 512 MB。每个文件包含的 token 不应超过 5,000,000(附加文件时会自动计算)。
文件搜索支持多种文件格式,包括 .pdf, .md,且 .docx。有关支持的文件扩展名(及其对应的 MIME 类型)的更多详细信息,请参阅 支持的文件 部分。
附加向量存储
你可以使用以下方式将向量存储附加到你的 Assistant 或 Thread 上 tool_resources parameter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
assistant = client.beta.assistants.create(
instructions="You are a helpful product support assistant and you answer questions based on the files provided to you.",
model="gpt-4o",
tools=[{"type": "file_search"}],
tool_resources={
"file_search": {
"vector_store_ids": ["vs_1"]
}
}
)
thread = client.beta.threads.create(
messages=[ { "role": "user", "content": "How do I cancel my subscription?"} ],
tool_resources={
"file_search": {
"vector_store_ids": ["vs_2"]
}
}
)你也可以在创建 Thread 或 Assistant 之后,通过使用正确的参数对其进行更新来附加向量存储 tool_resources.
在创建运行之前确保向量存储已就绪
我们强烈建议您确保 vector_store 中的所有文件在您创建运行之前已被完全处理。这将确保您的 vector_store 中的所有数据均可被搜索到。您可以通过使用我们 SDK 中的轮询辅助方法,或通过手动轮询 vector_store 来检查其就绪状态 vector_store 对象以确保 status is completed.
作为回退,我们构建了一个 最长 60 秒的等待 在 Run 对象中,当 线程的 向量存储包含仍在处理中的文件时。这是为了确保用户在线程中上传的任何文件在运行继续之前都完全可搜索。此回退等待 不会 应用于助手的向量存储。
自定义文件搜索设置
您可以自定义 file_search 工具如何对数据进行分块,以及它向模型上下文返回多少个分块。
分块配置
默认情况下, max_chunk_size_tokens 设置为 800 and chunk_overlap_tokens 设置为 400,即每个文件被分割成 800 个 token 的块来进行索引,连续块之间有 400 个 token 的重叠。
你可以通过设置来调整此选项 chunking_strategy 向向量存储添加文件时。存在某些限制: chunking_strategy:
max_chunk_size_tokens必须在 100 到 4096 之间(含)。chunk_overlap_tokens必须为非负数且不得超过max_chunk_size_tokens / 2.
块数
默认情况下, file_search 工具最多可为 gpt-4* 和 o 系列模型输出 20 个块,最多可为 gpt-3.5-turbo。你可以通过设置来调整此选项 file_search.max_num_results 在创建助手或运行时于工具中指定。
请注意,由于诸多原因, file_search 工具输出的块数可能会少于该值:
- 总块数少于
max_num_results. - 所有检索到的块的总 token 大小超出了分配给
file_search工具的 token “预算”。file_search工具目前的 token 预算为:- 4,000 个 token,适用于
gpt-3.5-turbo - 16,000 个 token,适用于
gpt-4*视觉 - o 系列模型支持 16,000 个 token
- 4,000 个 token,适用于
通过分块排序提升文件搜索结果的相关性
默认情况下,文件搜索工具会在生成响应时,将其认为具有任何相关性的所有搜索结果返回给模型。然而,如果生成响应所使用的内容相关性较低,可能会导致响应质量下降。您可以通过检查生成响应时返回的文件搜索结果,然后调整文件搜索工具排序器的行为,改变结果在被用于生成响应前所需达到的相关度门槛,从而调整此行为。
检查文件搜索块
提升文件搜索结果质量的第一步是检查助手当前的行为。通常,这涉及调查助手表现不佳的响应。您可以通过 REST API 获取 过往运行步骤的详细信息 ,具体来说是使用 include 查询参数来获取正用于生成结果的文件块。
1
2
3
4
5
6
7
8
9
10
11
from openai import OpenAI
client = OpenAI()
run_step = client.beta.threads.runs.steps.retrieve(
thread_id="thread_abc123",
run_id="run_abc123",
step_id="step_abc123",
include=["step_details.tool_calls[*].file_search.results[*].content"]
)
print(run_step)然后,您可以记录并检查在运行步骤中使用的搜索结果,并判断它们是否始终与您的助手应生成的响应相关。
配置排序选项
如果您发现您的文件搜索结果的相关性不足以生成高质量的响应,您可以调整结果排序器的设置,以选择应使用哪些搜索结果来生成响应。您可以在以下时机调整此设置: file_search.ranking_options in the tool when 创建助手时 or 创建 Run 时.
您可以配置的设置包括:
ranker- 决定使用哪些块时要使用的排序器。可用值为auto,它使用最新可用的排序器,并且default_2024_08_21.score_threshold- 介于 0.0 和 1.0 之间的排名值,其中 1.0 为最高排名。数值越高,将限制用于生成结果的文件块仅为具有更高可能相关性的块,代价是可能会遗漏相关的块。hybrid_search.embedding_weight(也称为rrf_embedding_weight)- 决定在使用 倒数排名融合。增加此权重以偏向在嵌入空间中更接近的块。hybrid_search.text_weight(也称为rrf_text_weight结合密集(向量)和稀疏(文本)排名时,赋予语义相似性的权重。- 决定在启用混合搜索时,赋予关键字/文本匹配的权重。增加此权重将有利于与查询共享精确术语的块。
在配置混合搜索时, hybrid_search.embedding_weight or hybrid_search.text_weight 中必须至少有一个大于零。
通过过期策略管理成本
The file_search 工具使用 vector_stores 对象作为其资源,您将根据所创建的 对象的大小进行计费。向量存储对象的大小是文件中所有已解析的块及其对应 embedding 的总和。 of the vector_store 您的首个 GB 是免费的,超出部分的使用量按 $0.10/GB/天 的向量存储标准计费。向量存储操作不产生其他费用。
为了帮助您管理与这些
对象相关的成本,我们在 vector_store 对象中添加了对过期策略的支持。您可以在创建或更新 vector_store 时设置这些策略。 vector_store object.
1
2
3
4
5
6
7
8
vector_store = client.vector_stores.create_and_poll(
name="Product Documentation",
file_ids=['file_1', 'file_2', 'file_3', 'file_4', 'file_5'],
expires_after={
"anchor": "last_active_at",
"days": 7
}
)线程向量存储具有默认的过期策略
使用线程辅助工具(例如消息中的 tool_resources.file_search.vector_stores in Threads or message.attachments )创建的向量存储具有默认过期策略,即在其上次处于活动状态(定义为向量存储上次参与运行的时间)后的 7 天。
当向量存储过期时,该线程上的运行将失败。要解决此问题,您只需使用相同的文件重新创建一个新的 vector_store 并将其重新附加到该线程即可。
1
2
3
4
5
6
7
8
9
10
11
12
all_files = list(client.vector_stores.files.list("vs_expired"))
vector_store = client.vector_stores.create(name="rag-store")
client.beta.threads.update(
"thread_abc123",
tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}},
)
for file_batch in chunked(all_files, 100):
client.vector_stores.file_batches.create_and_poll(
vector_store_id=vector_store.id, file_ids=[file.id for file in file_batch]
)支持的文件
For text/ MIME 类型,编码必须是以下之一 utf-8, utf-16, or ascii.
| 文件格式 | MIME 类型 |
|---|---|
.c | text/x-c |
.cpp | text/x-c++ |
.cs | text/x-csharp |
.css | text/css |
.doc | application/msword |
.docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.go | text/x-golang |
.html | text/html |
.java | text/x-java |
.js | text/javascript |
.json | application/json |
.md | text/markdown |
.pdf | application/pdf |
.php | text/x-php |
.pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
.py | text/x-python |
.py | text/x-script.python |
.rb | text/x-ruby |
.sh | application/x-sh |
.tex | text/x-tex |
.ts | application/typescript |
.txt | text/plain |