Create an Assistant to Discuss a Specific Document
Idea and Step-by-Step Plan
Today, weâre going to create an AI Assistant that helps users engage with the content of a particular document. This Assistant can answer questions about the text, explain specific sections, find relevant parts, and even participate in discussions â for example, by offering arguments, clarifying ambiguous points, or helping formulate conclusions. It's especially useful when working with technical documentation, legal texts, research papers, or project documents.
The following features need to be implemented:
Core Assistant functionality (ability to communicate with the user and respond accurately to questions using Chat Completion capability).
Document upload (TXT).
Streaming mode.
You can read the step-by-step explanation below or jump straight to the ready-to-use Python code at the bottom of this page.
Make sure you have your AIMLAPI key!
Step-by-Step Explanation
1. Preparing Input File
As input, we used a .txt file with the following content and placed it in the same directory as our Python script. For testing, we created a simple file with recipes for three different dishes.
Input Text File (recipes.txt)
1. Sun-Dried Tomato & Garlic Pasta
Prep Time: 25 minutes
Servings: 2
Ingredients:
⢠200g spaghetti
⢠6â8 sun-dried tomatoes in oil
⢠2 garlic cloves
⢠Olive oil â 2 tbsp
⢠Salt â to taste
⢠Black pepper â to taste
⢠Fresh basil (optional)
Required Kitchen Tools:
⢠Large pot
⢠Frying pan
⢠Strainer
⢠Cutting board & knife
⢠Wooden spoon
Instructions:
1. Boil a large pot of salted water and cook the spaghetti according to package instructions.
2. While the pasta cooks, finely chop the garlic and sun-dried tomatoes.
3. In a frying pan, heat olive oil over medium heat. Add garlic and cook for 30 seconds until fragrant.
4. Add sun-dried tomatoes and stir for 2â3 minutes.
5. Drain the pasta and toss it into the pan with the tomato-garlic mixture.
6. Mix well, season with salt and pepper, and garnish with fresh basil if desired.
7. Serve hot.
2. Chickpea & Avocado Salad
Prep Time: 15 minutes
Servings: 2
Ingredients:
⢠1 can of chickpeas (400g), drained and rinsed
⢠1 ripe avocado, diced
⢠1 small red onion, finely chopped
⢠Juice of 1 lemon
⢠Olive oil â 1 tbsp
⢠Salt & pepper â to taste
⢠Fresh parsley (optional)
Required Kitchen Tools:
⢠Mixing bowl
⢠Cutting board & knife
⢠Fork or spoon
⢠Citrus squeezer (optional)
Instructions:
1. In a bowl, combine chickpeas, diced avocado, and chopped red onion.
2. Squeeze in lemon juice and drizzle with olive oil.
3. Season with salt and pepper.
4. Toss everything gently to mix, trying not to mash the avocado.
5. Top with chopped parsley if desired.
6. Serve immediately or chill for 10 minutes.
3. Quick Oatmeal Banana Cookies
Prep Time: 10 minutes
Bake Time: 15 minutes
Servings: ~12 cookies
Ingredients:
⢠2 ripe bananas
⢠1 cup rolled oats
⢠1/4 cup chocolate chips or chopped nuts (optional)
⢠1/2 tsp cinnamon (optional)
Required Kitchen Tools:
⢠Mixing bowl
⢠Fork or potato masher
⢠Baking tray
⢠Parchment paper
⢠Oven
Instructions:
1. Preheat oven to 180°C (350°F). Line a baking tray with parchment paper.
2. In a bowl, mash the bananas until smooth.
3. Mix in oats and any add-ins like chocolate chips or cinnamon.
4. Scoop spoonfuls of the mixture onto the tray and flatten slightly.
5. Bake for 12â15 minutes until edges are golden.
6. Let cool for a few minutes before serving.
2. Core assistant functionality
Below, in the expandable sections, you can see a still quite basic example of creating a working Assistant, and a little further down, an example of a conversation with it in the console. To exit, you need to type exit or quit. Please note: this example is written without using the streaming mode, which means the Assistant does not provide an answer word by word, but first forms it completely, and then the entire response appears in the console at once.
Simple Example with the Core Assistant Functionality
import openai
from openai import OpenAI
# Connect to OpenAI API
client = OpenAI(
api_key="<YOUR_AIMLAPI_KEY>",
base_url="https://api.aimlapi.com/"
)
# Create an assistant
my_assistant = client.beta.assistants.create(
instructions="You are a helpful assistant.",
name="AI Assistant",
model="gpt-4o", # Specify the model
)
assistant_id = my_assistant.id # Store assistant ID
thread = client.beta.threads.create() # Create a new thread
thread_id = thread.id # Store the thread ID
def initial_request():
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="Hi! Let's chat!",
)
def send_message(user_message):
"""Send a message to the assistant and receive a full response"""
if not user_message.strip():
print("â ď¸ Message cannot be empty!")
return
# Add the user's message to the thread
client.beta.threads.messages.create(
thread_id=thread_id,
role="user",
content=user_message
)
# Start a new run and wait for completion
run = client.beta.threads.runs.create_and_poll(
thread_id=thread_id,
assistant_id=assistant_id,
instructions="Keep responses concise and clear."
)
# Check if the run was successful
if run.status == "completed":
# Retrieve messages from the thread
messages = client.beta.threads.messages.list(thread_id=thread_id)
# Find the last assistant message
for message in reversed(messages.data):
if message.role == "assistant":
print() # Add an empty line for spacing
print(f"assistant > {message.content[0].text.value}")
return
print("â ď¸ Error: Failed to get a response from the assistant.")
# Main chat loop
initial_request()
print("đ¤ AI Assistant is ready! Type 'exit' to quit.")
while True:
user_input = input("\nYou > ")
if user_input.lower() in ["exit", "quit"]:
print("đ Chat session ended. See you next time!")
break
send_message(user_input)
Interaction Example
đ¤ AI Assistant is ready! Type 'exit' to quit.
You > Hi! What could we discuss today?
assistant > Hi there! We could chat about a wide range of topics. Here are a few options:
Current events or news updates.
Technology advancements.
Book or movie recommendations.
Travel destinations.
Hobbies or personal interests.
Let me know what youâre interested in!
You > Cool! Okay, maybe next time! Bye!
assistant > Goodbye! If you have more questions in the future, feel free to ask. Have a great day! đ
You > exit
đ Chat session ended. See you next time!
3. Let's Add a File to Discuss!
Since we want to immediately start discussing the file contents with the Assistant upon launch, we need to pass it to the Assistant in advance, directly in the code.
First, we will open our .txt file using Pythonâs built-in mechanism and pass the file ID in the first user message created directly from the code. The text of this initial message will be set as follows: "Here's my .txt file â extract the text, read through it, and let me know when you're ready to start answering my questions about this document."
Creating the first message in code with attaching the file
# First message with file attachment
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="Here's my .txt file â extract the text, read through it, and let me know when you're ready to start answering my questions about this document.",
attachments=[
{
"file_id": txt_id,
"tools": [{"type": "file_search"}]
}
]
)
4. Add Streaming Mode
For a more dynamic interaction, the established practice when communicating with online AI chats is now streaming mode, where the model's response appears on the user's screen word by word as it is being formed. Let's add this feature to our Assistant as well.
Explanation
How to handle events
To do this, we will use the pre-built EventHandler class from the AssistantEventHandler library.
from openai import AssistantEventHandler
Creating the handler:
# Custom event handler to stream assistant responses
class EventHandler(AssistantEventHandler):
def on_text_created(self, text):
print("\nassistant >", end="", flush=True)
def on_text_delta(self, delta, snapshot):
print(delta.value, end="", flush=True)
def on_tool_call_created(self, tool_call):
print(f"\nassistant > {tool_call.type}\n", flush=True)
def on_tool_call_delta(self, delta, snapshot):
if delta.type == 'file_search':
if delta.file_search.input:
print(delta.file_search.input, end="", flush=True)
if delta.file_search.outputs:
print(f"\n\noutput >", flush=True)
for output in delta.file_search.outputs:
if output.type == "logs":
print(f"\n{output.logs}", flush=True)
What events are handled
on_text_created(self, text)
Triggered when the Assistant creates a text response. The code simply prints assistant > to indicate the beginning of the output.
on_text_delta(self, delta, snapshot)
Triggered when new parts of text (tokens) arrive. The code prints each new word or letter to the console without a newline (end=""), creating the effect of the text appearing gradually.
on_tool_call_created(self, tool_call)
Triggered if the Assistant decides to use one of the tools (e.g., Code Interpreter or external APIs). The code simply prints the type of the invoked tool.
on_tool_call_delta(self, delta, snapshot)
Triggered when the assistant sends data to a tool or receives a result from it.
How it works
When the Assistant starts forming a response, it triggers on_text_created.
Then, as tokens are generated, on_text_delta is triggered, updating the text in real time.
If a tool is used in the response, on_tool_call_created is triggered, followed by on_tool_call_delta to show the process of the tool handling the data.
In the next section, you will find the ready-made code for creating an Assistant, passing it an input file, and interacting with it in streaming mode. At the end of the page, you will also find the listing of our conversation with this Assistant.
Full Code Example
Code
import openai
from openai import OpenAI
from openai import AssistantEventHandler
client = openai.OpenAI(
base_url="https://api.aimlapi.com/",
# Replace with your AIMLAPI key
api_key="<YOUR_AIMLAPI_KEY>"
)
# Custom event handler to stream assistant responses
class EventHandler(AssistantEventHandler):
def on_text_created(self, text):
print("\nassistant >", end="", flush=True)
def on_text_delta(self, delta, snapshot):
print(delta.value, end="", flush=True)
def on_tool_call_created(self, tool_call):
print(f"\nassistant > {tool_call.type}\n", flush=True)
def on_tool_call_delta(self, delta, snapshot):
if delta.type == 'file_search':
if delta.file_search.input:
print(delta.file_search.input, end="", flush=True)
if delta.file_search.outputs:
print(f"\n\noutput >", flush=True)
for output in delta.file_search.outputs:
if output.type == "logs":
print(f"\n{output.logs}", flush=True)
# Upload the TXT file
file = client.files.create(
file=open("recipes.txt", "rb"),
purpose="assistants"
)
txt_id = file.id
# Create the assistant with appropriate instructions
assistant = client.beta.assistants.create(
name="Recipe Assistant",
instructions="You assist with questions based on the information from the file that was uploaded to you.",
tools=[{"type": "file_search"}],
model="gpt-4o" # Specify the model
)
# Create a thread for the conversation
thread = client.beta.threads.create()
# First message with file attachment
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="Here's my .txt file â extract the text, read through it, and let me know when you're ready to start answering my questions about this document.",
attachments=[
{
"file_id": txt_id,
"tools": [{"type": "file_search"}]
}
]
)
# Start the initial run and stream the response
with client.beta.threads.runs.stream(
thread_id=thread.id,
assistant_id=assistant.id,
event_handler=EventHandler()
) as stream:
stream.until_done()
# Start the interactive loop for further conversation
while True:
user_input = input("\nYou > ")
if user_input.lower() in ["exit", "quit"]:
break
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content=user_input
)
with client.beta.threads.runs.stream(
thread_id=thread.id,
assistant_id=assistant.id,
event_handler=EventHandler()
) as stream:
stream.until_done()
Conversation Example
assistant > file_search
assistant >I've accessed the file you uploaded and am ready to assist you with any questions you have about its content. Please feel free to ask!
You > Glad to hear that! Could you briefly summarize what kind of information is contained in this document?
assistant > file_search
assistant >The document you uploaded contains recipes for three different dishes. Here's a brief summary of each:
1. **Sun-Dried Tomato & Garlic Pasta**: This recipe involves cooking spaghetti and then mixing it with a sautĂŠed mixture of garlic and sun-dried tomatoes. It also includes optional garnishing with fresh basil.
2. **Chickpea & Avocado Salad**: This is a quick salad recipe that combines chickpeas, avocado, and red onion, dressed with lemon juice and olive oil. Optional parsley can be added for garnish.
3. **Quick Oatmeal Banana Cookies**: A simple cookie recipe that uses ingredients like ripe bananas and rolled oats, with optional additions like chocolate chips or nuts. The cookies are baked until golden brownă9:0â recipes.txtă.
Feel free to ask any further questions you may have about this document!
You > What's the fastest dish to make?
assistant >The fastest dish to make from the document is the **Chickpea & Avocado Salad**. It requires no cooking and is primarily a matter of combining ingredients like chickpeas, avocado, red onion, lemon juice, and olive oil. The preparation time
is minimal, especially if you have canned chickpeas which just need to be rinsedă9:0â recipes.txtă.
You > Thanx! is there smth else vegan?
assistant >Yes, aside from the Chickpea & Avocado Salad, the **Sun-Dried Tomato & Garlic Pasta** is also vegan. It consists of simple ingredients like spaghetti, garlic, and sun-dried tomatoes, with optional basil for garnishingă9:0â recipes.txtă.
You > Awesome, I'm off to the store to get some garlic! By the way, what do your notes mean? like "ă9:0â recipes.txtă"
assistant >The notes like "ă9:0â recipes.txtă" are citations that refer back to the specific part of the document where the
information was found. The numbers indicate:
- `9`: The index of the message or query where this information was accessed.
- `0`: The specific result in the search query result list.
- `recipes.txt`: The name of the document from which the information was extracted.
These citations help keep track of sources for extracted information.
You > Cool, you're honestly really easy to work with! Thanks and see you soon!
assistant >Thank you for your kind words! I'm glad I could help. Don't hesitate to return if you have more questions in the future. Have a great time cooking and see you soon!
You > exit
Copy the code, insert your AIMLAPI key, specify the path to your document in the code, and give it a try yourself!
Assistants are a highly advanced way of working with chat models. If you have never worked with OpenAI Assistants before, we recommend reviewing the key concepts and structure of how Assistants operate in the .