Cansado de estruturas inchadas de pano? Discover Shraga-Uma alternativa mínima pronta para a produção, construída pela BigData Boutique para simplificar e escalar aplicações genai sem a sobrecarga.
Quando você está construindo um aplicativo RAG, uma das primeiras decisões que você enfrenta é usar uma estrutura existente ou construir a sua. Estruturas como LangchainAssim, LanggraphAssim, Llamaindex e Crewai são ótimos para começar – eles abstravam muita complexidade e permitem que você se mova rapidamente. Mas assim que você quiser algo personalizado, performante ou grau de produção, essas abstrações geralmente atrapalham.
Na BigData Boutique, trabalhamos em alguns sistemas de pano – entre indústrias, casos de uso e arquiteturas – e continuamos correndo nos mesmos problemas. Então, construímos nossa própria estrutura de código aberto. Algo mínimo, composto e fácil de depurar.
Melhor ainda, ShpanoA foi projetado para colocá -lo em funcionamento em nenhum momento, e também permite implantar quase imediatamente o que você deve ser usado por usuários internos, depois externo e, em seguida, produção real com análise sobre uso, histórico e consumo de tokens e custo. Hoje, é assim que executamos a Genai POCs rápida e eficiente com os clientes e como eles geralmente acabam executando o produto final em produção.
Neste post, vou percorrer como nos aproximamos, o que funcionou para nós e por que achamos que vale a pena considerar se você está falando sério sobre colocar Genai em produção.
O que realmente precisávamos de uma estrutura
Quando começamos a procurar uma estrutura para se comprometer, a maioria das estruturas populares de trapos disponíveis era um não iniciante para nós. Eles abstraíram demais, eram opinativos demais sobre como as coisas deveriam ser estruturadas e vieram com uma longa lista de dependências pesadas. Isso pode ser bom para uma demonstração, mas não para equipes que precisam entender o que está acontecendo sob o capô – ou enviar algo confiável à produção.
Queríamos algo que pudesse nos servir em prototipagem e produção-Onde poderíamos começar “rápido e sujo” quando necessário, mas evoluir para um código limpo, modular e bem tocado à medida que o projeto amadurecia.
Nós também queríamos:
- Spin um projeto rapidamentecom mínimo de caldeira e poucas dependências.
- Apoiar Vários fornecedores de LLM, incorporadores, retrievers de documentos e serviços externos.
- Tem utilidades reutilizáveis para as peças que sempre levam tempo: EDA, limpeza de dados, chunking, incorporação, ingestão.
- Apoiar ambos Uso sem cabeça via API e a interface do usuário para que os clientes possam interagir diretamente com o sistema.
Em resumo: não precisávamos de uma estrutura que faça tudo – precisávamos de um kit de ferramentas que faça o certo coisas bem.
A evolução de um aplicativo de trapo com Shraga
Um ciclo de vida típico, ou evolução, de um aplicativo RAG construído com Shraga, seria algo assim:
- Todo projeto RAG começa como um POC na forma de esboços e tenta. Isso pode ser feito em notebooks de Jupyter, scripts Python em algum lugar ou com Shraga diretamente. Shraga é uma estrutura Python para que você possa aproveitar esse rico ecossistema, que também é o padrão de fato de qualquer coisa de Ciência e Genai.
- Quando o seu aplicativo RAG amadurece e você está satisfeito com os resultados e pronto para começar a coletar feedback, o Shraga pode ser usado como um teste para expô -lo a usuários internos ou aos parceiros de design. A interface do usuário embutida é ótima em servir seu pano aos clientes rapidamente, fornecendo informações de depuração e rastreamento e recebendo feedback.
- À medida que sua implementação de Shraga amadurece e você está preparando-o para a produção, você pode criar sua própria interface do usuário no topo da API fornecida pela Shraga ou apenas continuar usando o Shraga’s, que tem segurança embutida.
- Shraga facilita o aprendizado da experiência e itera rapidamente para melhorar à medida que seu pano amadurece. Colete feedback dos usuários, interno ou externo, classifique os problemas, melhore, execute avaliações e itine.
- Quando o seu pano recebe atenção e o uso começa a se acumular, os custos associados também podem. Aproveite as análises de Shraga para revisar consultas boas e ruins e também estimar o custo por consulta e oportunidades de otimização em custo e desempenho.
Fluxos: o bloco de construção do núcleo
A abstração central da estrutura de Shraga é o BaseFlow
. Representa uma única unidade modular de lógica – algo que recebe entrada, executa uma ação (que pode ou não envolver um LLM) e retorna uma resposta. Os fluxos são fáceis de registrar, compor e expor como ferramentas em sistemas baseados em agentes.
Esse design nos permite tratar tudo – desde responder a uma pergunta do usuário, buscar dados, encadear outros fluxos – como um componente consistente e testável.
class FlowBase(ABC):
@abstractmethod
async def execute(self, request: FlowRunRequest) -> FlowResponse:
"""Main entry point for the flow."""
raise NotImplementedError()
@staticmethod
def id() -> str:
"""Unique identifier for this flow."""
raise NotImplementedError()
@staticmethod
def description() -> str:
"""
Optional. A human-readable description. This is can be used by ReAct or planning flows as a
description for what the flow does.
"""
return ""
@staticmethod
def get_tool_desc():
"""
Optional. A pydantic schema of the flow's input. Used for ReAct or
planning flows
"""
return None
async def execute_another_flow_by_id(
self, flow_id: str, request: FlowRunRequest
) -> FlowResponse:
"""Helper method to delegate execution to another flow."""
Aqui está um exemplo básico de um fluxo que leva uma pergunta do usuário e chama o modelo de bate -papo do OpenAI para responder:
from shraga_common.core.models import FlowBase, FlowRunRequest, FlowResponse
from shraga_common.services import LLMService, OpenAIService
class SimpleQAFlow(BaseFlow):
llmservice: LLMService = None
def __init__(self, config: ShragaConfig, flows: Optional(dict) = None):
super().__init__(config, flows)
self.llmservice = OpenAIService(self.config)
@staticmethod
def id() -> str:
return "simple_qa"
async def execute(self, request: FlowRunRequest) -> FlowResponse:
question = request.input.get("question")
if not question:
return FlowResponse(error="Missing 'question' field.")
prompt = f"Answer the user's question {question}"
answer = await self.llmservice.invoke_model(
prompt, {"model_id": "sonnet_3_7"}
)
return FlowResponse(response_text=response,
payload={"answer": answer},
trace=self.trace_log,
)
Esse fluxo é mínimo por design – mas é fácil estendê -lo com recuperação, memória, modelos rápidos ou qualquer outra coisa. E como todos os fluxos seguem o mesmo padrão, eles podem ser compostos, reutilizados ou registrados em agentes e roteadores sem manuseio especial.
Uma camada de fastapi que faz as coisas chatas
Como todos os fluxos seguem a mesma interface, expor -os através de uma API é direto. Mas os aplicativos reais precisam de mais do que apenas roteamento – eles precisam de configuração, autenticação, log e rastreamento assados desde o primeiro dia.
É por isso que construímos uma fina camada de fastapi que envolve todos os fluxos e lida com as preocupações de infraestrutura que você esperaria em um sistema de produção. Ele nos permite passar da idéia para implantar o terminal em minutos, sem reescrever o Boilerplate todas as vezes.
O que está incluído:
- Configuração consciente do meio ambiente (
app/config
) - Autenticação do usuário, JWT e OAuthpara controle simples de acesso ao usuário (
app/auth
) - Estatísticas de registro, manuseio de erros, análise e desempenho (
app/services
)
Essa camada é intencionalmente chata – e esse é o ponto. Ele nos fornece uma base estável para a execução de serviços movidos a LLM, mantendo a lógica principal dentro dos fluxos limpos e focados.
Ingestão e incorporação: obtendo dados de qualidade em
As camadas de API e fluxo facilitam a exposição da funcionalidade – mas o que realmente faz um sistema de pano bom são os dados. Lixo, lixo ainda se aplica. E para a maioria dos projetos, a maior parte do trabalho real acontece antes que o LLM seja chamado: pré -processamento, crise, incorporação e indexação.
É por isso que construímos dois componentes principais para lidar com a ingestão:
Dochandler: conteúdo de pré -processamento e estrutura
DocHandler
é responsável por tomar documentos brutos – geralmente HTML, Markdown ou texto simples – e convertê -los em pedaços limpos e estruturados. Ele lida:
- Removendo tags html desnecessárias (mas mantendo estrutura como listas e tabelas)
- Texto normalizando
- Anexar metadados
- Documentos de Chunking com base em pistas semânticas ou estruturais
Cada manipulador pode ser personalizado por fonte de documentos e também suporta entradas em vários idiomas.
BaseEmbedder: incorporação plug-and-play
Uma vez que o conteúdo é dividido, passamos por um BaseEmbedder
– Uma interface que envolve qualquer provedor de incorporação. Atualmente, temos implementações para Bedrock, Google e OpenAI.
Principais recursos:
- Incorporar pedaços de texto em lotes
- Preserva metadados ao lado de vetores
- Suporta cache e armazenamento local opcional
- Fácil de alternar fornecedores ou modelos com uma única mudança de configuração
Juntos, esses componentes nos dão um pipeline de ingestão limpo que pode passar de HTML bruto para documentos totalmente indexados muito rapidamente – e, como ambos são modulares, eles podem ser reutilizados em projetos ou estendidos para formatos e verticais específicos.
Uma interface do usuário simples, mas poderosa: shraga-ui
Depois de ter dados limpos indexados e fluxos expostos através de uma API, a próxima etapa é tornar o sistema utilizável – idealmente sem reconstruir um front -end do zero sempre.
É aí que Shraga-Ui entra.
É uma biblioteca de componente React leve que criamos para facilitar a adição de uma interface de bate-papo genai completa a qualquer projeto. Você pode lançá -lo no seu aplicativo com apenas alguns arquivos, apontá -lo para sua API e terminar.
Fora da caixa, ele suporta recursos como:
- Uma interface de bate -papo responsiva
- Exibição de citação de origem
- História do bate -papo
- Análise
- Autenticação
Se o seu caso de uso precisar de uma interface do usuário, ele estará pronto para ir. Caso contrário, sua API ainda é totalmente utilizável sem cabeça – para integrações, aplicativos móveis ou plataformas externas.
Em seguida, mostraremos como amarramos tudo isso em um fluxo de pano completo com um exemplo de funcionamento.
Visão geral da configuração do projeto
Criamos um exemplo de funcionamento completo dessa estrutura em Shraga-tutorialque você pode clonar e executar localmente.
Back -end 🐍
O ponto de entrada principal para o back -end é main.py. Este arquivo inicializa o aplicativo FASTAPI, carrega a configuração do ambiente e registra todos os fluxos disponíveis no aplicativo. Enquanto Shraga-Common Inclui algumas implementações de fluxo padrão, normalmente implementamos nossos próprios fluxos por subclassificação BaseFlow, seguindo a estrutura padrão que descrevemos anteriormente. Isso facilita a adição de novas funcionalidades ou a alteração do comportamento sem precisar tocar o restante da infraestrutura.
import os
import uvicorn
from shraga_common.app import get_config, setup_app
from shraga_common.flows.demo import flows
config_path = os.getenv("CONFIG_PATH", "config.demo.yaml")
app = setup_app(config_path, flows)
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=os.environ.get("PORT") or get_config("server.port") or 8000,
reload=bool(os.environ.get("ENABLE_RELOAD", False)),
log_config=None,
server_header=False,
)
Para melhor organização, geralmente colocamos fluxos personalizados em um dedicado flows/
diretório. Cada fluxo é independente e pode se integrar a quaisquer ferramentas ou serviços necessários: bases de conhecimento da AWS, Pinecone, OpenSearch, Elasticsearch ou qualquer fornecedor LLM como Bedrock ou OpenAI.
Frequentemente escrevemos fluxos separados para diferentes tarefas – por exemplo, um fluxo para lidar Recuperação de documentos, e outro para manusear geração. Essa separação mantém as coisas modulares e nos permite compor fluxos em pipelines ou agentes mais complexos quando necessário.
Frontend ⚛️
O front -end vive em um separado frontend/
diretório e é construído com Reagir e Vite. Nós usamos PNPM Como gerente de pacotes, então você vai querer correr pnpm install
seguido pela pnpm dev
Para fazê -lo funcionando localmente. O front -end usa nosso pacote de interface do usuário publicado, @Shragaai/Shraga-Uique fornece um componente de bate-papo pronto para uso que se conecta à sua API de back-end.
O Bootstapping the UI é tão simples quanto criar um arquivo HTML simples e chamar:
createRoot(document.getElementById("root")!)
Isso monta a interface de bate -papo padrão em seu aplicativo. Se você deseja personalizar a experiência de bate -papo, pode trocar em seu próprio componente, passando -o como um segundo argumento:
createRoot(document.getElementById("root")!, AlternativeChatComponent)
Com essa configuração, você obtém um sistema de pano de pilha completa que é estruturada de maneira limpa, fácil de depurar e pronta para estender-se você está trabalhando em uma demonstração única ou na construção de algo destinado a durar.
Resumo
Com essa configuração no lugar, podemos passar rapidamente de dados brutos para um sistema de pano de trabalho e pronto para produção-sem depender de abstrações pesadas ou camadas de orquestração externa. Tudo, desde a ingestão até a lógica de fluxo e a interface do usuário, é modular e fácil de estender.
Dito isto, as grandes estruturas por aí – como Langchain ou Crewai – estão fazendo um ótimo trabalho, e nós as usamos quando fazem sentido para um projeto específico. Mas como uma equipe que valoriza prototipagem rápida, entrega forte e estar perto da infraestrutura, Shraga provou ser a ferramenta certa para nós.
Gostaríamos muito de ouvir o que está funcionando para você. Você está all-in em uma das estruturas populares? Você está construindo coisas do zero? Ou talvez você tenha lançado seu próprio sistema interno como nós? Deixe -nos saber – estamos sempre prontos para um bom bate -papo de arquitetura.
Leave a Reply