Biblioteca de Documentos
A Biblioteca de Documentos permite gerir e consultar documentos que alimentam o conhecimento do assistente IA.
Visão Geral
A biblioteca funciona como a base de conhecimento do APAH Assistant, permitindo:
- Upload de documentos (PDF, Word, etc.)
- Processamento automático com OCR e extração de texto
- Indexação vetorial para busca semântica
- Gestão de documentos (ativar/desativar, editar metadados)
Funcionalidades
📤 Upload de Documentos
Suporte para múltiplos formatos:
| Formato | Extensão | Suportado |
|---|---|---|
.pdf | ✅ | |
| Word | .docx | ✅ |
| Texto | .txt | ✅ |
| Markdown | .md | ✅ |
🔍 Processamento de Documentos
Após o upload, os documentos passam por um pipeline de processamento:
Upload → Extração → Chunking → Embedding → Indexação
│ │ │ │ │
│ │ │ │ └─ Guardar na BD
│ │ │ └─ Gerar vetores
│ │ └─ Dividir em chunks
│ └─ Extrair texto (PDF/DOCX)
└─ Validar ficheiro
📊 Gestão de Documentos
Interface administrativa para:
- Listar todos os documentos
- Ver detalhes e metadados
- Ativar/desativar documentos
- Eliminar documentos
- Pesquisar por título/descrição
Arquitetura
Modelo de Dados
// Documento principal
export const documents = pgTable("documents", {
id: text("id").primaryKey(),
title: text("title").notNull(),
description: text("description"),
fileName: text("file_name").notNull(),
fileType: text("file_type").notNull(),
fileSize: integer("file_size").notNull(),
filePath: text("file_path").notNull(),
uploadedBy: text("uploaded_by").references(() => users.id),
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Chunks com embeddings
export const documentChunks = pgTable("document_chunks", {
id: text("id").primaryKey(),
documentId: text("document_id").references(() => documents.id),
content: text("content").notNull(),
embedding: vector("embedding", { dimensions: 1024 }),
metadata: jsonb("metadata"),
chunkIndex: integer("chunk_index").notNull(),
});
Pipeline de Processamento
1. Extração de Texto
import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf";
import mammoth from "mammoth";
async function extractText(file: File): Promise<string> {
const fileType = file.type;
if (fileType === "application/pdf") {
const loader = new PDFLoader(file);
const docs = await loader.load();
return docs.map((d) => d.pageContent).join("\n");
}
if (fileType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
const result = await mammoth.extractRawText({ buffer: await file.arrayBuffer() });
return result.value;
}
// Texto simples
return await file.text();
}
2. Chunking
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
separators: ["\n\n", "\n", ". ", " ", ""],
});
const chunks = await splitter.splitText(documentText);
3. Geração de Embeddings
import { CohereEmbeddings } from "@langchain/cohere";
const embeddings = new CohereEmbeddings({
model: "embed-multilingual-v3.0",
});
const vectors = await embeddings.embedDocuments(chunks);
4. Indexação
for (let i = 0; i < chunks.length; i++) {
await db.insert(documentChunks).values({
id: generateId(),
documentId: document.id,
content: chunks[i],
embedding: vectors[i],
chunkIndex: i,
metadata: { page: calculatePage(i) },
});
}
API de Biblioteca
Endpoints REST
// Upload de documento
POST /api/library/upload
Content-Type: multipart/form-data
// Listar documentos
GET /api/library/documents
// Detalhes do documento
GET /api/library/documents/:id
// Atualizar documento
PATCH /api/library/documents/:id
// Eliminar documento
DELETE /api/library/documents/:id
tRPC Routers
export const libraryRouter = createTRPCRouter({
// Listar documentos
list: protectedProcedure
.input(
z.object({
search: z.string().optional(),
page: z.number().default(1),
limit: z.number().default(10),
})
)
.query(async ({ ctx, input }) => {
return ctx.db.query.documents.findMany({
where: input.search ? ilike(documents.title, `%${input.search}%`) : undefined,
limit: input.limit,
offset: (input.page - 1) * input.limit,
orderBy: desc(documents.createdAt),
});
}),
// Atualizar metadados
update: protectedProcedure
.input(
z.object({
id: z.string(),
title: z.string().optional(),
description: z.string().optional(),
isActive: z.boolean().optional(),
})
)
.mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
return ctx.db
.update(documents)
.set({ ...data, updatedAt: new Date() })
.where(eq(documents.id, id));
}),
// Eliminar documento
delete: protectedProcedure.input(z.object({ id: z.string() })).mutation(async ({ ctx, input }) => {
// Eliminar chunks primeiro (cascade)
await ctx.db.delete(documentChunks).where(eq(documentChunks.documentId, input.id));
// Eliminar documento
return ctx.db.delete(documents).where(eq(documents.id, input.id));
}),
});
Interface de Utilizador
Lista de Documentos
┌─────────────────────────────────────────────────────────────────┐
│ Biblioteca de Documentos [+ Upload] │
├─────────────────────────────────────────────────────────────────┤
│ 🔍 Pesquisar documentos... │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 📄 Diretrizes HAP 2024.pdf │ │
│ │ Diretrizes europeias para tratamento de HAP │ │
│ │ 📅 12 Nov 2024 • 2.4 MB • ✅ Ativo │ │
│ │ [Editar] [Desativar] [Eliminar] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 📄 Estudo Clinico HAP.pdf │ │
│ │ Resultados do estudo de fase 3... │ │
│ │ 📅 05 Out 2024 • 1.8 MB • ✅ Ativo │ │
│ │ [Editar] [Desativar] [Eliminar] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ◀ 1 2 3 4 5 ▶ │
└─────────────────────────────────────────────────────────────────┘
Diálogo de Upload
┌─────────────────────────────────────────────────────────────────┐
│ Upload de Documento [✕] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📤 Arraste um ficheiro ou clique para │ │
│ │ selecionar │ │
│ │ │ │
│ │ Formatos: PDF, DOCX, TXT, MD │ │
│ │ Tamanho máximo: 10 MB │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Título: [_________________________________] │
│ │
│ Descrição: │
│ [ ] │
│ [ ] │
│ │
│ [Cancelar] [📤 Fazer Upload] │
└─────────────────────────────────────────────────────────────────┘
Busca Semântica
A biblioteca suporta busca semântica para encontrar documentos relevantes:
async function searchDocuments(query: string, limit = 5) {
// Gerar embedding da query
const queryEmbedding = await embeddings.embedQuery(query);
// Buscar chunks similares
const results = await db
.select({
chunk: documentChunks,
document: documents,
similarity: cosineDistance(documentChunks.embedding, queryEmbedding),
})
.from(documentChunks)
.innerJoin(documents, eq(documentChunks.documentId, documents.id))
.where(eq(documents.isActive, true))
.orderBy(cosineDistance(documentChunks.embedding, queryEmbedding))
.limit(limit);
return results;
}
Permissões
| Ação | Admin | Editor | Viewer |
|---|---|---|---|
| Ver documentos | ✅ | ✅ | ✅ |
| Upload | ✅ | ✅ | ❌ |
| Editar | ✅ | ✅ | ❌ |
| Ativar/Desativar | ✅ | ❌ | ❌ |
| Eliminar | ✅ | ❌ | ❌ |
Boas Práticas
- Qualidade dos Documentos - Use documentos de fontes confiáveis
- Metadados Completos - Preencha título e descrição adequadamente
- Tamanho Razoável - Documentos muito grandes devem ser divididos
- Revisão Regular - Desative documentos desatualizados
- Formatos Preferidos - PDF com texto (não imagens) funciona melhor