# Zero PII Customer Intelligence - Parte 1.1: Generando Datos de Muestra (e Imágenes) Localmente

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

In [Parte 1](/blog/zero-pii-customer-intelligence-part1) cubrimos la filosofía: segmentación transparente sin PII. [Parte 2](/blog/zero-pii-customer-intelligence-part2) abarca los perfiles de sesión, las señales y las definiciones de segmentos.

Pero primero: **¿Cómo se valida todo esto sin tocar los datos reales del cliente?**

Para esta serie genero un conjunto completo de datos de comercio electrónico sintético localmente:

- Catálogo de productos (nombres, descripciones, etiquetas, precios)
- Anonymous “perfiles” / personas (intereses + señales)
- Imágenes del producto (y retratos de perfil) a través de ComfyUI
- Importación de DB opcional para que la aplicación real pueda ejecutarse contra ella

Este es uno de esos flujos de trabajo “se siente como engañar”: obtienes entradas realistas, puedes regenerarlas en cualquier momento, y nunca introduces PII en tu entorno de desarrollo.

## Por qué los datos sintéticos locales son tan poderosos

Si está construyendo segmentación / personalización, necesita conjuntos de datos que:

- Tener suficiente variación para romper la heurística ingenua
- Son seguros para compartir (en código, pruebas, demos)
- Son reproducibles (por lo que puede comparar los cambios de modelo)

Los datos reales son lo contrario: sensibles, desordenados, difíciles de mover, y llenos de prejuicios históricos.

Los datos sintéticos le dan tres superpoderes:

1. **Iteración rápida**: cambiar la lógica de segmentación, regenerar, volver a ejecutar.
2. **Endurecimiento**: simular extraños comportamientos de cola larga que no verás en un pequeño conjunto de datos dev.
3. **Pruebas de explicación**: usted puede *inspeccionar cada campo generado* y confirma tu "muéstrame por qué" UI es veraz.

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

## La herramienta (lo que realmente se ejecuta)

El generador vive en `Mostlylucid.SegmentCommerce.SampleData`.

intencionalmente no es “un marco”; es un CLI pragmático que habla con:

- **Ollama** (LLM local) para la generación JSON estructurada.
- **ComfyUI** (Stable Diffusion local) para imágenes al estilo de la fotografía del producto.
- A **taxonomía JSON** que mantiene las salidas coherentes (categorías, tipos, variantes, rangos de precios).

La configuración está cableada para que pueda conducirla a través de variables de entorno:

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

## Inicio rápido

Normalmente ejecutará tres servicios locales junto al generador:

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

Ejecute el generador desde la raíz de repo:

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

A continuación, generar un conjunto de datos:

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

Interruptores útiles:

```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: si ComfyUI no está disponible, el generador v1 cae de nuevo a las imágenes del marcador de posición (utiliza `picsum.photos`, por lo que el camino no está “totalmente desconectado”). Si desea estrictamente local, ejecutar con `--no-images`.

## v2 Generador: Vendedores → Productos → Clientes → Pedidos → Incrustaciones

También hay un nuevo comando de generador (`gen`) que construye un conjunto de datos “formado por el mercado” más completo:

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

Está orquestado como una tubería 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
```

Y puedes ver esas fases directamente en código:

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

### Incrustaciones (ONNX local, en cacheado después de la primera ejecución)

El generador v2 puede calcular incrustaciones usando un modelo ONNX (predeterminado: `all-MiniLM-L6-v2`La primera vez que lo ejecutas, descarga el modelo y vocabulario en tu carpeta de salida.

Eso significa:

- La primera ejecución necesita acceso a la red (descarga del modelo)
- Las siguientes carreras son locales y rápidas
- Si quiere “ninguna red nunca”, ejecute `--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);
}
```

## Cómo generamos productos con “forma de LLM” (sólo JSON)

El prompt de generación de productos es deliberadamente estricto: el LLM solo debe devolver JSON, con un ejemplo de esquema JSON integrado.

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

Esto importa porque los sistemas descendentes (generación de imágenes + importación + incrustaciones) quieren datos estructurados. *entradas a una tubería*, no escribir prosa.

### Respuestas LLM: Analice JSON o retroceda

Los LLMs no son compiladores. Incluso con “JSON only”, usted todavía necesita análisis defensivo y retroactividad elegante.

El generador v2 lo hace a través de un pequeño ayudante (`LlmService`) que extrae el primero `{...}` lo bloquea y lo deserializa:

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

Y la tubería de generación general verifica explícitamente la disponibilidad y baja a plantillas deterministas cuando es necesario:

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

### Ejemplo: Customer Personas (v2)

El prompt persona para los clientes es corto y estructurado por lo que se puede generar rápidamente con un pequeño modelo local:

```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: Fotografía de producto a través de API

ComfyUI es genial porque le da una tubería controlable (flujos de trabajo) en lugar de un “endpoint de imagen de caja negra simple”.

El generador:

1. Carga una plantilla de flujo de trabajo (`ComfyUI/workflows/product_image.json`)
2. Parchea el prompt en `CLIPTextEncode`
3. Colas `/prompt`
4. Encuestas `/history/{promptId}`
5. Descargas de la imagen a través de `/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
```

La selección de modelos ComfyUI también se parchea en el flujo de trabajo en tiempo de ejecución (para que pueda intercambiar puntos de control sin editar el 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");
```

Y el parche de flujo de trabajo es intencionalmente simple y 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;
    }
}
```

## Perfiles y personas (útiles, no espeluznantes)

El generador v1 crea perfiles anónimos y luego los enriquece con una persona (todavía no hay PII). Terminas con datos de prueba realistas en forma de personas sin correos electrónicos, direcciones o cualquier cosa que puedas enviar accidentalmente.

En v1, los perfiles están keyed usando un hash de un solo sentido, por lo que no hay nada que “recuperar”:

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

Esa es exactamente la mentalidad de toda la serie: no se puede filtrar lo que nunca almacenaste.

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

## Validación: lo que esto le permite probar

Esta tubería local es poderosa porque soporta *validación del modelo*, no sólo demos.

- **Segmentación cordura**: ¿Los segmentos que usted calcula “siente” son coherentes cuando usted inspecciona nombres de productos/etiquetas y personas generadas?
- **Explicabilidad de las recomendaciones**: ¿"muéstrame por qué" apunta a una señal real que puedes verificar?
- **Inicio en frío**: si se borra el conjunto de datos y se regenera, ¿el sistema se comporta previsiblemente?
- **Ensayos de regresión**: regenerar la misma forma de datos y asegurar que sus cambios no rompan la clasificación, puntuación, o interfaz de usuario.

La sutileza importante: al generar tanto el *texto* y el *imágenes*, usted puede validar toda la experiencia del producto, no sólo las matemáticas de fondo.

## ¿Qué sigue?

**[Parte 2](/blog/zero-pii-customer-intelligence-part2)**: Perfiles de sesión, señales y definiciones de segmento—donde conectamos estos datos de muestra en la segmentación de trabajo.

**Parte 3** (coming): Patrón de salida, cola de trabajo, y la interfaz de usuario de transparencia.

---


*Código del generador: `Mostlylucid.SegmentCommerce.SampleData/`*