Knowledge Base (FAQ customizado por Affiliate)¶
Mecanismo que permite responder perguntas de FAQ usando uma base de conhecimento própria de cada affiliate (documentos indexados em vetores no Qdrant), em vez do FAQ genérico e hardcoded da plataforma iFriend.
Duas partes: ingestão e consulta¶
Este documento descreve o mecanismo completo, dividido em dois componentes
deployáveis distintos dentro deste mesmo monorepo (ifriend-agents):
- Ingestão (
apps/knowledge-base-service/+runtime/workers/knowledge_base/) — o Knowledge Base Service: API de upload (Cloud Run) + worker de indexação (Cloud Function Gen2, via Pub/Sub). Recebe o documento do affiliate, faz parse/chunking/embedding e grava no Qdrant. Ver Arquitetura do Knowledge Base Service. - Consulta (runtime "Trip", este pacote
ifriend_agent/) — o que este documento detalha a partir daqui:knowledge_base_agent/knowledge_base_tool.pyconsultam o Qdrant em tempo de chat.
A API de ingestão já suporta gerenciamento, não só upload: listar os documentos de um affiliate, remover um documento específico, e resetar a KB inteira — ver Endpoints para quem for construir o front no Site.
[Knowledge Base Service — apps/knowledge-base-service + runtime/workers/knowledge_base]
Affiliate faz upload (JWT) → API valida e sobe pro GCS → cria job (Firestore)
→ publica no Pub/Sub → worker baixa, faz parse/chunk/embedding
→ grava no Qdrant, collection `affiliate_<id>_kb`
→ PATCH /affiliates/{id}/root_agent na iFriend API (knowledgeBaseCollection)
[Runtime "Trip" — ifriend_agent/]
JWT/message_metadata resolve affiliate_id
→ affiliate_config já carrega knowledgeBaseCollection (mecanismo existente,
ver Affiliate → Configuração)
→ knowledge_base_agent (AgentTool, sempre presente se ENABLE_KNOWLEDGE_BASE=true)
→ knowledge_base_tool: embed(pergunta) + busca no Qdrant
→ chunks relevantes (score >= threshold) → responde
→ nada relevante → sentinel NO_KB_MATCH → orquestrador usa faq_agent genérico
Write-back do knowledgeBaseCollection¶
Ao concluir a indexação, o worker chama a iFriend API para associar a collection ao affiliate:
PATCH {IFRIEND_API_BASE_URL}/affiliates/{affiliate_id}/root_agent
Authorization: Bearer <JWT sistêmico>
{"knowledgeBaseCollection": "affiliate_<id>_kb"}
- Autenticação: sistêmica (admin), via
runtime/workers/knowledge_base/ifriend_api_auth.py— mesmo padrão deAuthTokenManagerjá usado no agente principal (POST /authentication_tokencomIFRIEND_API_EMAIL/IFRIEND_API_PASSWORD, token cacheado ~55min, retry automático em 401). - Kill-switch:
IFRIEND_API_KB_WRITEBACK_ENABLED(padrãofalse) — habilitar explicitamente por ambiente; o endpoint foi liberado primeiro em dev, confirmar antes de habilitar em produção. - Falha não bloqueia o job: indexação e busca já funcionam
independentemente do resultado deste passo. O worker registra
writeback_status(success/failed, +writeback_errorse falhou) no documento do job no Firestore, sem alterar ostatusgeral (que reflete o resultado da indexação em si) — verruntime/workers/knowledge_base/affiliate_api.pyejob_status.py.
Contrato compartilhado entre ingestão e consulta¶
Como a leitura (ifriend_agent/tools/knowledge_base_tool.py) e a escrita
(runtime/workers/knowledge_base/) são componentes deployáveis diferentes,
a compatibilidade entre eles depende inteiramente de manter estes valores
idênticos nos dois lados:
| Item | Valor | Onde é usado |
|---|---|---|
QDRANT_URL / QDRANT_API_KEY |
idênticos nos dois serviços | mesma instância Qdrant compartilhada |
KB_EMBEDDING_MODEL |
idêntico (sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 por padrão) |
knowledge_base_tool.py e runtime/workers/knowledge_base/embedder.py — vetores precisam ser comparáveis |
| Nome da collection | affiliate_<id>_kb |
gerado por qdrant_writer.collection_name_for_affiliate(), lido via knowledgeBaseCollection |
| Distance metric | COSINE |
KB_SIMILARITY_THRESHOLD=0.75 do lado de leitura assume isso |
| Payload do ponto | {"text": <chunk>, "document_id": <job_id>, ...} |
knowledge_base_tool.py lê a chave text; document_id é usado pelos endpoints de remoção (DELETE /kb/documents/{job_id}) |
Restrição de arquitetura¶
O root_agent é montado uma vez por processo (AgentBuilder.build()), não
por request — não é possível ligar/desligar agentes por affiliate na
composição. Por isso knowledge_base_agent é registrado como um AgentTool
sempre presente (como faq_tool), controlado por dois níveis independentes:
- Kill-switch global:
ENABLE_KNOWLEDGE_BASE(env var) — decide se o tool existe no deployment. - Gate por affiliate: dentro da própria tool, lendo
affiliate_config.get("knowledgeBaseCollection")em runtime — decide se aquele affiliate tem KB configurada.
Campo em affiliate_config¶
| Campo | Tipo | Descrição |
|---|---|---|
knowledgeBaseCollection |
str | Nome da collection no Qdrant para este affiliate (ex.: affiliate_11522_kb). Ausência do campo = affiliate sem KB, cai direto no faq_agent. |
Este campo é de propriedade do backend externo — ver ressalva em Estendendo a Configuração do Agente.
Decisão de relevância é determinística, não do LLM¶
knowledge_base_tool.py aplica um threshold de similaridade
(KB_SIMILARITY_THRESHOLD, padrão 0.75) sobre o score do melhor resultado
do Qdrant antes de qualquer chamada ao LLM do knowledge_base_agent. Se
não passar do threshold, a tool já retorna None — evitando gastar uma
chamada de síntese de LLM "à toa" para affiliates sem conteúdo relevante
para aquela pergunta.
Embedding: modelo local gratuito¶
A pergunta do usuário é embeddada em processo via
fastembed (ONNX Runtime, sem
PyTorch, sem custo por chamada), modelo configurável via
KB_EMBEDDING_MODEL (padrão sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2, multilíngue).
Coordenação obrigatória com a indexação
O worker de indexação (runtime/workers/knowledge_base/embedder.py)
precisa gerar os vetores com exatamente o mesmo modelo configurado
para a consulta. Modelos diferentes entre indexação e consulta produzem
vetores incomparáveis — a busca simplesmente não funciona. Isso não é
uma sugestão, é um requisito de compatibilidade — os dois cloudbuild-*.yaml
devem apontar KB_EMBEDDING_MODEL para o mesmo valor.
Troca de modelo (2026-08-24) — reindexação pendente
intfloat/multilingual-e5-small (modelo anterior) foi removido do
catálogo de modelos suportados pelo fastembed numa versão mais recente
da lib — fastembed foi fixado em ==0.8.0 em ambos os
requirements.txt (consulta e indexação) e o modelo trocado para
paraphrase-multilingual-MiniLM-L12-v2 (mesma dimensão, 384D).
Isso não é compatível com collections já indexadas com o modelo
antigo. Toda collection por afiliado indexada antes desta mudança tem
vetores do e5-small — comparar contra queries do modelo novo produz
resultados incomparáveis (relevância quebrada silenciosamente, não um
erro visível). Não fazer deploy deste código em produção antes de
reindexar as collections existentes (ferramenta de reindexação ainda
não existe — precisa ser construída: não há endpoint de reindexação
hoje, só upload/status/delete; o arquivo original de cada documento só
persiste no GCS se o documento/afiliado nunca foi deletado).
Cache¶
Ver detalhes na seção "Performance e Cache" do design original — resumo:
affiliate_config(comknowledgeBaseCollection) já é cacheado peloIFriendAPIClient(ResponseCache, TTL 5 min) e por sessão.- Embedding da pergunta e resultado da busca no Qdrant têm cache dedicado
em memória (
KB_CACHE_TTL, padrão 10 min), por instância do processo — verifriend_agent/tools/knowledge_base_tool.py. - Sem cache compartilhado entre instâncias (Redis) nesta primeira versão — reavaliar apenas se a taxa de cache-miss em produção justificar.
Arquivos principais¶
Consulta (runtime "Trip"):
ifriend_agent/tools/knowledge_base_tool.py— busca vetorial + caches + threshold.ifriend_agent/agents/knowledge_base_agent.py— agente que consome a tool.ifriend_agent/agent_builder.py— registro condicional viaENABLE_KNOWLEDGE_BASE.ifriend_agent/prompts/orchestrator_prompt.py— roteamento condicional (FAQ_SECTION_WITH_KNOWLEDGE_BASE).ifriend_agent/config/feature_flags.py— flagENABLE_KNOWLEDGE_BASE.
Ingestão (Knowledge Base Service) — ver detalhes em Arquitetura do Knowledge Base Service:
apps/knowledge-base-service/— API de upload e status (Cloud Run).runtime/workers/knowledge_base/— worker de parse/chunk/embedding/Qdrant (Cloud Function Gen2).cloudbuild-knowledge-base-api.yaml/cloudbuild-knowledge-base-worker.yaml.