Partager via


Outil de recherche de fichiers des Assistants Azure OpenAI (préversion)

La recherche de fichiers enrichit l’Assistant de connaissances absentes de son modèle, telles que les informations produit privées ou les documents fournis par vos utilisateurs. OpenAI analyse et segmente automatiquement vos documents, crée et stocke les incorporations, et utilise la recherche de vecteurs et de mots clés pour récupérer du contenu pertinent pour répondre aux requêtes utilisateur.

Important

  • La recherche de fichiers comporte des frais supplémentaires au-delà des frais basés sur le jeton pour l’utilisation d’Azure OpenAI.

Remarque

  • La recherche de fichiers peut ingérer jusqu’à 10 000 fichiers par assistant, soit 500 fois plus qu’auparavant. Elle est rapide, prend en charge les requêtes parallèles par le biais de recherches multithread, et propose des fonctionnalités améliorées de reclassement et de réécriture des requêtes.
    • Le magasin de vecteurs est un nouvel objet dans l’API. Une fois qu’un fichier est ajouté à un magasin vectoriel, il est automatiquement analysé, découpé en morceaux et incorporé, prêt à être recherché. Les magasins vectoriels peuvent être utilisés par tous les assistants et threads, ce qui simplifie la gestion des fichiers et la facturation.
  • Nous avons ajouté la prise en charge du paramètre tool_choice qui peut être utilisé pour forcer l’utilisation d’un outil spécifique (comme la recherche de fichiers, l’interpréteur de code ou une fonction) dans une exécution particulière.

Prise en charge de la recherche de fichiers

Régions prises en charge

La recherche de fichiers est disponible dans les régions qui prennent en charge les Assistants.

Version d'API

  • 2024-05-01-preview

Types de fichiers pris en charge

Remarque

Pour les types texte/MIME, le codage doit être utf-8, utf-16 ou ASCII.

Format de fichier Type MIME
c. text/x-c
.cs text/x-csharp
.cpp text/x-c++
.doc application/msword
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
.html text/html
.java text/x-java
.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
.tex text/x-tex
.txt text/plain
.css texte/css
.js text/javascript
.sh application/x-sh
.ts application/typescript
from openai import AzureOpenAI
    
client = AzureOpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
    api_version="2024-05-01-preview",
    azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
    )

assistant = client.beta.assistants.create(
  name="Financial Analyst Assistant",
  instructions="You are an expert financial analyst. Use your knowledge base to answer questions about audited financial statements.",
  model="gpt-4-turbo",
  tools=[{"type": "file_search"}],
)

Pour accéder à vos fichiers, l’outil de recherche de fichiers utilise l’objet de magasin de vecteurs. Chargez vos fichiers et créez un magasin de vecteurs pour les contenir. Une fois le magasin de vecteurs créé, vous devez interroger son état jusqu’à ce que tous les fichiers soient hors de l’état in_progress pour vous assurer que tout le contenu a été traité. Le kit de développement logiciel (SDK) fournit une assistance pour le chargement et l’interrogation.

from openai import AzureOpenAI
    
client = AzureOpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
    api_version="2024-05-01-preview",
    azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
    )

# Create a vector store called "Financial Statements"
vector_store = client.beta.vector_stores.create(name="Financial Statements")
 
# Ready the files for upload to OpenAI
file_paths = ["mydirectory/myfile1.pdf", "mydirectory/myfile2.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.beta.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)

Mettre à jour l’Assistant pour utiliser le nouveau magasin de vecteurs

Pour rendre les fichiers accessibles à votre assistant, mettez à jour l’assistant tool_resources avec le nouvel ID vector_store.

assistant = client.beta.assistants.update(
  assistant_id=assistant.id,
  tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}},
)

Créer un thread

Vous pouvez également joindre des fichiers en tant que pièces jointes de message sur votre thread. Cela crée un autre vector_store associé au thread ou, s’il existe déjà un magasin de vecteurs attaché à ce thread, attache les nouveaux fichiers au magasin de vecteurs de thread existant. Lorsque vous créez une exécution sur ce thread, l’outil de recherche de fichiers interroge à la fois le vector_store de votre assistant et le vector_store du thread.

# Upload the user provided file to OpenAI
message_file = client.files.create(
  file=open("mydirectory/myfile.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 company shares were outstanding last quarter?",
      # 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)

Les magasins de vecteurs sont créés à l’aide de pièces jointes de message qui ont une stratégie d’expiration par défaut de sept jours après leur dernière activité (définie comme la dernière fois que le magasin de vecteurs faisait partie d’une exécution). Cette valeur par défaut vous permet de gérer vos coûts de stockage de vecteurs. Vous pouvez remplacer ces stratégies d’expiration à tout moment.

Créer une exécution et vérifier la sortie

Créez une exécution et observez que le modèle utilise l’outil de recherche de fichiers pour fournir une réponse à la question de l’utilisateur.

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()

Fonctionnement

L’outil de recherche de fichiers implémente plusieurs meilleures pratiques de récupération de façon native pour vous aider à extraire les données appropriées de vos fichiers et à enrichir les réponses du modèle. L’outil file_search :

  • Réécrit les requêtes utilisateur pour les optimiser pour la recherche.
  • Décompose les requêtes utilisateur complexes en plusieurs recherches qu’il peut exécuter en parallèle.
  • Exécute à la fois des recherches de mots clés et sémantiques dans les magasins de vecteurs d’assistant et de thread.
  • Reclasse les résultats de recherche pour sélectionner les plus pertinents avant de générer la réponse finale.
  • Par défaut, l’outil de recherche de fichiers utilise les paramètres suivants :
    • Taille de bloc : 800 jetons
    • Chevauchement de bloc : 400 jetons
    • Modèle d’incorporation : text-embedding-3-large à 256 dimensions
    • Nombre maximal de blocs ajoutés au contexte : 20

Magasins de vecteurs

Les objets de magasin de vecteurs donnent à l’outil de recherche de fichiers la possibilité de rechercher vos fichiers. L’ajout d’un fichier à un magasin de vecteurs analyse automatiquement, segmente, incorpore, et stocke le fichier dans une base de données de vecteurs capable d’effectuer des recherches de mots clés et sémantiques. Chaque magasin de vecteurs peut contenir jusqu’à 10 000 fichiers. Les magasins de vecteurs peuvent être attachés à la fois aux assistants et aux threads. Actuellement, vous pouvez attacher au plus un magasin de vecteurs à un assistant et au plus un magasin de vecteurs à un thread.

Création de magasins de vecteurs et ajout de fichiers

Vous pouvez créer un magasin de vecteurs et y ajouter des fichiers dans un seul appel d’API :

vector_store = client.beta.vector_stores.create(
  name="Product Documentation",
  file_ids=['file_1', 'file_2', 'file_3', 'file_4', 'file_5']
)

L’ajout de fichiers à des magasins de vecteurs est une opération asynchrone. Pour vous assurer que l’opération est terminée, nous vous recommandons d’utiliser les assistances « créer et interroger » dans nos kits SDK officiels. Si vous n’utilisez pas les kits SDK, vous pouvez récupérer l’objet vector_store et superviser sa propriété file_counts pour voir le résultat de l’opération d’ingestion de fichier.

Vous pouvez également ajouter des fichiers à un magasin de vecteurs après sa création en créant des fichiers de magasin de vecteurs.

file = client.beta.vector_stores.files.create_and_poll(
  vector_store_id="vs_abc123",
  file_id="file-abc123"
)

Vous pouvez également ajouter plusieurs fichiers à un magasin de vecteurs en créant des lots allant jusqu’à 500 fichiers.

batch = client.beta.vector_stores.file_batches.create_and_poll(
  vector_store_id="vs_abc123",
  file_ids=['file_1', 'file_2', 'file_3', 'file_4', 'file_5']
)

De même, vous pouvez supprimer ces fichiers d’un magasin de vecteurs en procédant comme suit :

  • En supprimant l’objet de fichier de magasin de vecteurs ou,
  • En supprimant l’objet de fichier sous-jacent (qui supprime le fichier de toutes les configurations vector_store et code_interpreter sur tous les assistants et threads de votre organisation)

La taille de fichier maximale est de 512 Mo. Chaque fichier ne doit pas contenir plus de 5 000 000 jetons par fichier (calculé automatiquement lorsque vous joignez un fichier).

Attachement de magasins de vecteurs

Vous pouvez attacher des magasins de vecteurs à votre Assistant ou Thread à l’aide du paramètre tool_resources.

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-4-turbo",
  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"]
    }
  }
)

Vous pouvez également attacher un magasin de vecteurs aux Threads ou Assistants une fois qu’ils ont été créés en les mettant à jour avec le tool_resources adapté.

Garantir la préparation du magasin de vecteurs avant la création d’exécutions

Nous vous recommandons vivement de vous assurer que tous les fichiers d’un vector_store sont entièrement traités avant de créer une exécution. Cela garantit que toutes les données de votre magasin de vecteurs peuvent faire l’objet d’une recherche. Vous pouvez vérifier la préparation du magasin de vecteurs à l’aide des assistances d’interrogation dans les kits SDK, ou en interrogeant manuellement l’objet pour vous assurer que l’état vector_store est terminé.

En guise de solution de secours, il existe une attente maximale de 60 secondes dans l’objet d’exécution lorsque le magasin de vecteurs du thread contient des fichiers qui sont encore en cours de traitement. Cela permet de s’assurer que tous les fichiers que vos utilisateurs chargent dans un thread peuvent faire l’objet d’une recherche complète avant la poursuite de l’exécution. Cette solution de secours ne s’applique pas au magasin de vecteurs de l’Assistant.

Gestion des coûts avec des stratégies d’expiration

L’outil file_search utilise l’objet vector_stores comme ressource et vous serez facturé en fonction de la taille des objets vector_store créés. La taille de l’objet de magasin de vecteurs est la somme de tous les blocs analysés de vos fichiers et de leurs incorporations correspondantes.

Pour vous aider à gérer les coûts associés à ces objets vector_store, nous avons ajouté la prise en charge des stratégies d’expiration dans l’objet vector_store. Vous pouvez définir ces stratégies lors de la création ou de la mise à jour de l’objet vector_store.

vector_store = client.beta.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
  }
)

Les magasins de vecteurs de thread ont des stratégies d’expiration par défaut

Les magasins de vecteurs créés à l’aide d’assistances de threads (comme tool_resources.file_search.vector_stores dans les Threads ou message.attachments dans les Messages) ont une stratégie d’expiration par défaut de sept jours après leur dernière activité (définie comme la dernière fois que le magasin de vecteurs faisait partie d’une exécution).

Lorsqu’un magasin de vecteurs expire, les exécutions sur ce thread échouent. Pour résoudre ce problème, vous pouvez recréer un vector_store avec les mêmes fichiers et le rattacher au thread.

all_files = list(client.beta.vector_stores.files.list("vs_expired"))

vector_store = client.beta.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.beta.vector_stores.file_batches.create_and_poll(
        vector_store_id=vector_store.id, file_ids=[file.id for file in file_batch]
    )