Herramienta de búsqueda de archivos de Azure OpenAI Assistants (versión preliminar)

La búsqueda de archivos aumenta el Asistente con conocimientos desde fuera de su modelo, como la información de producto propietaria o los documentos proporcionados por los usuarios. OpenAI analiza y fragmenta automáticamente los documentos, crea y almacena las incrustaciones y usa la búsqueda de vectores y palabras clave para recuperar contenido relevante para responder a las consultas del usuario.

Importante

  • La búsqueda de archivos cargos adicionales más allá de las tarifas basadas en tokens para el uso de Azure OpenAI.

Nota:

  • La búsqueda de archivos puede ingerir hasta 10 000 archivos por asistente, 500 veces más que antes. Es rápida, compatible con consultas paralelas a través de búsquedas multiproceso y presenta características mejoradas de reordenación y reescritura de consultas.
    • El almacén de vectores es un nuevo objeto de la API. Una vez que un archivo se agrega a un almacén de vectores, se analiza automáticamente, se divide en fragmentos y se inserta, quedando listo para su búsqueda. Los almacenes de vectores se pueden usar entre asistentes y subprocesos, lo que simplifica la administración de archivos y la facturación.
  • Hemos agregado compatibilidad con el parámetro tool_choice que se puede usar para forzar el uso de una herramienta específica (como la búsqueda de archivos, el intérprete de código o una función) en una ejecución determinada.

Compatibilidad con la búsqueda de archivos

Regiones admitidas

La búsqueda de archivos está disponible en regiones que admiten asistentes.

Versión de la API

  • 2024-05-01-preview

Tipos de archivo admitidos

Nota:

Para los tipos text/MIME, la codificación debe ser utf-8, utf-16 o ASCII.

Formato de archivo Tipo 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 text/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"}],
)

Para acceder a los archivos, la herramienta de búsqueda de archivos usa el objeto de almacén vectorial. Cargue los archivos y cree un almacén de vectores para contenerlos. Una vez creado el almacén de vectores, debe sondear su estado hasta que todos los archivos estén fuera del estado in_progress para asegurarse de que todo el contenido ha terminado de procesarse. El SDK proporciona asistentes para cargar y sondear.

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)

Actualizar el asistente para usar el nuevo almacén de vectores

Para que los archivos sean accesibles para el asistente, actualice el asistente tool_resources con el nuevo identificador vector_store.

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

Creación de un subproceso

También puede adjuntar archivos como datos adjuntos de mensajes en el subproceso. Al hacerlo, se creará otro vector_store asociado al subproceso o, si ya hay un almacén de vectores asociado a este subproceso, adjunte los nuevos archivos al almacén de vectores de subproceso existente. Al crear una ejecución en este subproceso, la herramienta de búsqueda de archivos consultará el vector_store desde el asistente y el vector_store en el subproceso.

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

Los almacenes de vectores se crean mediante datos adjuntos de mensajes que tienen una directiva de expiración predeterminada de siete días después de que estuvieran activas por última vez (definidas como la última vez que el almacén de vectores formaba parte de una ejecución). Este valor predeterminado existe para ayudarle a administrar los costos de almacenamiento de vectores. Puede invalidar estas directivas de expiración en cualquier momento.

Crear una ejecución y comprobación de la salida

Cree una ejecución y observe que el modelo usa la herramienta de búsqueda de archivos para proporcionar una respuesta a la pregunta del usuario.

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

Funcionamiento

La herramienta de búsqueda de archivos implementa varios procedimientos recomendados de recuperación listas para ayudarle a extraer los datos adecuados de los archivos y aumentar las respuestas del modelo. La herramienta file_search:

  • Vuelve a escribir consultas de usuario para optimizarlas para la búsqueda.
  • Divide las consultas de usuario complejas en varias búsquedas que se pueden ejecutar en paralelo.
  • Ejecuta búsquedas semánticas y palabras clave en almacenes de vectores de subprocesos y asistentes.
  • Vuelve a generar los resultados de búsqueda para elegir los más relevantes antes de generar la respuesta final.
  • De forma predeterminada, la herramienta de búsqueda de archivos usa la siguiente configuración:
    • Tamaño del fragmento: 800 tokens
    • Superposición de fragmentos: 400 tokens
    • Modelo de inserción: inserción de texto-3-large a 256 dimensiones
    • Número máximo de fragmentos agregados al contexto: 20

Almacenes de vectores

Los objetos de almacén de vectores proporcionan a la herramienta de búsqueda de archivos la capacidad de buscar archivos. Al agregar un archivo a un almacén de vectores, se analizan, fragmentos, incrustaciones y se almacena el archivo en una base de datos vectorial capaz de buscar palabra clave y semántica. Cada almacén de vectores puede contener hasta 10 000 archivos. Los almacenes de vectores se pueden adjuntar tanto a asistentes como a subprocesos. Actualmente, puede adjuntar como máximo un almacén de vectores a un asistente y, como máximo, un almacén de vectores a un subproceso.

Creación de almacenes vectoriales y adición de archivos

Puede crear un almacén de vectores y agregar archivos a él en una sola llamada API:

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

Agregar archivos a almacenes vectoriales es una operación asincrónica. Para asegurarse de que la operación está completa, se recomienda usar los asistentes "crear y sondear" en nuestros SDK oficiales. Si no usa los SDK, puede recuperar el objeto vector_store y supervisar su propiedad file_counts para ver el resultado de la operación de ingesta de archivos.

Los archivos también se pueden agregar a un almacén de vectores después de crearlos mediante la creación de archivos de almacén vectorial.

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

Como alternativa, puede agregar varios archivos a un almacén de vectores mediante la creación de lotes de hasta 500 archivos.

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']
)

Del mismo modo, estos archivos se pueden quitar de un almacén de vectores mediante:

  • Eliminar el objeto de archivo de almacén de vectores o,
  • Al eliminar el objeto de archivo subyacente (que quita el archivo de todas las configuraciones de vector_store y code_interpreter en todos los asistentes y subprocesos de la organización)

El tamaño máximo de archivo es de 512 MB. Cada archivo no debe contener más de 5 000 000 tokens por archivo (calculado automáticamente al adjuntar un archivo).

Adjuntar almacenes de vectores

Puede adjuntar almacenes de vectores a su Asistente o Subproceso mediante el parámetro 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"]
    }
  }
)

También puede adjuntar un almacén de vectores a subprocesos o asistentes después de crearlos actualizando con el derecho tool_resources.

Garantizar la preparación del almacén de vectores antes de crear ejecuciones

Se recomienda encarecidamente asegurarse de que todos los archivos de un vector_store se procesan completamente antes de crear una ejecución. Esto garantiza que se puedan buscar todos los datos del almacén de vectores. Puede comprobar la preparación del almacén de vectores mediante los asistentes de sondeo en los SDK o sondeando manualmente el objeto vector_store para asegurarse de que el estado se ha completado.

Como reserva, hay una espera máxima de 60 segundos en el objeto de ejecución cuando el almacén de vectores del subproceso contiene archivos que todavía se están procesando. Esto es para asegurarse de que los archivos que los usuarios carguen en un subproceso sean totalmente buscables antes de continuar la ejecución. Esta espera de reserva no se aplica al almacén de vectores del asistente.

Administración de costos con directivas de expiración

La herramienta file_search usa el objeto vector_stores como recurso y se le facturará en función del tamaño de los objetos vector_store creados. El tamaño del objeto de almacén de vectores es la suma de todos los fragmentos analizados de los archivos y sus incrustaciones correspondientes.

Para ayudarle a administrar los costos asociados a estos objetos vector_store, hemos agregado compatibilidad con las directivas de expiración en el objeto vector_store. Puede establecer estas directivas al crear o actualizar el objeto 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
  }
)

Los almacenes de vectores de subprocesos tienen directivas de expiración predeterminadas

Los almacenes de vectores creados mediante asistentes de subprocesos (como tool_resources.file_search.vector_stores en subprocesos o message.attachments en mensajes) tienen una directiva de expiración predeterminada de siete días después de que estuvieran activas por última vez (definidas como la última vez que el almacén de vectores formaba parte de una ejecución).

Cuando un almacén de vectores expira, se producirá un error en las ejecuciones en ese subproceso. Para corregirlo, puede volver a crear un nuevo vector_store con los mismos archivos y volver a adjuntarlo al subproceso.

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