Pular para o conteúdo

Visao Geral da Arquitetura

O Valter e um monolito modular com 4 runtimes (API, Worker, MCP stdio, MCP HTTP), todos compartilhando o mesmo codebase Python com separacao rigorosa em camadas.

O Valter segue um padrao de monolito modular — nao microservicos. Todos os runtimes compartilham o mesmo pacote Python (src/valter/), os mesmos modelos de dominio e a mesma logica de negocio. O que muda entre runtimes e o entry point e a camada de transporte, nao a logica central.

Esse design foi escolhido por tres razoes:

  1. Consistencia — um unico codebase garante que o comportamento de uma tool MCP e de seu endpoint REST equivalente sejam sempre identicos, porque ambos chamam a mesma funcao do core.
  2. Simplicidade — existe uma unica unidade de deploy para construir, testar e raciocinar sobre. Nenhuma comunicacao inter-servico, nenhum contrato de API entre servicos, nenhum estado distribuido.
  3. Testabilidade — todos os quatro runtimes podem ser validados pela mesma suite de testes, ja que os testes miram a logica do core e nao codigo especifico de transporte.

O runtime e selecionado pelo entry point usado para iniciar o processo. Em producao no Railway, multiplas instancias do mesmo codebase rodam com entry points diferentes (API na porta 8000, MCP remoto na porta 8001, ARQ worker para jobs em background).

O codebase aplica uma regra estrita de dependencia unidirecional entre camadas:

api/ → core/ → models/

Isso significa:

  • api/ pode importar de core/ e models/, mas nunca de stores/.
  • core/ pode importar de models/, mas nunca de stores/ ou api/.
  • models/ tem zero imports internos — e a camada folha.
  • stores/ implementa protocols definidos em core/protocols.py e e injetado em runtime via mecanismo Depends() do FastAPI, configurado em api/deps.py.

Essa separacao garante que core/ contenha logica de negocio pura sem acoplamento a nenhum driver concreto de banco de dados. Um PostgresDocStore pode ser substituido por um mock nos testes (ou por uma implementacao completamente diferente) sem alterar uma unica linha no core/.

A camada API e o limite mais externo da aplicacao. Ela lida com transporte HTTP, validacao de requests, autenticacao e serializacao de responses. Os route handlers sao intencionalmente finos — validam o input usando schemas Pydantic, chamam uma funcao do core e retornam o resultado.

Componentes-chave:

  • 11 routers FastAPIhealth, retrieve, verify, enrich, similar, graph, features, factual, ingest, memories, datasets
  • Schemas Pydantic v2 — modelos de request e response em api/schemas/, separados dos modelos de dominio
  • Container de DIapi/deps.py conecta stores concretos as funcoes do core usando Depends()
  • Stack de middleware — requests passam por 5 camadas de middleware em ordem: CORS, Metrics IP Allowlist, Request Tracking (trace_id + Prometheus), Rate Limiter (Redis sliding window), Auth (API key + scopes)

A camada core contem toda a logica de negocio. Possui aproximadamente 25 modulos organizados por capacidade de dominio:

GrupoModulosProposito
BuscaHybridRetriever, DualVectorRetriever, QueryExpanderBusca hibrida com BM25 + semantica + KG boost, busca factual dual-vector, expansao multi-query
AnaliseDocumentEnricher, LegalVerifier, SimilarityFinder, FactualExtractorAnalise IRAC, verificacao anti-alucinacao, similaridade de casos, extracao factual via Groq
WorkflowWorkflowOrchestrator, ProjudiOrchestrator, PhaseAnalysis (5 modulos)Pipeline completo de ingestao, do upload de PDF ate artefatos revisados por humano
InfraestruturaProtocols (interfaces runtime-checkable)Contratos que stores devem implementar

Todo modulo no core/ depende apenas de protocols e models — nunca de implementacoes concretas de stores.

A camada store fornece implementacoes concretas dos protocols definidos em core/protocols.py. Cada store e especializado para seu backend de dados:

StoreBackendResponsabilidade
PostgresDocStorePostgreSQLCRUD de documentos, busca full-text (BM25)
PostgresFeaturesStorePostgreSQLFeatures extraidas por IA (21 campos por decisao)
PostgresSTJStorePostgreSQLMetadados do STJ (810K registros)
PostgresIngestStorePostgreSQLJobs de ingestao, estado do workflow
PostgresMemoryStorePostgreSQLMemoria de sessao com TTL
QdrantVectorStoreQdrantBusca semantica (vetores 768-dim, similaridade por cosseno)
Neo4jGraphStoreNeo4jQueries no knowledge graph (12+ metodos analiticos)
RedisCacheStoreRedisCache de queries (TTL 180s), contadores de rate limiting
GroqLLMClientGroq APIChamadas LLM para classificacao, extracao, expansao de query
ArtifactStorageCloudflare R2 / localArmazenamento de artefatos PDF e JSON com canary rollout

A camada model define entidades de dominio como modelos Pydantic v2. Esses modelos sao compartilhados entre todas as camadas e representam o formato canonico dos dados no sistema:

ModuloModelos
document.pyDocument, DocumentMetadata
chunk.pyChunk, ChunkMetadata
irac.pyEstrutura da analise IRAC (Issue, Rule, Application, Conclusion)
graph.py30+ modelos de entidades do grafo (divergencias, perfis de ministros, PageRank, comunidades, etc.)
frbr.pyModelos da ontologia FRBR (Work, Expression, Manifestation)
phase.pyModelos de fases processuais
features.pyFeatures de documentos extraidas por IA (21 campos)
factual.pyDigest factual e tese juridica
stj_metadata.pyMetadados do tribunal STJ
memory.pyPares chave-valor de memoria de sessao

Todos os modelos usam model_config = {"strict": False} para permitir coercao a partir de resultados do banco enquanto mantem type safety no codigo da aplicacao.

O Valter expoe quatro entry points de runtime, todos a partir do mesmo codebase:

Entry PointArquivoComandoPortaConsumidores
REST APIsrc/valter/main.pymake dev8000Frontend Juca, clientes diretos da API
MCP stdiosrc/valter/mcp/__main__.pypython -m valter.mcpClaude Desktop, Claude Code
MCP HTTP/SSEsrc/valter/mcp/remote_server.pymake mcp-remote8001ChatGPT Apps via auth HMAC
ARQ Workersrc/valter/workers/__main__.pymake worker-ingestJobs de ingestao em background

Em producao (Railway), a REST API e o MCP HTTP/SSE rodam como servicos separados com URLs distintas, enquanto o ARQ Worker roda como um processo separado consumindo a fila de jobs do Redis.

Em alto nivel, todo request segue o mesmo pipeline independentemente do entry point:

Consumidor (Juca / ChatGPT / Claude)
Entry Point (REST API / MCP stdio / MCP HTTP)
Stack de Middleware (CORS → Metrics → Tracking → RateLimit → Auth)
Route Handler (valida input, delega para o core)
Logica Core (retriever, enricher, verifier, etc.)
Stores (PostgreSQL, Qdrant, Neo4j, Redis, Groq, R2)
Response (serializada via schema Pydantic)

Tools MCP seguem o mesmo caminho: a implementacao de cada tool chama funcoes do core, que por sua vez chamam stores. A camada MCP nao adiciona logica de negocio — e um adaptador fino que traduz chamadas de tools MCP nas mesmas chamadas de funcao do core que os route handlers REST fazem.

Para diagramas visuais detalhados das relacoes entre componentes e do pipeline de busca, veja Diagramas de Arquitetura.