Para quem está a construir assistentes de IA e procura um repositório de conhecimento prático
O ponto de partida: o problema do armazenamento
Quando se quer dar ao assistente de IA conhecimento real sobre o produto, a primeira pergunta que surge é: onde guardar os documentos?
As opções óbvias são: Notion, Google Docs, Confluence, uma pasta de PDF, um CMS feito à medida. Cada uma tem as suas vantagens, mas todas têm atrito:
- Notion — ótimo para editar, mas a API é paga e pouco fiável
- Google Docs — familiar, mas sem estrutura nem controlo de versões a sério
- Confluence — de nível empresarial, excessivo para uma equipa pequena
- Uma pasta de ficheiros — simples, mas sem interface de edição, sem controlo de acessos, sem webhooks
Cheguei a uma solução diferente: já tinha o WordPress. E decidi não multiplicar sistemas.
O WordPress como CMS da base de conhecimento
O WordPress não é só um blogue. É um sistema de gestão de conteúdos completo, com:
- Um editor (Gutenberg ou Clássico) — qualquer gestor consegue escrever e editar artigos sem conhecimentos técnicos
- Controlo de acessos — quem pode editar, quem só pode ler
- Uma API REST de origem —
GET /wp-json/wp/v2/postsdevolve JSON com o conteúdo, os campos meta e as datas de atualização - Hooks — é possível associar uma ação à gravação de um artigo (
save_post) - Estados de publicação — publish, private, draft
O último ponto acabou por ser o decisivo.
Dois tipos de conteúdo num só CMS
Na nossa implementação, a base de conhecimento é construída a partir de duas fontes dentro de um único WordPress:
As páginas públicas são artigos reais do website: descrições de produtos, tutoriais, perguntas frequentes. Servem ao mesmo tempo de conteúdo de SEO para as pessoas e de fonte de conhecimento para o assistente de IA.
As páginas privadas (status: private) são documentos internos, invisíveis para os visitantes do site, mas acessíveis através da API REST com autenticação. Incluem: guiões de vendas, guias de resposta a objeções, prompts de sistema da IA e especificações técnicas.
Esta separação resolve um problema importante: o mesmo WordPress é, em simultâneo, um website público e uma base de conhecimento privada.
WordPress
├── Public posts/pages → website + AI knowledge base
└── Private pages → AI knowledge base onlyAlém disso, um campo “Excluir da IA” (uma caixa de seleção personalizada _wifly_no_ai) permite marcar um artigo específico para que não seja indexado. É útil para páginas de serviço, rascunhos e textos promocionais que não devem influenciar as respostas do assistente.
Arquitetura da sincronização
A parte mais interessante: como o conteúdo do WordPress chega à base de dados vetorial em tempo real.
O plugin de WordPress como gatilho
Escrevemos um pequeno plugin de WordPress (~80 linhas de PHP) que se liga ao save_post:
add_action('save_post', function($post_id) {
if (wp_is_post_revision($post_id)) return;
if (get_post_meta($post_id, '_wifly_no_ai', true)) return;
// Debounce: no more than once every 2 minutes
$last = get_transient('wifly_kb_sync_' . $post_id);
if ($last) return;
set_transient('wifly_kb_sync_' . $post_id, 1, 120);
// Webhook to the AI assistant server
wp_remote_post(KB_SYNC_URL, [
'body' => json_encode(['post_id' => $post_id]),
'headers' => ['Authorization' => 'Bearer ' . KB_SYNC_SECRET],
'timeout' => 5,
'blocking' => false, // don't wait for a response
]);
});Detalhes importantes:
blocking: false— o WordPress não espera pela resposta do servidor; a página é guardada de imediato- Debounce com
set_transient— evita chamadas repetidas durante a gravação automática - Token secreto — o webhook está protegido com autorização Bearer
O servidor recebe o webhook e atualiza o índice
Do lado do servidor Node.js/Express, o handler do webhook:
- Recebe o
post_id - Vai buscar o conteúdo mais recente através da API REST do WP
- Analisa o HTML → extrai o texto limpo
- Divide em chunks pelos cabeçalhos h2/h3 (~800 caracteres cada)
- Vetoriza cada chunk com o
text-embedding-3-large - Atualiza os registos no Qdrant (upsert por
post_id + chunk_index)
app.post('/api/kb/sync', verifySecret, async (req, res) => {
res.json({ ok: true }); // Respond immediately
const { post_id } = req.body;
const post = await fetchFromWordPress(post_id);
const chunks = splitByHeadings(post.content, 800);
for (const [i, chunk] of chunks.entries()) {
const vector = await openai.embeddings.create({
model: 'text-embedding-3-large',
input: chunk.text,
});
await qdrant.upsert('wifly_kb', {
points: [{
id: `${post_id}_${i}`,
vector: vector.data[0].embedding,
payload: { text: chunk.text, heading: chunk.heading, post_id, url: post.link }
}]
});
}
});Resultado: um editor guarda um artigo — e, em 5–10 segundos, o assistente de IA já conhece o conteúdo atualizado.
Vetorização e pesquisa
Modelo de embeddings
Usamos o text-embedding-3-large da OpenAI (3072 dimensões). É mais caro do que o text-embedding-3-small, mas a precisão da pesquisa é visivelmente melhor — sobretudo com terminologia técnica específica de um domínio.
Pesquisa híbrida: densa + por palavras-chave → RRF
A pesquisa vetorial pura é boa a encontrar conteúdo semanticamente semelhante, mas tem dificuldades com nomes exatos — códigos de produto, nomes de produto, abreviaturas. Se um utilizador perguntar por “flyAir” ou por um plano de preços específico, o vetor pode falhar.
A solução é a pesquisa híbrida com Reciprocal Rank Fusion (RRF):
User query
│
├──→ Dense search (Qdrant ANN) → TOP-14 candidates
│
└──→ Keyword search (full-text filter) → TOP-10 candidates
│
▼
RRF Fusion (k=60)
│
▼
TOP-7 final chunks → into LLM contextA fórmula do RRF para cada documento:
score(d) = Σ 1 / (k + rank_i(d))Os documentos que ficam bem classificados nas duas listas recebem a pontuação final mais alta. É um método simples e muito eficaz, que dispensa a afinação de pesos.
O índice de texto integral no Qdrant é criado no arranque do servidor (de forma idempotente):
await qdrant.createPayloadIndex('wifly_kb', {
field_name: 'text',
field_schema: 'text',
});O que fica guardado no Qdrant
Cada chunk é um ponto no espaço vetorial com um payload:
{
"id": "1847_2",
"vector": [0.023, -0.041, ...], // 3072 floats
"payload": {
"text": "flyAir is a device for passive MAC address collection...",
"heading": "How the MAC Radar Works",
"post_id": 1847,
"post_type": "page",
"url": "https://wifly.ru/flyair",
"updated_at": "2026-04-27T11:32:00Z"
}
}Dividir pelos cabeçalhos é importante: preserva a integridade semântica de cada fragmento. Um corte mecânico aos 800 caracteres parte muitas vezes o contexto no sítio errado.
O que funciona bem
Fluxo editorial sem atrito. Os gestores continuam a trabalhar no WordPress que já conhecem. Não sabem que os seus artigos passam a fazer parte de um sistema de IA — simplesmente funciona.
Dados vivos. O RAG clássico, com carregamento manual de ficheiros, fica desatualizado depressa. A sincronização por webhook mantém o índice atualizado automaticamente.
Separação entre conteúdo público e privado. Um WordPress — duas camadas: um website público e uma base de conhecimento privada. Não é preciso manter dois sistemas separados.
Pesquisa de texto integral + vetorial. A abordagem híbrida fecha os pontos cegos da pesquisa vetorial pura — sobretudo nos nomes de produtos e nos termos técnicos.
Armadilhas
save_post dispara muitas vezes. Na gravação automática, na publicação e na atualização dos campos meta — o hook pode disparar 3–5 vezes para uma única ação. O debounce com set_transient é obrigatório.
As páginas privadas exigem autenticação na API REST. Um simples GET /wp-json/wp/v2/pages não as devolve. É preciso uma Application Password e um cabeçalho Authorization: Basic ....
HTML no conteúdo da API. O WordPress devolve HTML, não texto limpo. É preciso um parser — nós usamos o cheerio do lado do Node.js. É importante remover shortcodes, scripts e blocos de publicidade.
Custo dos embeddings. O text-embedding-3-large custa 0,13 $ por 1 milhão de tokens. Com 800 caracteres por chunk e 1000 artigos, o carregamento inicial custa quase nada — mas tenha isto em conta se houver atualizações frequentes em bases de conhecimento grandes.
Arquitetura final
WordPress (cms.wifly.ru)
├── Public pages → SEO + KB
├── Private pages → KB only
└── Plugin: save_post → webhook (non-blocking)
│
▼
Node.js API Server
├── Fetch WP content (REST API)
├── Parse HTML → clean text
├── Chunk by headings (~800 chars)
├── Embed (text-embedding-3-large)
└── Upsert → Qdrant
│
▼
Hybrid Search (dense + keyword → RRF)
│
▼
GPT-4o with KB context → user responseConclusão
Se já tem o WordPress, não se apresse a procurar outra solução de armazenamento para RAG. Ele dá-lhe tudo o que é preciso: um editor, controlo de acessos, uma API REST e hooks para a sincronização. Acrescente a vetorização por webhook — e o CMS que já tem torna-se uma fonte de conhecimento viva, que se atualiza sozinha, para o seu assistente de IA.
A stack completa que usamos: WordPress → Node.js/Express → Qdrant → OpenAI Realtime API — e funciona em produção.
Interessado? O próximo artigo vai mostrar como construir um assistente de voz sobre esta mesma base de conhecimento, usando a OpenAI Realtime API.
Relacionado: Quer pôr isto em prática? Veja o guia prático — Crie um chatbot RAG com conteúdo do WordPress, passo a passo.
Perguntas frequentes
É possível usar o WordPress como backend de RAG?
Sim. O WordPress funciona bem como camada de base de conhecimento de um pipeline de RAG (Retrieval-Augmented Generation). Os documentos são escritos e estruturados no editor familiar do WordPress, expostos através da API REST e, depois, divididos em chunks e convertidos em embeddings numa base de dados vetorial. O LLM recupera os chunks relevantes no momento da pergunta. Fica-se com um fluxo editorial a sério — rascunhos, revisões, funções, multimédia — sem ter de construir uma ferramenta de conteúdos à medida.
Como se constrói um chatbot RAG com conteúdo do WordPress?
O fluxo tem quatro passos: (1) obter o conteúdo publicado através da API REST do WordPress; (2) dividir cada documento em chunks com sobreposição; (3) gerar embeddings para cada chunk e guardá-los numa base de dados vetorial, como o Qdrant; (4) no momento da conversa, gerar o embedding da pergunta do utilizador, recuperar os chunks mais próximos e passá-los ao LLM como contexto. O chatbot responde a partir do conhecimento real do seu produto, em vez de alucinar, e cada resposta pode citar o artigo de origem.
Como é que o conteúdo do WordPress é dividido em chunks e convertido em embeddings para RAG?
Os artigos longos são divididos em passagens mais pequenas (algumas centenas de tokens cada), com uma pequena sobreposição, para que o contexto não se perca nas fronteiras. Cada chunk é convertido num vetor de embedding por um modelo de embeddings e guardado juntamente com metadados — ID do artigo, título, URL e data de atualização. Esses metadados permitem filtrar a recuperação e ligar as respostas ao artigo original do WordPress.
Que base de dados vetorial funciona melhor com o WordPress para RAG?
Em produção usamos o Qdrant — é open source, rápido, pode ser alojado em servidor próprio e tem uma API de filtragem de metadados limpa. Alternativas como o Pinecone, o Weaviate ou o pgvector funcionam da mesma forma; ao lado do WordPress é indiferente qual escolhe. O essencial é guardar os metadados do artigo com cada vetor, para manter o índice sincronizado e atribuir as respostas a uma fonte.
Como se mantém o índice vetorial sincronizado com as edições no WordPress?
Ligue-se aos eventos de gravação/publicação do WordPress, para que, sempre que um artigo é criado, atualizado ou eliminado, os chunks correspondentes sejam novamente convertidos em embeddings e atualizados (ou removidos) na base vetorial. Associar a sincronização à data de atualização do artigo garante que a base de conhecimento nunca se afasta do que está realmente publicado, e os editores não têm de pensar no pipeline.
Porquê usar o WordPress em vez de uma base vetorial dedicada ou de ficheiros?
A base vetorial continua a fazer a recuperação — o WordPress é a fonte de verdade, cómoda para as pessoas, que fica por cima dela. Editar ficheiros Markdown ou linhas da base de dados em bruto deixa de ser viável quando quem mantém a base de conhecimento não é programador. O WordPress dá-lhe um CMS a sério — controlo de versões, permissões, pesquisa e uma API REST —, enquanto a base de dados vetorial trata da matemática. Fica com o melhor das duas camadas.


