Misata
Gere dados de teste realistas para múltiplas tabelas, com chaves estrangeiras que resolvem e agregados que reconciliam, retornados com uma verificação de integridade. Popula Postgres, MySQL e SQLite a partir do seu próprio esquema, ou exporta CSV/JSON/SQL/Parquet. Determinístico, de modo que o mesmo esquema e seed geram as mesmas linhas.
Documentação
Misata
Você declara o resultado. A Misata gera os dados que comprovadamente correspondem a ele.
Linhas relacionais e realistas que atingem curvas de receita exatas, taxas de fraude, integridade referencial e estrutura estatística. A partir de uma frase, YAML ou seu banco de dados. Sem dados reais, sem modelo de ML.
Prefere sem código? Experimente o Misata Studio, o gerador de dados sintéticos sem código: desenhe um esquema em uma tela ou descreva seu conjunto de dados em inglês simples, e gere-o no seu navegador. Mesmo mecanismo, mesma prova de integridade.
A maioria das ferramentas de dados sintéticos aprende com um conjunto de dados real e o imita. A Misata funciona ao contrário: você declara o resultado desejado: "a receita mensal sobe de $50 mil para $200 mil", "a fraude é de 3% no Q1 subindo para 8% até o Q4", "o total_spent de cada cliente é igual à soma de seus pedidos", e a Misata gera linhas individuais cujos agregados atingem esses alvos exatamente, com integridade referencial completa, sem nenhum dado de origem.
Isso é geração conforme ao resultado. O mecanismo é formalizado em um preprint do arXiv (2606.08736): um método de forma fechada que satisfaz agregados declarados com erro de $0,00, onde sintetizadores de imitação prontos para uso treinados nos mesmos dados erram por 74–86%. Cada execução também pode emitir um relatório Oracle, um pacote de prova que cobre integridade referencial, restrições, consistência temporal e reprodutibilidade.
Ele gera a partir de uma descrição em inglês simples, um esquema YAML ou um esquema de banco de dados existente. Nenhum modelo de aprendizado de máquina é necessário. Nenhum dado real é preciso.
Feito para:
- Testes com resposta conhecida: declare o KPI, gere os dados e então afirme que sua transformação dbt, Spark ou SQL retorna exatamente esse número. Um teste de pipeline com verdade absoluta, antes de qualquer dado real existir
- Semeadura de banco de dados: preencha ambientes de desenvolvimento e staging com dados semelhantes aos de produção
- Testes de integração: fixtures relacionais com integridade de chave estrangeira em todas as tabelas
- Demonstrações e protótipos: números, nomes e distribuições realistas, sem PII
- Desenvolvimento de BI e dashboards: dados moldados como seu domínio real antes do lançamento
- Validação de métodos estatísticos: conjuntos de dados longitudinais, agrupados e multissite que passam em modelos de efeitos mistos, testes de ICC e verificações de autocorrelação
Declare ou imite: duas formas de entrada
A Misata funciona em dois modos, e a diferença é o ponto central:
- Declare (o padrão, sem necessidade de dados). Você informa o esquema e os resultados desejados, curvas de receita exatas, taxas de fraude, rollups, restrições, e a Misata gera linhas do zero que estão em conformidade com eles. Use isso quando você não tem dados reais ou quando precisa de uma resposta conhecida para testar um pipeline, dashboard ou demonstração.
- Imite (quando você já tem dados). Aponte o
misata.mimic()para um CSV real e obtenha um gêmeo sintético que corresponde às distribuições e correlações, mas não contém nenhuma das linhas originais, comfidelity_reporteprivacy_reportpara medir o resultado. Use isso para cópias seguras de privacidade dos dados que você já possui.
A maioria das ferramentas de dados sintéticos só faz a segunda opção, aprendendo com um conjunto de dados real e imitando-o. A Misata lidera com a primeira: você declara a resposta e então gera os dados ao redor dela.
Pesquisa
O mecanismo de agregados exatos da Misata é respaldado por um preprint do arXiv:
Síntese Declarativa Conforme ao Resultado: Satisfação de Especificação Exata e de Forma Fechada e um Benchmark de Conformidade
Muhammed Rasin, arXiv:2606.08736 (2026)
https://arxiv.org/abs/2606.08736v1
O artigo formaliza a afirmação central: quando você declara "SaaS MRR from $50k in January to $200k in December", a Misata gera transações individuais cujos totais mensais correspondem à curva declarada com erro exatamente de $0,00, não aproximadamente, mas comprovadamente, por meio de um mecanismo de soma condicional Gamma de forma fechada (caracterização de Lukacs). Sintetizadores de imitação prontos para uso treinados nos mesmos dados erram o agregado mensal declarado por 74–86%; a Misata atinge exatamente 0.
O artigo também apresenta o SpecBench: o primeiro benchmark que mede a conformidade com resultados analíticos para síntese relacional de partida a frio. A Misata é a implementação de referência.
@article{rasin2026declarative,
title = {Declarative Outcome-Conformant Synthesis: Exact, Closed-Form
Specification Satisfaction and a Conformance Benchmark},
author = {Rasin, Muhammed},
year = {2026},
url = {https://arxiv.org/abs/2606.08736v1}
}
Instalação
pip install misata
Extras opcionais:
pip install "misata[llm]" # multi-provider LLM schema generation
pip install "misata[documents]" # PDF output via weasyprint
pip install "misata[advanced]" # SDV/CTGAN statistical synthesis
pip install "misata[mcp]" # MCP server, expose Misata to Claude, Cursor, and other AI agents
pip install "misata[evalpack]" # evalpacks: verified eval databases for data agents (DuckDB)
Use a partir de um agente de codificação
A Misata inclui uma Skill de Agente, para que o Claude Code e qualquer outra coisa que leia
SKILL.md saiba qual ponto de entrada atende a qual solicitação e o que vale a pena
declarar:
/plugin marketplace add rasinmuhammed/misata
/plugin install misata@misata
A skill aciona a CLI, então o pip install misata ainda é necessário. Há
também um servidor MCP (pip install "misata[mcp]") e uma extensão do Claude Desktop
em mcpb/.
Use a Misata a partir do Claude / Cursor / Windsurf (MCP)
A Misata inclui um servidor Model Context Protocol integrado com uma divisão clara de trabalho: o agente de IA projeta o esquema, a Misata garante a matemática. Agentes são bons em saber que uma clínica veterinária precisa de uma coluna species; a Misata é boa em criar 50.000 linhas onde cada chave estrangeira é resolvida, cada rollup reconcilia até o centavo, e a mesma semente reproduz uma saída byte a byte idêntica. A ferramenta principal, generate_from_schema, aceita o dict de esquema do agente e retorna os dados mais uma prova de integridade: contagens de órfãos por relacionamento que o agente pode mostrar a você.
1. Instale:
pip install "misata[mcp]"
2. Adicione ao Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"misata": {
"command": "misata-mcp"
}
}
}
Reinicie o Claude Desktop. Então é só pedir:
"Gere um conjunto de dados fintech com 1.000 clientes, pagamentos e uma taxa de fraude de 2%."
"Projete um banco de dados de ensaios clínicos (locais, pacientes, visitas, eventos adversos) e gere 100 mil linhas."
"Preciso de dados de SaaS: MRR de $50 mil em janeiro, dobrando até dezembro, com uma queda no Q3."
O agente projeta as tabelas que a solicitação precisar (qualquer domínio; não está limitado aos recursos integrados da Misata), chama a Misata, grava os CSVs em disco e reporta com prévias e o resumo de integridade verificado. Consulte o guia MCP para configuração no Cursor/Windsurf/Zed e todas as seis ferramentas disponíveis.
mcp-name: io.github.rasinmuhammed/misata
Início rápido
misata generate \
--story "Brazilian fintech with R$ payments, CPF verification, and 3% fraud" \
--rows 1000 \
--output-dir ./demo_data
# Writes CSVs plus:
# ./demo_data/oracle_report.json
import misata
# One sentence → multi-table DataFrame dict
tables = misata.generate("A SaaS company with 5k users, monthly subscriptions, and 20% churn")
print(tables["users"].head())
print(tables["subscriptions"].head())
# Or from the CLI
misata generate --story "A SaaS company with 5k users and 20% churn" --rows 5000
Misata Oracle
O relatório Oracle é a camada de prova da Misata. Ele separa garantias rígidas de verificações de realismo consultivas, para que os dados gerados possam ser confiáveis em CI, demonstrações, notebooks e comparações de pesquisa.
Verificações garantidas:
- integridade referencial entre relacionamentos configurados
- cumprimento da contagem de linhas solicitada
- validação de esquema e restrições configuradas
- reprodutibilidade determinística quando uma semente é definida
Verificações consultivas:
- pontuação de qualidade e avisos de plausibilidade
- heurísticas de privacidade
- pontuação de fidelidade esquema-vs-saída
- adequação de localidade/domínio para países, cidades, prefixos telefônicos e IDs nacionais
- metadados de data-card
import misata
schema = misata.parse("Brazilian fintech with CPF verification", rows=1000)
tables = misata.generate_from_schema(schema)
oracle = misata.build_oracle_report(tables, schema, seed=schema.seed)
print(oracle["passed"])
print(oracle["advisory"]["locale_domain_fit"]["locale"])
Modo imitação: clone qualquer CSV em uma única chamada
Aponte o misata.mimic() para um conjunto de dados real e obtenha um gêmeo sintético que corresponde às distribuições de cada coluna, mas não contém nenhuma das linhas originais. Sem criação de esquema, sem configuração.
import pandas as pd
import misata
real = pd.read_csv("titanic.csv")
twin = misata.mimic(real, rows=2000, seed=42, table_name="passengers")["passengers"]
O perfilador lida com as colunas que quebram outras ferramentas:
- Colunas de código alfanumérico (Ticket
"A/5 21171", Cabine"C85", SKUs, números de referência) são detectadas pela forma de sua classe de caracteres e reproduzidas estruturalmente, mesmas formas nas proporções certas, valores totalmente novos, zero vazamento verbatim da fonte. Elas não caem mais na geração de texto em prosa. - Flutuantes mantêm seus centavos. Uma Tarifa de
7.25gera valores no formato7.25. O perfilador infere casas decimais a partir dos dados; a quantização semântica (preços de charme) nunca é acionada em colunas imitadas. - Distribuições são ajustadas a partir dos dados. Colunas com assimetria positiva recebem lognormal; colunas constantes recebem um stub uniforme; todo o resto recebe normal. Colunas categóricas com menos de 50 valores carregam suas frequências reais.
# Verify: no verbatim rows can leak through
shared = [c for c in real.columns if c in twin.columns]
overlap = pd.merge(real[shared].astype(str), twin[shared].astype(str), how="inner")
assert len(overlap) == 0
Oito maneiras de gerar dados
1. Inglês simples, sem configuração necessária
tables = misata.generate("A fintech startup with 10k customers, fraud rate 3%, and IBAN accounts")
A Misata lê a história, infere o domínio (fintech), a escala (10.000 linhas) e a semântica das colunas (flag de fraude, formato IBAN), sem necessidade de criação de esquema.
2. Esquema YAML como código, faça commit no git
misata init # scaffolds misata.yaml in the current directory
misata generate # reads misata.yaml automatically
# misata.yaml
name: my-app
seed: 42
tables:
users:
rows: 1000
columns:
user_id: { type: int, unique: true }
email: { type: text, text_type: email }
plan: { type: categorical, choices: [free, pro, enterprise] }
orders:
rows: 5000
columns:
order_id: { type: int, unique: true }
user_id: { type: foreign_key }
amount: { type: float, min: 5.0, max: 500.0 }
relationships:
- "users.user_id → orders.user_id"
constraints:
- name: amount_above_cost
table: orders
type: inequality
column_a: amount
operator: ">"
column_b: cost
schema = misata.load_yaml_schema("misata.yaml")
tables = misata.generate_from_schema(schema)
3. Semeie um banco de dados existente diretamente
from misata import schema_from_db, generate_from_schema, seed_database
# Introspect the live schema: no manual column definitions
schema = schema_from_db("postgresql://user:pass@localhost/myapp")
tables = generate_from_schema(schema)
# Seed it back: insert order respects FK dependencies automatically
report = seed_database(tables, "postgresql://user:pass@localhost/myapp_dev")
# SeedReport: seeded 6 tables, 47,300 rows in 1.2s
# One-command workflow
misata init --db postgresql://user:pass@localhost/myapp # writes misata.yaml
misata generate --db-url postgresql://user:pass@localhost/myapp_dev --db-create
Modelos SQLAlchemy também são suportados:
from misata import seed_from_sqlalchemy_models
from myapp.models import Base
report = seed_from_sqlalchemy_models(Base, db_url="sqlite:///test.db", row_count=500, create_tables=True)
4. A partir do schema.yml do próprio projeto dbt
cd my-dbt-project && misata dbt-seed
Sem história, sem configuração. A Misata lê o YAML de propriedades que seu projeto
já tem e gera CSVs de semente que o satisfazem: testes relationships tornam-se
chaves estrangeiras com integridade garantida, accepted_values tornam-se os pools
exatos de categorias, unique e not_null tornam-se restrições rígidas, e
data_type mais a semântica dos nomes de colunas decidem o resto. Então:
dbt build # seed + run + test — the tests you already wrote, passing on day zero
Tanto a sintaxe de teste inline legada quanto o aninhamento arguments: do dbt 1.9+ são
compreendidos. Testes que a Misata não consegue traduzir (dbt_utils.*, genéricos personalizados) são
listados na saída em vez de serem adivinhados silenciosamente.
5. A partir de um esquema Prisma
cd my-app && misata prisma-seed
Lê o schema.prisma que seu aplicativo já mantém: @relation torna-se chaves
estrangeiras com zero órfãos, enums tornam-se os pools exatos de valores, @id e @unique
são respeitados, @@id/@@unique tornam-se unicidade composta, e campos opcionais
podem ser nulos. Os CSVs vão para seed-data/ prontos para seu script de semente.
6. Esquema dict Python
schema = misata.from_dict_schema({
"customers": {
"id": {"type": "integer", "primary_key": True},
"email": {"type": "email"},
"plan": {"type": "string", "enum": ["free", "pro", "enterprise"]},
},
"orders": {
"id": {"type": "integer", "primary_key": True},
"customer_id": {"type": "integer", "foreign_key": {"table": "customers", "column": "id"}},
"amount": {"type": "float", "min": 1.0, "max": 999.0},
"order_date": {"type": "date"},
},
}, row_count=5_000)
tables = misata.generate_from_schema(schema)
Curvas de resultado declaradas: adicione __outcome_curves__ como uma chave de nível superior junto com as definições de tabela. As linhas geradas somam exatamente para cada alvo declarado, até o centavo:
import pandas as pd
schema = misata.from_dict_schema({
"__outcome_curves__": [{
"table": "orders",
"column": "amount",
"time_column": "order_date",
"time_unit": "month",
"value_mode": "absolute",
"start_date": "2024-01-01",
"avg_transaction_value": 120.0,
"curve_points": [
{"month": 1, "target_value": 50_000.0},
{"month": 6, "target_value": 110_000.0},
{"month": 12, "target_value": 200_000.0},
],
}],
"orders": {
"__rows__": 5000,
"order_id": {"type": "integer", "primary_key": True},
"amount": {"type": "float", "min": 5, "max": 500},
"order_date": {"type": "date"},
},
}, seed=42)
tables = misata.generate_from_schema(schema)
monthly = (
tables["orders"]
.assign(m=pd.to_datetime(tables["orders"]["order_date"]).dt.month)
.groupby("m")["amount"].sum()
)
assert abs(monthly[1] - 50_000) < 0.01 # exact
assert abs(monthly[12] - 200_000) < 0.01 # exact
Participações de grupo exatas: declare como uma medida se divide em uma coluna categórica ("Eletrônicos é 40% da receita, Casa 25%") com __group_shares__. Combinado com uma curva de resultado na mesma tabela e medida, as participações são mantidas até o centavo dentro de cada período declarado, e os totais do período ainda são mantidos; sem uma curva, as participações são mantidas sobre o total da tabela:
schema = misata.from_dict_schema({
"__group_shares__": [{
"table": "orders",
"measure": "amount",
"group_column": "category",
"shares": {"Electronics": 0.4, "Home": 0.25, "Toys": 0.2, "Grocery": 0.15},
}],
# ... same orders table and __outcome_curves__ as above,
# plus a "category" enum column
}, seed=42)
Um período com menos linhas do que grupos de participação positiva é ignorado com um aviso em vez de ser silenciosamente corrompido; consulte LIMITATIONS.md. story_audit verifica as participações na saída, e os evalpacks transformam cada par período-grupo em uma pergunta verificada de agregação filtrada.
Restrições e correlações: aplique regras de negócio e relacionamentos entre colunas diretamente no esquema dict:
schema = misata.from_dict_schema({
"patients": {
"__rows__": 1000,
"__constraints__": [
# visit must be on or after enrollment: enforced at generation, not post-processing
{"type": "inequality", "column_a": "visit_date",
"operator": ">=", "column_b": "enroll_date", "action": "cap"},
],
"__correlations__": [
# heavier patients tend to have higher blood pressure (r = 0.41)
{"col_a": "bmi", "col_b": "systolic_bp", "r": 0.41},
],
"patient_id": {"type": "integer", "primary_key": True},
"enroll_date": {"type": "date"},
"visit_date": {"type": "date"},
"bmi": {"type": "float", "min": 16, "max": 55},
"systolic_bp": {"type": "float", "min": 90, "max": 200},
},
})
__rate_curves__ funciona da mesma forma para alvos de taxa por período em colunas booleanas ou categóricas (taxas de fraude, flags de churn, distribuições de planos).
7. Geração assistida por LLM, semântica mais rica, opcional
from misata import LLMSchemaGenerator
gen = LLMSchemaGenerator(provider="groq", model="llama-3.3-70b-versatile") # free tier, fast & reliable
# gen = LLMSchemaGenerator(provider="anthropic") # Claude
# gen = LLMSchemaGenerator(provider="ollama", model="llama3") # fully local, no API key
schema = gen.generate_from_story(
"A fraud detection dataset, 2% positive rate, FICO scores, transaction velocity features"
)
tables = misata.generate_from_schema(schema)
Requer pip install "misata[llm]" mais um de GROQ_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY.
Dica de modelo Groq:
llama-3.3-70b-versatileé o padrão confiável do nível gratuito. Modelos maiores (por exemplo,openai/gpt-oss-120b) podem retornar413 Request too largeno nível gratuito do Groq, então use-os apenas em um nível pago. Independentemente do que o modelo retornar, a geração nunca falha em um esquema imperfeito: relacionamentos ausentes, probabilidades malformadas etime_units fora do intervalo são reparados automaticamente.
8. Geração incremental, expanda um conjunto de dados sem re-semear
tables = misata.generate("A fintech company with 1000 customers", seed=1)
# Add 1 000 more rows: IDs auto-offset, FK integrity maintained across both batches
tables = misata.generate_more(tables, schema, n=1000, seed=2)
print(len(tables["customers"])) # 2000
Realismo que sobrevive à inspeção
Dados sintéticos raramente falham nos números grandes; eles falham nos pequenos detalhes que um revisor percebe em cinco segundos. A Misata elimina cada detalhe com um mecanismo específico e determinístico. Nenhum LLM está envolvido; tudo é semeado e reproduzível.
| O indício | O mecanismo |
|---|---|
Pablo Müller, Female: nomes, gêneros e culturas sorteados independentemente | Amostragem conjunta de identidade: (culture, gender, first, last) é um sorteio de pools por cultura, com uma mistura intercultura medida de 6% (populações reais não são endogâmicas). Os e-mails derivam do nome final. |
appointment_date: 2022-08-29 06:36:12.995319155: precisão de nanossegundos, 6h, um domingo | Perfis temporais: eventos agendados se ajustam a grades de 15 minutos em horário comercial, com fins de semana atenuados; inscrições seguem ritmos de horas acordadas; apenas eventos de máquina (logs, cliques) mantêm precisão de subsegundo; datas de nascimento são datas. |
| Toda categoria igualmente provável | Marginais de Zipf–Mandelbrot: categóricas não ponderadas seguem a lei de potência de frequência de classificação que status, países e categorias reais seguem, com o valor dominante variando por coluna. Probabilidades declaradas sempre vencem. |
Chicago → San Diego, 145.6 km | Fatos geográficos: distâncias entre cidades nomeadas são calculadas (haversine × circunferência rodoviária) a partir de 289 coordenadas de cidades embutidas, e tempos de viagem decorrem das distâncias. Fatos, não distribuições: para que o Oracle possa verificá-los. |
| Uma avaliação de cinco estrelas que diz "decepcionante", ou lorem ipsum | Microtexto gramatical: o texto da avaliação é gerado a partir da nota da linha por uma gramática com semente (1★ parece irritado, 5★ parece encantado), um invariante verificável. Notas de texto livre vêm de uma gramática de notas de negócios. Lorem ipsum não pode chegar à saída. |
| Uma consulta de 19 minutos, um preço de $43,27 | Quantização numérica: durações agendadas se ajustam às grades de horários que os calendários realmente oferecem (15/30/45/60), preços de varejo terminam em .99/.95/.00, idades são inteiros. Quantidades medidas são deixadas como estão. |
| Um pedido enviado antes de ser feito, por um cliente que ainda não se cadastrou | Ordenação de ciclo de vida e causalidade: os carimbos de data/hora de uma linha se ordenam ao longo do ciclo de vida real de e-commerce/SaaS/logística, e uma linha filha é deslocada para nunca anteceder seu pai de FK, em cadeias de vários níveis, preservando as lacunas da própria linha. |
state: cancelled ao lado de city: Los Angeles, uma linha de Tóquio com CEP dos EUA, telefones +1 em todo lugar | Coerência da cadeia de endereço: cidade, estado, formato postal e código de chamada telefônica concordam com o país da linha (e uma cidade conhecida carrega seu estado exato), em 14 países e 8 formatos postais. |
is_fraud verdadeiro em metade das linhas, salários em um sino simétrico, toda quantidade 1-5 uniforme | Base de conhecimento de priors estatísticos: nomes de colunas reconhecidos desenham sua forma do mundo real automaticamente: avaliações em forma de J, quantidades de pedido de Zipf (60% de uns), salários lognormais, terminações de preço .99, flags de eventos raros ~3%. Declarações explícitas sempre vencem. |
Um order_total que não é igual à soma de seus itens de linha | Coerência de valor entre tabelas: o unit_price de um item de linha é copiado do produto que ele referencia, e uma coluna de total de entidade é consolidada a partir de seu filho de item de linha, nunca contando duas vezes uma tabela irmã. |
tables = misata.generate("A hospital with 300 patients, doctors and appointments", seed=7)
# patients: Tae-yang Ahn (Male) · Valentina Esposito (Female) · pooja.kapoor@icloud.com
# appointments: 2023-03-08 14:00:00 · 2022-07-21 09:15:00: 15-min grid, business hours, 2% weekends
O conjunto de dados se autoavalia
Cada classe de coerência acima também é um detector. story_audit verifica um conjunto de dados gerado contra o catálogo completo de invariantes: órfãos de FK, causalidade temporal entre tabelas, concordância de roll-up, controle de status, limites de contagem e percentual, taxas base de flags raros, idade contra data de nascimento e mais. Nada incoerente é entregue silenciosamente.
tables = misata.generate_from_schema(schema, verify=True) # warns on any finding
report = misata.story_audit(tables, schema) # or audit explicitly
print(report.summary()) # "Coherence: clean" or a scored list of findings
Todo manifesto de evalpack incorpora esse veredito junto com seu certificado de resposta DuckDB, de modo que um pacote afirma tanto que suas respostas estão corretas quanto que os dados que contam a história são internamente coerentes.
Reprodutibilidade e estabilidade
- Dentro de uma versão, a geração é determinística. O mesmo esquema, semente e versão do misata produzem tabelas byte-idênticas. Manifestos de evalpack registram a versão, a semente e um SHA-256 da especificação exatamente por esse motivo.
- Entre versões, os fluxos de RNG podem mudar quando a geração melhora (mudaram em 0.8.1.29 e 0.8.2). Resultados declarados ainda valem: agregados, taxas, identidades e integridade sobrevivem a qualquer atualização; as linhas individuais podem diferir. Fixe a versão quando precisar de regeneração bit-idêntica.
- A API pública é a superfície de nível superior documentada (
misata.generate,generate_from_schema,story_audit,coherence_audit,build_evalpack, as classes de esquema e os construtores). Módulos e funções com prefixo de sublinhado podem mudar sem aviso.
Onde a biblioteca deve falhar é documentado honestamente, limite por limite, em LIMITATIONS.md. Cada entrada ali começou como um defeito reproduzido ou uma recusa deliberada de design.
Domínios desconhecidos: compostos, não confabulados
Os 18 domínios integrados são modelos. Para todo o resto, o Misata se recusa a fingir compreensão e se recusa a desistir. Um sintetizador composicional deriva estrutura da sua frase: frases nominais plurais viram tabelas, "80 apicultores" vincula uma contagem de linhas, e uma pequena rede de arquétipos (pessoa / ativo / lugar / evento / documento) fornece colunas estruturais honestas e fiação de chave estrangeira.
tables = misata.generate(
"A beekeeping cooperative with 12 apiaries, 80 beekeepers, hives, inspections and honey harvests"
)
# beekeepers: beekeeper_id, first_name, last_name, email, joined_at, status
# inspections: inspection_id, beekeeper_id, apiary_id, hive_id, inspection_date, status
# → full FK integrity, profiled timestamps, Zipfian statuses: from one sentence, no LLM
O que ele não fará é inventar semântica de domínio: entidades desconhecidas recebem colunas estruturais (códigos de referência, status, datas) e o relatório de detecção diz exatamente isso, apontando para os dois caminhos de atualização, um dicionário de esquema ou um LLM. O mesmo portão também previne confabulação: uma história que corresponde apenas fracamente a um modelo integrado (uma palavra-chave incidental) é composta a partir de suas próprias entidades, em vez de ser forçada ao modelo errado.
Cápsulas: ensine um domínio ao Misata uma vez
Uma cápsula é um único arquivo JSON compartilhável de vocabulários de domínio (as espécies, tratamentos e nomes de modelos que um domínio usa) com proveniência para cada lista. A inteligência é gasta uma vez, na criação; a geração permanece determinística, offline e gratuita.
# Mine a capsule from example data you already have: no LLM, no key
misata capsule create --domain veterinary --from-csv ./samples/ -o vet.capsule.json
misata capsule show vet.capsule.json
# Vocabularies override built-in pools for matching columns
tables = misata.generate("a veterinary clinic with patients and visits",
capsule="vet.capsule.json")
Cápsulas também podem ser escritas por um LLM uma vez e revisadas antes do uso (capsule_from_llm, chave própria; o nível gratuito da Groq funciona), ou escritas à mão: é JSON. Como uma cápsula é um arquivo, é um artefato da comunidade. Compartilhe via git, um gist ou datasets de HF.
Localização
O Misata detecta automaticamente o contexto de país da sua história e gera dados estatisticamente precisos para esse local, os nomes certos, distribuições salariais, formatos de ID nacional, moedas, códigos postais e convenções de nomenclatura de empresas.
# Locale is detected automatically: no extra flag needed
tables = misata.generate("German SaaS company in Berlin with 2k enterprise customers")
# → names from de_DE Faker pool, salary ~ lognormal(μ=10.71, σ=0.5) ≈ €45k median,
# postcodes are 5-digit, company names end in GmbH/AG/UG
tables = misata.generate("Brazilian fintech with R$ payments and CPF verification, 50k users")
# → pt_BR names, salary median ~BRL 33.6k, national IDs match CPF format ###.###.###-##
tables = misata.generate("Indian startup in Bangalore with ₹ salary bands and Aadhaar KYC")
# → hi_IN names, salary median ~₹350k/yr, national IDs match Aadhaar 12-digit format
Force ou substitua um local explicitamente:
schema = misata.parse("An ecommerce store with 10k orders")
tables = misata.generate_from_schema(schema) # defaults to en_US
# CLI
misata generate --story "Ecommerce store" --locale ja_JP
15 locais integrados
| Localidade | País | Moeda | Mediana salarial | ID nacional |
|---|---|---|---|---|
en_US | Estados Unidos | USD / $ | $62 000 | SSN ###-##-#### |
en_GB | Reino Unido | GBP / £ | £34 000 | NIN AA######A |
de_DE | Alemanha | EUR / € | €45 000 | Steuer-IdNr |
fr_FR | França | EUR / € | €38 000 | NIR |
pt_BR | Brasil | BRL / R$ | R$33 600 | CPF ###.###.###-## |
es_ES | Espanha | EUR / € | €27 000 | NIE |
hi_IN | Índia | INR / ₹ | ₹350 000 | Aadhaar ####-####-#### |
ja_JP | Japão | JPY / ¥ | ¥4 400 000 | My Number |
zh_CN | China | CNY / ¥ | ¥90 000 | Resident ID |
ar_SA | Arábia Saudita | SAR | SAR 96 000 | National ID |
ko_KR | Coreia do Sul | KRW / ₩ | ₩42 000 000 | RRN |
nl_NL | Países Baixos | EUR / € | €42 000 | BSN |
it_IT | Itália | EUR / € | €29 000 | Codice Fiscale |
pl_PL | Polônia | PLN | PLN 72 000 | PESEL |
tr_TR | Turquia | TRY | TRY 720 000 | TC Kimlik |
Cada pacote traz distribuições salariais reais (medianas e priors lognormais), distribuições etárias, cidades mais bem classificadas, prefixos de telefone, padrões de código postal, sufixos de empresas e alíquotas de IVA, com fontes da OCDE, Banco Mundial, OIT e escritórios nacionais de estatística (dados de 2023–24).
# Inspect a locale pack directly
pack = misata.get_locale_pack("de_DE")
print(pack.salary_median) # 45000
print(pack.currency_symbol) # €
print(pack.top_cities[:3]) # ['Berlin', 'Hamburg', 'Munich']
print(pack.company_suffixes) # ['GmbH', 'AG', 'UG', 'KG', 'e.K.']
# Auto-detect from a story
locale = misata.detect_locale("South Korean company in Seoul with KRW salaries")
# → "ko_KR"
Restrições
Imponha regras de negócio que sobrevivem a cada linha de geração:
from misata.constraints import (
InequalityConstraint, # price > cost on every row
ColumnRangeConstraint, # min_price <= price <= max_price
RatioConstraint, # 70% free / 30% pro
UniqueConstraint, # no duplicate (user_id, date) pairs
SumConstraint, # total_hours per employee per day <= 8
NotNullConstraint, # no nulls in required columns
)
c = InequalityConstraint("price", ">", "cost")
df = c.apply(df)
Restrições também podem ser declaradas em misata.yaml, elas rodam no momento da geração, não como uma etapa de pós-processamento.
Roll-ups entre tabelas
Faça colunas de resumo do pai reconciliarem com linhas filhas, para que os dados sobrevivam a um GROUP BY ... JOIN. Uma coluna customers.total_spent gerada independentemente dos pedidos reais daquele cliente é um sinal de que os dados são falsos; um roll-up a calcula a partir das linhas filhas reais.
schema = misata.from_dict_schema({
"name": "shop",
"tables": {
"customers": {
"rows": 500,
"columns": {
"customer_id": {"type": "int", "unique": True},
# total_spent = sum(orders.amount) per customer
"total_spent": {"type": "float", "rollup": {
"from_table": "orders", "fk": "customer_id",
"agg": "sum", "column": "amount"}},
# completed_spend = sum(amount) where status == "completed"
"completed_spend": {"type": "float", "rollup": {
"from_table": "orders", "fk": "customer_id", "agg": "sum",
"column": "amount", "where": {"status": "completed"}}},
},
},
"orders": {
"rows": 3000,
"columns": {
"order_id": {"type": "int", "unique": True},
"customer_id": {"type": "foreign_key", "references": "customers.customer_id"},
"amount": {"type": "float", "distribution": "lognormal", "mu": 4, "sigma": 0.5, "min": 1},
"status": {"type": "categorical", "choices": ["completed", "cancelled", "pending"]},
},
},
},
})
tables = misata.generate_from_schema(schema)
# tables["customers"]["total_spent"] reconciles exactly with the orders table.
Agregações: sum, count, mean, max, min. Quando um nome de coluna pai nomeia explicitamente uma tabela filha (num_orders, total_orders), o roll-up é inferido automaticamente sem declaração. Roll-ups sobrevivem ao round-trip de misata.yaml e rodam no momento da geração.
Realismo estatístico: dados que passam na validação de métodos
A maioria das ferramentas de dados sintéticos gera linhas independentemente. Isso funciona para semeadura de banco de dados e testes de pipeline. Quebra no momento em que os dados precisam passar por um método estatístico: um teste de autocorrelação em medições repetidas, um modelo de efeitos mistos verificando se grupos diferem, ou uma auditoria que detecta valores fora de limites plausíveis.
O Misata 0.8.1.0 adiciona um conjunto de recursos que fecha essa lacuna. Todos são declarados no mesmo esquema de dicionário simples e são acessíveis a partir de agentes MCP, Studio e chamadores Python diretos.
Perfis de distribuição estratificados: distribuições diferentes por subgrupo
Um conjunto de dados de teste A/B realista não extrai todos os usuários de uma única distribuição de conversão. O grupo de controle parece diferente do grupo de tratamento. Use profiles para declarar isso precisamente em qualquer coluna:
schema = misata.from_dict_schema({
"users": {
"__rows__": 5000,
"user_id": {"type": "integer", "primary_key": True},
"cohort": {
"type": "string",
"enum": ["control", "variant_a", "variant_b"],
"probabilities": [0.50, 0.25, 0.25],
},
"session_duration": {
"type": "float",
"distribution": "lognormal",
"mean": 180.0, "std": 90.0, # fallback for unmatched rows
"profiles": [
{"when": "cohort == 'control'", "distribution": "lognormal", "mean": 180.0, "std": 90.0},
{"when": "cohort == 'variant_a'", "distribution": "lognormal", "mean": 240.0, "std": 100.0},
{"when": "cohort == 'variant_b'", "distribution": "lognormal", "mean": 310.0, "std": 120.0},
],
},
}
})
A expressão when é avaliada como uma consulta pandas contra colunas já geradas no mesmo lote. Linhas que não correspondem a nenhum perfil recebem a distribuição de nível superior da coluna. Perfis podem referenciar qualquer coluna gerada antes da atual na ordem de declaração.
Ausência informativa: MAR e MNAR
Conjuntos de dados do mundo real têm valores ausentes não aleatórios. O Misata modela ambos os mecanismos:
Ausente ao Acaso (MAR): A probabilidade de um valor estar ausente depende de uma coluna observada. Usuários de alto gasto têm mais probabilidade de pular o campo opcional de renda.
"annual_income": {
"type": "float",
"nullable": True,
"missing_if": {
"predictor": "total_spend",
"relationship": "higher_increases_probability",
"base_rate": 0.05,
"max_rate": 0.40,
"mechanism": "MAR",
},
}
Ausente Não ao Acaso (MNAR): A probabilidade de um valor estar ausente depende do próprio valor. Pontuações de satisfação muito baixas são as mais prováveis de não serem relatadas.
"satisfaction_score": {
"type": "float",
"distribution": "normal", "mean": 7.5, "std": 1.8,
"nullable": True,
"missing_if": {
"predictor": "satisfaction_score", # references its own column
"mechanism": "MNAR",
"relationship": "lower_increases_probability",
"base_rate": 0.02,
"max_rate": 0.50,
},
}
Nulos condicionais (null_when): Anule uma coluna sempre que uma expressão booleana for verdadeira.
"cancellation_reason": {
"type": "string",
"enum": ["price", "competitor", "unused", "other"],
"nullable": True,
"null_when": "churned == False",
}
Controle exato de incidência: taxas precisas, não aproximações estatísticas
Uma coluna boolean com probability: 0.03 dá aproximadamente 3% de valores True em muitas execuções. Se você precisa que o conjunto de dados contenha exatamente 3% (auditável contra sua própria especificação), use exact_incidence:
"is_fraud": {
"type": "boolean",
"exact_incidence": {
"mode": "exact",
"rate": 0.03, # exactly floor(n * 0.03) rows are True
},
}
Taxas exatas por segmento funcionam da mesma forma:
"converted": {
"type": "boolean",
"exact_incidence": {
"mode": "exact",
"group_by": "cohort",
"rates": {"control": 0.12, "variant_a": 0.18, "variant_b": 0.24},
},
}
A diferença entre "aproximadamente 3% de fraude" e "exatamente 3% de fraude" é a diferença entre um conjunto de dados que passa em uma auditoria e um que não passa.
Autocorrelação temporal dentro da entidade: dados longitudinais que passam em testes estatísticos
Sem autocorrelação, um conjunto de dados longitudinal (sessões de usuário, leituras de IoT, séries temporais financeiras) é estatisticamente idêntico a um transversal. Todo teste de série temporal (Ljung-Box, Durbin-Watson, gráfico de autocorrelação) detectará imediatamente que as linhas são independentes e que os dados são sintéticos.
A especificação time_series reescreve uma coluna para ter autocorrelação real dentro da entidade:
"daily_revenue": {
"type": "float",
"distribution": "lognormal", "mean": 8500.0, "std": 3000.0,
"time_series": {
"entity_id": "store_id", # one process per store
"order_by": "day_number",
"model": "AR1", # AR1 | LINEAR_TREND | RANDOM_WALK | MEAN_REVERSION
"phi": 0.72, # autocorrelation coefficient (0 = independent, 1 = random walk)
"noise_std": 800.0,
"trend": {
"slope_mean": 45.0, # average daily growth per store
"slope_std": 12.0, # per-store growth variability
},
},
}
Quatro modelos estão disponíveis:
| Modelo | Caso de uso |
|---|---|
AR1 | Medições que persistem entre períodos: receita, usuários ativos, estoque |
LINEAR_TREND | KPIs com direção declarada: crescimento, decaimento, perda de peso, melhoria de habilidade |
RANDOM_WALK | Preços de ativos, taxas de câmbio, qualquer processo browniano sem média |
MEAN_REVERSION | Métricas limitadas que puxam de volta para a média: NPS, taxa de preenchimento de estoque |
Distribuições ancoradas por entidade: separando variação dentro da entidade e entre entidades
Quando a coluna de uma tabela filha deve ser ancorada ao valor da entidade pai, use uma fórmula em distribution.mean:
"stores": {
"__rows__": 50,
"store_id": {"type": "integer", "primary_key": True},
"baseline_daily_revenue": {"type": "float", "distribution": "lognormal", "mean": 8500.0, "std": 3000.0},
},
"daily_sales": {
"__rows__": 18250, # 50 stores × 365 days
"record_id": {"type": "integer", "primary_key": True},
"store_id": {"type": "integer", "foreign_key": {"table": "stores", "column": "store_id"}},
"revenue": {
"type": "float",
"distribution": "normal",
"mean": {"formula": "@stores.baseline_daily_revenue"}, # anchored to each store's baseline
"std": 800.0, # day-to-day noise
},
}
O mecanismo resolve a FK para cada linha e amostra da distribuição personalizada daquela entidade. A variação entre lojas vem da dispersão de baseline_daily_revenue; o ruído diário dentro da loja é std: 800. Gerar todas as linhas a partir de uma única distribuição compartilhada (como faz todo gerador independente de coluna) colapsa a variância entre entidades e dentro da entidade em um único número e falha em todo teste de efeitos aleatórios.
Efeitos de cluster ICC hierárquicos: estrutura de grupo que sobrevive a testes estatísticos
Quando linhas são agrupadas sob entidades pai (lojas, regiões, filiais), observações dentro do mesmo grupo tendem a ser mais semelhantes entre si do que observações entre grupos. Essa homogeneidade dentro do grupo (coeficiente de correlação intraclasse (ICC)) é uma característica definidora de dados agrupados. Sem ela, todos os grupos parecem estatisticamente idênticos.
__cluster_effect__ é declarado na tabela pai e aplica interceptos aleatórios por entidade às colunas da tabela filha:
"regions": {
"__rows__": 8,
"__cluster_effect__": {
"affects_table": "stores",
"affects_columns": {
"avg_order_value": {
"icc": 0.22, # target intraclass correlation
"sd_total": 45.0, # sd_between = sqrt(0.22) * 45 ≈ 21
},
"conversion_rate": {
"sd_between": 0.04, # supply sd_between directly
},
},
},
"region_id": {"type": "integer", "primary_key": True},
"name": {"type": "string", "enum": ["North", "South", "East", "West", "Central", "NW", "NE", "SE"]},
}
Um intercepto aleatório é amostrado por entidade pai de N(0, sd_between) e adicionado a cada linha filha naquele grupo. A distribuição marginal em todas as linhas é preservada. Valores típicos de ICC: 0,05–0,20 para métricas de varejo no nível de loja, 0,10–0,30 para resultados educacionais entre escolas, 0,15–0,40 para métricas bancárias no nível de filial.
Matriz de correlação completa: declare a estrutura de covariância inteira de uma vez
Para tabelas com muitas colunas correlacionadas, a sintaxe de matriz é mais limpa do que uma lista de pares:
"__correlations__": {
"matrix": {
"columns": ["session_duration", "pages_viewed", "revenue", "satisfaction"],
"values": {
"session_duration": [1.00, 0.71, 0.55, 0.32],
"pages_viewed": [0.71, 1.00, 0.48, 0.28],
"revenue": [0.55, 0.48, 1.00, 0.41],
"satisfaction": [0.32, 0.28, 0.41, 1.00],
}
}
}
A matriz é expandida em pares e aplicada via reordenação de postos Iman-Conover, que atinge os valores de r de Pearson declarados enquanto preserva exatamente a distribuição marginal de cada coluna. A sintaxe de lista de pares ainda funciona sem alterações.
Estados terminais de máquina de estados: colunas categóricas corretas para processos
Qualquer coluna que represente a posição de uma entidade em um processo (estágio do ciclo de vida do cliente, estado de atendimento do pedido, status de assinatura) deve seguir uma cadeia de Markov, não uma probabilidade plana. __state_machine__ gera a distribuição terminal correta:
"orders": {
"__state_machine__": {
"state_column": "status",
"initial_state": "placed",
"transitions": {
"placed": {"confirmed": 0.95, "cancelled": 0.05},
"confirmed": {"shipped": 0.92, "cancelled": 0.08},
"shipped": {"delivered": 0.97, "returned": 0.03},
},
},
...
}
Estados sem transições de saída são terminais. O mecanismo percorre a cadeia por linha até atingir um estado terminal. As probabilidades de transição declaradas são preservadas em expectativa. Funciona junto com incidência exata, perfis, correlações e séries temporais na mesma tabela.
Validação de dados: capture valores fora dos limites antes que cheguem ao seu pipeline
Após a geração, valide contra os limites de domínio declarados antes que os dados cheguem a um modelo ou dashboard:
tables = misata.generate_from_schema(schema)
report = misata.validate_domain(tables, domain="financial")
print(report.summary())
# Domain validation (financial): 0 errors, 0 warnings.
assert report.passed
Intervalos integrados para financial / fintech: preço ≥ 0, desconto 0–1, taxa –1 a 100, salário ≥ 0. A correspondência de colunas é por substring no nome da coluna em minúsculas, "unit_price" corresponde à regra price.
Adicione intervalos personalizados via o dicionário custom_ranges para qualquer tipo de coluna. Declare "__domain__": "financial" no esquema do dicionário para anexar o domínio ao SchemaConfig para ferramentas downstream.
Exportação
# Columnar / analytical
misata.to_parquet(tables, "data/")
misata.to_arrow(tables, "data/") # Apache Arrow IPC; requires pip install pyarrow
misata.to_duckdb(tables, "data/dataset.duckdb")
# Row-oriented
misata.to_jsonl(tables, "data/")
misata.to_sql(tables, "data/", dialect="postgresql") # CREATE TABLE + INSERT statements
# dialects: ansi, postgresql, mysql
Linhas incrementais reproduzíveis
Gere linhas adicionais que se anexam limpo a um conjunto de dados existente sem colisões de ID:
# Day 1: generate the base dataset
schema = misata.from_dict_schema({...}, seed=1)
base = misata.generate_from_schema(schema)
for name, df in base.items():
df.to_csv(f"./data/{name}.csv", index=False)
# Day 2: generate only new rows, PKs offset above existing max
new_rows = misata.generate_diff(
schema,
existing_dir="./data/",
new_rows={"customers": 200, "orders": 1500},
output_dir="./data/delta/", # optional: write delta CSVs
)
generate_diff lê CSVs existentes para encontrar o PK máximo por tabela e gera novas linhas com PKs deslocados acima desse máximo. Use para pipelines de streaming, fixtures de teste dia a dia e qualquer fluxo de trabalho onde você precise estender um conjunto de dados sem regenerá-lo do zero.
Databricks e Apache Spark
Gere dados de teste realistas e referencialmente corretos diretamente no Delta Lake: nenhum dado de produção necessário. O módulo misata.spark conecta a saída pandas do Misata ao Spark/Delta no Databricks (Edição Gratuita ou completa), AWS Glue, EMR ou qualquer cluster PySpark 3.3+.
import misata
from misata import spark as mspark
schema = misata.from_dict_schema({
"customers": {"__rows__": 500, "id": {"type": "integer", "primary_key": True},
"email": {"type": "email"}, "country": {"type": "string", "text_type": "country"}},
"orders": {"__rows__": 2000, "id": {"type": "integer", "primary_key": True},
"customer_id": {"type": "integer",
"foreign_key": {"table": "customers", "column": "id"}},
"total": {"type": "float", "distribution": "lognormal", "mu": 4.5, "sigma": 0.9}},
})
# One call: generate all tables (FK integrity guaranteed) and write to Delta
result = mspark.generate_to_delta(schema, spark, catalog="dev", database="bronze", mode="overwrite")
print(result.summary())
# ✅ customers (500 rows) → dev.bronze.customers
# ✅ orders (2,000 rows) → dev.bronze.orders
O que faz que o dbldatagen não pode: múltiplas tabelas relacionadas em uma única chamada, integridade referencial garantida, distribuições realistas e conformidade de resultado, declare um agregado ou taxa exata (por exemplo, "fraude é 1,8% em janeiro aumentando para 4,1% em junho") e os dados se conformam, dando aos testes de pipeline downstream uma verdade fundamental conhecida para afirmar contra.
| Função | Propósito |
|---|---|
generate_to_delta(schema, spark, …) | Uma linha: gerar + escrever todas as tabelas no Delta |
to_spark(tables, spark, schema_config=…) | Converter DataFrames do Misata para Spark com um esquema explícito e de tipo correto |
write_delta(tables, spark, …) | Escrever no Delta com particionamento, clustering líquido, propriedades de tabela ou upsert MERGE |
verify_delta_integrity(spark, relationships, …) | Verificar integridade de FK de tabelas Delta via anti-joins Spark SQL |
from_catalog_schema(spark, database, …) | Importar um esquema existente do Unity Catalog (somente estrutura) → gerar dados correspondentes, FKs inferidas automaticamente |
append_to_delta(schema, spark, n_rows=…) | Anexar linhas incrementais com PKs sem colisão |
write_delta_stream(schema, spark, …) | Gravar em fluxo conjuntos de dados com 100M+ linhas sem buffer |
No Databricks serverless / Edição Gratuita, instale misata simples (PySpark já está no cluster, instalar misata[spark] interromperia uma sessão serverless). Em outros ambientes: pip install misata[spark].
Tutorial de ponta a ponta: um pipeline completo de medalhão de detecção de fraude (Bronze → Prata → Ouro) testado inteiramente em dados sintéticos, com uma afirmação de verdade fundamental de nível CI, examples/databricks/. Referência completa da API: docs/spark.md.
Geração de documentos
Renderize um documento por linha de qualquer tabela, útil para conjuntos de dados de demonstração que precisam parecer reais de ponta a ponta:
# Built-in templates: invoice, patient_report, transaction_receipt, user_profile
paths = misata.generate_documents(
tables, "invoice", table="orders", output_dir="/tmp/invoices", format="html"
)
# format="pdf" requires: pip install "misata[documents]"
# Custom Jinja2 template
tmpl = "<h1>Order #{{ order_id }}</h1><p>Amount: ${{ amount }}</p>"
paths = misata.generate_documents(tables, tmpl, table="orders", output_dir="/tmp/custom")
Análise de qualidade e privacidade
bundle = misata.analyze_generation(tables, schema) # runs privacy, fidelity, data_card
print(bundle.fidelity.overall_score) # 0–100 statistical fidelity score vs. schema intent
print(bundle.fidelity.grade) # letter grade for the same score
print(bundle.privacy.overall_risk_score) # heuristic PII / re-identification risk
print(bundle.data_card.tables) # per-table row counts and metadata
Evalpacks: bancos de dados de avaliação onde a chave de resposta não pode estar errada
Benchmarks publicados de texto-para-SQL são construídos anotando pares de pergunta/resposta sobre um banco de dados existente, e essa etapa de anotação é onde erros generalizados de chave de resposta se infiltram. Um evalpack inverte a ordem: a verdade fundamental é a própria especificação declarada (curvas de resultado, curvas de taxa, relacionamentos de FK), o Misata gera um banco de dados que a satisfaz, e cada pergunta enviada no pacote é então verificada executando seu SQL dourado contra os arquivos CSV escritos com DuckDB, um mecanismo que não compartilha código com o gerador. Perguntas cuja resposta observada não corresponde exatamente à resposta declarada são descartadas e registradas no manifesto. Uma chave de resposta errada é impossível por construção e verificada duas vezes por execução independente.
pip install "misata[evalpack]"
misata evalpack --config misata.yaml -o ./my_pack --seed 42
from misata.evalpack import build_evalpack
result = build_evalpack(schema, "my_pack")
assert result.all_verified
Cada pacote inclui as tabelas como CSVs, questions.jsonl, um certificado de verificação por pergunta, um manifesto com o hash da especificação e a semente, e um verify.py autônomo que qualquer pessoa pode reexecutar com nada além de duckdb instalado. Use para avaliar agentes SQL, sistemas RAG-sobre-banco-de-dados ou qualquer ferramenta que afirme responder perguntas sobre dados, contra um banco de dados cujas respostas corretas são conhecidas antes de uma única linha existir.
Domínios suportados
18 esquemas de domínio integrados, cada um gera um conjunto de dados totalmente relacional e multi-tabela com distribuições realistas, integridade de FK e semântica de coluna apropriada ao domínio.
| Domínio | Palavras-chave de gatilho | Tabelas geradas |
|---|---|---|
| SaaS | saas, assinatura, mrr, churn | usuários, assinaturas, faturas |
| Ecommerce | ecommerce, pedidos, loja, varejo | clientes, produtos, pedidos, itens_pedido |
| Fintech | fintech, pagamentos, bancário, fraude | clientes, contas, transações |
| Saúde | saúde, pacientes, médicos, clínica | médicos, pacientes, consultas |
| Marketplace | marketplace, vendedores, compradores, anúncios | vendedores, compradores, anúncios, pedidos |
| Logística | logística, envio, motoristas, rotas | motoristas, veículos, rotas, remessas |
| RH | rh, funcionários, folha de pagamento, força de trabalho | departamentos, funcionários, folha de pagamento |
| Social | mídia social, instagram, feed, seguidores | usuários, postagens, seguidores, reações |
| Imobiliário | imobiliário, habitação, hipoteca | agentes, propriedades, transações |
| Farma | farma, clínico, ensaios | pesquisadores, projetos, ensaios, planilhas de tempo |
| Entrega de Comida | entrega de comida, restaurante, retirada | restaurantes, clientes, entregadores, pedidos, itens_pedido |
| EdTech | edtech, cursos, estudantes, matrículas | instrutores, cursos, estudantes, matrículas, tentativas_quiz |
| Jogos | jogos, jogadores, leaderboard, esports | jogadores, partidas, sessões, conquistas |
| CRM | crm, salesforce, negócios, pipeline | empresas, contatos, negócios, atividades |
| Crypto / Web3 | crypto, blockchain, ethereum, defi | carteiras, tokens, transações, preços_tokens |
| Seguros | seguros, apólice, sinistros, prêmio | clientes, apólices, sinistros, pagamentos |
| Viagens | viagens, hotel, voos, reservas | usuários, hotéis, voos, reservas, avaliações |
| Streaming | streaming, netflix, assinantes, histórico de visualização | assinantes, conteúdo, histórico_visualização, avaliações |
Sem correspondência de palavra-chave → o sintetizador composicional constrói um esquema estrutural multi-tabela a partir das próprias entidades da sua frase (veja Domínios desconhecidos acima); histórias sem entidades nenhuma caem em uma tabela única genérica com inferência inteligente de colunas.
Como funciona
story / YAML / dict / DB introspection / MCP tool call
↓
StoryParser · compositional synthesizer · locale detection · load_yaml_schema · schema_from_db
↓
DetectionReport (domain, confidence, near_misses, table_preview, warnings)
↓
SchemaConfig ← validate_schema() catches issues before any rows are generated
↓
DataSimulator
├─ topological sort (FK dependency order)
├─ domain priors → locale priors (salary, age, monetary)
├─ constraint engine (inequality, range, ratio, sum, unique)
├─ outcome curves (monthly targets from narrative control points)
├─ stratified profiles (per-subgroup distributions, pandas eval)
├─ AR1 / time-series autocorrelation (per entity, 4 models)
├─ state machine (Markov terminal states)
├─ ICC cluster effects (per-parent-entity random intercepts)
├─ Iman-Conover correlation engine (pairwise + full matrix)
├─ MAR / MNAR missingness (predictor-scaled and value-dependent)
├─ exact incidence (floor(n × rate), per-group rates)
├─ realism core (joint identities, temporal profiles, Zipf marginals,
│ geo facts, grammar microtext, numeric quantization)
└─ RealisticTextGenerator (capsules + Faker locale + vocabulary assets)
↓
{table_name: DataFrame}
↓
validate_domain · seed_database · to_parquet · to_arrow
to_duckdb · to_sql · to_jsonl · generate_documents · MCP CSV output
Priors de domínio: colunas monetárias recebem distribuições log-normais. Categóricas usam amostragem Zipf. Tipos sanguíneos, distribuições de países e faixas salariais refletem estatísticas do mundo real.
Priors de localidade: distribuições de salário e idade são substituídas por parâmetros lognormais/normais específicos do país, provenientes de estatísticas nacionais. "Brazilian fintech" na sua história significa que salários são amostrados da distribuição BRL, não da USD.
Curvas de resultado: narrativa em linguagem natural é analisada em pontos de controle mensais exatos. Eventos nomeados, trimestres e multiplicadores funcionam todos:
# All of these produce precise, shaped outcome curves:
misata.generate("SaaS mrr from $50k in Jan to $200k in Dec, with a Q3 slump")
misata.generate("Ecommerce orders, Black Friday spike, Christmas peak")
misata.generate("SaaS startup, MRR 10x growth over the year")
misata.generate("Fintech payments, strong Q4, dip in Q1")
Regras de realismo: cost é sempre menor que price. delivered_at é sempre depois de shipped_at. hire_date é depois de date_of_birth + 18 anos e nunca no futuro. tenure_years é derivado na mesma linha de hire_date. Endereços de e-mail derivam de colunas de primeiro e último nome, nomes concordam com gêneros declarados, distâncias de rota concordam com suas cidades e texto de avaliação concorda com sua classificação por estrelas.
O que torna o Misata diferente
A comparação reflete o comportamento documentado e pronto para uso de cada ferramenta no final de 2025; todas são bibliotecas capazes construídas para objetivos diferentes, e um "Não" significa "não é um recurso integrado", não "impossível".
| Faker | Synth | syda | SDV | Misata | |
|---|---|---|---|---|---|
| Sem configuração, uma linha para dados multi-tabela | Não | Não | Não | Não | Sim |
| História detecta automaticamente localidade + estatísticas do país | Não | Não | Não | Não | Sim |
| 18 esquemas de domínio integrados (SaaS → streaming) | Não | Não | Não | Não | Sim |
| Curvas narrativas (impulso do Q4, Black Friday, 10×) | Não | Não | Não | Não | Sim |
| Domínios desconhecidos compostos a partir da própria frase | Não | Não | Não | Não | Sim |
| Identidades coerentes (nome ↔ gênero ↔ e-mail concordam) | Não | Não | Não | Não | Sim |
| Texto de avaliação comprovadamente corresponde à classificação por estrelas | Não | Não | Não | Não | Sim |
| Distâncias reais entre cidades em tabelas de rotas | Não | Não | Não | Não | Sim |
| Cápsulas de vocabulário de domínio compartilháveis | Não | Não | Não | Não | Sim |
| Modo imitação: clonar distribuições de um CSV | Não | Não | Não | Sim | Sim |
| Correlação pareada + matriz completa (Iman-Conover) | Não | Não | Não | Sim | Sim |
| Colunas geoespaciais (lat, lng, postal_code) | Não | Não | Não | Não | Sim |
| Injeção de anomalias (taxa de outliers por coluna) | Não | Não | Não | Não | Sim |
| Servidor MCP: utilizável a partir do Claude / Cursor | Não | Não | Não | Não | Sim |
| Esquema YAML versionado no git | Não | Sim | Sim | Não | Sim |
| Validação JSON Schema + preenchimento automático do editor | Não | Não | Não | Não | Sim |
| Introspecção de banco → gerar → re-semear | Não | Sim | Não | Limitado | Sim |
| Semeadura direta de banco (Postgres / MySQL / SQLite) | Não | Não | Não | Não | Sim |
| Semeadura de modelos SQLAlchemy | Não | Não | Não | Não | Sim |
| Integridade referencial em todas as tabelas FK | Não | Sim | Sim | Sim | Sim |
Restrições de desigualdade / intervalo (price > cost) | Não | Limitado | Não | Sim | Sim |
| Curvas de meta agregadas (formato MRR mensal) | Não | Não | Não | Não | Sim |
| Distribuições estratificadas por subgrupo (perfis) | Não | Não | Não | Não | Sim |
| Ausência informativa MAR e MNAR | Não | Não | Não | Não | Sim |
| Controle exato de incidência (valores True de piso(n × taxa)) | Não | Não | Não | Não | Sim |
| AR(1) / autocorrelação de séries temporais por entidade | Não | Não | Não | Não | Sim |
| Efeitos de cluster ICC hierárquicos (multi-site) | Não | Não | Não | Não | Sim |
| Fórmula @parent na média/desvio padrão da distribuição | Não | Não | Não | Não | Sim |
| Estados terminais de máquina de estados Markov | Não | Não | Não | Não | Sim |
| Validação ciente de domínio (faixas clínicas/financeiras) | Não | Não | Não | Não | Sim |
| Exportação SQL INSERT (ansi / postgresql / mysql) | Não | Não | Não | Não | Sim |
| Exportação Apache Arrow IPC | Não | Não | Não | Não | Sim |
| Linhas incrementais reproduzíveis (generate_diff) | Não | Não | Não | Não | Sim |
| Distribuições realistas de domínio | Não | Não | Não | Limitado | Sim |
| LLM multi-provedor (Groq / OpenAI / Claude / Gemini / Ollama) | Não | Não | Sim | Não | Sim |
| Totalmente offline, sem necessidade de LLM | Sim | Sim | Não | Sim | Sim |
| Geração de documentos (HTML / PDF por linha) | Não | Não | Não | Não | Sim |
| Relatórios de qualidade + privacidade | Não | Não | Não | Limitado | Sim |
| Python puro, sem serviços externos | Sim | Não | Não | Sim | Sim |
Faker gera valores falsos individuais, não relacionais, sem esquema, sem precisão estatística.
Synth se destaca em fluxos de trabalho git de esquema-como-código; controle limitado de distribuição.
syda usa um LLM para cada linha, semanticamente rico, mas caro, lento e requer uma chave de API.
SDV aprende com dados reais, um problema diferente (você precisa de dados reais primeiro).
Gretel é um serviço em nuvem que precisa de uma chave de API e envia dados para fora do ambiente; Misata roda localmente.
Misata gera a partir da intenção, offline por padrão, semeia bancos de dados diretamente e agora traz estatísticas precisas por país para cada coluna automaticamente.
Comparações completas frente a frente: Misata vs Faker, Misata vs SDV, Misata vs Gretel.
Desempenho
Medido em Apple M-series (núcleo único, sem GPU):
| Carga de trabalho | Linhas | Tempo | Taxa de transferência |
|---|---|---|---|
| Tabela única, lognormal | 1 000 000 | 0,06 s | ~16M linhas/s |
| Esquema estrela (5 tabelas, 4 FKs) | 1 055 030 | 1,54 s | ~687k linhas/s |
Contribuindo
git clone https://github.com/rasinmuhammed/misata
cd misata
pip install -e ".[dev]"
pytest tests/
1.088 testes, 0 falhas. Issues e PRs são bem-vindos, github.com/rasinmuhammed/misata/issues