The OpenAI Assistants API provides built-in thread management, file search, and code interpreter — removing the plumbing work from agentic applications. This guide covers creating an assistant, managing threads, and using built-in tools.
Building agents with the raw Chat Completions API requires you to manage conversation history, implement tool loops, handle file uploads, and maintain state between requests. The Assistants API handles all of this server-side: threads store conversation history, runs manage the agentic loop, and built-in tools (file search, code interpreter) require no implementation on your end.
The trade-off is less flexibility and slightly higher cost — OpenAI charges for thread storage and tool use. For production applications, the reduced engineering complexity often justifies the cost.
Create an assistant with tools and model: `assistant = client.beta.assistants.create(name='Data Analyst', instructions='You are a data analyst. When given a CSV file, you analyse it and provide statistical summaries and insights.', tools=[{"type": "code_interpreter"}, {"type": "file_search"}], model='gpt-4o')`. Store the `assistant.id` — you will reuse this assistant across many user sessions.
The `instructions` field is the persistent system prompt. Unlike a regular system prompt, it can reference the assistant's tools and the context they provide. Keep it focused: describe what the assistant does, not generic 'be helpful' platitudes.
Create a thread per user session: `thread = client.beta.threads.create()`. Add messages to the thread: `client.beta.threads.messages.create(thread_id=thread.id, role='user', content='Analyse the attached file and tell me the top 5 products by revenue.')`. Threads persist across sessions — store the `thread.id` in your database tied to the user.
Attach files to messages by uploading them first: `file = client.files.create(file=open('sales.csv', 'rb'), purpose='assistants')`. Then reference the file ID in the message content: `content=[{'type': 'text', 'text': 'Analyse this file'}, {'type': 'image_file', 'image_file': {'file_id': file.id}}]`.
Start the assistant working on the thread: `run = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant.id)`. Poll for completion: `while run.status in ['queued', 'in_progress']: time.sleep(1); run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)`.
For production, use the streaming API instead of polling: `with client.beta.threads.runs.stream(thread_id=thread.id, assistant_id=assistant.id) as stream: for text in stream.text_deltas: print(text, end='', flush=True)`. This is faster and avoids the polling overhead.
After the run completes, list messages: `messages = client.beta.threads.messages.list(thread_id=thread.id, order='desc', limit=1)`. The latest message from the assistant contains the response. Access the text: `response_text = messages.data[0].content[0].text.value`.
If the run used code interpreter, the messages may include image outputs (plots, charts). Check for `type == 'image_file'` in the content blocks and download them: `client.files.content(file_id).read()` returns the image bytes.
Hermes is NousResearch's open-source model family trained specifically for function calling and agentic workflows. This guide covers running Hermes locally, defining tools in JSON Schema, and building a basic tool-calling loop.
Read guideReAct (Reason + Act) is the foundational pattern for LLM agents. This guide builds a working ReAct agent from scratch using the OpenAI API and function calling, with a web search and calculator tool.
Read guide