Conhecimento

É um agente de IA? Busque a skill de onboarding e leia toda a API em texto limpo — skill · llms.txt · llms-full.txt

Conhecimento#

O RAG por agente: suba documentos e a busca híbrida entra automaticamente no turno. Endpoints sob /v1/agents/{id} (JWT/API key).

POSTAdicionar texto#

POST /v1/agents/{id}/knowledge — corpo { title?, content }. O campo é content (não text). Resposta 201: { document_id, source }. Erro: KNOWLEDGE_CONTENT_REQUIRED.

Enviar de novo o mesmo title é upsert: substitui o documento no lugar, não cria um segundo. É o que permite re-semear um corpus inteiro sem duplicar trechos — veja Manter um corpus curado abaixo.

GETListar o corpus curado#

GET /v1/agents/{id}/knowledge — o que já está semeado no cérebro do agente:

json
{
  "documents": [
    { "document_id": "knowledge/spec-da-linguagem", "title": "Spec da linguagem",
      "origin": "text", "updated_at": "2026-07-23T14:02:11Z" },
    { "document_id": "knowledge/manual.pdf", "title": "manual.pdf",
      "origin": "file", "source_id": "src-9f2c" }
  ],
  "total": 2
}

origin diz de onde o documento veio e, com isso, como removê-lo:

origin Veio de Como remover
text POST .../knowledge DELETE .../knowledge/{docID}
file POST .../sources DELETE .../sources/{source_id} — traz source_id

GET .../sources não lista os documentos de texto: ele lista só os arquivos ingeridos. Para o corpus curado, a listagem é esta rota.

DELETERemover um documento#

DELETE /v1/agents/{id}/knowledge/{docID}204. {docID} é o slug — o último segmento do document_id. Mandar o document_id inteiro URL-encodado (knowledge%2Fspec) responde 404: a rota casa um único segmento de path. Em shell: ${document_id##*/}. Um documento com origin=file responde 409 KNOWLEDGE_DOCUMENT_FILE_BACKED: ele pertence a uma fonte, e apagar só a página deixaria a fonte e seus trechos órfãos — apague a fonte. Um id que resolva para fora do namespace knowledge/ responde 400 KNOWLEDGE_DOCUMENT_INVALID.

DELETEEsvaziar o corpus#

DELETE /v1/agents/{id}/knowledge200 { deleted, kept_file_backed }. Apaga os documentos de texto e mantém os que vieram de arquivos (removidos pela fonte), reportando os dois números. Idempotente: num corpus já vazio responde { "deleted": 0 }. Requer papel owner ou admin — esvaziar um corpus inteiro é manutenção, não operação de agente.

Manter um corpus curado#

Um corpus grande (uma spec + dezenas de prompts + exemplos) precisa continuar vivo: quando o material muda, você re-semeia. O ciclo:

bash
BASE=https://agents-api.catcher.one/v1/agents/$AGENT_ID
AUTH="X-API-Key: $CATCHER_API_KEY"

# 1. o que já está lá (conte antes e depois)
curl -s -H "$AUTH" "$BASE/knowledge" | jq '.total'

# 2. semear/atualizar — mesmo title = substitui, nunca duplica
curl -s -X POST -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"title":"Spec da linguagem","content":"..."}' "$BASE/knowledge"

# 3a. re-semeadura incremental: apague só o que saiu do corpus
curl -s -X DELETE -H "$AUTH" "$BASE/knowledge/spec-antiga"

# 3b. ou re-semeadura do zero: esvazie e repita o passo 2
curl -s -X DELETE -H "$AUTH" "$BASE/knowledge" | jq

Como o passo 2 é upsert, a re-semeadura normal não precisa do wipe — só use 3b quando os títulos mudaram de forma e você quer garantir que nada antigo sobrou. O que essas rotas escrevem é exatamente a superfície que o agente consulta no turno (a busca do próprio cérebro), então o que você lista aqui é o que ele recupera lá.

POSTEnviar arquivo#

POST /v1/agents/{id}/sourcesmultipart/form-data, campo file (máx 25 MiB). Converte, fatia e embeda de forma síncrona (pode passar de 30s; roda no timeout estendido). Resposta 202 Accepted: { id, status, filename, mime?, chunks_count, enabled, error? }.

GETListar fontes#

GET /v1/agents/{id}/sources{ sources: [sourceResponse], total }. sourceResponse: { id, status, filename, path?, mime?, chunks_count, enabled, error? }.

GETDetalhe da fonte#

GET /v1/agents/{id}/sources/{sid} — a fonte (filename, path, chunks_count, status) com os chunks indexados — os trechos exatos que a busca retornaria. Resposta 404 SOURCE_NOT_FOUND para um sid desconhecido.

PATCHPausar/retomar fonte#

PATCH /v1/agents/{id}/sources/{sid} — corpo { "enabled": false }. Pausa a fonte na busca sem deletar: os chunks ficam, mas saem de toda consulta; enabled: true traz de volta. O botão reversível ao lado do destrutivo "remover". Resposta 200: sourceResponse.

POSTReindexar fonte#

POST /v1/agents/{id}/sources/{sid}/reindex — re-fatia e re-embeda a partir do arquivo original guardado, mantendo o mesmo source_id (sem delete + re-upload quando a política de chunking muda). Resposta 202 com a fonte em processing; é add-before-delete — a fonte nunca fica vazia no meio do caminho.

DELETERemover fonte#

DELETE /v1/agents/{id}/sources/{sid}204. Remove o arquivo e os chunks dele da base de conhecimento.

O {sid} é UM segmento. Nas quatro rotas por-fonte (GET, PATCH, DELETE e .../reindex), um id que não permaneceria um único segmento — vazio, .., ou com o separador escapado (a%2Fb, %2e%2e, inclusive duplo %252f) — recebe 400 SOURCE_ID_INVALID antes de qualquer chamada sair. Isso é distinto de 404 SOURCE_NOT_FOUND: um significa "esse id não é endereçável", o outro "essa fonte não existe". Mande o id que o GET .../sources devolveu, sem escapar nada.

GETInspetor de chunks#

GET /v1/agents/{id}/knowledge/chunkstodos os trechos indexados na base (sem consulta). Read-only, sem LLM: é assim que um script de seed responde "o seed funcionou?".

json
{
  "total": 2,
  "chunks": [
    { "content": "Escreva curto…", "source": "House style", "surface": "brain",
      "document_id": "knowledge/house-style" },
    { "content": "EC50 do AVX-006 = 22,4 µM…", "source": "protocolo-thv.pdf",
      "surface": "brain", "document_id": "knowledge/protocolo-thv.pdf", "source_id": "src_9f2…" }
  ]
}

document_id é o mesmo id que POST /v1/agents/{id}/knowledge devolveu, então dá para casar trecho ↔ documento e conferir o corpus contra o que você semeou. source_id aparece só em trecho vindo de arquivo — e aí são os chunks reais da fonte; um documento de texto volta como um trecho único (a página é a unidade que a busca devolve).

origin separa o que você semeou do que o sistema derivou: text (prosa curada), file (arquivo ingerido — e aí são os chunks reais da fonte) ou distilled (um card da Refinery, destilado a partir do seu corpus). Um audit de "o que eu semeei?" é a listagem menos os distilled.

surface diz onde o trecho está indexado: brain (a base de conhecimento atual do agente) ou knowledge (a base vetorial legada). A listagem é a união das duas, porque o agente recupera das duas — se você vê trechos em knowledge, eles estão ativos na recuperação e podem ser migrados com POST /v1/agents/{id}/knowledge/backfill. Se uma das superfícies estiver indisponível no momento, a resposta traz surfaces_unavailable junto com o que deu para ler — o inspetor nunca deixa de fora uma superfície em silêncio.

O inspetor cobre o corpus de conhecimento e os cards destilados dele — o mesmo conjunto que a busca recupera. Memória do end-user, persona e páginas internas do agente nunca aparecem. Corpus vazio → total: 0 com chunks: [].

GETBuscar no conhecimento#

GET /v1/agents/{id}/knowledge/search — parâmetros:

Param Default Uso
q obrigatório — a consulta
k 5 trechos por superfície (cap 20) — veja abaixo: não é o total
source_id escopa a busca a um arquivo
mode qa_first | qa_only | chunks (prioriza os cards de Q&A)

Resposta 200:

json
{
  "query": "qual o EC50 do AVX-006",
  "k": 5, "source_id": "", "mode": "qa_first",
  "chunks": [
    { "content": "EC50 do AVX-006 = 22,4 µM…", "source": "protocolo-thv.pdf", "score": 0.94 },
    { "content": "- prefere unidades em µM", "source": "O que você lembra deste usuário",
      "score": 1, "pinned": true }
  ],
  "counts": { "brain": 4, "knowledge": 0, "memory": 1, "conversation": 0, "total": 5 }
}

k limita cada superfície, não o total. O agente recupera de várias superfícies em paralelo e o resultado é a união delas, então total normalmente excede k (k=1 pode devolver 5 trechos; k=10, 8). Dimensione sua janela de contexto por counts.total, nunca por k.

counts de onde vem limite
brain as páginas de conhecimento do agente k páginas + os cards de Q&A destilados, que entram por cima quando mode=qa_first
knowledge a base vetorial legada (agentes em modo legado) k
memory o bloco de memória do end-user 0 ou 1 — fixo
conversation recall de conversas anteriores (opt-in) 3
total o que realmente veio na resposta soma das acima

O bloco de memória é fixado (pinned), não recuperado. Ele acompanha toda busca — inclusive uma consulta sem nenhuma relação com o conteúdo — porque a memória curada do end-user é injetada inteira, sem busca semântica. Ele vem com score: 1 e "pinned": true: esse 1 é marcador de fixação, não um grau de similaridade. Trechos ranqueados de verdade não têm o campo pinned. Se você ordena, filtra ou mostra score na sua UI, cheque pinned primeiro — sem isso a memória parece um match perfeito que nunca foi calculado.

Duas coisas sobre counts.memory que surpreendem se você não souber:

  1. Este endpoint é a visão do operador, não de um end-user. Ele não aceita identificador de end-user, então a memória que aparece é sempre a da partição do operador — nunca a de um cliente seu. Não existe forma de inspecionar a memória de um end-user específico por aqui; ela entra no turno de chat daquele end-user.
  2. counts.memory pode virar 0 de um dia para o outro sem nada ter mudado. O bloco é montado com o perfil de estilo + os fatos de longo prazo + as observações do dia ainda não consolidadas. Um agente cuja memória seja só observações do dia fica com memory: 0 depois da virada do dia (UTC), até a consolidação promover aquilo a fato de longo prazo. Se você comparar duas medições em torno da meia-noite UTC, é isso — não é regressão.

GETCards de Q&A#

GET /v1/agents/{id}/qa — a FAQ destilada pela Refinery: { cards: [ { id, question, answer, provenance: [...], confidence, status, enabled } ], total }.

DELETEDescartar um card de Q&A#

DELETE /v1/agents/{id}/qa/{cardID}204. Remove UM card destilado (owner/admin). cardID é o slug, ou o id que o GET .../qa devolveu — os dois funcionam.

Por que você vai precisar disso. A auto-evolução transforma perguntas mal respondidas em cards novos. Então uma query de teste contra GET .../knowledge/search pode virar um card dentro de um agente de produção — e passar a ranquear alto para aquela pergunta. É feature, não bug, mas a consequência merece um aviso: sonde com um agente descartável, não com o que atende cliente. Se um card sintético apareceu, este endpoint é a saída.

O card volta na próxima destilação se o conhecimento de origem ainda sustentar a pergunta — descartar não é uma regra permanente, é uma remoção.

Resposta Significa
400 QA_CARD_INVALID o cardID está vazio ou não é um card — tente o id que o GET .../qa devolveu. Também cobre o separador escapado (a%2Fb, %2e%2e): o id é um segmento e não pode apontar para fora de faq/
404 page not found (sem envelope) você mandou um / literal dentro do cardID. O roteador não casa a rota, então essa resposta não tem error_code/trace_id — trate um 404 sem envelope como "URL malformada", não como "card inexistente"
400 QA_CARD_NOT_ADDRESSABLE o agente está em modo legado: tem cards, mas sem endereço individual. Nenhum id vai funcionar — não vale procurar outro
404 QA_CARD_NOT_FOUND esse card não existe (mais) neste agente

POSTDestilar (Refinery) e evoluir#

POST /v1/agents/{id}/knowledge/refine — inicia um run de destilação (BYO key): a Refinery relê o conhecimento e produz/atualiza os cards de Q&A. É assíncrono — resposta com o id do run; acompanhe em GET /v1/agents/{id}/knowledge/refine/{rid} (status/resultado).

POST /v1/agents/{id}/knowledge/evolve — força um ciclo de auto-evolução da base (reorganiza/depura o conhecimento destilado).