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 mesmo404 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_idaparece na página Feedback de busca e emGET /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
| Campo | Regra |
|---|---|
event_id | Seu identificador para esta busca, ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$, diferencia maiúsculas. Também é a chave de repetição. |
model | O 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. |
query | O texto da busca, 1..8.192 bytes UTF-8, não apenas espaços. |
results | 1..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— apenashttps, no máximo 2.048 bytes, sem informações de usuário, query string ou fragmento.
6. Limites
| Limite | Valor |
|---|---|
| Resultados por evento | 20 |
| Texto da consulta | 8 KiB (UTF-8) |
| Texto do resultado | 32 KiB (UTF-8) |
| Texto total por evento | 128 KiB |
| Corpo de requisição única | 128 KiB |
| Corpo de lote | 1 MiB, no máximo 50 eventos |
| Eventos retidos por dataset | 10.000 |
| Texto retido por dataset | 256 MiB |
| Requisições por dataset por minuto UTC | 60 |
| Novos eventos por dataset por minuto UTC | 500 |
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_idcom o mesmo conteúdo retorna o evento armazenado com200e"created": false. Ordem das chaves, fusos horários,1versus1.0e um objetometadatavazio não contam como conteúdo diferente; espaços no texto contam. - O mesmo
event_idcom 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
201se algo foi criado,200se 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
}
}
| HTTP | code |
|---|---|
| 400 | invalid_retrieval_event, invalid_cursor |
| 404 | retrieval_dataset_not_found, retrieval_event_not_found, retrieval_model_not_found |
| 409 | retrieval_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 |
| 413 | retrieval_payload_too_large |
| 415 | unsupported_retrieval_media_type |
| 422 | Validação de campos (apenas type, loc, msg) |
| 429 | retrieval_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
scoresã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_conflicte 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_exceededem 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_targetousource_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.