Ferramenta de pesquisa de arquivos dos Assistentes do OpenAI do Azure (versão prévia)

A Pesquisa de Arquivos aprimora o Assistente com conhecimento externo ao seu modelo, como informações de produtos proprietários ou documentos fornecidos por seus usuários. O OpenAI analisa e divide automaticamente seus documentos, cria e armazena as inserções e usa a pesquisa de vetor e palavra-chave para recuperar o conteúdo relevante para responder a consultas de usuário.

Importante

  • Pesquisa de arquivo tem encargos adicionais além das taxas baseadas em token para uso do OpenAI do Azure.

Observação

  • A pesquisa de arquivos pode ingerir até 10.000 arquivos por assistente – 500 vezes mais do que antes. Ela é rápida, dá suporte a consultas paralelas por meio de pesquisas com vários threads e recursos aprimorados de reclassificação e reescrita de consulta.
    • O repositório de vetores é um novo objeto na API. Depois que um arquivo é adicionado a um repositório de vetores, ele é analisado automaticamente, em partes e inserido, pronto para ser pesquisado. Os repositórios de vetores podem ser usados entre assistentes e threads, simplificando o gerenciamento de arquivos e a cobrança.
  • Adicionamos suporte para o parâmetro tool_choice que pode ser usado para forçar o uso de uma ferramenta específica (como pesquisa de arquivo, interpretador de código ou uma função) em uma execução específica.

Suporte a pesquisa de arquivos

Regiões com suporte

A pesquisa de arquivos está disponível em regiões que dão suporte a Assistentes.

Versão da API

  • 2024-05-01-preview

Tipos de arquivo com suporte

Observação

Para texto/tipos MIME, a codificação deve ser utf-8, utf-16 ou ASCII.

Formato de arquivo 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 aplicativo/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 texto/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 acessar seus arquivos, a ferramenta de pesquisa de arquivos usa o objeto do repositório de vetores. Carregue seus arquivos e crie um repositório de vetores para contê-los. Depois que o repositório de vetores for criado, você deverá sondar seu status até que todos os arquivos estejam fora do estado in_progress para garantir que todo o conteúdo tenha concluído o processamento. O SDK fornece auxiliares para carregamento e sondagem.

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)

Atualizar o assistente para usar o novo repositório de vetores

Para tornar os arquivos acessíveis ao assistente, atualize o tool_resources do assistente com a nova ID vector_store.

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

Criar um thread

Você também pode anexar arquivos como anexos de mensagem no thread. Isso criará outro vector_store associado com thread ou, se já houver um repositório de vetores anexado a esse thread, anexará os novos arquivos ao repositório de vetores de thread existente. Quando você cria uma Execução nesse thread, a ferramenta de pesquisa de arquivos consultará o vector_store do seu assistente e o vector_store no 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)

Os repositórios de vetores são criados com anexos de mensagens que têm uma política de expiração padrão de sete dias após a última ativa (definida como a última vez em que o repositório de vetores fazia parte de uma execução). Esse padrão existe para ajudá-lo a gerenciar seus custos de armazenamento de vetor. É possível substituir essas políticas de expiração a qualquer momento.

Criar uma execução e verificar a saída

Crie uma Execução e observe que o modelo usa a ferramenta de pesquisa de arquivos para fornecer uma resposta à pergunta do usuário.

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

Como ele funciona

A ferramenta de pesquisa de arquivos implementa as melhores práticas de recuperação prontas para ajudá-lo a extrair os dados certos de seus arquivos e aumentar as respostas do modelo. A ferramenta file_search:

  • Reescreve as consultas de usuário para otimizá-las para pesquisa.
  • Divide consultas de usuário complexas em várias pesquisas que podem ser executadas em paralelo.
  • Executa pesquisas de palavra-chave e semânticas em repositórios de vetores de thread e assistente.
  • Reclassifica os resultados da pesquisa para escolher os mais relevantes antes de gerar a resposta final.
  • Por padrão, a ferramenta de pesquisa de arquivos usa as seguintes configurações:
    • Tamanho da parte: 800 tokens
    • Sobreposição da parte: 400 tokens
    • Modelo de inserção: text-embedding-3-large em 256 dimensões
    • Número máximo de partes adicionadas ao contexto: 20

Repositórios de vetores

Os objetos do repositório de vetores dão à ferramenta de pesquisa de arquivos a capacidade de pesquisar seus arquivos. A adição de um arquivo a um repositório de vetores analisa automaticamente, divide, incorpora e armazena automaticamente o arquivo em um banco de dados de vetores capaz de fazer a pesquisa semântica e de palavra-chave. Cada repositório de vetores pode armazenar até 10.000 arquivos. Os repositórios de vetores podem ser anexados a Assistentes e Threads. No momento, você pode anexar, no máximo, um repositório de vetores a um assistente e, no máximo, um repositório de vetores a um thread.

Criar repositórios de vetores e adicionar arquivos

Você pode criar um repositório de vetores e adicionar arquivos a ele em uma única chamada à API:

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

A adição de arquivos a repositórios de vetores é uma operação assíncrona. Para garantir que a operação seja concluída, é recomendável usar os auxiliares para "criar e sondar" em nossos SDKs oficiais. Se você não estiver usando os SDKs, poderá recuperar o objeto vector_store e monitorar sua propriedade file_counts para ver o resultado da operação de ingestão de arquivos.

Os arquivos também podem ser adicionados a um repositório de vetores após sua criação, criando arquivos de armazenamento de vetores.

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

Como alternativa, você pode adicionar vários arquivos a um repositório de vetores criando lotes de até 500 arquivos.

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

Da mesma forma, esses arquivos podem ser removidos de um repositório de vetores ao:

  • Excluir o objeto do arquivo do repositório de vetores ou
  • Excluindo o objeto de arquivo subjacente (que remove o arquivo de todas as configurações de vector_store e code_interpreter em todos os assistentes e threads em sua organização)

O arquivo pode ter no máximo 512 MB. Cada arquivo não deve conter mais de 5.000.000 tokens por arquivo (computado automaticamente quando você anexa um arquivo).

Anexar repositórios de vetores

Você pode anexar repositórios de vetores ao Assistente ou Thread usando o 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"]
    }
  }
)

Você também pode anexar um repositório de vetores a Threads ou Assistentes depois que eles forem criados atualizando-os com o tool_resourcescorreto.

Garantir a preparação do repositório de vetores antes de criar execuções

É altamente recomendável garantir que todos os arquivos em um vector_store sejam totalmente processados antes de criar uma execução. Isso garante que todos os dados no seu repositório de vetores sejam pesquisáveis. Para verificar a preparação do repositório de vetores, use os auxiliares de sondagem nos SDKs ou pesquise manualmente o objeto vector_store para garantir que o status seja concluído.

Como um fallback, há uma espera de, no máximo, 60 segundos no objeto run quando o repositório de vetores do thread contém arquivos que ainda estão sendo processados. Isso é para garantir que todos os arquivos carregados por seus usuários em um thread sejam totalmente pesquisáveis antes que a execução continue. Essa espera de fallback não se aplica ao repositório de vetores do assistente.

Gerenciar custos com políticas de expiração

A ferramenta file_search usa o objeto vector_stores como seu recurso, e você será cobrado de acordo com o tamanho dos objetos vector_store criados. O tamanho do objeto do repositório de vetores é a soma de todas as partes analisadas de seus arquivos e suas inserções correspondentes.

Para ajudá-lo a gerenciar os custos associados a esses objetos vector_store, adicionamos suporte para políticas de expiração no objeto vector_store. Você pode definir essas políticas ao criar ou atualizar o 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
  }
)

Os repositórios de vetores de thread têm políticas de expiração padrão

Os repositórios de vetores criados com auxiliares de thread (como tool_resources.file_search.vector_stores em Threads ou message.attachments em Mensagens) têm uma política de expiração padrão de sete dias após a última ativa (definida como a última vez em que o repositório de vetores fazia parte de uma execução).

Quando um repositório de vetores expirar, as execuções nesse thread falharão. Para corrigir isso, você pode recriar um novo vector_store com os mesmos arquivos e reanexá-lo ao 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]
    )