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

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.

PyPI version Python versions CI License Open in Colab Paper HF Paper smithery badge Misata Studio

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, com fidelity_report e privacy_report para 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.25 gera valores no formato 7.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 retornar 413 Request too large no 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 e time_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ícioO mecanismo
Pablo Müller, Female: nomes, gêneros e culturas sorteados independentementeAmostragem 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 domingoPerfis 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ávelMarginais 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 kmFatos 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 ipsumMicrotexto 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,27Quantizaçã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 cadastrouOrdenaçã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 lugarCoerê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 uniformeBase 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 linhaCoerê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

LocalidadePaísMoedaMediana salarialID nacional
en_USEstados UnidosUSD / $$62 000SSN ###-##-####
en_GBReino UnidoGBP / ££34 000NIN AA######A
de_DEAlemanhaEUR / €€45 000Steuer-IdNr
fr_FRFrançaEUR / €€38 000NIR
pt_BRBrasilBRL / R$R$33 600CPF ###.###.###-##
es_ESEspanhaEUR / €€27 000NIE
hi_INÍndiaINR / ₹₹350 000Aadhaar ####-####-####
ja_JPJapãoJPY / ¥¥4 400 000My Number
zh_CNChinaCNY / ¥¥90 000Resident ID
ar_SAArábia SauditaSARSAR 96 000National ID
ko_KRCoreia do SulKRW / ₩₩42 000 000RRN
nl_NLPaíses BaixosEUR / €€42 000BSN
it_ITItáliaEUR / €€29 000Codice Fiscale
pl_PLPolôniaPLNPLN 72 000PESEL
tr_TRTurquiaTRYTRY 720 000TC 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:

ModeloCaso de uso
AR1Medições que persistem entre períodos: receita, usuários ativos, estoque
LINEAR_TRENDKPIs com direção declarada: crescimento, decaimento, perda de peso, melhoria de habilidade
RANDOM_WALKPreços de ativos, taxas de câmbio, qualquer processo browniano sem média
MEAN_REVERSIONMé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çãoPropó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ínioPalavras-chave de gatilhoTabelas geradas
SaaSsaas, assinatura, mrr, churnusuários, assinaturas, faturas
Ecommerceecommerce, pedidos, loja, varejoclientes, produtos, pedidos, itens_pedido
Fintechfintech, pagamentos, bancário, fraudeclientes, contas, transações
Saúdesaúde, pacientes, médicos, clínicamédicos, pacientes, consultas
Marketplacemarketplace, vendedores, compradores, anúnciosvendedores, compradores, anúncios, pedidos
Logísticalogística, envio, motoristas, rotasmotoristas, veículos, rotas, remessas
RHrh, funcionários, folha de pagamento, força de trabalhodepartamentos, funcionários, folha de pagamento
Socialmídia social, instagram, feed, seguidoresusuários, postagens, seguidores, reações
Imobiliárioimobiliário, habitação, hipotecaagentes, propriedades, transações
Farmafarma, clínico, ensaiospesquisadores, projetos, ensaios, planilhas de tempo
Entrega de Comidaentrega de comida, restaurante, retiradarestaurantes, clientes, entregadores, pedidos, itens_pedido
EdTechedtech, cursos, estudantes, matrículasinstrutores, cursos, estudantes, matrículas, tentativas_quiz
Jogosjogos, jogadores, leaderboard, esportsjogadores, partidas, sessões, conquistas
CRMcrm, salesforce, negócios, pipelineempresas, contatos, negócios, atividades
Crypto / Web3crypto, blockchain, ethereum, deficarteiras, tokens, transações, preços_tokens
Segurosseguros, apólice, sinistros, prêmioclientes, apólices, sinistros, pagamentos
Viagensviagens, hotel, voos, reservasusuários, hotéis, voos, reservas, avaliações
Streamingstreaming, netflix, assinantes, histórico de visualizaçãoassinantes, 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".

FakerSynthsydaSDVMisata
Sem configuração, uma linha para dados multi-tabelaNãoNãoNãoNãoSim
História detecta automaticamente localidade + estatísticas do paísNãoNãoNãoNãoSim
18 esquemas de domínio integrados (SaaS → streaming)NãoNãoNãoNãoSim
Curvas narrativas (impulso do Q4, Black Friday, 10×)NãoNãoNãoNãoSim
Domínios desconhecidos compostos a partir da própria fraseNãoNãoNãoNãoSim
Identidades coerentes (nome ↔ gênero ↔ e-mail concordam)NãoNãoNãoNãoSim
Texto de avaliação comprovadamente corresponde à classificação por estrelasNãoNãoNãoNãoSim
Distâncias reais entre cidades em tabelas de rotasNãoNãoNãoNãoSim
Cápsulas de vocabulário de domínio compartilháveisNãoNãoNãoNãoSim
Modo imitação: clonar distribuições de um CSVNãoNãoNãoSimSim
Correlação pareada + matriz completa (Iman-Conover)NãoNãoNãoSimSim
Colunas geoespaciais (lat, lng, postal_code)NãoNãoNãoNãoSim
Injeção de anomalias (taxa de outliers por coluna)NãoNãoNãoNãoSim
Servidor MCP: utilizável a partir do Claude / CursorNãoNãoNãoNãoSim
Esquema YAML versionado no gitNãoSimSimNãoSim
Validação JSON Schema + preenchimento automático do editorNãoNãoNãoNãoSim
Introspecção de banco → gerar → re-semearNãoSimNãoLimitadoSim
Semeadura direta de banco (Postgres / MySQL / SQLite)NãoNãoNãoNãoSim
Semeadura de modelos SQLAlchemyNãoNãoNãoNãoSim
Integridade referencial em todas as tabelas FKNãoSimSimSimSim
Restrições de desigualdade / intervalo (price > cost)NãoLimitadoNãoSimSim
Curvas de meta agregadas (formato MRR mensal)NãoNãoNãoNãoSim
Distribuições estratificadas por subgrupo (perfis)NãoNãoNãoNãoSim
Ausência informativa MAR e MNARNãoNãoNãoNãoSim
Controle exato de incidência (valores True de piso(n × taxa))NãoNãoNãoNãoSim
AR(1) / autocorrelação de séries temporais por entidadeNãoNãoNãoNãoSim
Efeitos de cluster ICC hierárquicos (multi-site)NãoNãoNãoNãoSim
Fórmula @parent na média/desvio padrão da distribuiçãoNãoNãoNãoNãoSim
Estados terminais de máquina de estados MarkovNãoNãoNãoNãoSim
Validação ciente de domínio (faixas clínicas/financeiras)NãoNãoNãoNãoSim
Exportação SQL INSERT (ansi / postgresql / mysql)NãoNãoNãoNãoSim
Exportação Apache Arrow IPCNãoNãoNãoNãoSim
Linhas incrementais reproduzíveis (generate_diff)NãoNãoNãoNãoSim
Distribuições realistas de domínioNãoNãoNãoLimitadoSim
LLM multi-provedor (Groq / OpenAI / Claude / Gemini / Ollama)NãoNãoSimNãoSim
Totalmente offline, sem necessidade de LLMSimSimNãoSimSim
Geração de documentos (HTML / PDF por linha)NãoNãoNãoNãoSim
Relatórios de qualidade + privacidadeNãoNãoNãoLimitadoSim
Python puro, sem serviços externosSimNãoNãoSimSim

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 trabalhoLinhasTempoTaxa de transferência
Tabela única, lognormal1 000 0000,06 s~16M linhas/s
Esquema estrela (5 tabelas, 4 FKs)1 055 0301,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


Construído por Muhammed Rasin