Tokia

RAG com Embeddings

RAG (Retrieval-Augmented Generation) permite que o modelo responda perguntas sobre seus proprios documentos — contratos, manuais, FAQs, bases de conhecimento — sem fine-tuning. O fluxo: carregar documentos, dividir em chunks, vetorizar, buscar os trechos relevantes, e gerar a resposta com contexto.

Pre-requisitos

bash
pip install openai numpy

Usamos o SDK OpenAI oficial + NumPy para similaridade vetorial. Em producao, troque o vetor store in-memory por FAISS, Pinecone, Qdrant ou pgvector.

1. Carregar e dividir documentos

python
def chunk_text(text: str, max_chars: int = 500, overlap: int = 50) -> list[str]:
    chunks = []
    start = 0
    while start < len(text):
        end = start + max_chars
        chunks.append(text[start:end])
        start = end - overlap
    return chunks

with open("manual.txt", "r", encoding="utf-8") as f:
    texto = f.read()

chunks = chunk_text(texto)
print(f"{len(chunks)} chunks criados")

2. Vetorizar com embeddings do Tokia

python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.usetokia.com/v1",
    api_key="sk-...",
)

def embed(texts: list[str]) -> list[list[float]]:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=texts,
    )
    return [item.embedding for item in response.data]

vectors = embed(chunks)

3. Buscar chunks relevantes

python
import numpy as np

def cosine_similarity(a: list[float], b: list[float]) -> float:
    a_arr, b_arr = np.array(a), np.array(b)
    return float(np.dot(a_arr, b_arr) / (np.linalg.norm(a_arr) * np.linalg.norm(b_arr)))

def search(query: str, top_k: int = 3) -> list[str]:
    query_vec = embed([query])[0]
    scores = [(i, cosine_similarity(query_vec, v)) for i, v in enumerate(vectors)]
    scores.sort(key=lambda x: x[1], reverse=True)
    return [chunks[i] for i, _ in scores[:top_k]]

relevantes = search("Qual o prazo de garantia?")

4. Gerar resposta com contexto

python
def ask(question: str) -> str:
    context_chunks = search(question)
    context = "\n---\n".join(context_chunks)

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": (
                    "Responda APENAS com base no contexto fornecido. "
                    "Se a resposta nao estiver no contexto, diga que nao encontrou.\n\n"
                    f"Contexto:\n{context}"
                ),
            },
            {"role": "user", "content": question},
        ],
    )
    return response.choices[0].message.content or ""

print(ask("Qual o prazo de garantia do produto?"))

Adaptacoes para producao

  • Vetor store persistente: substitua a lista in-memory por FAISS, Qdrant, Pinecone ou pgvector. Evita re-vetorizar a cada restart.
  • Chunking inteligente: divida por paragrafos ou secoes do documento, nao por tamanho fixo. Mantenha overlap de 10-20%.
  • Modelo de embeddings: text-embedding-3-small e o melhor custo-beneficio. Para maior precisao, use text-embedding-3-large.
  • Modelo de geracao: gpt-4o-mini e suficiente para a maioria dos casos. Para documentos tecnicos complexos, use claude-sonnet-46.
  • Batch de embeddings: a API aceita ate 2048 textos por chamada. Envie em lote em vez de um por um.

Com frameworks

Para uma implementacao mais robusta com menos codigo manual, use LangChain ou LlamaIndex. Ambos suportam o Tokia como backend.

Proximos passos