Обзор Assistants API (Python SDK)
Новый Assistants API — это эволюция нашего Chat Completions API с сохранением состояния, предназначенная для упрощения создания ассистентов и предоставления разработчикам доступа к мощным инструментам, таким как Code Interpreter и File Search.

Chat Completions API против Assistants API
Примитивами Chat Completions API являются Messages, над которыми вы выполняете Completion с помощью Model (gpt-4o, gpt-4o-mini и т. д.). Он легковесен и мощен, но по своей сути бесстатусен, что означает, что вам приходится вручную управлять состоянием диалога, определениями инструментов, документами для поиска и выполнением кода.
Примитивами Assistants API являются:
Assistants, которые инкапсулируют базовую модель, инструкции, инструменты и (контекстные) документы;Threads, которые представляют состояние диалога; иRuns, которые обеспечивают выполнениеAssistantвThread, включая текстовые ответы и многошаговое использование инструментов.
Мы рассмотрим, как их можно использовать для создания мощных интерактивных возможностей с сохранением состояния.
Настройка
Python SDK
Примечание Мы обновили наш Python SDK, чтобы добавить поддержку Assistants API, поэтому вам потребуется обновить его до последней версии (
1.59.4на момент написания).
!pip install --upgrade openai
Requirement already satisfied: openai in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (1.59.4)
Requirement already satisfied: anyio<5,>=3.5.0 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from openai) (3.7.1)
Requirement already satisfied: distro<2,>=1.7.0 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from openai) (1.9.0)
Requirement already satisfied: httpx<1,>=0.23.0 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from openai) (0.27.0)
Requirement already satisfied: jiter<1,>=0.4.0 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from openai) (0.7.0)
Requirement already satisfied: pydantic<3,>=1.9.0 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from openai) (2.8.2)
Requirement already satisfied: sniffio in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from openai) (1.3.1)
Requirement already satisfied: tqdm>4 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from openai) (4.66.4)
Requirement already satisfied: typing-extensions<5,>=4.11 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from openai) (4.12.2)
Requirement already satisfied: idna>=2.8 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from anyio<5,>=3.5.0->openai) (3.7)
Requirement already satisfied: certifi in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (2024.7.4)
Requirement already satisfied: httpcore==1.* in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (1.0.5)
Requirement already satisfied: h11<0.15,>=0.13 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)
Requirement already satisfied: annotated-types>=0.4.0 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.7.0)
Requirement already satisfied: pydantic-core==2.20.1 in /Users/lee.spacagna/myenv/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (2.20.1)
И убедитесь, что он обновлен, запустив:
!pip show openai | grep Version
Version: 1.59.4
Вспомогательная функция для красивого вывода
import json
def show_json(obj):
display(json.loads(obj.model_dump_json()))
Полный пример с Assistants API
Assistants
Самый простой способ начать работу с Assistants API — через Assistants Playground.

Начнем с создания ассистента! Мы создадим Math Tutor, как и в нашей документации.

Вы также можете создавать Assistants напрямую через Assistants API, вот так:
from openai import OpenAI
import os
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"))
assistant = client.beta.assistants.create(
name="Math Tutor",
instructions="You are a personal math tutor. Answer questions briefly, in a sentence or less.",
model="gpt-4o",
)
show_json(assistant)
{'id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'created_at': 1736340398,
'description': None,
'instructions': 'You are a personal math tutor. Answer questions briefly, in a sentence or less.',
'metadata': {},
'model': 'gpt-4o',
'name': 'Math Tutor',
'object': 'assistant',
'tools': [],
'response_format': 'auto',
'temperature': 1.0,
'tool_resources': {'code_interpreter': None, 'file_search': None},
'top_p': 1.0} 'tools': [],
'response_format': 'auto',
'temperature': 1.0,
'tool_resources': {'code_interpreter': None, 'file_search': None},
'top_p': 1.0}
Независимо от того, создаете ли вы свой Assistant через Dashboard или с помощью API, вам нужно будет отслеживать Assistant ID. Именно так вы будете ссылаться на своего Assistant в Threads и Runs.
Далее мы создадим новый Thread и добавим в него Message. Это будет хранить состояние нашего диалога, чтобы нам не приходилось каждый раз повторно отправлять всю историю сообщений.
Threads
Создайте новый Thread:
thread = client.beta.threads.create()
show_json(thread)
{'id': 'thread_j4dc1TiHPfkviKUHNi4aAsA6',
'created_at': 1736340398,
'metadata': {},
'object': 'thread',
'tool_resources': {'code_interpreter': None, 'file_search': None}} 'object': 'thread',
'tool_resources': {'code_interpreter': None, 'file_search': None}}
Затем добавьте Message в Thread:
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="I need to solve the equation `3x + 11 = 14`. Can you help me?",
)
show_json(message)
{'id': 'msg_1q4Y7ZZ9gIcPoAKSx9UtrrKJ',
'assistant_id': None,
'attachments': [],
'completed_at': None,
'attachments': [],
'completed_at': None,
'content': [{'text': {'annotations': [],
'value': 'I need to solve the equation `3x + 11 = 14`. Can you help me?'},
'type': 'text'}],
'created_at': 1736340400,
'incomplete_at': None,
'incomplete_details': None,
'metadata': {},
'object': 'thread.message',
'role': 'user',
'run_id': None,
'status': None,
'thread_id': 'thread_j4dc1TiHPfkviKUHNi4aAsA6'}
Примечание Несмотря на то, что вы больше не отправляете всю историю каждый раз, с вас все равно будет взиматься плата за токены всей истории диалога при каждом
Run.
Runs
Обратите внимание, что созданный нами Thread не связан с Assistant, который мы создали ранее! Threads существуют независимо от Assistants, что может отличаться от того, что вы ожидаете, если вы использовали ChatGPT (где Thread привязан к модели/GPT).
Чтобы получить completion от Assistant для данного Thread, мы должны создать Run. Создание Run укажет Assistant, что он должен просмотреть messages в Thread и предпринять действие: либо добавив один ответ, либо используя инструменты.
Примечание
Runs— это ключевое отличие между Assistants API и Chat Completions API. В то время как в Chat Completions модель всегда будет отвечать однимmessage, в Assistants APIRunможет привести к тому, чтоAssistantиспользует один или несколько инструментов и потенциально добавит несколькоmessagesвThread.
Чтобы наш Assistant ответил пользователю, давайте создадим Run. Как упоминалось ранее, вы должны указать и Assistant, и Thread.
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id,
)
show_json(run)
{'id': 'run_qVYsWok6OCjHxkajpIrdHuVP',
'assistant_id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'cancelled_at': None,
'completed_at': None,
'created_at': 1736340403,
'expires_at': 1736341003,
'failed_at': None,
'incomplete_details': None,
'incomplete_details': None,
'instructions': 'You are a personal math tutor. Answer questions briefly, in a sentence or less.',
'last_error': None,
'max_completion_tokens': None,
'max_prompt_tokens': None,
'max_completion_tokens': None,
'max_prompt_tokens': None,
'metadata': {},
'model': 'gpt-4o',
'object': 'thread.run',
'parallel_tool_calls': True,
'parallel_tool_calls': True,
'required_action': None,
'response_format': 'auto',
'started_at': None,
'status': 'queued',
'thread_id': 'thread_j4dc1TiHPfkviKUHNi4aAsA6',
'tool_choice': 'auto',
'tools': [],
'truncation_strategy': {'type': 'auto', 'last_messages': None},
'usage': None,
'temperature': 1.0,
'top_p': 1.0,
'tool_resources': {}}
В отличие от создания completion в Chat Completions API, создание Run — это асинхронная операция. Она немедленно вернет метаданные Run, которые включают status, изначально установленный в queued. status будет обновляться по мере выполнения Assistant операций (таких как использование инструментов и добавление messages).
Чтобы узнать, когда Assistant завершил обработку, мы можем опрашивать Run в цикле. (Поддержка streaming появится скоро!) Хотя здесь мы проверяем только status queued или in_progress, на практике Run может претерпевать различные изменения статуса, которые вы можете отображать пользователю. (Они называются Steps и будут рассмотрены позже.)
import time
def wait_on_run(run, thread):
while run.status == "queued" or run.status == "in_progress":
run = client.beta.threads.runs.retrieve(
thread_id=thread.id,
run_id=run.id,
)
time.sleep(0.5)
return run
run = wait_on_run(run, thread)
show_json(run)
{'id': 'run_qVYsWok6OCjHxkajpIrdHuVP',
'assistant_id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'cancelled_at': None,
'completed_at': 1736340406,
'created_at': 1736340403,
'expires_at': None,
'failed_at': None,
'incomplete_details': None,
'incomplete_details': None,
'instructions': 'You are a personal math tutor. Answer questions briefly, in a sentence or less.',
'last_error': None,
'max_completion_tokens': None,
'max_prompt_tokens': None,
'max_completion_tokens': None,
'max_prompt_tokens': None,
'metadata': {},
'model': 'gpt-4o',
'object': 'thread.run',
'parallel_tool_calls': True,
'parallel_tool_calls': True,
'required_action': None,
'response_format': 'auto',
'started_at': 1736340405,
'status': 'completed',
'thread_id': 'thread_j4dc1TiHPfkviKUHNi4aAsA6',
'tool_choice': 'auto',
'tools': [],
'truncation_strategy': {'type': 'auto', 'last_messages': None},
'usage': {'completion_tokens': 35,
'prompt_tokens': 66,
'total_tokens': 101,
'prompt_token_details': {'cached_tokens': 0},
'completion_tokens_details': {'reasoning_tokens': 0}},
'temperature': 1.0,
'top_p': 1.0,
'tool_resources': {}}
Messages
Теперь, когда Run завершился, мы можем вывести список Messages в Thread, чтобы увидеть, что было добавлено Assistant.
messages = client.beta.threads.messages.list(thread_id=thread.id)
show_json(messages)
{'data': [{'id': 'msg_A5eAN6ZAJDmFBOYutEm5DFCy',
'assistant_id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'attachments': [],
'completed_at': None,
'content': [{'text': {'annotations': [],
'value': 'Sure! Subtract 11 from both sides to get \\(3x = 3\\), then divide by 3 to find \\(x = 1\\).'},
'type': 'text'}],
'created_at': 1736340405,
'incomplete_at': None,
'incomplete_details': None,
'metadata': {},
'object': 'thread.message',
'role': 'assistant',
'run_id': 'run_qVYsWok6OCjHxkajpIrdHuVP',
'status': None,
'thread_id': 'thread_j4dc1TiHPfkviKUHNi4aAsA6'},
{'id': 'msg_1q4Y7ZZ9gIcPoAKSx9UtrrKJ',
'assistant_id': None,
'attachments': [],
'completed_at': None,
'attachments': [],
'completed_at': None,
'content': [{'text': {'annotations': [],
'value': 'I need to solve the equation `3x + 11 = 14`. Can you help me?'},
'type': 'text'}],
'created_at': 1736340400,
'incomplete_at': None,
'incomplete_details': None,
'metadata': {},
'object': 'thread.message',
'role': 'user',
'run_id': None,
'status': None,
'thread_id': 'thread_j4dc1TiHPfkviKUHNi4aAsA6'}],
'object': 'list',
'first_id': 'msg_A5eAN6ZAJDmFBOYutEm5DFCy',
'last_id': 'msg_1q4Y7ZZ9gIcPoAKSx9UtrrKJ',
'has_more': False}
Как видите, Messages упорядочены в обратно-хронологическом порядке – это сделано для того, чтобы самые свежие результаты всегда были на первой page (поскольку результаты могут быть разбиты на страницы). Обратите на это внимание, так как это противоположный порядок по сравнению с messages в Chat Completions API.
Давайте попросим нашего Assistant объяснить результат подробнее!
# Create a message to append to our thread
# Создаем сообщение для добавления в наш Thread
message = client.beta.threads.messages.create(
thread_id=thread.id, role="user", content="Could you explain this to me?"
)
# Execute our run
# Выполняем наш Run
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id,
)
# Wait for completion
# Ждем завершения
wait_on_run(run, thread)
# Retrieve all the messages added after our last user message
# Получаем все сообщения, добавленные после нашего последнего пользовательского сообщения
messages = client.beta.threads.messages.list(
thread_id=thread.id, order="asc", after=message.id
)
show_json(messages)
{'data': [{'id': 'msg_wSHHvaMnaWktZWsKs6gyoPUB',
'assistant_id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'attachments': [],
'completed_at': None,
'content': [{'text': {'annotations': [],
'value': 'Certainly! To isolate \\(x\\), first subtract 11 from both sides of the equation \\(3x + 11 = 14\\), resulting in \\(3x = 3\\). Then, divide both sides by 3 to solve for \\(x\\), giving you \\(x = 1\\).'},
'type': 'text'}],
'created_at': 1736340414,
'incomplete_at': None,
'incomplete_details': None,
'metadata': {},
'object': 'thread.message',
'role': 'assistant',
'run_id': 'run_lJsumsDtPTmdG3Enx2CfYrrq',
'status': None,
'thread_id': 'thread_j4dc1TiHPfkviKUHNi4aAsA6'}],
'object': 'list',
'first_id': 'msg_wSHHvaMnaWktZWsKs6gyoPUB',
'last_id': 'msg_wSHHvaMnaWktZWsKs6gyoPUB',
'has_more': False}
Это может показаться множеством шагов для получения ответа, особенно для такого простого примера. Однако вы скоро увидите, как мы можем добавить очень мощную функциональность нашему Assistant, почти не меняя код!
Пример
Давайте посмотрим, как мы могли бы собрать все это вместе. Ниже приведен весь код, необходимый для использования созданного вами Assistant.
Поскольку мы уже создали нашего Math Assistant, я сохранил его ID в MATH_ASSISTANT_ID. Затем я определил две функции:
submit_message: создаетMessageвThread, затем запускает (и возвращает) новыйRunget_response: возвращает списокMessagesвThread
from openai import OpenAI
MATH_ASSISTANT_ID = assistant.id # or a hard-coded ID like "asst-..."
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"))
def submit_message(assistant_id, thread, user_message):
client.beta.threads.messages.create(
thread_id=thread.id, role="user", content=user_message
)
return client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant_id,
)
def get_response(thread):
return client.beta.threads.messages.list(thread_id=thread.id, order="asc")
Я также определил функцию create_thread_and_run, которую могу повторно использовать (которая на самом деле почти идентична составной функции client.beta.threads.create_and_run в нашем API ;)). Наконец, мы можем отправить каждый из наших имитированных пользовательских запросов в новый Thread.
Обратите внимание, что все эти вызовы API являются асинхронными операциями; это означает, что мы фактически получаем асинхронное поведение в нашем коде без использования async libraries! (например, asyncio)
def create_thread_and_run(user_input):
thread = client.beta.threads.create()
run = submit_message(MATH_ASSISTANT_ID, thread, user_input)
return thread, run
# Emulating concurrent user requests
# Эмуляция параллельных пользовательских запросов
thread1, run1 = create_thread_and_run(
"I need to solve the equation `3x + 11 = 14`. Can you help me?"
)
thread2, run2 = create_thread_and_run("Could you explain linear algebra to me?")
thread3, run3 = create_thread_and_run("I don't like math. What can I do?")
# Now all Runs are executing...
# Теперь все Runs выполняются...
Как только все Runs запущены, мы можем дождаться каждого и получить ответы.
import time
# Pretty printing helper
# Вспомогательная функция для красивого вывода
def pretty_print(messages):
print("# Messages")
for m in messages:
print(f"{m.role}: {m.content[0].text.value}")
print()
# Waiting in a loop
# Ожидание в цикле
def wait_on_run(run, thread):
while run.status == "queued" or run.status == "in_progress":
run = client.beta.threads.runs.retrieve(
thread_id=thread.id,
run_id=run.id,
)
time.sleep(0.5)
return run
# Wait for Run 1
# Ждем Run 1
run1 = wait_on_run(run1, thread1)
pretty_print(get_response(thread1))
# Wait for Run 2
# Ждем Run 2
run2 = wait_on_run(run2, thread2)
pretty_print(get_response(thread2))
# Wait for Run 3
# Ждем Run 3
run3 = wait_on_run(run3, thread3)
pretty_print(get_response(thread3))
# Thank our assistant on Thread 3 :)
# Благодарим нашего ассистента в Thread 3 :)
run4 = submit_message(MATH_ASSISTANT_ID, thread3, "Thank you!")
run4 = wait_on_run(run4, thread3)
pretty_print(get_response(thread3))
# Messages
user: I need to solve the equation `3x + 11 = 14`. Can you help me?
assistant: Sure! Subtract 11 from both sides to get \(3x = 3\), then divide by 3 to find \(x = 1\).
# Messages
user: Could you explain linear algebra to me?
assistant: Linear algebra is the branch of mathematics concerning vector spaces, linear transformations, and systems of linear equations, often represented with matrices.
# Messages
user: I don't like math. What can I do?
assistant: Try relating math to real-life interests or hobbies, practice with fun games or apps, and gradually build confidence with easier problems.
# Messages
user: I don't like math. What can I do?
assistant: Try relating math to real-life interests or hobbies, practice with fun games or apps, and gradually build confidence with easier problems.
user: Thank you!
assistant: You're welcome! If you have any more questions, feel free to ask!
Вуаля!
Возможно, вы заметили, что этот код на самом деле не специфичен для нашего Math Assistant... этот код будет работать для любого нового Assistant, который вы создадите, просто изменив Assistant ID! В этом и заключается мощь Assistants API.
Tools
Ключевой особенностью Assistants API является возможность оснащать наших Assistants Tools, такими как Code Interpreter, File Search и пользовательские Functions. Давайте рассмотрим каждый из них.
Code Interpreter
Давайте оснастим нашего Math Tutor инструментом Code Interpreter, что мы можем сделать из Dashboard...

...или через API, используя Assistant ID.
assistant = client.beta.assistants.update(
MATH_ASSISTANT_ID,
tools=[{"type": "code_interpreter"}],
)
show_json(assistant)
{'id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'created_at': 1736340398,
'description': None,
'instructions': 'You are a personal math tutor. Answer questions briefly, in a sentence or less.',
'metadata': {},
'model': 'gpt-4o',
'name': 'Math Tutor',
'object': 'assistant',
'tools': [{'type': 'code_interpreter'}],
'response_format': 'auto',
'temperature': 1.0,
'tool_resources': {'code_interpreter': {'file_ids': []}, 'file_search': None},
'top_p': 1.0} 'tools': [{'type': 'code_interpreter'}],
'response_format': 'auto',
'temperature': 1.0,
'tool_resources': {'code_interpreter': {'file_ids': []}, 'file_search': None},
'top_p': 1.0}
Теперь давайте попросим Assistant использовать свой новый инструмент.
thread, run = create_thread_and_run(
"Generate the first 20 fibbonaci numbers with code."
)
run = wait_on_run(run, thread)
pretty_print(get_response(thread))
# Messages
user: Generate the first 20 fibbonaci numbers with code.
assistant: The first 20 Fibonacci numbers are: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181.
И это все! Assistant использовал Code Interpreter в фоновом режиме и дал нам окончательный ответ.
Для некоторых случаев использования этого может быть достаточно – однако, если мы хотим получить более подробную информацию о том, что именно делает Assistant, мы можем посмотреть на Steps Run.
Steps
Run состоит из одного или нескольких Steps. Как и Run, каждый Step имеет status, который вы можете запросить. Это полезно для отображения прогресса Step пользователю (например, индикатор загрузки, пока Assistant пишет код или выполняет retrieval).
run_steps = client.beta.threads.runs.steps.list(
thread_id=thread.id, run_id=run.id, order="asc"
)
Давайте посмотрим на step_details каждого Step.
for step in run_steps.data:
step_details = step.step_details
print(json.dumps(show_json(step_details), indent=4))
{'tool_calls': [{'id': 'call_E1EE1loDmcWoc7FpkOMKYj6n',
'code_interpreter': {'input': 'def generate_fibonacci(n):\n fib_sequence = [0, 1]\n while len(fib_sequence) < n:\n next_value = fib_sequence[-1] + fib_sequence[-2]\n fib_sequence.append(next_value)\n return fib_sequence\n\n# Generate the first 20 Fibonacci numbers\nfirst_20_fibonacci = generate_fibonacci(20)\nfirst_20_fibonacci',
'outputs': []},
'type': 'code_interpreter'}],
'type': 'tool_calls'}
null
{'message_creation': {'message_id': 'msg_RzTnbBMmzDYHk79a0x9qM5uU'},
'type': 'message_creation'}
null
Мы видим step_details для двух Steps:
tool_calls(множественное число, так как их может быть более одного в одномStep)message_creation
Первый Step — это tool_calls, конкретно использующий code_interpreter, который содержит:
input, который был сгенерированным Python кодом до вызова инструмента, иoutput, который был результатом выполнения Code Interpreter.
Второй Step — это message_creation, который содержит message, добавленное в Thread для передачи результатов пользователю.
File search
Еще одним мощным инструментом в Assistants API является File search. Это позволяет загружать файлы в Assistant для использования в качестве knowledge base при ответе на вопросы.

# Upload the file
# Загружаем файл
file = client.files.create(
file=open(
"data/language_models_are_unsupervised_multitask_learners.pdf",
"rb",
),
purpose="assistants",
)
# Create a vector store
# Создаем vector store
vector_store = client.beta.vector_stores.create(
name="language_models_are_unsupervised_multitask_learners",
)
# Add the file to the vector store
# Добавляем файл в vector store
vector_store_file = client.beta.vector_stores.files.create_and_poll(
vector_store_id=vector_store.id,
file_id=file.id,
)
# Confirm the file was added
# Подтверждаем, что файл был добавлен
while vector_store_file.status == "in_progress":
time.sleep(1)
if vector_store_file.status == "completed":
print("File added to vector store")
elif vector_store_file.status == "failed":
raise Exception("Failed to add file to vector store")
# Update Assistant
# Обновляем Assistant
assistant = client.beta.assistants.update(
MATH_ASSISTANT_ID,
tools=[{"type": "code_interpreter"}, {"type": "file_search"}],
tool_resources={
"file_search":{
"vector_store_ids": [vector_store.id]
},
"code_interpreter": {
"file_ids": [file.id]
}
},
)
show_json(assistant)
File added to vector store
{'id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'created_at': 1736340398,
'description': None,
'instructions': 'You are a personal math tutor. Answer questions briefly, in a sentence or less.',
'metadata': {},
'model': 'gpt-4o',
'name': 'Math Tutor',
'object': 'assistant',
'tools': [{'type': 'code_interpreter'},
{'type': 'file_search',
'file_search': {'max_num_results': None,
'ranking_options': {'score_threshold': 0.0,
'ranker': 'default_2024_08_21'}}}],
'response_format': 'auto',
'temperature': 1.0,
'tool_resources': {'code_interpreter': {'file_ids': ['file-GQFm2i7N8LrAQatefWKEsE']},
'file_search': {'vector_store_ids': ['vs_dEArILZSJh7J799QACi3QhuU']}},
'top_p': 1.0}
thread, run = create_thread_and_run(
"What are some cool math concepts behind this ML paper pdf? Explain in two sentences."
)
run = wait_on_run(run, thread)
pretty_print(get_response(thread))
# Messages
user: What are some cool math concepts behind this ML paper pdf? Explain in two sentences.
assistant: The paper explores the concept of multitask learning where a single model is used to perform various tasks, modeling the conditional distribution \( p(\text{output} | \text{input, task}) \), inspired by probabilistic approaches【6:10†source】. It also discusses the use of Transformer-based architectures and parallel corpus substitution in language models, enhancing their ability to generalize across domain tasks without explicit task-specific supervision【6:2†source】【6:5†source】.
Примечание В File Search есть больше тонкостей, таких как Annotations, которые могут быть рассмотрены в другом cookbook.
# Delete the vector store
# Удаляем vector store
client.beta.vector_stores.delete(vector_store.id)
VectorStoreDeleted(id='vs_dEArILZSJh7J799QACi3QhuU', deleted=True, object='vector_store.deleted')
Functions
В качестве последнего мощного инструмента для вашего Assistant вы можете указать пользовательские Functions (очень похоже на Function Calling в Chat Completions API). Во время Run Assistant может затем указать, что он хочет вызвать одну или несколько указанных вами функций. Затем вы несете ответственность за вызов Function и предоставление output обратно Assistant.
Давайте рассмотрим пример, определив Function display_quiz() для нашего Math Tutor.
Эта функция будет принимать title и массив question, отображать викторину и получать ввод от пользователя для каждого:
titlequestionsquestion_textquestion_type: [MULTIPLE_CHOICE,FREE_RESPONSE]choices: ["choice 1", "choice 2", ...]
Я буду имитировать ответы с помощью get_mock_response.... Здесь вы будете получать фактический ввод пользователя.
def get_mock_response_from_user_multiple_choice():
return "a"
def get_mock_response_from_user_free_response():
return "I don't know."
def display_quiz(title, questions):
print("Quiz:", title)
print()
responses = []
for q in questions:
print(q["question_text"])
response = ""
# If multiple choice, print options
if q["question_type"] == "MULTIPLE_CHOICE":
for i, choice in enumerate(q["choices"]):
print(f"{i}. {choice}")
response = get_mock_response_from_user_multiple_choice()
# Otherwise, just get response
elif q["question_type"] == "FREE_RESPONSE":
response = get_mock_response_from_user_free_response()
responses.append(response)
print()
return responses
Вот как будет выглядеть пример викторины:
responses = display_quiz(
"Sample Quiz",
[
{"question_text": "What is your name?", "question_type": "FREE_RESPONSE"},
{
"question_text": "What is your favorite color?",
"question_type": "MULTIPLE_CHOICE",
"choices": ["Red", "Blue", "Green", "Yellow"],
},
],
)
print("Responses:", responses)
Quiz: Sample Quiz
What is your name?
What is your favorite color?
0. Red
1. Blue
2. Green
3. Yellow
Responses: ["I don't know.", 'a']
Теперь давайте определим интерфейс этой функции в формате JSON, чтобы наш Assistant мог ее вызвать:
function_json = {
"name": "display_quiz",
"description": "Displays a quiz to the student, and returns the student's response. A single quiz can have multiple questions.",
"parameters": {
"type": "object",
"properties": {
"title": {"type": "string"},
"questions": {
"type": "array",
"description": "An array of questions, each with a title and potentially options (if multiple choice).",
"items": {
"type": "object",
"properties": {
"question_text": {"type": "string"},
"question_type": {
"type": "string",
"enum": ["MULTIPLE_CHOICE", "FREE_RESPONSE"]
},
"choices": {"type": "array", "items": {"type": "string"}}
},
"required": ["question_text"]
}
}
},
"required": ["title", "questions"]
}
}
Еще раз, давайте обновим наш Assistant либо через Dashboard, либо через API.

Примечание Вставка JSON функции в Dashboard была немного затруднительной из-за отступов и т. д. Я просто попросил ChatGPT отформатировать мою функцию так же, как один из примеров на Dashboard :).
assistant = client.beta.assistants.update(
MATH_ASSISTANT_ID,
tools=[
{"type": "code_interpreter"},
{"type": "file_search"},
{"type": "function", "function": function_json},
],
)
show_json(assistant)
{'id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'created_at': 1736340398,
'description': None,
'instructions': 'You are a personal math tutor. Answer questions briefly, in a sentence or less.',
'metadata': {},
'model': 'gpt-4o',
'name': 'Math Tutor',
'object': 'assistant',
'tools': [{'type': 'code_interpreter'},
{'type': 'file_search',
'file_search': {'max_num_results': None,
'ranking_options': {'score_threshold': 0.0,
'ranker': 'default_2024_08_21'}}},
{'function': {'name': 'display_quiz',
'description': "Displays a quiz to the student, and returns the student's response. A single quiz can have multiple questions.",
'description': "Displays a quiz to the student, and returns the student's response. A single quiz can have multiple questions.",
'parameters': {'type': 'object',
'properties': {'title': {'type': 'string'},
'questions': {'type': 'array',
'description': 'An array of questions, each with a title and potentially options (if multiple choice).',
'items': {'type': 'object',
'properties': {'question_text': {'type': 'string'},
'question_type': {'type': 'string',
'enum': ['MULTIPLE_CHOICE', 'FREE_RESPONSE']},
'choices': {'type': 'array', 'items': {'type': 'string'}}},
'required': ['question_text']}}},
'required': ['title', 'questions']},
'strict': False},
'type': 'function'}],
'response_format': 'auto',
'temperature': 1.0,
'tool_resources': {'code_interpreter': {'file_ids': ['file-GQFm2i7N8LrAQatefWKEsE']},
'file_search': {'vector_store_ids': []}},
'top_p': 1.0}
И теперь мы просим викторину.
thread, run = create_thread_and_run(
"Make a quiz with 2 questions: One open ended, one multiple choice. Then, give me feedback for the responses."
)
run = wait_on_run(run, thread)
run.status
'requires_action'
Теперь, однако, когда мы проверяем status Run, мы видим requires_action! Давайте посмотрим поближе.
show_json(run)
{'id': 'run_ekMRSI2h35asEzKirRf4BTwZ',
'assistant_id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'cancelled_at': None,
'completed_at': None,
'created_at': 1736341020,
'expires_at': 1736341620,
'failed_at': None,
'incomplete_details': None,
'incomplete_details': None,
'instructions': 'You are a personal math tutor. Answer questions briefly, in a sentence or less.',
'last_error': None,
'max_completion_tokens': None,
'max_prompt_tokens': None,
'max_completion_tokens': None,
'max_prompt_tokens': None,
'metadata': {},
'model': 'gpt-4o',
'object': 'thread.run',
'parallel_tool_calls': True,
'required_action': {'submit_tool_outputs': {'tool_calls': [{'id': 'call_uvJEn0fxM4sgmzek8wahBGLi',
'function': {'arguments': '{"title":"Math Quiz","questions":[{"question_text":"What is the derivative of the function f(x) = 3x^2 + 2x - 5?","question_type":"FREE_RESPONSE"},{"question_text":"What is the value of \\\\( \\\\int_{0}^{1} 2x \\\\, dx \\\\)?","question_type":"MULTIPLE_CHOICE","choices":["0","1","2","3"]}]}',
'name': 'display_quiz'},
'type': 'function'}]},
'type': 'submit_tool_outputs'},
'response_format': 'auto',
'started_at': 1736341022,
'status': 'requires_action',
'thread_id': 'thread_8bK2PXfoeijEHBVEzYuJXt17',
'tool_choice': 'auto',
'tools': [{'type': 'code_interpreter'},
{'type': 'file_search',
'file_search': {'max_num_results': None,
'ranking_options': {'score_threshold': 0.0,
'ranker': 'default_2024_08_21'}}},
{'function': {'name': 'display_quiz',
'description': "Displays a quiz to the student, and returns the student's response. A single quiz can have multiple questions.",
'description': "Displays a quiz to the student, and returns the student's response. A single quiz can have multiple questions.",
'parameters': {'type': 'object',
'properties': {'title': {'type': 'string'},
'questions': {'type': 'array',
'description': 'An array of questions, each with a title and potentially options (if multiple choice).',
'items': {'type': 'object',
'properties': {'question_text': {'type': 'string'},
'question_type': {'type': 'string',
'enum': ['MULTIPLE_CHOICE', 'FREE_RESPONSE']},
'choices': {'type': 'array', 'items': {'type': 'string'}}},
'required': ['question_text']}}},
'required': ['title', 'questions']},
'strict': False},
'type': 'function'}],
'truncation_strategy': {'type': 'auto', 'last_messages': None},
'usage': None,
'temperature': 1.0,
'top_p': 1.0,
'tool_resources': {}} 'strict': False},
'type': 'function'}],
'truncation_strategy': {'type': 'auto', 'last_messages': None},
'usage': None,
'temperature': 1.0,
'top_p': 1.0,
'tool_resources': {}}
Поле required_action указывает, что Tool ожидает, пока мы запустим его и отправим его output обратно Assistant. В частности, это функция display_quiz! Давайте начнем с разбора name и arguments.
Примечание Хотя в данном случае мы знаем, что есть только один вызов
Tool, на практикеAssistantможет выбрать вызов нескольких инструментов.
# Extract single tool call
# Извлекаем единственный вызов инструмента
tool_call = run.required_action.submit_tool_outputs.tool_calls[0]
name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
print("Function Name:", name)
print("Function Arguments:")
arguments
Function Name: display_quiz
Function Arguments:
{'title': 'Math Quiz',
'questions': [{'question_text': 'What is the derivative of the function f(x) = 3x^2 + 2x - 5?',
'question_type': 'FREE_RESPONSE'},
{'question_text': 'What is the value of \\( \\int_{0}^{1} 2x \\, dx \\)?',
'question_type': 'MULTIPLE_CHOICE',
'choices': ['0', '1', '2', '3']}]}
Теперь давайте фактически вызовем нашу функцию display_quiz с аргументами, предоставленными Assistant:
responses = display_quiz(arguments["title"], arguments["questions"])
print("Responses:", responses)
Quiz: Math Quiz
Quiz: Math Quiz
What is the derivative of the function f(x) = 3x^2 + 2x - 5?
What is the value of \( \int_{0}^{1} 2x \, dx \)?
0. 0
1. 1
2. 2
3. 3
Responses: ["I don't know.", 'a']
Отлично! (Помните, что эти ответы — те, которые мы имитировали ранее. В реальности мы бы получали ввод от пользователя из этого вызова функции.)
Теперь, когда у нас есть ответы, давайте отправим их обратно Assistant. Нам понадобится ID tool_call, найденный в tool_call, который мы разобрали ранее. Нам также нужно будет закодировать наш list ответов в str.
run = client.beta.threads.runs.submit_tool_outputs(
thread_id=thread.id,
run_id=run.id,
tool_outputs=tool_outputs
)
show_json(run)
{'id': 'run_ekMRSI2h35asEzKirRf4BTwZ',
'assistant_id': 'asst_qvXmYlZV8zhABI2RtPzDfV6z',
'cancelled_at': None,
'completed_at': None,
'created_at': 1736341020,
'expires_at': 1736341620,
'failed_at': None,
'incomplete_details': None,
'incomplete_details': None,
'instructions': 'You are a personal math tutor. Answer questions briefly, in a sentence or less.',
'last_error': None,
'max_completion_tokens': None,
'max_prompt_tokens': None,
'max_completion_tokens': None,
'max_prompt_tokens': None,
'metadata': {},
'model': 'gpt-4o',
'object': 'thread.run',
'parallel_tool_calls': True,
'parallel_tool_calls': True,
'required_action': None,
'response_format': 'auto',
'started_at': 1736341022,
'status': 'queued',
'thread_id': 'thread_8bK2PXfoeijEHBVEzYuJXt17',
'tool_choice': 'auto',
'tools': [{'type': 'code_interpreter'},
{'type': 'file_search',
'file_search': {'max_num_results': None,
'ranking_options': {'score_threshold': 0.0,
'ranker': 'default_2024_08_21'}}},
{'function': {'name': 'display_quiz',
'description': "Displays a quiz to the student, and returns the student's response. A single quiz can have multiple questions.",
'description': "Displays a quiz to the student, and returns the student's response. A single quiz can have multiple questions.",
'parameters': {'type': 'object',
'properties': {'title': {'type': 'string'},
'questions': {'type': 'array',
'description': 'An array of questions, each with a title and potentially options (if multiple choice).',
'items': {'type': 'object',
'properties': {'question_text': {'type': 'string'},
'question_type': {'type': 'string',
'enum': ['MULTIPLE_CHOICE', 'FREE_RESPONSE']},
'choices': {'type': 'array', 'items': {'type': 'string'}}},
'required': ['question_text']}}},
'required': ['title', 'questions']},
'strict': False},
'type': 'function'}],
'truncation_strategy': {'type': 'auto', 'last_messages': None},
'usage': None,
'temperature': 1.0,
'top_p': 1.0,
'tool_resources': {}} 'strict': False},
'type': 'function'}],
'truncation_strategy': {'type': 'auto', 'last_messages': None},
'usage': None,
'temperature': 1.0,
'top_p': 1.0,
'tool_resources': {}}
Теперь мы можем снова дождаться завершения Run и проверить наш Thread!
run = wait_on_run(run, thread)
pretty_print(get_response(thread))
# Messages
user: Make a quiz with 2 questions: One open ended, one multiple choice. Then, give me feedback for the responses.
assistant: Since no specific information was found in the uploaded file, I'll create a general math quiz for you:
1. **Open-ended Question**: What is the derivative of the function \( f(x) = 3x^2 + 2x - 5 \)?
2. **Multiple Choice Question**: What is the value of \( \int_{0}^{1} 2x \, dx \)?
- A) 0
- B) 1
- C) 2
- D) 3
I will now present the quiz to you for response.
assistant: Here is the feedback for your responses:
1. **Derivative Question**:
- Your Response: "I don't know."
- Feedback: The derivative of \( f(x) = 3x^2 + 2x - 5 \) is \( f'(x) = 6x + 2 \).
2. **Integration Question**:
- Your Response: A) 0
- Feedback: The correct answer is B) 1. The integration \(\int_{0}^{1} 2x \, dx \) evaluates to 1.
Ура 🎉
Заключение
Мы проделали большую работу в этом notebook, похвалите себя! Надеемся, теперь у вас есть прочная основа для создания мощных интерактивных возможностей с сохранением состояния с помощью таких инструментов, как Code Interpreter, Retrieval и Functions!
Мы не рассмотрели несколько разделов ради краткости, поэтому вот несколько ресурсов для дальнейшего изучения:
- Annotations: разбор цитат из файлов
- Files: область видимости
Threadпротив области видимостиAssistant - Parallel Function Calls: вызов нескольких инструментов за один
Step - Multi-Assistant Thread Runs: один
ThreadсMessagesот несколькихAssistants - Streaming: скоро!
А теперь идите и создайте что-нибудь потрясающее!