Quality is not optional. It's our standard. Free QA tools for testers and developers.

AI Test Data Deduplication: Prompts and Post-Processing

QTQA3 Team

AI Test Data Deduplication: Prompts and Post‑Processing

Duplicate rows in a synthetic data set are more than a nuisance. They inflate test‑run times, hide boundary‑condition bugs, and make coverage metrics look better than they really are. When the data comes from an AI test data generator, the problem is amplified because the model can repeatedly emit the same “high‑probability” patterns unless you explicitly steer it away.

This post walks through a practical, evidence‑driven workflow for deduplicating AI‑generated test data—from prompt design to post‑processing pipelines—so you can keep data volumes high and uniqueness high.


1. Why Duplicates Appear in AI‑Generated Data

Root causeTypical symptomQuick diagnostic
Mode collapse – the model favors a few “safe” templatesLarge clusters of identical rows (e.g., same email domain, same address format)Count distinct values per column; a sharp drop vs. expected cardinality
Prompt underspecification – missing constraints on uniquenessWhole tables repeated across runsCompare row‑hashes between consecutive generations
Sampling temperature too low – deterministic outputIdentical output for identical seedRaise temperature, re‑run, observe variance
Post‑generation concatenation – merging multiple batches without de‑dupingSudden spikes in row count after a merge stepTrack row‑hash set size after each merge

If any of these show up, you have a deduplication problem worth solving before the data reaches your test harness.


2. Decision Criteria: When to Deduplicate

CriterionThreshold (example)Action
Duplicate‑row ratio> 2 % of total rowsRun deduplication
Column‑level cardinality loss< 80 % of expected distinct valuesAdd uniqueness constraints to prompt
Test‑execution time increase> 15 % vs. baselineDeduplicate + re‑balance data volume
Coverage metric drift> 5 % drop in branch coverage after data refreshVerify data diversity, then deduplicate

Set the thresholds once per project; revisit them when the data model changes.


3. End‑to‑End Workflow

┌─────────────────────┐
│ 1. Prompt design    │
│    (uniqueness cues)│
└───────┬─────────────┘
        ▼
┌─────────────────────┐
│ 2. Generate batch   │
│    (AI test data    │
│     generator)      │
└───────┬─────────────┘
        ▼
┌─────────────────────┐
│ 3. Immediate hash   │
│    deduplication    │
│    (exact‑match)    │
└───────┬─────────────┘
        ▼
┌─────────────────────┐
│ 4. Fuzzy/cluster    │
│    deduplication    │
│    (near‑duplicates)│
└───────┬─────────────┘
        ▼
┌─────────────────────┐
│ 5. Validation &     │
│    metrics          │
│    (uniqueness,     │
│     referential,    │
│     distribution)   │
└───────┬─────────────┘
        ▼
┌─────────────────────┐
│ 6. Store / publish  │
│    (versioned data  │
│     artifact)       │
└─────────────────────┘

Each stage can be a separate CI job, making the pipeline auditable and replayable.


4. Prompt Engineering for Uniqueness

4.1 Core Principles

  1. Explicitly request diversity – “Generate 1 000 distinct user profiles.”
  2. Constrain high‑cardinality fields – “Email domain must be one of: example.com, test.org, demo.net; each used at most 5 % of the time.”
  3. Add a uniqueness token – “Prefix each user_id with a UUIDv4 fragment.”
  4. Ask for a checksum column – “Include a row_hash = SHA‑256(concat(all_fields)).”

4.2 Sample Prompts

GoalPrompt snippet
Distinct customersCreate 5,000 unique customer records. Each customer_idmust be a UUID.email must use a different domain for every 100 rows (cycle through @alpha.io, @beta.io, @gamma.io). No two rows may share the same (first_name, last_name, email) triple.
Transaction varietyProduce 10,000 financial transactions. txn_id= UUID.amountdrawn from a log‑normal distribution (μ=4, σ=1).currency ∈ {USD,EUR,GBP,JPY} with equal probability. Ensure no duplicate (txn_id, account_id, timestamp) combination.
Schema‑awareGiven the following Avro schema … generate 2,000 records that satisfy all uniqueconstraints defined in the schema. Add arecord_hash field containing SHA‑256 of all other fields.

Tip: Put the uniqueness rules before the “generate N rows” instruction. LLMs treat early constraints as higher priority.

4.3 Iterative Prompt Refinement

  1. Run a small batch (≈200 rows).
  2. Compute duplicate‑row ratio.
  3. If > 1 %, add a stricter clause (“email must be globally unique”).
  4. Re‑run until the ratio drops below your threshold.

Because the model’s stochasticity changes with each run, keep the prompt versioned alongside the data artifact.


5. Post‑Processing Deduplication Techniques

5.1 Exact‑Match Hash Deduplication (Fast, Zero‑Loss)

import hashlib, pandas as pd


def row_hash(row: pd.Series) -> str:
    # deterministic ordering
    blob = "|".join(str(row[c]) for c in sorted(row.index))
    return hashlib.sha256(blob.encode()).hexdigest()


df["row_hash"] = df.apply(row_hash, axis=1)
df_unique = df.drop_duplicates(subset="row_hash")

Pros: O(N) with a hash set, guarantees bit‑wise identical rows are removed.
Cons: Does not catch “near‑duplicates” (e.g., same address with different casing).

5.2 Fuzzy / Near‑Duplicate Detection

TechniqueWhen to useImplementation sketch
Token‑based Jaccard (e.g., datasketch.MinHash)Free‑text fields (address, description)Build MinHash per row, LSH bucket, keep one per bucket
Embedding + cosine similarityHigh‑dimensional categorical combossentence-transformers → 384‑dim vectors → FAISS index, threshold 0.95
Rule‑based canonicalisationKnown normalisation (phone, email)Lower‑case email, strip non‑digits from phone, then exact‑hash

Example – MinHash LSH (Python)

from datasketch import MinHash, MinHashLSH


def minhash_of(row, cols, num_perm=128):
    m = MinHash(num_perm=num_perm)
    for c in cols:
        for token in str(row[c]).split():
            m.update(token.encode())
    return m


lsh = MinHashLSH(threshold=0.9, num_perm=128)
keep = []
for idx, row in df.iterrows():
    mh = minhash_of(row, ["address", "city", "state"])
    if not lsh.query(mh):
        lsh.insert(str(idx), mh)
        keep.append(idx)
df_fuzzy = df.loc[keep]

5.3 Cluster‑Based Representative Selection

When you need representative diversity rather than strict uniqueness:

  1. Cluster rows (e.g., K‑Means on embeddings, or hierarchical on categorical distance).
  2. Pick the centroid or a random member per cluster.
  3. Validate that cluster count meets coverage goals.

This is useful for property‑based test suites where you want a spread of input shapes.

5.4 Referential‑Integrity Preservation

Deduplication must not break foreign‑key relationships.

-- Example: keep only one address per customer_id, but preserve all orders
WITH dedup_addr AS (
  SELECT DISTINCT ON (customer_id) *
  FROM addresses
  ORDER BY customer_id, created_at DESC
)
SELECT o.*, a.*
FROM orders o
JOIN dedup_addr a ON o.customer_id = a.customer_id;

In a pipeline, run referential checks after deduplication:

CheckQuery
Orphan foreign keysSELECT COUNT(*) FROM child LEFT JOIN parent ON child.fk = parent.pk WHERE parent.pk IS NULL;
Duplicate primary keysSELECT pk, COUNT(*) FROM table GROUP BY pk HAVING COUNT(*) > 1;

6. Worked Example: User‑Profile Data Set

6.1 Requirements

AttributeConstraint
user_idUUID, primary key
emailGlobally unique, domain rotation (5 domains)
phoneE.164 format, unique per country code
addressFree text, but no two rows share the same normalized address
created_atTimestamp, monotonic per batch

Target size: 20 000 rows.

6.2 Prompt (sent to the AI test data generator)

Generate 20,000 unique user profiles.
- user_id: UUIDv4.
- email: use exactly five domains (alpha.io, beta.io, gamma.io, delta.io, epsilon.io). Distribute evenly (4,000 each). No duplicate full email.
- phone: E.164, country code +1, +44, +49, +61, +81. Each code used 4,000 times. Full phone number unique.
- address: free‑form, but after lower‑casing and removing punctuation, each address must be unique.
- created_at: ISO‑8601 timestamps, strictly increasing by 1 second per row.
- Add a column row_hash = SHA256(concat(user_id,email,phone,address,created_at)).
Return as CSV with header.

6.3 Generation & Immediate Hash Dedup



# 1. Call the generator (CLI wrapper around the model)


qa3-gen --prompt prompt.txt --out raw_profiles.csv --rows 20000


# 2. Exact‑hash dedup


python dedup_exact.py raw_profiles.csv dedup_exact.csv

Result: 19 987 rows (13 exact duplicates removed).

6.4 Fuzzy Address Dedup

python dedup_fuzzy_address.py dedup_exact.csv dedup_fuzzy.csv

Result: 19 950 rows (37 near‑duplicate addresses collapsed).

6.5 Validation

MetricExpectedObservedPass?
Row count20 00019 950✅ (≤ 2 % loss)
Distinct email20 00019 950✅
Distinct phone20 00019 950✅
Distinct normalized address20 00019 950✅
row_hash uniqueness100 %100 %✅
Referential integrity (none)N/AN/A✅

All thresholds satisfied; the data artifact is version‑tagged v1.3‑profiles‑2024‑03‑15 and stored in the test‑data registry.


7. Tool Considerations

CategoryOptionsStrengthsCaveats
AI generationCommercial LLM APIs, open‑source models (Mistral, LLaMA), QA3 free test data generator at /tools/test-data-generatorPrompt‑driven schema conformity, fast iterationRequires prompt tuning; output nondeterministic
Exact deduppandas.drop_duplicates, awk '!seen[$0]++', Spark dropDuplicatesZero‑config, linear timeOnly catches byte‑wise copies
Fuzzy dedupdatasketch (MinHash LSH), dedupe.io, recordlinkageScales to millions, configurable similarityNeeds tuning of threshold & num_perm
Embedding‑basedsentence-transformers + FAISS / AnnoyCaptures semantic similarityGPU‑heavy for large batches
Pipeline orchestrationGitHub Actions, GitLab CI, Airflow, PrefectVersioned artifacts, retry logicAdds operational overhead

Recommendation: Start with the free QA3 generator for prompt experimentation, then plug the CSV output into a lightweight Python dedup script (exact + MinHash). Promote to a managed pipeline only when the data volume exceeds ~100 k rows or when multiple teams consume the same artifact.


8. Validation Checklist (Run After Every Generation)

  • Row‑level hash uniqueness – df.row_hash.nunique() == len(df)
  • Column cardinality – each high‑cardinality column ≥ 95 % of target distinct count
  • Domain constraints – enum fields only contain allowed values
  • Format checks – regex for email, phone, UUID, ISO‑8601 timestamps
  • Referential integrity – foreign keys resolve in parent tables
  • Distribution sanity – histograms of numeric fields match specified parameters (KS test p‑value > 0.05)
  • Duplicate‑ratio – exact + fuzzy duplicate ratio ≤ 2 %
  • Artifact metadata – prompt version, generator model, temperature, timestamp stored alongside data
  • Immutability – artifact written to read‑only storage (e.g., S3 with Object Lock)

Automate the checklist as a CI job; fail the build on any red item.


9. Common Pitfalls & Mitigations

PitfallSymptomMitigation
Over‑aggressive fuzzy thresholdLoss of legitimate edge cases (e.g., two users living at “123 Main St” vs “123 Main Street”)Keep a sample of collapsed rows for manual review; set threshold per column (address 0.9, description 0.85)
Prompt driftDuplicate ratio creeps up after model upgradePin model version in CI; run a nightly “prompt regression” test
Hash collisions (theoretical)Two distinct rows share SHA‑256Practically impossible; if paranoia, add a secondary UUID column
Referential breakage after dedupOrphaned child rowsPerform dedup only on parent tables; cascade keep‑one‑per‑parent to children
Data‑volume shortfallAfter dedup you fall below required test‑case countGenerate a 10‑15 % overshoot in the prompt; dedup will bring you back to target
Non‑deterministic timestampscreated_at not monotonic, causing flaky time‑based

Read more

Using AI to Expand a Small Test Dataset

A practical guide to “Using AI to Expand a Small Test Dataset,” with worked scenarios, tool considerations, validation checks, and actionable advice for QA teams.

AI-Generated Addresses, Names, and Profiles: Realism vs Safety

A buyer-focused guide to “AI-Generated Addresses, Names, and Profiles: Realism vs Safety,” with concrete selection criteria, trade-offs, and an evaluation path QA teams can use.

Multilingual Test Data Generation with AI

A practical guide to “Multilingual Test Data Generation with AI,” with worked scenarios, tool considerations, validation checks, and actionable advice for QA teams.