Zero PII Customer Intelligence - Del 1.1: Generera provdata (och bilder) Lokalt (Svenska (Swedish))

Zero PII Customer Intelligence - Del 1.1: Generera provdata (och bilder) Lokalt

Wednesday, 24 December 2025

//

9 minute read

Till Häfte 1 Vi täckte filosofin: transparent segmentering utan PII. Häfte 2 omfattar sessionsprofiler, signaler och segmentdefinitioner.

Men först: Hur validerar du något av detta utan att någonsin röra verkliga kunddata?

För denna serie genererar jag en komplett syntetisk e-handel dataset lokalt:

  • Produktkatalog (namn, beskrivningar, taggar, priser)
  • Anonyma profiler/personas (intressen + signaler)
  • Produktbilder (och profilporträtt) via ComfyUI
  • Valfri DB import så att den verkliga appen kan köras mot den

Detta är en av de där "det känns som fusk" arbetsflöden: du får realistiska ingångar, du kan återskapa dem när som helst, och du introducerar aldrig PII i din dev miljö.

Varför lokala syntetiska data är så kraftfulla

Om du bygger segmentering / personalisering, behöver du dataset som:

  • Har tillräckligt med variation för att bryta naiva heuristiker
  • Är säkra att dela (i kod, tester, demos)
  • Är reproducerbara (så att du kan jämföra modelländringar)

Real data är motsatsen: känslig, rörig, svår att röra sig runt, och full av historiska fördomar.

Syntetiska data ger dig tre superkrafter:

  1. Snabb iteration: ändra segmenteringslogiken, regenerera, re-run.
  2. Förhårdnad: simulera konstiga lång-tail beteenden du inte kommer att se i en liten dev dataset.
  3. Förklarande provning: du kan inspektera varje genererat fält och bekräfta din Visa mig varför UI är sanningsenlig.
flowchart LR
    Taxonomy[gadget-taxonomy.json] --> Gen[SampleData generator]
    Gen --> Products[products.json]
    Gen --> Profiles[profiles.json]
    Gen --> Images[images/*]
    Products --> Import[Import into Postgres]
    Profiles --> Import

    style Taxonomy stroke:#1971c2,stroke-width:3px
    style Gen stroke:#1971c2,stroke-width:3px
    style Import stroke:#2f9e44,stroke-width:3px

Verktyget (Vad som faktiskt körs)

Generatorn bor i Mostlylucid.SegmentCommerce.SampleData.

Det är avsiktligt inte en ram; det är en pragmatisk CLI som talar till:

  • Ollama Ordförande (lokal LLM) för strukturerad JSON-generation.
  • KomfyUi Ordförande (lokal Stable Diffusion) för bilder av produktfotografering.
  • En förteckning över de behöriga myndigheter som avses i punkt 1 i denna artikel ska upprättas i enlighet med det förfarande som avses i artikel 4 i förordning (EU) nr 952/2013. taxonomi JSON som håller resultaten konsekventa (kategorier, typer, varianter, prisintervall).

Konfigurationen är uppkopplad så att du kan köra den via miljövariabler:

// Mostlylucid.SegmentCommerce.SampleData/Program.cs
var configuration = new ConfigurationBuilder()
    .SetBasePath(AppContext.BaseDirectory)
    .AddJsonFile("appsettings.json", optional: true)
    .AddEnvironmentVariables("SAMPLEDATA_")
    .Build();

Snabbstart

Du kommer vanligtvis köra tre lokala tjänster tillsammans med generatorn:

flowchart LR
    CLI[SampleData CLI] --> Ollama["Ollama
    http://localhost:11434"]
    CLI --> Comfy["ComfyUI
    http://localhost:8188"]
    CLI -. optional .-> DB[(Postgres)]

    style CLI stroke:#1971c2,stroke-width:3px
    style Ollama stroke:#1971c2,stroke-width:3px
    style Comfy stroke:#2f9e44,stroke-width:3px
    style DB stroke:#fab005,stroke-width:3px

Kör generatorn från reporoten:

dotnet run --project Mostlylucid.SegmentCommerce.SampleData -- status

Skapa sedan en datauppsättning:

# v1 generator: taxonomy + optional Ollama + optional ComfyUI
# Writes ./Output/products.json, ./Output/profiles.json, ./Output/images/...
dotnet run --project Mostlylucid.SegmentCommerce.SampleData -- generate --count 20

Användbara strömbrytare:

# No LLM calls, taxonomy only
dotnet run --project Mostlylucid.SegmentCommerce.SampleData -- generate --no-ollama

# No ComfyUI images
dotnet run --project Mostlylucid.SegmentCommerce.SampleData -- generate --no-images

# Write into Postgres (uses configured connection string)
dotnet run --project Mostlylucid.SegmentCommerce.SampleData -- generate --db

Observera: om ComfyUI inte är tillgänglig, faller v1-generatorn tillbaka till platshållarbilder (den använder picsum.photos, så att sökvägen inte är helt offline). Om du vill strikt lokal, kör med --no-images.

v2 Generator: Säljare → Produkter → Kunder → Beställningar → Inbäddning

Det finns också en nyare generator kommando (gen) som bygger en mer komplett marknadsplatsformad datauppsättning:

# v2 generator
# Writes dataset.json plus sellers/products/customers/orders split files
dotnet run --project Mostlylucid.SegmentCommerce.SampleData -- gen --sellers 50 --products 20 --customers 1000

Det är orkestrerat som en flerfas rörledning:

flowchart LR
    A[Sellers] --> B[Products]
    B --> C[Customers]
    C --> D[Orders]
    D --> E[Embeddings]

    B -. optional .-> I[ComfyUI images]

    style A stroke:#1971c2,stroke-width:3px
    style B stroke:#1971c2,stroke-width:3px
    style C stroke:#1971c2,stroke-width:3px
    style D stroke:#1971c2,stroke-width:3px
    style E stroke:#2f9e44,stroke-width:3px
    style I stroke:#2f9e44,stroke-width:3px

Och du kan se dessa faser direkt i kod:

// Mostlylucid.SegmentCommerce.SampleData/Services/DataGenerator.cs
// 1. Generate Sellers
// 2. Generate Products for each seller
// 3. Generate Customers
// 4. Generate Orders (with fake checkout data via Bogus)
// 5. Generate embeddings for all entities

Inbäddningar (lokala ONNX, cached efter första körningen)

V2-generatorn kan beräkna inbäddningar med hjälp av en ONNX-modell (standard: all-MiniLM-L6-v2). Första gången du kör den, laddar den ner modellen och vokab till din utdatamapp.

Detta innebär följande:

  • Första körningen behöver nätverksåtkomst (modellnedladdning)
  • Efterföljande körningar är lokala och snabba
  • Om du vill ha inget nätverk någonsin, kör --no-embeddings
// Mostlylucid.SegmentCommerce.SampleData/Services/EmbeddingService.cs
private const string ModelUrl = "https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main/onnx/model.onnx";
private const string VocabUrl = "https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main/vocab.txt";

// Download model if not exists
if (!File.Exists(_config.ModelPath))
{
    await DownloadFileAsync(ModelUrl, _config.ModelPath, ct);
}

Hur vi skapar produkter med LLM-Shaped. (endast JSON)

Produktgenereringsprompten är avsiktligt strikt: LLM måste endast returnera JSON, med ett inbäddat JSON schema exempel.

// Mostlylucid.SegmentCommerce.SampleData/Services/OllamaProductGenerator.cs
return $"""
    You are a product catalog generator for an e-commerce store. Generate {count} unique, realistic product listings for the \"{category.DisplayName}\" category.

    Category description: {category.Description}
    Example products in this category: {category.ExampleProducts}
    Price range: £{category.PriceRange.Min:F2} - £{category.PriceRange.Max:F2}

    For each product, provide:
    1. A compelling product name (realistic brand-style naming)
    2. A detailed description (2-3 sentences, highlighting key features and benefits)
    3. A realistic price within the range
    4. Optional original price if on sale (20-40% higher than current price)
    5. 3-5 relevant tags
    6. Whether it's trending (about 20% should be trending)
    7. Whether it's featured (about 15% should be featured)
    8. An image prompt for AI image generation (detailed, product photography style)
    9. 2-3 colour variants for the product

    IMPORTANT: Respond with ONLY valid JSON, no markdown formatting, no code blocks, no explanations.

    Generate {count} diverse products now:
    """;

Detta spelar roll eftersom nedströmssystem (bildgenerering + import + inbäddningar) vill ha strukturerade data. LLM producerar tillförsel till en rörledning, inte skriva prosa.

Svar från LLM: Parse JSON, eller fall tillbaka

LLMs är inte kompilatorer. Även med JSON bara, du behöver fortfarande defensiva tolkning och graciös reserv.

V2-generatorn gör det via en liten hjälpare (LlmService) som utvinner den första {...} blockerar och deserialiserar det:

// Mostlylucid.SegmentCommerce.SampleData/Services/LlmService.cs
var jsonStart = response.IndexOf('{');
var jsonEnd = response.LastIndexOf('}');

if (jsonStart >= 0 && jsonEnd > jsonStart)
{
    var jsonStr = response.Substring(jsonStart, jsonEnd - jsonStart + 1);
    return JsonSerializer.Deserialize<T>(jsonStr, new JsonSerializerOptions
    {
        PropertyNameCaseInsensitive = true
    });
}

Och den totala produktionsledningen kontrollerar uttryckligen tillgänglighet och nedgraderar till deterministiska mallar vid behov:

flowchart LR
    A[EnableLlm=true?] -->|no| F[Fallback templates]
    A -->|yes| B[GET /api/tags]
    B -->|model present| C[LLM JSON prompts]
    B -->|not available| F

    style A stroke:#1971c2,stroke-width:3px
    style C stroke:#2f9e44,stroke-width:3px
    style F stroke:#fab005,stroke-width:3px

Exempel: Kundpersonas (v2)

Personaprompten för kunder är kort och strukturerad så att den kan skapas snabbt med en liten lokal modell:

// Mostlylucid.SegmentCommerce.SampleData/Services/DataGenerator.cs
var prompt = $$"""
    Generate a shopper persona interested in: {{string.Join(", ", categoryNames)}}.

    Return JSON only:
    {
      "persona": "Brief persona description (e.g. 'Tech enthusiast who values quality')",
      "name": "Realistic first name",
      "bio": "One sentence about their shopping habits",
      "age": 25,
      "shopping_style": "budget|value|premium|luxury",
      "preferred_categories": ["category1", "category2"]
    }
    """;

ComfyUI: Produktfotografering via API

ComfyUI är bra eftersom det ger dig en kontrollerbar pipeline (arbetsflöden) snarare än en enda svart låda bild endpoint.

Generatorn:

  1. Laddar en arbetsflödesmall (ComfyUI/workflows/product_image.json)
  2. Kopplar in prompten i CLIPTextEncode
  3. Köer /prompt
  4. Undersökningar /history/{promptId}
  5. Ladda ner bilden via /view?...
sequenceDiagram
    participant Gen as SampleData
    participant Comfy as ComfyUI

    Gen->>Comfy: POST /prompt (workflow + prompt)
    Comfy-->>Gen: prompt_id
    loop poll
        Gen->>Comfy: GET /history/{prompt_id}
        Comfy-->>Gen: outputs / images
    end
    Gen->>Comfy: GET /view?filename=...&type=output
    Comfy-->>Gen: PNG bytes

ComfyUI modellval är också lappat i arbetsflödet vid körning (så att du kan byta kontrollpunkter utan att redigera JSON):

// Mostlylucid.SegmentCommerce.SampleData/Services/ComfyUIImageGenerator.cs
TryPatchCheckpoint(workflow, _config.ComfyUICheckpointName ?? "sd_xl_base_1.0.safetensors");
TryPatchRefiner(workflow, _config.ComfyUIRefinerName ?? "sd_xl_refiner_1.0.safetensors");

Och arbetsflödet patching är avsiktligt enkel och robust:

// Mostlylucid.SegmentCommerce.SampleData/Services/ComfyUIImageGenerator.cs
// Update CLIPTextEncode nodes with our prompt
if (classType == "CLIPTextEncode")
{
    var inputs = nodeObj["inputs"]?.AsObject();
    if (inputs != null && inputs.ContainsKey("text"))
    {
        var currentText = inputs["text"]?.GetValue<string>() ?? "";
        if (!currentText.Contains("bad") && !currentText.Contains("ugly") && !currentText.Contains("deformed"))
        {
            inputs["text"] = prompt;
        }
    }
}

// Update image dimensions
if (classType == "EmptyLatentImage")
{
    var inputs = nodeObj["inputs"]?.AsObject();
    if (inputs != null)
    {
        inputs["width"] = _config.ImageWidth;
        inputs["height"] = _config.ImageHeight;
    }
}

Profiler och personer (användbara, inte läskiga)

V1-generatorn skapar anonyma profiler och berikar dem sedan med en persona (fortfarande ingen PII). Du slutar med realistiska folkformade testdata utan e-post, adresser, eller något du någonsin av misstag kunde skicka.

I v1, profiler är knappade med hjälp av en enkelriktad hash, så det finns inget att återställa:

// Mostlylucid.SegmentCommerce.SampleData/Services/ProfileGenerator.cs
var profileKey = Hash($"fp-{Guid.NewGuid():N}");

private static string Hash(string input)
{
    using var sha = SHA256.Create();
    var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(input));
    return Convert.ToHexString(bytes).ToLowerInvariant();
}

Det är exakt tänkesättet i hela serien: du kan inte läcka vad du aldrig lagrat.

flowchart TB
    Signals["Generated signals
    views/cart/purchase weights"] --> Persona["Persona enrichment
    Ollama JSON-only"]
    Persona --> Portrait[Portrait prompt]
    Portrait --> Comfy[ComfyUI]

    style Signals stroke:#1971c2,stroke-width:3px
    style Persona stroke:#1971c2,stroke-width:3px
    style Comfy stroke:#2f9e44,stroke-width:3px

Validering: Vad detta låter dig bevisa

Denna lokala rörledning är kraftfull eftersom den stöder Modellvalidering, inte bara demos.

  • Segmenteringssanitet: Är de segment du beräknar sammanhållna när du inspekterar produktnamn/taggar och genererade personas?
  • Redogörelse för rekommendationen: visar mig varför pekar du på en riktig signal som du kan verifiera?
  • Kallstart: om du torkar datasetet och regenererar, beter sig systemet förutsägbart?
  • Provning av regression: regenerera samma form av data och se till att dina ändringar inte bryta ranking, poäng, eller UI.

Den viktiga subtiliteten: genom att generera båda Texten och bilder, Du kan validera hela produktupplevelsen, inte bara back-end matte.

Vad händer härnäst?

Häfte 2: Sessionsprofiler, signaler och segmentdefinitioner – där vi överför provdata till arbetssegmentering.

Häfte 3 (kommer): Utkorg mönster, jobb kö, och transparens UI.


Generatorkod: Mostlylucid.SegmentCommerce.SampleData/

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.