@enerlence/suntropy-cli 0.11.9 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -1
- package/dist/bin/suntropy.js +1267 -24
- package/dist/bin/suntropy.js.map +1 -1
- package/package.json +1 -1
- package/skills/satvolt-api.md +279 -0
- package/skills/satvolt-campaign.md +228 -0
- package/skills/satvolt-cli.md +185 -0
- package/skills/satvolt-lead-troubleshooting.md +89 -0
- package/skills/satvolt-probe-to-full-campaign.md +185 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Referencia de `suntropy satvolt`, los comandos de la CLI de Suntropy para las campañas de captación de leads de Satvolt. Úsala para saber qué comando hace qué, con qué opciones y qué devuelve. Para procesos completos, usa las skills de workflow: `satvolt-campaign`, `satvolt-probe-to-full-campaign` y `satvolt-lead-troubleshooting`.
|
|
2
|
+
|
|
3
|
+
## Conexión
|
|
4
|
+
|
|
5
|
+
`suntropy satvolt` llama a la API pública de Satvolt con el mismo token que el resto de la CLI.
|
|
6
|
+
|
|
7
|
+
| Entorno | `--server` (o perfil) | URL a la que llama |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Producción | `https://api.enerlence.com` | `https://api.enerlence.com/satvolt/api/v1` |
|
|
10
|
+
| Local | `http://localhost` | `http://localhost:8099/api/v1` (el puerto del servidor se ignora) |
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
suntropy auth set-key --key <jwt> # o SUNTROPY_API_KEY=<jwt>, o --token <jwt>
|
|
14
|
+
suntropy --profile dev satvolt campaigns list
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
El token es un JWT con `clientUID`. Todo queda acotado a la empresa del token. Con `401 TOKEN_EXPIRED` hay que renovar el token.
|
|
18
|
+
|
|
19
|
+
## Opciones globales y formato
|
|
20
|
+
|
|
21
|
+
| Opción | Uso |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `--format json\|human\|csv` | `json` por defecto (lo mejor para agentes); `human` imprime tablas y resúmenes |
|
|
24
|
+
| `--fields a,b` | Selecciona campos del resultado |
|
|
25
|
+
| `--save <fichero>` | Guarda la salida (útil con `config get`, `templates get`, `export-tables get`) |
|
|
26
|
+
| `--server`, `--profile`, `--token` | Destino y credenciales |
|
|
27
|
+
|
|
28
|
+
- **Argumentos JSON** (`--steps`, `--config`, `--data`, `--columns`, `--polygon`): aceptan JSON en línea, `@fichero.json` o `-` para leer de stdin.
|
|
29
|
+
- **Errores:** salen por stderr como `{"error":true,"message","status","code","details"}` y el comando termina con código ≠ 0. Decide qué hacer según el `code`, no según el texto.
|
|
30
|
+
- **Permisos:** la CLI puede estar limitada a un nivel (`SUNTROPY_COMMAND_PROFILE`). En `read` no aparecen los comandos que cambian datos. `write` añade `create`, `update`, `patch`, `set`, `add`, `remove`, `start`, `resume`, `run`, `run-step`, `extend` y `duplicate`. `delete` añade `delete` y `reset`.
|
|
31
|
+
|
|
32
|
+
## Conceptos que hay que tener claros
|
|
33
|
+
|
|
34
|
+
- **Pasos del pipeline:**
|
|
35
|
+
- Solo se definen los pasos LEAD. SECTORIZE, FIND_LEADS y COMPLETE los pone el backend; en `config get` salen con `structural: true`.
|
|
36
|
+
- Cada paso tiene un `uid`, que es su identidad: los parches, `steps set`, `leads run-step` y las claves de fullData (`qualification_<uid>`, `aiAgent_<uid>`) lo usan.
|
|
37
|
+
- Donde se pide un paso (`--step`, `leads run-step`) vale el uid, la acción si aparece una sola vez, el nombre del paso (`"Buscador de CIF"`, sin distinguir mayúsculas ni tildes) o la clave de fullData donde deja sus datos (`cif`, `qualification_<uid>`). Si hay varias coincidencias, el error es `AMBIGUOUS_STEP` y lista los uids.
|
|
38
|
+
- `catalog actions` da de cada acción los créditos por lead, las dependencias, si admite repetirse (`multiple`) y el JSON Schema de su `config`.
|
|
39
|
+
- Los valores `default` se rellenan solos. Las dependencias que falten se añaden y se avisa en `warnings`.
|
|
40
|
+
- **Créditos:** 1 crédito = 0,005 €.
|
|
41
|
+
- Cada acción cobra un precio fijo por lead (`creditCost` en `catalog actions` o `steps list`). Todos los agentes de AI_AGENT cuestan lo mismo.
|
|
42
|
+
- Las ejecuciones fallidas o saltadas (`skipped`) no cobran.
|
|
43
|
+
- `estimatedCreditsPerLead` al crear es el máximo, como si todos los leads pasaran todos los pasos; los filtros (QUALIFY) lo reducen. El consumo real por lead lo da `campaigns usage` (`avgCreditsPerLead`); para estimar una campaña nueva, usa el de una campaña anterior con la misma configuración.
|
|
44
|
+
- **Estados:** consulta `catalog states`.
|
|
45
|
+
- Campaña: `queued` → `inProgress` → `completed`, `failed`, `paused` o `canceled`.
|
|
46
|
+
- Lead: `pending` → estados intermedios (`rooftopFound`, `qualified`, `consumptionEstimated`…) → `completed`, `unQualified` o `failed`.
|
|
47
|
+
- **Área:** círculo de 100 m a 50 km, rectángulo o polígono. El polígono se busca en su rectángulo envolvente y devuelve un aviso.
|
|
48
|
+
|
|
49
|
+
## Catálogo
|
|
50
|
+
|
|
51
|
+
| Comando | Devuelve |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `catalog actions` | Acciones LEAD: `creditCost`, `isAsync`, `multiple`, `dependencies`, `resultsPropertyKeys`, `configSchema` |
|
|
54
|
+
| `catalog ai-agents` | Agentes válidos para `AI_AGENT.config.agentId` |
|
|
55
|
+
| `catalog business-groups` | Grupos de negocio para `--business-groups` (`businesses` = solo negocios) |
|
|
56
|
+
| `catalog states` | Estados de campaña y de lead |
|
|
57
|
+
|
|
58
|
+
## Plantillas (`templates`)
|
|
59
|
+
|
|
60
|
+
Una plantilla guarda todo lo que define una campaña salvo el nombre y el área: los pasos con sus uids, los grupos de negocio, la descripción de la configuración, la consulta de texto y el límite de leads. Se referencian por id o por nombre exacto.
|
|
61
|
+
|
|
62
|
+
| Comando | Qué hace |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `templates list [--search t]` | Lista las plantillas |
|
|
65
|
+
| `templates get <id\|nombre>` | Detalle con pasos y config |
|
|
66
|
+
| `templates create --name N --from-campaign <id> [--description t]` | Guarda como plantilla la configuración de una campaña de Maps |
|
|
67
|
+
| `templates create --name N --steps @steps.json [--business-groups ids] [--max-leads n] [--search-query t] [--configuration-description t]` | Crea una plantilla desde JSON |
|
|
68
|
+
| `templates update <id\|nombre> --data @t.json` | PUT: la sustituye entera (los pasos son obligatorios) |
|
|
69
|
+
| `templates patch <id\|nombre> [--name] [--description] [--steps @parches] [--business-groups ids] [--max-leads n \| --no-max-leads] [--search-query t]` | Cambios sueltos; los pasos se modifican con parches por uid |
|
|
70
|
+
| `templates delete <id\|nombre> --yes` | Borra la plantilla (las campañas creadas desde ella no cambian) |
|
|
71
|
+
|
|
72
|
+
`TEMPLATE_NAME_TAKEN` (409) significa que ya existe una plantilla con ese nombre.
|
|
73
|
+
|
|
74
|
+
## Campañas (`campaigns`)
|
|
75
|
+
|
|
76
|
+
| Comando | Qué hace |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `campaigns list [--state a,b] [--search t] [--source maps\|excel\|campaign] [--limit/--offset]` | Lista, las más recientes primero |
|
|
79
|
+
| `campaigns get <id>` | Detalle: área, leads por estado, `sectorSearch` y configuración |
|
|
80
|
+
| `campaigns create --name N <área> [base] [opciones]` | Crea una campaña de Maps en cola (`--start` la arranca) |
|
|
81
|
+
| `campaigns start <id>` | Arranca una campaña `queued` (gasta créditos) |
|
|
82
|
+
| `campaigns logs <id> [--level error] [--since ts] [--follow]` | Logs de procesamiento (30 días, 5.000 entradas); `--follow` termina solo |
|
|
83
|
+
| `campaigns funnel <id>` | Por paso (`steps[]` con `uid`, `action`, `name`, `reached`, `success`, `failure`, `skipped`, `processing`, `pending`), más `leadStates` |
|
|
84
|
+
| `campaigns usage <id> [--by-lead]` | Créditos cobrados (regla de la pestaña Usage) |
|
|
85
|
+
| `campaigns extend <id> --max-leads N \| --no-limit` | Más leads sin relanzar: solo busca en los sectores pendientes |
|
|
86
|
+
| `campaigns resume <id> --action A [--config json]` | Añade un paso al final y lo ejecuta sobre los leads existentes |
|
|
87
|
+
| `campaigns reset <id> --yes [--start]` | Borra leads y resultados y vuelve a `queued` (se vuelve a pagar todo) |
|
|
88
|
+
| `campaigns delete <id> --yes` | Borra la campaña con todo lo que generó |
|
|
89
|
+
|
|
90
|
+
**Opciones de `create`:**
|
|
91
|
+
|
|
92
|
+
| Grupo | Opciones |
|
|
93
|
+
|---|---|
|
|
94
|
+
| Área (una) | `--circle lat,lng --radius m`, `--bounds nwLat,nwLng,seLat,seLng` o `--polygon json\|@geojson` |
|
|
95
|
+
| Base (opcional, una) | `--template <id\|nombre>` o `--from-campaign <id>`: aporta pasos, grupos, descripción, consulta y límite, y los flags explícitos tienen prioridad |
|
|
96
|
+
| Pipeline | `--steps @steps.json`, `--business-groups ids`, `--description t` |
|
|
97
|
+
| Búsqueda | `--search-query t` (búsqueda por texto en vez de por cercanía), `--max-leads n` |
|
|
98
|
+
| Otros | `--address t`, `--region t`, `--start`, `--data @body.json` |
|
|
99
|
+
|
|
100
|
+
La respuesta trae `campaign.idCampaign`, `configuration`, `estimatedCreditsPerLead`, `basedOn` y `warnings`.
|
|
101
|
+
|
|
102
|
+
**`extend`:**
|
|
103
|
+
- La campaña tiene que ser de Maps, estar ya ejecutada y no estar en marcha.
|
|
104
|
+
- El nuevo límite tiene que ser mayor que los leads actuales.
|
|
105
|
+
- Los sectores agotados no se vuelven a buscar; solo los leads nuevos pasan por el pipeline.
|
|
106
|
+
- En `campaigns get`, `sectorSearch.incomplete + unknown > 0` indica que todavía se pueden encontrar más leads.
|
|
107
|
+
- Errores: `CAMPAIGN_RUNNING`, `CAMPAIGN_NOT_STARTED`, `UNSUPPORTED_SOURCE`, `VALIDATION_ERROR`.
|
|
108
|
+
|
|
109
|
+
## Configuración del pipeline (`config`, `steps`)
|
|
110
|
+
|
|
111
|
+
| Comando | Qué hace |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `config get <id> [--save f]` | `{campaignId, source, businessGroups, description, steps[]}` |
|
|
114
|
+
| `config update <id> --data @cfg.json` | PUT: lista completa de pasos editables (se puede reenviar lo que devuelve `config get`) |
|
|
115
|
+
| `config patch <id> --data @patch.json` | PATCH por uid: `{uid, config}` fusiona (`null` devuelve la clave a su default), `{uid, remove:true}` borra, `{action, config}` añade, `before`/`after` mueven, `disable` desactiva |
|
|
116
|
+
| `steps list <id>` | Pasos con créditos, `executedLeads`, `runnable` y `reason` |
|
|
117
|
+
| `steps add <id> --action A [--config j] [--before uid \| --after uid] [--disabled]` | Añade un paso (antes de COMPLETE por defecto) |
|
|
118
|
+
| `steps set <id> <uid> [--config j] [--replace-config] [--enable\|--disable] [--before\|--after uid]` | Edita o mueve un paso |
|
|
119
|
+
| `steps remove <id> <uid>` | Quita un paso |
|
|
120
|
+
| `steps run <id> <uid>` | Ejecuta sobre los leads existentes un paso que ningún lead ha ejecutado y continúa el pipeline |
|
|
121
|
+
|
|
122
|
+
Si la campaña está en marcha, cambiar el orden o quitar pasos devuelve un aviso: los leads que están a mitad podrían quedarse sin paso siguiente.
|
|
123
|
+
|
|
124
|
+
## Leads (`leads`)
|
|
125
|
+
|
|
126
|
+
| Comando | Qué hace |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `leads list <id> [--name t] [--search t] [--state a,b] [--step uid\|ACCIÓN] [--step-status s] [--with-steps] [--limit/--offset/--page]` | Lista paginada con estado, `stateError`, última acción y veredictos de QUALIFY |
|
|
129
|
+
| `leads get <id> <leadId> [--full-data [claves]]` | Detalle: estado de cada paso, historial y claves de fullData |
|
|
130
|
+
| `leads full-data <id> <leadId> [--keys a,b] [--path a.b.c]` | Solo el fullData, o un valor concreto |
|
|
131
|
+
| `leads run-step <id> <leadId> <uid\|ACCIÓN> [--continue] [--force]` | Ejecuta un paso en un lead |
|
|
132
|
+
| `leads fields <id> [--sample n] [--step uid\|ACCIÓN] [--search t]` | Campos para columnas de exportación por paso (igual que `export-tables fields`) |
|
|
133
|
+
|
|
134
|
+
`--step-status` admite `reached` (por defecto), `success`, `failure`, `skipped`, `processing` o `pending`.
|
|
135
|
+
|
|
136
|
+
**`run-step`:**
|
|
137
|
+
- Pasa por la misma cola que el pipeline y cobra los créditos del paso.
|
|
138
|
+
- **Modo por defecto (solo ese paso):** no encola los siguientes. Un lead `completed` o `unQualified` conserva su estado, salvo que el paso cambie el resultado del filtro.
|
|
139
|
+
- **`--continue`:** sigue el pipeline desde ese paso, así que vuelve a ejecutar y cobrar los pasos posteriores.
|
|
140
|
+
- **Dependencias:** sin `--force`, exige que el lead haya completado las dependencias del paso.
|
|
141
|
+
- Errores: `DEPENDENCY_NOT_MET`, `STEP_IN_PROGRESS` (un paso asíncrono aún esperando), `STEP_NOT_RUNNABLE`, `LEAD_NOT_FOUND`, `AMBIGUOUS_STEP`.
|
|
142
|
+
|
|
143
|
+
## Tablas de exportación (`export-tables`)
|
|
144
|
+
|
|
145
|
+
| Comando | Qué hace |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `export-tables fields <campaignId> [--step uid\|ACCIÓN] [--search t] [--sample n]` | Campos disponibles para columnas, agrupados por paso: ruta, etiqueta, tipo, origen, cobertura y ejemplo |
|
|
148
|
+
| `export-tables list <campaignId>` | Tablas de la campaña |
|
|
149
|
+
| `export-tables get <tableId>` | Definición con columnas |
|
|
150
|
+
| `export-tables create <campaignId> --name N --columns "Etiqueta=ruta[:tipo];..."` | Crea una tabla. Tipos: `string`, `number`, `boolean`, `date`, `url` |
|
|
151
|
+
| `export-tables update <tableId> --data @t.json` | PUT (conserva los `id` de las columnas) |
|
|
152
|
+
| `export-tables patch <tableId> [--name] [--description] [--columns]` | Nombre, descripción o lista entera de columnas |
|
|
153
|
+
| `export-tables columns list <tableId>` | Columnas en orden, con su posición (desde 0) |
|
|
154
|
+
| `export-tables columns add <tableId> --label L --path P [--type T] [--position n\|--before col\|--after col]` | Añade una columna. `--columns "A=ruta;B=ruta"` añade varias |
|
|
155
|
+
| `export-tables columns set <tableId> <col> [--label] [--path] [--type] [--position n\|--before\|--after]` | Cambia una columna; lo que no se pasa se queda igual |
|
|
156
|
+
| `export-tables columns move <tableId> <col> --position n\|--before col\|--after col` | Mueve una columna |
|
|
157
|
+
| `export-tables columns reorder <tableId> <col>...` | Orden completo (todas las columnas, una vez cada una) |
|
|
158
|
+
| `export-tables columns remove <tableId> <col>...` | Quita una o varias columnas |
|
|
159
|
+
| `export-tables duplicate <tableId> --campaign <id> [--name]` | Copia la tabla a otra campaña |
|
|
160
|
+
| `export-tables data <tableId> [--limit ≤500] [--offset] [--search]` | Filas con los valores de las columnas |
|
|
161
|
+
| `export-tables export <tableId> --file-format xlsx\|csv [--out f]` | Descarga todas las filas (hasta 100.000) |
|
|
162
|
+
| `export-tables delete <tableId>` | Borra la tabla (sin confirmación: pregúntale antes al usuario) |
|
|
163
|
+
|
|
164
|
+
- **Rutas de columna:** `lead.<columna>`, `synthetic.<clave>` (por ejemplo `synthetic.googleMapsUrl`) y `fullData.<ruta>` (por ejemplo `fullData.consumptionEstimate.annualKwh`). Se descubren con `export-tables fields <campaignId>`.
|
|
165
|
+
- **`fields`:** cada paso lista los campos que declara aunque la campaña aún no tenga leads (`source: catalog`), más los vistos en la muestra (`observed`). La respuesta de un AI_AGENT depende del agente: si la campaña aún no tiene respuestas, sus campos se deducen de otra campaña tuya con el mismo agente (`otherCampaign`). Las filas `dynamic` (`fullData.<clave>.response.*`) marcan partes cuya forma depende de la configuración.
|
|
166
|
+
- **`<col>`:** id o etiqueta de la columna (la etiqueta no distingue mayúsculas). Si dos columnas tienen la misma etiqueta, usa el id.
|
|
167
|
+
- **Tipo por defecto:** sin `:tipo` o `--type`, la columna toma el tipo de la columna del lead o el que declara el paso para esa ruta (`annualKwh` → `number`); si no, `string`.
|
|
168
|
+
- **`warnings`:** crear o editar columnas avisa con `UNKNOWN_FULLDATA_KEY` si ningún paso de la campaña escribe esa clave de `fullData` (errata, alias cambiado). La columna se guarda igualmente.
|
|
169
|
+
- **Tablas entre campañas:** las campañas de una misma plantilla comparten uids, así que una tabla con `fullData.qualification_<uid>` se puede duplicar entre ellas.
|
|
170
|
+
|
|
171
|
+
## Códigos de error frecuentes
|
|
172
|
+
|
|
173
|
+
| Código | Qué hacer |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `VALIDATION_ERROR` (400/422) | Corrige el cuerpo; `details[]` indica `index`, `uid`, `field` y `message` |
|
|
176
|
+
| `INVALID_AREA` | Área mal formada o fuera de límites |
|
|
177
|
+
| `CAMPAIGN_NOT_FOUND`, `LEAD_NOT_FOUND`, `TEMPLATE_NOT_FOUND`, `EXPORT_TABLE_NOT_FOUND` | Id inexistente o de otra empresa |
|
|
178
|
+
| `INVALID_CAMPAIGN_STATE` | `start` solo funciona sobre campañas `queued` |
|
|
179
|
+
| `CAMPAIGN_RUNNING` / `CAMPAIGN_NOT_STARTED` | Espera a que termine, o arráncala primero |
|
|
180
|
+
| `PENDING_STEP`, `NO_LEADS`, `STEP_NOT_RUNNABLE` | Condiciones de `resume` y `steps run` no cumplidas; lee `message` |
|
|
181
|
+
| `AMBIGUOUS_STEP` | Usa uno de los uids de `details` |
|
|
182
|
+
| `DEPENDENCY_NOT_MET` | Ejecuta antes la dependencia o usa `--force` |
|
|
183
|
+
| `STEP_IN_PROGRESS` | Espera a que llegue el webhook del paso asíncrono |
|
|
184
|
+
| `UNSUPPORTED_SOURCE` | La operación solo vale para campañas de Google Maps |
|
|
185
|
+
| `TOKEN_EXPIRED`, `INVALID_TOKEN`, `MISSING_TOKEN` | Renueva o configura el token |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Diagnostica y corrige pasos del pipeline que fallan o se quedan colgados en una campaña de Satvolt, sin relanzarla: localiza qué paso y qué leads están afectados, entiende la causa y vuelve a ejecutar solo lo necesario con `suntropy satvolt`.
|
|
2
|
+
|
|
3
|
+
## Parámetros de entrada
|
|
4
|
+
|
|
5
|
+
| Parámetro | Obligatorio | Default |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| Id de la campaña | Sí | - |
|
|
8
|
+
| Paso o leads concretos que fallan | No | se detectan en el paso 1 |
|
|
9
|
+
|
|
10
|
+
Relanzar pasos gasta los créditos de ese paso por lead (y con `--continue`, también los de los pasos posteriores). Una ejecución que vuelve a fallar no cobra. Enséñale al usuario cuántos leads y cuántos créditos implica, y pide confirmación.
|
|
11
|
+
|
|
12
|
+
## Paso 1: Localizar el fallo
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
suntropy satvolt campaigns get <id> # estado de la campaña y leads por estado
|
|
16
|
+
suntropy satvolt campaigns funnel <id> --format human # failure / processing / pending por paso
|
|
17
|
+
suntropy satvolt campaigns logs <id> --level error --limit 50 --format human
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **`failure` > 0 en un paso:** saca sus leads con `leads list <id> --step <uid|ACCIÓN> --step-status failure`.
|
|
21
|
+
- **`processing` que no baja:** son pasos asíncronos esperando su webhook. Lístalos con `--step-status processing`.
|
|
22
|
+
- **Leads con `state: failed` y `stateError`:** `leads list <id> --state failed --format human` muestra el motivo.
|
|
23
|
+
|
|
24
|
+
Detalle de un lead: estado de cada paso, historial con `errorMessage` y claves de fullData:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
suntropy satvolt leads get <id> <leadId> --format json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Paso 2: Identificar la causa
|
|
31
|
+
|
|
32
|
+
| Síntoma (logs o `stateError`) | Causa | Solución |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| FIND_ROOFTOP: `ECONNREFUSED ...:8090` | En local, falta el servicio `sharing` de Suntropy, donde se suben las imágenes | Levántalo y relanza el paso |
|
|
35
|
+
| ESTIMATE_CONSUMPTION: `ECONNREFUSED ...:8765` o `Consumption model returned 5xx` | Modelo de consumo caído | Levántalo y relanza el paso |
|
|
36
|
+
| ESTIMATE_CONSUMPTION: `Catastral parcel with reference is required` | El lead no tiene parcela (FIND_ROOFTOP falló o no la encontró) | Arregla antes FIND_ROOFTOP en ese lead |
|
|
37
|
+
| QUALIFY o AI_AGENT se quedan en `processing` | El agente no llamó al webhook (Suntropy AI o Devic caídos, o no alcanzan la URL del backend) | Comprueba los servicios; cuando lleguen, relanza el paso |
|
|
38
|
+
| AI_AGENT `skipped` en muchos leads | `skipIfEmpty` apunta a un dato vacío (p. ej. LinkedIn de empresa no encontrado) | No es un error: no se cobra y el lead sigue |
|
|
39
|
+
| El paso sale `success` pero la columna llega vacía | El agente terminó sin error respondiendo que no encontró el dato | Mídelo con `campaigns funnel <id> --mode success`; define `successIf` en el paso y, si no es determinista, `maxRetries` |
|
|
40
|
+
| Paso con config inválida (`VALIDATION_ERROR` al editar) | Falta un campo obligatorio de `configSchema` | Corrígelo con `steps set <id> <uid> --config ...` |
|
|
41
|
+
| Resultados raros en todos los leads (p. ej. consumo con confianza "baja") | Configuración mejorable, no un fallo: `cnaeTemplate` vacío, orden de pasos… | Ver `satvolt-probe-to-full-campaign`, paso 5 |
|
|
42
|
+
|
|
43
|
+
Si trabajas contra el backend local, comprueba los servicios que usa el pipeline:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
lsof -nP -iTCP -sTCP:LISTEN | grep -E ':(8099|8090|8765|8500|8033) '
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Paso 3: Elegir cómo relanzar
|
|
50
|
+
|
|
51
|
+
| Situación | Comando | Qué hace |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| Un paso falló en algunos leads y el resto del pipeline de esos leads está bien | `leads run-step <id> <leadId> <uid\|ACCIÓN>` | Solo ese paso; no encola los siguientes |
|
|
54
|
+
| El fallo cortó la cadena del lead (los pasos siguientes nunca se ejecutaron) | `leads run-step <id> <leadId> <uid> --continue` | Ese paso y el resto del pipeline |
|
|
55
|
+
| Un paso nuevo que ningún lead ha ejecutado, sobre todos los leads | `steps run <id> <uid>` | Se lanza en todos los leads que el pipeline habría alcanzado y continúa |
|
|
56
|
+
| Quieres añadir un paso al final y ejecutarlo | `campaigns resume <id> --action A --config @c.json` | Lo añade antes de COMPLETE y lo ejecuta |
|
|
57
|
+
| La configuración invalida la campaña entera | `campaigns reset <id> --yes --start` | Borra leads y resultados y vuelve a pagarlo todo (último recurso) |
|
|
58
|
+
|
|
59
|
+
**Reglas de `leads run-step`:**
|
|
60
|
+
- El paso se indica por uid, por acción si aparece una sola vez, por nombre (`"Decisor LinkedIn"`) o por su clave de fullData (`AMBIGUOUS_STEP` lista los uids).
|
|
61
|
+
- **Modo por defecto:** un lead `completed` o `unQualified` conserva su estado. Solo cambia si el paso cambia el resultado del filtro: QUALIFY puede rescatar un lead descartado, y un paso que ahora lo descarta lo deja en `unQualified`.
|
|
62
|
+
- **`--continue`:** vuelve a ejecutar y cobrar todos los pasos siguientes.
|
|
63
|
+
- **`DEPENDENCY_NOT_MET`:** el lead no completó la dependencia (p. ej. FIND_ROOFTOP antes de ESTIMATE_CONSUMPTION). Relanza primero la dependencia; `--force` solo si sabes que el dato existe.
|
|
64
|
+
- **`STEP_IN_PROGRESS`:** el paso asíncrono aún espera su webhook. Espera o investiga ese servicio.
|
|
65
|
+
- **Registro:** la ejecución queda marcada como `manual` y cobra los créditos del paso.
|
|
66
|
+
|
|
67
|
+
**Muchos leads.** Recorre la lista y relanza uno a uno. Si falló la primera ejecución de un paso en casi todos los leads, comprueba antes con `steps list <id>` si el paso sale `runnable`: entonces `steps run` lo lanza en bloque.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
suntropy satvolt leads list <id> --step FIND_ROOFTOP --step-status failure --limit 200 --fields idLead --format csv \
|
|
71
|
+
| tail -n +2 | while read lead; do
|
|
72
|
+
suntropy satvolt leads run-step <id> "$lead" FIND_ROOFTOP --continue
|
|
73
|
+
done
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Paso 4: Verificar
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
suntropy satvolt campaigns funnel <id> --format human # failure del paso debe bajar
|
|
80
|
+
suntropy satvolt leads get <id> <leadId> # estado del paso: success
|
|
81
|
+
suntropy satvolt leads full-data <id> <leadId> --keys consumptionEstimate
|
|
82
|
+
suntropy satvolt campaigns logs <id> --since <lastTs> --level error
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **Pasos síncronos** (FIND_ROOFTOP, ESTIMATE_CONSUMPTION): el resultado está en segundos.
|
|
86
|
+
- **Pasos asíncronos** (QUALIFY, AI_AGENT): tardan lo que el agente, de uno a varios minutos.
|
|
87
|
+
- **Cierre de la campaña:** vuelve a `completed` cuando todos sus leads llegan a un estado final.
|
|
88
|
+
|
|
89
|
+
Resume al usuario: causa, leads afectados, qué se relanzó y con qué modo, créditos consumidos (`campaigns usage <id>`) y leads que siguen fallando.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Lanza una campaña de Satvolt en dos fases: primero una sonda con pocos leads para validar que el pipeline funciona y da datos útiles, y después la ampliación a toda la zona sin repetir ni volver a pagar lo ya hecho. Todo con `suntropy satvolt`. Es el proceso seguido con la campaña 62 (Huévar del Aljarafe, círculo de 5 km): sonda de 50 leads, corrección de la configuración del consumo, ampliación a 100, verificación y ampliación sin límite.
|
|
2
|
+
|
|
3
|
+
Ten a mano `satvolt-cli` para las opciones y `satvolt-lead-troubleshooting` si algo falla.
|
|
4
|
+
|
|
5
|
+
## Parámetros de entrada
|
|
6
|
+
|
|
7
|
+
| Parámetro | Obligatorio | Default |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Zona: centro y radio, rectángulo o polígono | Sí | - |
|
|
10
|
+
| Configuración base: plantilla, campaña anterior o pasos nuevos | Sí | - |
|
|
11
|
+
| Nombre de la campaña | No | `<base> Sonda - <municipio>` |
|
|
12
|
+
| Leads de la sonda | No | 50 (ver paso 2) |
|
|
13
|
+
| Grupos de negocio | No | los de la base |
|
|
14
|
+
| Si se amplía y hasta cuántos leads | No | se decide tras validar |
|
|
15
|
+
|
|
16
|
+
Pide confirmación explícita al usuario antes de cualquier comando que gaste créditos: `start`, `extend`, `steps run`, `leads run-step` y `reset`.
|
|
17
|
+
|
|
18
|
+
## Paso 1: Elegir la configuración base
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
suntropy satvolt templates list --format human
|
|
22
|
+
suntropy satvolt campaigns list --state completed --format human
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **Si existe una plantilla adecuada:** úsala con `--template "<nombre>"`.
|
|
26
|
+
- **Si hay una campaña ya validada:** conviértela en plantilla, para reutilizarla en otras zonas. Si solo vas a lanzar esta sonda y ampliarla en el sitio, basta con `campaigns create --from-campaign <id>`.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
suntropy satvolt templates create --name "Greenvolt industria" --from-campaign 59 \
|
|
30
|
+
--description "QUALIFY industria + CIF + LinkedIn + decisor, consumo con CNAE"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **Si no hay base:** escribe los pasos consultando `catalog actions` y crea la plantilla con `--steps @steps.json`.
|
|
34
|
+
|
|
35
|
+
Revisa la base antes de usarla (`templates get "<nombre>"`). Estos son los fallos de configuración detectados en campañas reales:
|
|
36
|
+
|
|
37
|
+
| Revisa | Por qué |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `ESTIMATE_CONSUMPTION` va **después** del paso que obtiene el CNAE y lo referencia en `cnaeTemplate` (p. ej. `{{fullData.cif.response.extras.cnae}}` del agente "Buscador de CIF") | Sin CNAE la confianza no pasa de "media" y el consumo de los fabricantes sale muy por debajo (mediana ×1,77 al añadirlo) |
|
|
40
|
+
| `businessGroups` no vacío (p. ej. `businesses`) | Con `[]` entran cementerios, iglesias, gasolineras…; QUALIFY los descarta, pero cada uno ya ha pagado FIND_ROOFTOP, consumo y QUALIFY |
|
|
41
|
+
| `qualificationDefinition` de QUALIFY acotada al objetivo | Si incluye "restaurantes, hoteles…", en los cascos urbanos cualifican bares sin CIF ni LinkedIn que después pasan por los agentes caros |
|
|
42
|
+
| Los agentes caros (55 créditos) van detrás de QUALIFY con `filterUnqualifiedLeads: true` | Así solo los pagan los leads cualificados |
|
|
43
|
+
|
|
44
|
+
Para corregir la plantilla: `templates patch "<nombre>" --steps '[{"uid":"<uid>","config":{...}}]'`, o `--business-groups businesses`.
|
|
45
|
+
|
|
46
|
+
## Paso 2: Dimensionar y crear la sonda
|
|
47
|
+
|
|
48
|
+
**Coste de la sonda.** Estímalo con una campaña anterior con la misma base (`campaigns usage <id>` → `avgCreditsPerLead`). Si no hay ninguna, usa `estimatedCreditsPerLead`, que es el máximo. Coste ≈ leads × créditos/lead × 0,005 €. Enséñale al usuario opciones con su coste:
|
|
49
|
+
|
|
50
|
+
| Leads | Para qué sirve |
|
|
51
|
+
|---|---|
|
|
52
|
+
| 20 | Comprobar que no falla nada; casi ningún lead llega a los agentes (cualifica un 25–35 %) |
|
|
53
|
+
| 50 | Recomendado: unos 15 leads cualificados recorren todo el pipeline |
|
|
54
|
+
| 100 | Estimar con más fiabilidad la tasa de cualificación y la cobertura de CIF y LinkedIn |
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
suntropy satvolt campaigns create --name "Greenvolt Sonda - Huévar" \
|
|
58
|
+
--template "Greenvolt industria" \
|
|
59
|
+
--circle 37.3509,-6.2757 --radius 5000 --max-leads 50 \
|
|
60
|
+
--address "41830 Huévar del Aljarafe, Sevilla" --region "Andalucía"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- **Qué comprobar en la respuesta:** `basedOn`, que los pasos y uids son los de la base, `warnings` y `estimatedCreditsPerLead`. La campaña queda en `queued`.
|
|
64
|
+
- **Sesgo de la muestra:** el área se divide en sectores de 1 km que se buscan de norte a sur, y con `--max-leads` la búsqueda se corta al llenarse. La sonda sale de la franja norte del área, no de toda. Avísalo al usuario. Si lo que interesa es el centro, haz la sonda con un radio pequeño alrededor del punto de interés.
|
|
65
|
+
|
|
66
|
+
## Paso 3: Arrancar y seguir
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
suntropy satvolt campaigns start <id>
|
|
70
|
+
suntropy satvolt campaigns logs <id> --level error # repítelo mientras avanza
|
|
71
|
+
suntropy satvolt campaigns funnel <id> --format human
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- **`logs --follow`** termina solo cuando la campaña pasa a `completed`, `failed` o `canceled`. En un agente, sondea `funnel` o `get` cada 1–3 minutos.
|
|
75
|
+
- **Si un paso acumula `failure`:** para y sigue `satvolt-lead-troubleshooting`. Es típico FIND_ROOFTOP con `ECONNREFUSED` porque falta un servicio en local.
|
|
76
|
+
- **Pasos asíncronos** (QUALIFY, AI_AGENT): salen como `processing` hasta que llega su webhook. Si alguno se queda en `processing` durante horas, apúntalo: no bloquea al resto.
|
|
77
|
+
|
|
78
|
+
## Paso 4: Validar la sonda
|
|
79
|
+
|
|
80
|
+
Revísala con el usuario antes de ampliar. Comprueba:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
suntropy satvolt campaigns get <id> # estado, leadStates, sectorSearch
|
|
84
|
+
suntropy satvolt campaigns funnel <id> --format human # tasa de cualificación = QUALIFY success / reached
|
|
85
|
+
suntropy satvolt campaigns usage <id> --format human # créditos reales por lead
|
|
86
|
+
suntropy satvolt leads list <id> --state completed --format human
|
|
87
|
+
suntropy satvolt leads list <id> --step <uid decisor> --step-status skipped # cualificados sin LinkedIn de empresa
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Calidad del enriquecimiento.** Monta una tabla con las columnas clave y revísala entera. Las rutas de `fullData` de este ejemplo son las de la configuración de la campaña 62 (agentes con alias `cif`, `companyLinkedin` y `decisoresLinkedin`): sácalas de `export-tables fields` para tu campaña. Añade la columna de QUALIFY (`fullData.qualification_<uid>.qualifies`) para separar los cualificados, porque `export-tables data` no filtra por estado:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
suntropy satvolt export-tables fields <id> --format human
|
|
94
|
+
suntropy satvolt export-tables create <id> --name "Validación sonda" --columns \
|
|
95
|
+
"Empresa=lead.commercialName;Tipo=lead.googlePlacesType;Estado=lead.state;\
|
|
96
|
+
CIF=fullData.cif.response.cif;CNAE=fullData.cif.response.extras.cnae;\
|
|
97
|
+
LinkedIn=fullData.companyLinkedin.response.linkedinUrl;\
|
|
98
|
+
Consumo kWh=fullData.consumptionEstimate.annualKwh:number;\
|
|
99
|
+
Confianza=fullData.consumptionEstimate.confidence.label;\
|
|
100
|
+
Parcela=fullData.catastralParcel.catastralReference"
|
|
101
|
+
suntropy satvolt export-tables data <tableId> --limit 100 --format human
|
|
102
|
+
# Añadir o ajustar columnas sin rehacer la tabla
|
|
103
|
+
suntropy satvolt export-tables columns add <tableId> --label Decisor --path fullData.decisoresLinkedin.response.mainDecisionMaker.name --after LinkedIn
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Señal | Qué suele significar | Acción |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| Muchos tipos que no son negocio | `businessGroups` vacío | Pon `businesses` en la configuración o la plantilla |
|
|
109
|
+
| Cualifican bares y restaurantes sin CIF | La definición de QUALIFY incluye el terciario | Acótala antes de ampliar |
|
|
110
|
+
| Confianza "baja" con 56.431 kWh repetido | El paso no recibió ni CNAE ni superficie | Revisa `cnaeTemplate` y el orden. Las parcelas con varios inmuebles no tienen superficie: es lo esperado |
|
|
111
|
+
| Varios leads con la misma `Parcela` y el mismo consumo | FIND_ROOFTOP asignó a varias empresas de un polígono la misma parcela grande | Anótalo como limitación; no bloquea |
|
|
112
|
+
| CIF o LinkedIn muy bajos | Zona rural o negocios pequeños | Espera menos decisores por lead; ajusta el coste esperado |
|
|
113
|
+
| Consumo o QUALIFY en `failure` | Servicio caído o error de config | `satvolt-lead-troubleshooting` |
|
|
114
|
+
|
|
115
|
+
## Paso 5: Corregir sin relanzar
|
|
116
|
+
|
|
117
|
+
- **Cambios de configuración:** aplícalos en la campaña y también en la plantilla, para que la próxima campaña salga bien.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
suntropy satvolt steps set <id> <uid consumo> --after <uid agente CIF> \
|
|
121
|
+
--config '{"cnaeTemplate":"{{fullData.cif.response.extras.cnae}}"}'
|
|
122
|
+
suntropy satvolt templates patch "Greenvolt industria" --steps '[{"uid":"<uid consumo>","after":"<uid agente CIF>","config":{"cnaeTemplate":"{{fullData.cif.response.extras.cnae}}"}}]'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- **Leads sueltos afectados:** repite solo el paso sobre ellos (1 crédito por lead en el consumo):
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
suntropy satvolt leads run-step <id> <leadId> ESTIMATE_CONSUMPTION
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- **Relanzar todo (`reset`):** solo si la configuración invalida la sonda entera. Borra los leads y los vuelve a pagar.
|
|
132
|
+
|
|
133
|
+
## Paso 6: Ampliar por etapas
|
|
134
|
+
|
|
135
|
+
Antes de cada ampliación, confirma con el usuario el coste estimado. Calcúlalo con los datos de la sonda, no con los del catálogo: leads nuevos × `avgCreditsPerLead` × 0,005 €.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
suntropy satvolt campaigns get <id> # sectorSearch: incomplete + unknown > 0 → queda área por buscar
|
|
139
|
+
suntropy satvolt campaigns extend <id> --max-leads 100
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- **Qué hace:** solo busca en los sectores pendientes y solo los leads nuevos pasan por el pipeline.
|
|
143
|
+
- **Qué se repite:** las primeras peticiones a Places de los sectores pendientes (unos 0,025 $ por petición). En campañas antiguas los sectores salen como `unknown` y se buscan todos una vez.
|
|
144
|
+
|
|
145
|
+
Cuando termine, repite el paso 4 solo sobre los leads nuevos: compara la tasa de cualificación, los CIF, LinkedIn y decisores y los créditos por lead. En Huévar la primera ampliación salió mejor que la sonda (34 % frente a 24 % de cualificación) porque se acercó al centro.
|
|
146
|
+
|
|
147
|
+
**Comprueba que no se reprocesó nada.** En `campaigns usage <id> --by-lead`, los leads anteriores no deben tener ejecuciones nuevas, y en `funnel` los números de los pasos solo suben con los leads nuevos.
|
|
148
|
+
|
|
149
|
+
**Cuando esté validado, amplía a toda la zona:**
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
suntropy satvolt campaigns extend <id> --no-limit
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Sin límite, cada sector se barre entero, con hasta 40 peticiones a Places por sector si es denso. Al terminar, `sectorSearch.exhausted = total`: la zona está agotada y otra ampliación no encontrará nada.
|
|
156
|
+
|
|
157
|
+
## Paso 7: Cierre
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
suntropy satvolt campaigns usage <id> --format human # créditos consumidos: total, por lead y por paso
|
|
161
|
+
suntropy satvolt export-tables export <tableId> --file-format xlsx --out campaña.xlsx
|
|
162
|
+
suntropy satvolt templates patch "Greenvolt industria" --max-leads 50 # deja la plantilla lista para la próxima sonda
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Resume al usuario:
|
|
166
|
+
- leads totales y cualificados por tanda;
|
|
167
|
+
- cobertura de CIF, LinkedIn y decisores;
|
|
168
|
+
- confianza del consumo;
|
|
169
|
+
- créditos consumidos (total y por lead) de cada tanda;
|
|
170
|
+
- incidencias (servicios caídos, parcelas repetidas, sesgo de la muestra);
|
|
171
|
+
- ajustes hechos en la plantilla.
|
|
172
|
+
|
|
173
|
+
## Si trabajas contra el backend local (desarrollo)
|
|
174
|
+
|
|
175
|
+
El pipeline completo necesita estos servicios levantados. Compruébalo con `lsof -iTCP -sTCP:LISTEN` antes de arrancar.
|
|
176
|
+
|
|
177
|
+
| Servicio | Puerto | Sin él |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| Backend Satvolt | 8099 | La CLI da `ECONNREFUSED` |
|
|
180
|
+
| `sharing` de Suntropy | 8090 | FIND_ROOFTOP falla con `ECONNREFUSED ...:8090` |
|
|
181
|
+
| Modelo de consumo | 8765 | ESTIMATE_CONSUMPTION falla |
|
|
182
|
+
| Suntropy AI | 8500 | QUALIFY no arranca |
|
|
183
|
+
| Devic | 8033 | AI_AGENT no arranca |
|
|
184
|
+
|
|
185
|
+
Con poca memoria, macOS mata procesos en segundo plano. Arranca backend y `sharing` desde build (`node dist/main`) en vez de en modo watch.
|