ShvIA

Docs · API nativa

Coleções: envie seus próprios documentos

Uma coleção é um conjunto de documentos que você envia ao gateway uma vez e que uma chamada de chat pode usar depois. É assim que um app dá a um modelo um conhecimento que ele não tem, como o manual do seu próprio produto, para que as pessoas perguntem sobre ele e recebam respostas que vêm dele.

O gateway divide cada documento em trechos, indexa, e a cada chamada que nomeia a coleção encontra os trechos que dizem respeito à pergunta e os coloca no prompt. Isso é geração aumentada por recuperação, geralmente chamada de RAG.

Como funciona#

  1. Crie uma coleção: POST /api/v1/collections.
  2. Envie os documentos: um PUT para cada um, em …/documents/{document id}. Enviar um documento de novo o substitui.
  3. Nomeie a coleção numa chamada de chat: acrescente "collections": [12] ao corpo de POST /v1/chat/completions, POST /v1/messages ou POST /v1/responses.

Os documentos ficam guardados no gateway. Seu app os envia uma vez, e de novo só quando mudam.

Tudo nesta página exige a sua chave de API e que o grupo da sua conta tenha acesso à API. Veja autenticação. As rotas que gerenciam coleções recusam um token estreito, como o que o modo Code carrega, com 403 e codigo: ability_insuficiente. Uma chamada de chat não precisa de mais do que a chave que ela já usa.

Limites#

O quêPadrão
coleções por conta20
documentos por coleção300
texto de um documento1.000.000 bytes
trechos em todas as suas coleções3000
coleções nomeadas numa chamada de chat5
trechos que entram em um prompt5

O operador de uma instalação pode mudar esses números. Os trechos têm cerca de 1200 caracteres cada, então 3000 deles comportam alguns megabytes de texto. Uma requisição que passaria de um limite é recusada, e nada dela é guardado.

As chamadas de embedding que toda escrita faz também têm limite: 30 escritas por minuto, 6 chamadas de reindex por minuto e 60 buscas por minuto, cada uma em um contador próprio e além do limite geral de 300 requisições por minuto. Veja limites e cotas.

Coleções#

POST/api/v1/collections#

bash
curl -s "$SHVIA_URL/api/v1/collections" \
  -H "Authorization: Bearer $SHVIA_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Product manual", "description": "How the app works, screen by screen" }'
json
{
  "id": 12,
  "name": "Product manual",
  "description": "How the app works, screen by screen",
  "documents": 0,
  "chunks": 0,
  "created_at": "2026-10-15T12:00:00+00:00",
  "updated_at": "2026-10-15T12:00:00+00:00"
}

Responde 201. name é obrigatório, até 120 caracteres, e deve ser único entre as suas coleções. description é opcional, até 2000 caracteres. chunks é o número de trechos em que os documentos foram divididos.

As outras rotas#

RotaO que faz
GET /api/v1/collections{"collections": […]}, só as suas
GET /api/v1/collections/{id}uma coleção
PUT /api/v1/collections/{id}muda name e description. Envie só o que muda
DELETE /api/v1/collections/{id}apaga a coleção e todos os documentos dela. Responde {"status": "ok", "deleted": true}

Uma coleção que não é sua responde 404 com code: collection_not_found, exatamente como uma que não existe, então um 404 nunca diz se o id é real.

Documentos#

Um documento tem um id que você escolhe, de 1 a 191 letras, dígitos, ., _, : e -. É assim que você envia o mesmo documento de novo: o caminho do PUT é a identidade do documento.

PUT /api/v1/collections/{id}/documents/{document id}#

bash
curl -s -X PUT "$SHVIA_URL/api/v1/collections/12/documents/screens-deadlines.md" \
  -H "Authorization: Bearer $SHVIA_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "title": "Deadlines screen",
    "text": "# Deadlines\n\nA notice received from the court gazette opens a deadline. The system counts it in business days.\n"
  }'
json
{
  "id": "screens-deadlines.md",
  "title": "Deadlines screen",
  "size_bytes": 112,
  "chunks": 1,
  "embed_model": "nomic-embed-text",
  "hash": "9b1d…",
  "created_at": "2026-10-15T12:01:00+00:00",
  "updated_at": "2026-10-15T12:01:00+00:00",
  "result": "created"
}
CampoObservações
textobrigatório. O documento, em texto simples ou Markdown. Espaços no início e no fim são removidos
titleopcional, até 255 caracteres. Ele rotula o documento nos trechos. O padrão é o id do documento

result diz o que aconteceu:

resultStatusSignificado
created201um documento novo
updated200o texto mudou, então os trechos antigos foram trocados por novos
unchanged200o mesmo texto de antes. Nada foi dividido nem gerou embedding, então reenviar tudo é barato. Um title novo sozinho é aplicado sem embedding

Escreva títulos em Markdown. O gateway divide um documento primeiro nos títulos e depois nas quebras de parágrafo, e coloca a cadeia de títulos (Deadlines > Business days) na frente de cada trecho, de modo que o trecho guarda o contexto da seção de onde veio. Um documento sem títulos é dividido nos parágrafos.

O texto é dividido e recebe embedding antes de qualquer coisa ser guardada. Se o serviço de embedding não puder ser alcançado, a chamada responde 503 e o documento fica como estava.

As outras rotas#

RotaO que faz
GET /api/v1/collections/{id}/documents{"documents": […]}, sem o texto
GET /api/v1/collections/{id}/documents/{document id}um documento, com o seu text
DELETE /api/v1/collections/{id}/documents/{document id}apaga o documento e os trechos dele

POST/api/v1/collections/{id}/search#

Roda a mesma recuperação que uma chamada de chat roda e mostra o que voltou. Use para conferir uma coleção antes de apontar um chat para ela, e para ver por que uma resposta deixou algo de fora.

bash
curl -s "$SHVIA_URL/api/v1/collections/12/search" \
  -H "Authorization: Bearer $SHVIA_API_KEY" -H "Content-Type: application/json" \
  -d '{ "query": "how is a deadline counted?", "top_k": 3 }'
json
{
  "results": [
    {
      "document_id": "screens-deadlines.md",
      "title": "Deadlines screen",
      "heading_path": "Deadlines",
      "text": "[Deadlines]\n\nA notice received from the court gazette opens a deadline. …",
      "score": 0.03279,
      "similarity": 0.61
    }
  ],
  "semantic": true
}

query é obrigatório, até 2000 caracteres. top_k é opcional, de 1 a 12. score ordena os resultados e não tem escala própria: compare os resultados entre si, não com um limiar. similarity é o cosseno entre a pergunta e o trecho, ou null para um trecho encontrado só pelas palavras. semantic é false quando o serviço de embedding não pôde ser alcançado e a ordem vem só das palavras da pergunta; os resultados continuam reais.

Como os trechos são escolhidos#

Dois rankings são combinados. Um compara o significado, por meio dos embeddings. O outro compara as próprias palavras, o que pega nomes que um embedding borra, como o título de uma tela ou uma sigla. Acentos e maiúsculas não importam no segundo.

POST/api/v1/collections/{id}/reindex#

Divide e gera embedding de novo de todos os documentos da coleção, a partir do texto que o gateway guardou, e responde {"status": "ok", "reindexed": 3}. Chame depois que o operador trocar o modelo de embedding: trechos feitos por outro modelo só são encontrados pelas palavras até lá.

Usando uma coleção numa chamada de chat#

Acrescente collections ao corpo de um destes endpoints:

bash
curl -si "$SHVIA_URL/v1/chat/completions" \
  -H "Authorization: Bearer $SHVIA_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "model": "<an id from GET /v1/models>",
    "collections": [12],
    "messages": [
      { "role": "system", "content": "You are the help assistant of the Acme app. Answer briefly." },
      { "role": "user", "content": "How is a deadline counted?" }
    ]
  }'

collections é uma lista de 1 a 5 ids de coleção. Com um SDK da OpenAI, envie como campo extra do corpo (extra_body={"collections": [12]} em Python). Uma chamada que não nomeia uma coleção não recebe nada de nenhuma delas, e o campo nunca é repassado ao servidor do modelo.

O que o gateway faz com ele:

  1. Ele pega o texto da busca nas suas mensagens: a última mensagem user. Uma continuação curta de menos de 40 caracteres, como "e num domingo?", é buscada junto com a mensagem user anterior.
  2. Ele recupera até 5 trechos das coleções nomeadas.
  3. Ele os acrescenta à mensagem de sistema da chamada: à que você enviou, ou como uma nova quando você não enviou nenhuma. Os trechos são marcados como dados, e uma instrução dentro de um documento não é obedecida, então um documento não consegue dar ordens ao modelo. A sua própria mensagem de sistema é o lugar das instruções.
  4. O modelo responde. O prompt manda responder a partir dos trechos, avisar quando eles não contêm a resposta e responder no idioma da pergunta.

Quando nenhum trecho combina, o prompt diz que nenhum combinou, para o modelo não responder uma pergunta sobre os seus documentos com o conhecimento geral dele como se soubesse.

A resposta é a resposta normal do endpoint. Estes headers dizem o que foi usado:

HeaderValor
X-Inference-Collectionsos ids que foram usados, separados por vírgula
X-Inference-Excerptsquantos trechos entraram no prompt. 0 significa que nenhum combinou

Os trechos contam como tokens de entrada da chamada, e portanto contra a sua cota. Cinco trechos são cerca de 1500 a 2500 tokens, conforme o idioma. A recuperação custa uma chamada de embedding por pergunta e não é medida.

Pelo que uma chamada pode ser recusada#

StatuscodeQuando
422invalid_collectionscollections não é uma lista de 1 a 5 inteiros positivos
404collection_not_foundum id não é seu ou não existe
403collections_blocked_by_lgpda conta está com a proteção LGPD ligada e o modelo não está nos servidores do operador. Veja abaixo

O código está em error.code em /v1/chat/completions e /v1/responses, e em error.code do objeto de erro da Anthropic em /v1/messages. Uma chamada recusada não chega a nenhum modelo.

Uma chamada é recusada em vez de respondida sem os documentos de propósito: um app que pediu o manual dele e recebeu uma resposta do conhecimento geral do modelo ficaria errado com confiança, e nada na resposta diria isso.

Seus dados#

Os trechos são o seu próprio texto, enviado de propósito e nomeado em cada chamada. Eles seguem a regra das instruções e dos arquivos de um projeto, e não a regra da base de conhecimento do operador:

  • Eles podem ir para um modelo de nuvem, como o resto do seu prompt, a menos que você tenha ligado a proteção LGPD nas configurações da conta. Com ela ligada, uma chamada que nomeia uma coleção e acaba num modelo de nuvem é recusada com 403, porque deixar os trechos de fora em silêncio daria uma resposta errada. Um modelo nos servidores do operador só tem como reserva outro modelo de lá, então uma chamada que começa lá fica lá.
  • Num modelo de nuvem, dados pessoais nos trechos são mascarados como o resto do prompt. Veja seus dados.
  • Os documentos ficam guardados pelo gateway, no banco de dados dele, e só a sua conta os lê. Apagar uma coleção ou um documento apaga o texto e os trechos.
  • Não ponha numa coleção o que nunca deve sair dos seus próprios servidores. Confira onde um modelo roda com data_locality em modelos.

Receita: um assistente que conhece o seu app#

O seu app quer responder "onde eu emito uma nota fiscal?" a partir da própria documentação. Mantenha a documentação como arquivos Markdown com títulos, um por tela ou assunto, e sincronize com um script:

bash
#!/bin/sh
# Sends every .md file of ./help to collection $COLLECTION. Run it on every release.
for f in help/*.md; do
  id=$(basename "$f")
  jq -n --rawfile text "$f" --arg title "$id" '{title: $title, text: $text}' |
    curl -s -X PUT "$SHVIA_URL/api/v1/collections/$COLLECTION/documents/$id" \
      -H "Authorization: Bearer $SHVIA_API_KEY" -H "Content-Type: application/json" \
      --data-binary @- | jq -r '"\(.id): \(.result)"'
done

Arquivos que não mudaram respondem unchanged e não custam nada. Um arquivo que você apagou de help/ continua na coleção até você dar DELETE no documento dele.

Depois, cada pergunta vai ao modelo com a coleção nomeada. Ponha o comportamento do assistente na mensagem de sistema e os fatos na coleção:

python
from openai import OpenAI

client = OpenAI(base_url="https://ai.shvia.org/v1", api_key=SHVIA_API_KEY)

reply = client.chat.completions.create(
    model=MODEL_ID,  # copy an id from GET /v1/models
    messages=[
        {"role": "system", "content": "You are the help assistant of Acme. Answer in two or three sentences and say which screen to open."},
        {"role": "user", "content": question},
    ],
    extra_body={"collections": [12]},
)
print(reply.choices[0].message.content)

Alguns pontos práticos:

  • Uma conta atende todos os seus usuários. O seu app chama com a própria chave, e cada chamada conta contra os limites da conta: 20 requisições por minuto somando todos os endpoints /v1 por padrão, veja limites e cotas. Peça ao operador um limite maior antes de pôr um app movimentado atrás de uma chave.
  • Mantenha a pergunta sobre o produto. A recuperação parte da última mensagem do usuário. Uma pergunta sobre algo que os seus documentos não cobrem traz trechos fracos, e o prompt manda o modelo dizer que não sabe.
  • Deixe as pessoas escolherem um modelo, ou escolha por elas. Leia a lista uma vez em GET /api/v1/profiles, guarde os ids e envie como model o id que a pessoa escolheu.
  • Confira antes de confiar. Use a rota de busca com as perguntas que as pessoas fazem e olhe o que volta. Uma resposta que falta costuma ser um título que falta ou um documento que nunca disse aquilo.

Erros#

As rotas em /api/v1/collections respondem erros assim:

json
{ "status": "error", "code": "collection_not_found", "message": "No such collection." }

Decida pelo status e por code; o texto é para pessoas. Uma falha de validação é o 422 usual do Laravel com message e errors.

StatuscodeQuando
401a chave não é válida, ou a conta dela não tem acesso à API
403api_not_allowedum token de sessão cujo grupo de conta não tem acesso à API. Uma chave nessa situação recebe 401
403ability_insuficienteum token estreito. O campo aqui é codigo
404collection_not_found, document_not_foundo id não é seu ou não existe
413document_too_largeo texto passa do tamanho de um documento
422collection_limitvocê já tem o máximo de coleções que uma conta pode ter
422collection_fulla coleção já tem o máximo de documentos que pode ter
422account_fullo documento levaria as suas coleções além do máximo de trechos que uma conta pode ter
422empty_documento texto não tem nada
429um limite de requisições foi atingido. Veja limites e cotas
503embedding_unavailableo serviço de embedding não pôde ser alcançado, então nada foi guardado. Tente de novo