Pular para o conteúdo principal

Feedback de busca

Buscas revisadas promovidas a Pares de treino e pares salvos explicitamente pela Busca do Playground recebem automaticamente a tag retrieval_feedback do projeto. Tags existentes em um par reutilizado são preservadas; adicionar a tag incrementa a revisão desse par. Repetir exatamente uma promoção não restaura uma tag removida depois. A capacidade é verificada de forma atômica: 409 tag_limit_reached mantém a revisão e o conjunto de dados inalterados. Registrar uma busca não revisada não cria nem marca um par de treino.

O feedback de busca transforma as buscas que sua aplicação já faz em exemplos de treino de embeddings. Uma chamada de embedding contém texto, não qual resultado respondeu à consulta; por isso o ReOpenly nunca salva requisições de embedding como dados de treino. Em vez disso, o seu servidor informa a consulta e os resultados ranqueados que exibiu, e o dono do projeto revisa a lista inteira e mantém todos os resultados úteis.

1. Disponibilidade​

O feedback de busca pertence ao dataset de um projeto de embeddings. Ele só está disponível quando o projeto lista a capacidade dataset.search_feedback (GET /api/v1/projects/{owner_slug}/{project_slug} → project.capabilities). Enquanto estiver indisponível, todas as rotas abaixo respondem 409 retrieval_feedback_unavailable após a autorização, e nada já armazenado é apagado.

2. Endpoint, autorização e tipo de mídia​

POST /api/v1/projects/{owner_slug}/{project_slug}/datasets/{dataset_id}/retrieval-events
POST /api/v1/projects/{owner_slug}/{project_slug}/datasets/{dataset_id}/retrieval-events/batch
  • Autentique com uma chave de API da conta (Authorization: Bearer fnf_...) ou com uma sessão conectada. Apenas o dono do projeto pode informar ou revisar eventos; acesso a um modelo compartilhado ou público não concede nada aqui. Toda forma de não ser dono do dataset recebe o mesmo 404 retrieval_dataset_not_found.
  • Envie Content-Type: application/json, sem compressão.
  • Chame apenas a partir do seu servidor. Nunca coloque uma chave de API em código de navegador.
  • O dataset_id aparece na página Feedback de busca e em GET /api/v1/projects/{owner_slug}/{project_slug}/datasets.

Esta é uma API versionada: adições compatíveis evoluem /api/v1; qualquer coisa que remova, renomeie, restrinja ou reinterprete um campo ou erro exige /api/v2. Ignore campos de resposta que você não reconhecer.

3. Modelo básico e exemplo cURL​

Modelo básico — substitua todos os valores <...> antes de enviar:

curl -X POST "https://reopenly.com/api/v1/projects/<owner>/<project>/datasets/<dataset-id>/retrieval-events" \
-H "Authorization: Bearer $REOPENLY_API_KEY" \
-H "Content-Type: application/json" \
--data '{"event_id":"<unique-event-id>","model":"<model-slug>","query":"<search-query>","results":[{"rank":1,"text":"<result-text>"}]}'

Exemplo cURL — valores de exemplo; substitua antes de enviar:

curl -X POST "https://reopenly.com/api/v1/projects/acme/support/datasets/42/retrieval-events" \
-H "Authorization: Bearer $REOPENLY_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"event_id": "search-2026-09-17-000123",
"model": "citations-06b",
"query": "How do I resume a paused fine-tune?",
"metadata": {
"occurred_at": "2026-09-17T14:02:11Z",
"model_revision": 1,
"search_target": "support-docs-production"
},
"results": [
{"rank": 1, "text": "Resume continues a run rather than replacing it.",
"metadata": {"score": 0.83, "external_id": "chunk-782"}},
{"rank": 2, "text": "Cancel is available while a run is queued."}
]
}'

4. Campos obrigatórios​

CampoRegra
event_idSeu identificador para esta busca, ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$, diferencia maiúsculas. Também é a chave de repetição.
modelO slug do modelo de embeddings do projeto — o mesmo SavedModel.slug que você envia como model para a API de embeddings. O evento registra a revisão exata do modelo que o produziu.
queryO texto da busca, 1..8.192 bytes UTF-8, não apenas espaços.
results1..20 itens em ordem crescente de rank, com ranks exatamente 1..n. Cada um tem rank e text (1..32.768 bytes UTF-8, não apenas espaços).

O texto é armazenado após a normalização Unicode NFC; os espaços são preservados.

Modelos base também funcionam sem um alias hospedado: defina model como ReOpenly/Qwen3-Embedding-0.6B ou ReOpenly/Qwen3-Embedding-4B. Use a mesma referência que gerou seus vetores. O evento registra a versão verificada do modelo base e suas configurações; model_revision é 1 (omita ou envie 1). Versões desconhecidas retornam retrieval_model_not_found. Informe explicitamente a consulta e os resultados ordenados, depois revise os rótulos de relevância; chamadas de vetores sozinhas não criam exemplos de treino. Índices persistentes de Busca ainda exigem um alias hospedado. Referências anteriores Qwen/... continuam aceitas.

5. Metadados opcionais​

Valores opcionais existem apenas dentro de objetos metadata tipados. Omita um membro que você não tem — nunca envie null. Um objeto metadata vazio equivale a omiti-lo. Campos que a API não conhece são ignorados, para que um cliente escrito para uma versão mais nova continue funcionando; um campo conhecido com valor errado continua sendo recusado.

metadata do evento:

  • occurred_at — quando sua busca rodou (RFC 3339, no máximo 24 horas no futuro e 365 dias no passado em relação ao recebimento). Apenas exibição.
  • model_revision — a revisão do modelo que seu servidor usou, para envios atrasados. Ela precisa existir no histórico desse modelo. Se omitida, é registrada a revisão atual no recebimento.
  • search_target — um rótulo opaco para o índice, coleção ou namespace pesquisado, ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$, nunca contendo ://. O ReOpenly nunca se conecta a ele. Não coloque credenciais nem dados pessoais nele.

metadata do resultado:

  • score — a pontuação do seu sistema de busca, qualquer número finito (pode ser negativo). Armazenada e exibida como enviada; nunca usada para escolher um rótulo.
  • external_id — o id do seu trecho ou documento, no máximo 128 bytes UTF-8.
  • title — no máximo 512 bytes UTF-8.
  • source_uri — apenas https, no máximo 2.048 bytes, sem informações de usuário, query string ou fragmento.

6. Limites​

LimiteValor
Resultados por evento20
Texto da consulta8 KiB (UTF-8)
Texto do resultado32 KiB (UTF-8)
Texto total por evento128 KiB
Corpo de requisição única128 KiB
Corpo de lote1 MiB, no máximo 50 eventos
Eventos retidos por dataset10.000
Texto retido por dataset256 MiB
Requisições por dataset por minuto UTC60
Novos eventos por dataset por minuto UTC500

GET .../retrieval-settings retorna os mesmos limites para o seu dataset.

7. Repetição, conflitos, lotes e erros​

event_id é a única chave de idempotência; não há cabeçalho de idempotência nem campo de versão de esquema no corpo.

  • O mesmo event_id com o mesmo conteúdo retorna o evento armazenado com 200 e "created": false. Ordem das chaves, fusos horários, 1 versus 1.0 e um objeto metadata vazio não contam como conteúdo diferente; espaços no texto contam.
  • O mesmo event_id com conteúdo diferente é 409 idempotency_conflict.
  • Um lote é tudo-ou-nada para eventos novos: um item inválido, em conflito ou acima da cota não armazena nada. Repetições exatas podem vir junto com eventos novos. A resposta mantém a ordem de entrada e retorna 201 se algo foi criado, 200 se todos os itens eram repetições.
{
"object": "retrieval_event",
"event_id": "search-2026-09-17-000123",
"state": "unreviewed",
"revision": 1,
"received_at": "2026-09-17T14:02:16Z",
"created": true
}

Os erros usam um único envelope e nunca repetem o seu conteúdo:

{
"detail": {
"code": "retrieval_ingest_rate_limited",
"message": "Too many search-feedback requests for this dataset this minute.",
"limit": 60,
"window_seconds": 60,
"retry_after_seconds": 44
}
}
HTTPcode
400invalid_retrieval_event, invalid_cursor
404retrieval_dataset_not_found, retrieval_event_not_found, retrieval_model_not_found
409retrieval_feedback_unavailable, idempotency_conflict, revision_conflict, retrieval_feedback_label_conflict, retrieval_feedback_positive_conflict, retrieval_event_not_promotable, embedding_example_conflict, embedding_dataset_capacity_exceeded
413retrieval_payload_too_large
415unsupported_retrieval_media_type
422Validação de campos (apenas type, loc, msg)
429retrieval_ingest_rate_limited, retrieval_dataset_quota_exceeded

Todo 429 traz o cabeçalho Retry-After igual a retry_after_seconds. Repetir com o mesmo event_id é sempre seguro.

8. Revisão e promoção​

O dono revisa cada busca na página Feedback de busca ou pela API:

  • Marque resultados ranqueados úteis como Positivo e, opcionalmente, escreva mais um positivo. Cada um vira um exemplo de treino com a consulta como âncora.
  • Escolha no máximo um negativo: qualquer resultado ranqueado não positivo, a sugestão visível de maior posição, um texto escrito ou Sem negativo. Todos os positivos da revisão compartilham esse único negativo opcional, nunca todas as combinações de positivo e negativo.
  • Resultados sem seleção permanecem como contexto da busca. Eles não são rótulos. Valores de score são procedência exibida e nunca escolhem rótulos.
  • Escolher nenhum negativo cria pares em vez de trios. Pares criados com Sem negativo podem treinar sem editar os exemplos salvos. Se um par não tiver parceiro de lote, o treino pode emprestar uma passagem segura da própria divisão; veja treinamento de embeddings.
  • Ignorar não cria nada, e uma busca que ninguém revisa não cria nada. Um clique, um rank ou uma revisão abandonada nunca é um rótulo.
  • Se o candidato a negativo já for um positivo conhecido para a mesma consulta, a promoção é recusada com retrieval_feedback_label_conflict e apenas o rank em conflito; nada é gravado e nenhum resultado inferior é colocado no lugar.
  • Se um resultado mantido já for um negativo difícil para a mesma consulta, a promoção é recusada com retrieval_feedback_positive_conflict. Corrija o Par de treino anterior antes de mantê-lo como positivo.
GET .../retrieval-events?view=inbox|history&q=&model=&received_after=&received_before=&limit=&cursor=
GET .../retrieval-events/{event_id}
PATCH .../retrieval-events/{event_id} {"expected_revision":1,"state":"skipped"}
POST .../retrieval-events/{event_id}/promote {"expected_revision":1,"kept_ranks":[1,2],"negative_mode":"rank","negative_rank":3}
GET .../retrieval-settings

negative_mode pode ser auto (a sugestão não positiva de maior posição), rank, custom ou none. Envie negative_rank apenas com rank e negative_text apenas com custom. positive_text adiciona um positivo escrito; ao menos um rank positivo ou positive_text é obrigatório.

{
"object": "retrieval_event_promotion",
"event_id": "search-2026-09-17-000123",
"state": "promoted",
"revision": 2,
"example_ids": [441, 442],
"positive_ranks": [1, 2],
"hard_negative_rank": 3,
"positive_count": 2,
"has_negative": true
}

As páginas usam paginação por chave ({data, next_cursor, total_count}); um cursor só funciona com os filtros para os quais foi emitido. Um expected_revision desatualizado é 409 revision_conflict com expected_revision e actual_revision.

9. Armazenamento, privacidade e custo​

  • As buscas informadas ficam guardadas até você pedir que sejam apagadas. Nada expira automaticamente e não existe configuração de retenção. O único limite é o de armazenamento do dataset, abaixo; ao atingi-lo, o envio responde 429 retrieval_dataset_quota_exceeded em vez de descartar buscas mais antigas.
  • A promoção copia a consulta, o positivo escolhido e o negativo opcional para Pares de treino. Esses exemplos seguem o dataset e são removidos quando você os exclui lá. A busca de origem informada permanece intacta até a exclusão solicitada, e seu vínculo de promoção registra o evento e os ranks de origem.
  • O ReOpenly armazena a consulta, o texto dos resultados e os metadados que você envia. Não armazena vetores, nunca se conecta ao seu search_target ou source_uri, nunca executa um modelo para o feedback de busca e nunca cobra por ele.
  • Para exportar ou apagar o feedback de busca de um dataset, escreva para o endereço de privacidade indicado na política de privacidade.

10. Correção de Pares de treino​

Exemplos promovidos aparecem no editor Pares de treino do dataset. Use Editar para corrigir a âncora, o positivo ou o negativo opcional e então salve ou cancele em linha. O ReOpenly preserva a origem Feedback de busca e a procedência da revisão enquanto atualiza o conteúdo atual de treino. Excluir um Par de treino o remove do dataset ativo, mas mantém o histórico sem conteúdo da revisão de origem e remove a linha de available_example_ids.

11. Formatos de resposta​

Detalhe (GET .../retrieval-events/{event_id}):

{
"object": "retrieval_event",
"event_id": "search-2026-09-17-000123",
"model": "citations-06b",
"query": "How do I resume a paused fine-tune?",
"metadata": {"occurred_at": "2026-09-17T14:02:11Z", "model_revision": 1, "search_target": "support-docs-production"},
"results": [
{"rank": 1, "text": "Resume continues a run rather than replacing it.", "metadata": {"score": 0.83, "external_id": "chunk-782"}},
{"rank": 2, "text": "Cancel is available while a run is queued."}
],
"state": "promoted",
"revision": 2,
"received_at": "2026-09-17T14:02:16Z",
"decision": {"state": "promoted", "decided_at": "2026-09-17T14:05:00Z", "positive_ranks": [1], "hard_negative_rank": 2, "example_ids": [441], "available_example_ids": [441], "positive_count": 1, "has_negative": true}
}

decision é null enquanto não revisado e {"state":"skipped","decided_at":...} após ignorar. available_example_ids exclui exemplos que você apagou depois em Pares de treino.

Configurações (GET .../retrieval-settings):

{
"object": "retrieval_settings",
"revision": 1,
"workflow": {"waiting_events": 83, "promoted_events": 38, "skipped_events": 27, "training_examples_added": 38, "training_examples": 41, "retained_events": 148},
"storage": {"retained_text_bytes": 19293798, "max_retained_events": 10000, "max_retained_text_bytes": 268435456},
"ingest_limits": {"requests_per_utc_minute": 60, "events_per_utc_minute": 500, "events_per_batch": 50, "results_per_event": 20}
}

Cada campo e resposta está na referência da API, em Feedback de busca, e no documento OpenAPI para download.

Se a promoção ultrapassar o limite de pares de treinamento, toda a operação retorna 409 embedding_dataset_capacity_exceeded. Nenhum exemplo parcial ou decisão de revisão é salvo. Remova pares desnecessários antes de tentar novamente.