# Zero PII Customer Intelligence - Parte 1.1: Generazione dei dati campione (e delle immagini) localmente

<!--category-- Product, Privacy, LLM, ComfyUI, C# -->
<datetime class="hidden">2025-12-24T20:00</datetime>

Dentro [Parte 1](/blog/zero-pii-customer-intelligence-part1) abbiamo coperto la filosofia: segmentazione trasparente senza PII. [Parte 2](/blog/zero-pii-customer-intelligence-part2) copre i profili di sessione, i segnali e le definizioni dei segmenti.

Ma prima: **Come convalidate tutto questo senza mai toccare i dati reali del cliente?**

Per questa serie creo un set di dati completo di ecommerce sintetico a livello locale:

- Catalogo prodotti (nomi, descrizioni, tag, prezzi)
- Profili anonimi [49] / personas (interessi + segnali)
- Immagini del prodotto (e ritratti di profilo) tramite ComfyUI
- Importazione DB opzionale in modo che l'applicazione reale possa funzionare contro di essa

Questo è uno di quei flussi di lavoro di Follow: si ottengono input realistici, si possono rigenerare in qualsiasi momento, e non si introduce mai PII nel vostro ambiente dev.

## Perché i dati sintetici locali sono così potenti

Se costruisci segmentazione/personalizzazione, hai bisogno di set di dati che:

- Avere abbastanza variazione per rompere l'euristica ingenua
- Sono sicuri da condividere (in codice, test, demo)
- Sono riproducibili (in modo da poter confrontare le modifiche del modello)

I dati reali sono il contrario: sensibili, disordinati, difficili da spostare, e pieni di pregiudizi storici.

I dati sintetici ti danno tre superpoteri:

1. **Iterazione rapida**: cambiare la logica di segmentazione, rigenerare, ri-run.
2. **Indurimento**: simulare strani comportamenti a coda lunga che si possono vedere in un piccolo set di dati dev.
3. **Spiegabilità del test**: puoi *ispezionare ogni campo generato* e confermare il vostro ... Mostrami perche' l'interfaccia utente e' veritiera.

```mermaid
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
```

## Lo strumento (che cosa in realtà funziona)

Il generatore vive in `Mostlylucid.SegmentCommerce.SampleData`.

Essa non definisce intenzionalmente il quadro di riferimento dell'impresa, ma una CLI pragmatica che si occupa di:

- **OllamaCity name (optional, probably does not need a translation)** (LLM locale) per la generazione strutturata JSON.
- **ConfortevoleUI** (locale Stable Diffusion) per immagini in stile fotografico di prodotto.
- A **tassonomia JSON** che mantiene i risultati coerenti (categorie, tipi, varianti, fasce di prezzo).

La configurazione è cablata in modo da poterla guidare tramite variabili d'ambiente:

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

## Avvio rapido

Di solito è possibile eseguire tre servizi locali accanto al generatore:

```mermaid
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
```

Eseguire il generatore dalla radice repo:

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

Quindi generare un set di dati:

```bash
# 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
```

Interruttori utili:

```bash
# 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
```

Nota: se ComfyUI è disponibile, il generatore v1 rientra nelle immagini segnaposto (utilizza `picsum.photos`, in modo che il percorso non è completamente offline. Se si desidera strettamente-locale, eseguire con `--no-images`.

## v2 Generator: Venditori → Prodotti → Clienti → Ordini → Inserzioni

C'è anche un comando generatore più recente (`gen`) che costruisce un set di dati più completo a forma di mercato:

```bash
# 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
```

È orchestrata come pipeline multifase:

```mermaid
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
```

E puoi vedere quelle fasi direttamente in codice:

```csharp
// 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
```

### Abbinamenti (locale ONNX, cache dopo la prima esecuzione)

Il generatore v2 può calcolare embeddings utilizzando un modello ONNX (default: `all-MiniLM-L6-v2`). La prima volta che lo esegui, scarica il modello e il vocabolario nella tua cartella di output.

Significa:

- Prima esecuzione ha bisogno di accesso alla rete (model download)
- Le corse successive sono locali e veloci
- Se desiderate la rete di dispersore mai, eseguite `--no-embeddings`

```csharp
// 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);
}
```

## Come generiamo i prodotti a forma di JSON (solo JSON)

Il prompt di produzione del prodotto è volutamente rigoroso: la LLM deve restituire solo JSON, con un esempio schema JSON incorporato.

```csharp
// 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:
    """;
```

Questo è importante perché i sistemi a valle (generazione immagini + importazione + embeddings) vogliono dati strutturati. LLM sta producendo *inputs to a pipeline*, non scrivere prosa.

### LLM Responses: Parse JSON, or Fall Back

I LLMs non sono compilatori. Anche con il solo JSON, avete ancora bisogno del parsing difensivo e del ripiego grazioso.

Il generatore v2 lo fa tramite un piccolo helper (`LlmService`) che estrae il primo `{...}` blocca e deserializza:

```csharp
// 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
    });
}
```

E la pipeline generale di generazione verifica esplicitamente la disponibilità e i declassamenti ai modelli deterministici quando necessario:

```mermaid
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
```

### Esempio: Personalità del cliente (v2)

Il prompt persona per i clienti è breve e strutturato in modo che possa essere generato rapidamente con un piccolo modello locale:

```csharp
// 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: Fotografia del prodotto tramite API

ComfyUI è grande perché ti dà una pipeline controllabile (flussi di lavoro) piuttosto che un endpoint di immagine monoscafo black box [56].

Il generatore:

1. Carica un modello di flusso di lavoro (`ComfyUI/workflows/product_image.json`)
2. Patches il prompt in `CLIPTextEncode`
3. Coda `/prompt`
4. Sondaggi `/history/{promptId}`
5. Scarica l'immagine tramite `/view?...`

```mermaid
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
```

Anche la selezione del modello ComfyUI viene inserita nel flusso di lavoro durante il runtime (in modo da poter scambiare i punti di controllo senza modificare il JSON):

```csharp
// 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");
```

E il patching del flusso di lavoro è intenzionalmente semplice e robusto:

```csharp
// 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;
    }
}
```

## Profili e personaggi (utili, non inquietanti)

Il generatore v1 crea profili anonimi e poi li arricchisce con una persona (ancora nessun PII). Finisci con dati realistici di test di football a forma di football senza e-mail, indirizzi, o qualsiasi cosa si potrebbe mai accidentalmente spedire.

In v1, i profili sono tastiati usando un hash a senso unico, quindi non c'è nulla da recuperare da 

```csharp
// 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();
}
```

Che è esattamente il mindset di tutta la serie: è possibile trasferire ciò che non hai mai memorizzato.

```mermaid
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
```

## Convalida: ciò che ti permette di provare

Questo gasdotto locale è potente perché supporta *convalida del modello*Non solo demo.

- **Sanità di segmentazione**: i segmenti che computi sono coerenti quando ispezioni nomi/tag dei prodotti e personaggi generati?
- **Spiegabilità della raccomandazione**: mi mostra il perché di un vero segnale che puoi verificare?
- **Inizio a freddo**: se si cancella il set di dati e si rigenera, il sistema si comporta in modo prevedibile?
- **Prova di regressione**: rigenerare la stessa forma di dati e garantire che le modifiche non rompere il ranking, punteggio, o UI.

L'importante sottigliezza: generando sia il *testo* e della *immagini*, è possibile convalidare l'intera esperienza del prodotto, non solo matematica back-end.

## Cosa c'e' dopo?

**[Parte 2](/blog/zero-pii-customer-intelligence-part2)**: Profili di sessione, segnali e definizioni di segmento [49]dove cabiamo i dati di questo campione nella segmentazione di lavoro.

**Parte 3** (coming): Schema in uscita, coda di lavoro, e l'interfaccia utente di trasparenza.

---


*Codice generatore: `Mostlylucid.SegmentCommerce.SampleData/`*