@kwirthmagnify/kwirth-docs-pinocchio 0.2.31
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/_sidebar.md +21 -0
- package/admin/01-setup.md +98 -0
- package/admin/02-ai-config.md +125 -0
- package/admin/03-tools.md +131 -0
- package/admin/04-prompts.md +147 -0
- package/admin/05-rbac.md +89 -0
- package/admin/06-limits.md +113 -0
- package/images/admin-ai-llm.png +0 -0
- package/images/admin-ai-provider.png +0 -0
- package/images/import-export.png +0 -0
- package/images/playground-call.png +0 -0
- package/images/playground-in.png +0 -0
- package/images/playground-llm.png +0 -0
- package/images/playground-out.png +0 -0
- package/images/tool-selector.png +0 -0
- package/images/triggers-dialog.png +0 -0
- package/images/ui-clear-dialog.png +0 -0
- package/images/ui-config-menu.png +0 -0
- package/images/ui-tab.png +0 -0
- package/index.html +64 -0
- package/index.md +42 -0
- package/kwirth-dark.css +458 -0
- package/package.json +9 -0
- package/serve.cmd +1 -0
- package/serve.mjs +84 -0
- package/user/00-how-it-works.md +88 -0
- package/user/01-introduction.md +60 -0
- package/user/02-ui-tour.md +96 -0
- package/user/03-concepts.md +160 -0
- package/user/04-triggers.md +96 -0
- package/user/05-findings.md +108 -0
- package/user/06-playground.md +134 -0
- package/user/07-import-export.md +83 -0
package/_sidebar.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
- [Pinocchio](index.md)
|
|
2
|
+
|
|
3
|
+
- **Conceptos**
|
|
4
|
+
- [Introducción y modelo mental](user/01-introduction.md)
|
|
5
|
+
- [Cómo funciona](user/00-how-it-works.md)
|
|
6
|
+
- [Triggers, versiones y prompts](user/03-concepts.md)
|
|
7
|
+
|
|
8
|
+
- **Guía de usuario**
|
|
9
|
+
- [Recorrido por la UI](user/02-ui-tour.md)
|
|
10
|
+
- [Configurar triggers](user/04-triggers.md)
|
|
11
|
+
- [Leer los findings](user/05-findings.md)
|
|
12
|
+
- [El Playground](user/06-playground.md)
|
|
13
|
+
- [Import / Export de triggers](user/07-import-export.md)
|
|
14
|
+
|
|
15
|
+
- **Guía de administrador**
|
|
16
|
+
- [Instalación](admin/01-setup.md)
|
|
17
|
+
- [Providers y modelos de IA](admin/02-ai-config.md)
|
|
18
|
+
- [Tools y pasos del agente](admin/03-tools.md)
|
|
19
|
+
- [Plantillas de prompt](admin/04-prompts.md)
|
|
20
|
+
- [Permisos y acceso](admin/05-rbac.md)
|
|
21
|
+
- [Límites conocidos](admin/06-limits.md)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Instalación
|
|
2
|
+
|
|
3
|
+
Pinocchio es un plugin de tipo **canal**. Se instala como cualquier otro plugin de kwirth, pero tiene una
|
|
4
|
+
dependencia dura y una condición previa que conviene resolver antes de que los usuarios abran el canal.
|
|
5
|
+
|
|
6
|
+
## Requisitos
|
|
7
|
+
|
|
8
|
+
| Requisito | Detalle |
|
|
9
|
+
|-----------|---------|
|
|
10
|
+
| Provider `business` | **Obligatorio.** El manifiesto declara `requiresExtension: ["provider:business:0.1.6"]`. kwirth no deja instalar el plugin sin él. |
|
|
11
|
+
| Providers `events` y `metrics` | Los aporta el core. No hay nada que instalar. |
|
|
12
|
+
| Almacenamiento del canal | El canal declara `storage: true`: kwirth le da un almacén donde guarda la clave `pinocchio-config`. |
|
|
13
|
+
| Un provider de IA y un LLM | **No es un requisito de instalación, pero sí de uso.** Sin ellos el canal arranca y no sirve para nada. Ver [Providers y modelos de IA](02-ai-config.md). |
|
|
14
|
+
| Tipo de clúster | El canal declara `sources: [KUBERNETES]`. No funciona sobre orígenes que no sean Kubernetes. |
|
|
15
|
+
|
|
16
|
+
**Reinicio:** el manifiesto declara `requiresRestart: false`. Instalarlo o actualizarlo no obliga a reiniciar
|
|
17
|
+
el backend de kwirth.
|
|
18
|
+
|
|
19
|
+
## Instalar en producción
|
|
20
|
+
|
|
21
|
+
Desde la UI de administración de kwirth, en el gestor de plugins: búscalo en el marketplace, o instálalo
|
|
22
|
+
desde la URL del tarball publicado:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
https://registry.npmjs.org/@kwirthmagnify/kwirth-plugin-pinocchio/-/kwirth-plugin-pinocchio-<version>.tgz
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Instala **antes** el provider `business`; si no, la instalación del plugin se rechaza por dependencia no
|
|
29
|
+
satisfecha.
|
|
30
|
+
|
|
31
|
+
## Instalar en desarrollo
|
|
32
|
+
|
|
33
|
+
En un entorno de desarrollo, el backend carga los plugins desde disco leyendo `back/kwirth-dev.json`:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"plugins": {
|
|
38
|
+
"pinocchio": "../plugins/pinocchio/dist"
|
|
39
|
+
},
|
|
40
|
+
"providers": {
|
|
41
|
+
"business": "../providers/business/dist"
|
|
42
|
+
},
|
|
43
|
+
"docs": {
|
|
44
|
+
"pinocchio": "../plugins/pinocchio/docs/pinocchio.tgz"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
La entrada de `docs` es la que hace que **esta guía** quede accesible desde la propia UI de kwirth.
|
|
50
|
+
|
|
51
|
+
## Construir desde fuente
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cd plugins/pinocchio
|
|
55
|
+
npm install
|
|
56
|
+
npm run build # front.js + back.js + docs/pinocchio.tgz + dist/package.json
|
|
57
|
+
npm run watch # reconstruye todo en cada cambio, la guía incluida
|
|
58
|
+
npm run docs # sólo reempaqueta la guía
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`build` y `watch` cubren **las tres piezas**: el bundle de front, el de back y el tarball de la guía. No hace
|
|
62
|
+
falta acordarse de empaquetar la documentación por separado.
|
|
63
|
+
|
|
64
|
+
Con `watch` en marcha:
|
|
65
|
+
|
|
66
|
+
- un cambio en `src/front/` lo recoge el navegador solo (el front sondea `front.js` cada 2 s);
|
|
67
|
+
- un cambio en `src/back/` **requiere reiniciar el core**, que cachea el módulo de back;
|
|
68
|
+
- un cambio en `docs/guide/` reconstruye el tarball al vuelo, pero el core lo instala al arrancar, así que se
|
|
69
|
+
ve tras reiniciarlo.
|
|
70
|
+
|
|
71
|
+
## Qué hace el canal al arrancar
|
|
72
|
+
|
|
73
|
+
Cada vez que un usuario abre el canal, la instancia del backend:
|
|
74
|
+
|
|
75
|
+
1. Se suscribe a los providers `metrics`, `business` (espacios `customers.status`, `branches.status`,
|
|
76
|
+
`launch.immediate`) y `events` (los 11 kinds que vigila).
|
|
77
|
+
2. Lee los **providers de IA** del almacén común (`kwirth-ai-providers`) y construye los modelos.
|
|
78
|
+
3. Lee su propia configuración (`pinocchio-config`) del almacén del canal.
|
|
79
|
+
4. Lee los **LLMs** del almacén común (`kwirth-ai-llms`); si hay alguno, sustituye a los que tuviera guardados.
|
|
80
|
+
|
|
81
|
+
> ⚠️ **Los puntos 2 y 4 sólo ocurren al arrancar el canal.** Si un administrador crea un provider desde los
|
|
82
|
+
> menús *AI Providers* del core con el canal ya abierto, ese canal no lo verá hasta que se cierre y se vuelva
|
|
83
|
+
> a abrir. Ver [Límites conocidos](06-limits.md).
|
|
84
|
+
|
|
85
|
+
## Comportamiento del canal
|
|
86
|
+
|
|
87
|
+
Lo que el canal declara al core, y lo que significa para el usuario:
|
|
88
|
+
|
|
89
|
+
| Propiedad | Valor | Efecto |
|
|
90
|
+
|-----------|-------|--------|
|
|
91
|
+
| `routable` | `false` | No se puede dirigir a un recurso concreto: siempre es de clúster. |
|
|
92
|
+
| `pauseable` | `true` | Se puede pausar y continuar. |
|
|
93
|
+
| `modifiable` | `false` | No se puede reconfigurar la instancia en caliente. |
|
|
94
|
+
| `reconnectable` | `true` | Sobrevive a una reconexión del websocket. |
|
|
95
|
+
| `resourced` | `false` | No selecciona namespaces ni pods al arrancar. |
|
|
96
|
+
| `cluster` | `true` | Es un canal de ámbito de clúster. |
|
|
97
|
+
|
|
98
|
+
No tiene diálogo de *Setup*: no hay nada que elegir antes de arrancarlo.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Providers y modelos de IA
|
|
2
|
+
|
|
3
|
+
Esta es la configuración que hay que resolver **antes** de que Pinocchio sirva para algo. Y lo primero que
|
|
4
|
+
hay que entender es que **no es configuración de Pinocchio**.
|
|
5
|
+
|
|
6
|
+
## La configuración de IA es de kwirth, no del plugin
|
|
7
|
+
|
|
8
|
+
Pinocchio no guarda sus propios providers ni sus propios modelos. Los lee del **almacén común de kwirth**,
|
|
9
|
+
compartido con todos los demás plugins que usan IA:
|
|
10
|
+
|
|
11
|
+
| Qué | Clave | Dónde |
|
|
12
|
+
|-----|-------|-------|
|
|
13
|
+
| Providers (con las API keys) | `kwirth-ai-providers` | **Secret** de Kubernetes |
|
|
14
|
+
| LLMs | `kwirth-ai-llms` | **ConfigMap** de Kubernetes |
|
|
15
|
+
|
|
16
|
+
Las consecuencias prácticas son tres:
|
|
17
|
+
|
|
18
|
+
1. Si ya configuraste un provider para otro plugin, **Pinocchio lo ve**. No hay que repetir nada.
|
|
19
|
+
2. Si lo configuras desde Pinocchio, **lo verán los demás**. Los diálogos del grupo *AI* del menú Config
|
|
20
|
+
(*AI providers* / *AI models*) son literalmente los mismos componentes comunes
|
|
21
|
+
(`AiConfigProvider` / `AiConfigLlm`) que usa el core en sus menús *AI Providers* / *AI Models*.
|
|
22
|
+
3. Las claves viven en un **Secret**, no en un ConfigMap. Quien tenga permiso de lectura sobre los Secrets del
|
|
23
|
+
namespace de kwirth puede leerlas.
|
|
24
|
+
|
|
25
|
+
## Provider
|
|
26
|
+
|
|
27
|
+
Un *provider* es una cuenta contra un proveedor de IA: el adaptador y la clave.
|
|
28
|
+
|
|
29
|
+
| Campo | Qué es |
|
|
30
|
+
|-------|--------|
|
|
31
|
+
| `Name` | Identificador libre de **esta** instancia de provider (`openai-prod`, `huawei-maas`). Es lo que referencian los LLMs. |
|
|
32
|
+
| `Type` | El adaptador de SDK. Lista cerrada: `google`, `openai`, `openrouter`, `mistral`, `groq`, `deepseek`, `anthropic`, `openai-compat`. |
|
|
33
|
+
| `API Key / Token` | La credencial. Campo de contraseña con botón de ojo para revelarla. |
|
|
34
|
+
| `Base URL` | **Sólo para `openai-compat`.** La URL base de la API compatible con OpenAI (Huawei MaaS, vLLM, LM Studio…). Debe apuntar a la raíz; el adaptador añade `/v1` si falta. |
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
Al elegir un `Type`, si el nombre está vacío se rellena solo con el tipo. **Déjalo así salvo que tengas una
|
|
39
|
+
razón** — ver la advertencia del final de la página.
|
|
40
|
+
|
|
41
|
+
**Load models** consulta al proveedor el catálogo de modelos disponibles y lo cachea en el provider. La
|
|
42
|
+
llamada la hace el **core** de kwirth (`POST /core/aiconfig/loadmodels`), no el plugin, porque es el core
|
|
43
|
+
quien tiene los adaptadores de cada SDK. Si el botón no aparece o falla, revisa que la clave sea válida.
|
|
44
|
+
|
|
45
|
+
Sin modelos cargados, el provider aparece **deshabilitado** en el desplegable del diálogo de LLM.
|
|
46
|
+
|
|
47
|
+
> ⚠️ El botón **Export** de este diálogo descarga `kwirth-providers.json` **con las API keys en claro**.
|
|
48
|
+
> Trátalo como un secreto: no lo subas a un repositorio ni lo mandes por correo.
|
|
49
|
+
|
|
50
|
+
## LLM
|
|
51
|
+
|
|
52
|
+
Un *LLM* es un modelo concreto de un provider concreto, con sus parámetros. Es lo que referencian los
|
|
53
|
+
triggers, por su `LLM ID`.
|
|
54
|
+
|
|
55
|
+
| Campo | Qué es |
|
|
56
|
+
|-------|--------|
|
|
57
|
+
| `LLM ID` | El identificador que usarán los triggers. **Elígelo con cuidado**: es lo que viaja en un export de triggers. |
|
|
58
|
+
| `Provider` | Uno de los providers configurados. Sólo se pueden elegir los que tienen modelos cargados. |
|
|
59
|
+
| `Model` | Desplegable con los modelos cargados del provider; si el provider no tiene catálogo, se convierte en una caja de texto libre. |
|
|
60
|
+
| `Model temperature` | Temperatura. ⚠️ Pinocchio la **recorta al rango 0–1** antes de llamar al modelo. |
|
|
61
|
+
| `Input / Output cost / M tokens` | Coste informativo por millón de tokens. No lo usa el motor: es para que puedas calcular la factura. |
|
|
62
|
+
| `Use provider API Key` | Si está marcado, usa la clave del provider. Si no, puedes darle una clave específica a este LLM. |
|
|
63
|
+
|
|
64
|
+

|
|
65
|
+
|
|
66
|
+
Para análisis de seguridad, **temperatura baja** (0 – 0.2). Lo que quieres es un auditor consistente, no uno
|
|
67
|
+
creativo.
|
|
68
|
+
|
|
69
|
+
## Salida estructurada: no todos los modelos valen
|
|
70
|
+
|
|
71
|
+
Los triggers de tipo `artifact` exigen al modelo una respuesta que cumpla un esquema JSON estricto. Un modelo
|
|
72
|
+
pequeño o antiguo que no soporte *structured output* fallará sistemáticamente, y lo verás como un análisis con
|
|
73
|
+
dos findings `critical` y el error del proveedor.
|
|
74
|
+
|
|
75
|
+
Pinocchio añade opciones específicas por proveedor para que esto funcione:
|
|
76
|
+
|
|
77
|
+
| Proveedor | Opciones que envía |
|
|
78
|
+
|-----------|--------------------|
|
|
79
|
+
| `google` | `{ google: { structuredOutputs: true } }` |
|
|
80
|
+
| `groq` | `{ groq: { structuredOutputs: true } }` |
|
|
81
|
+
| `mistral` | `{ mistral: { strictJsonSchema: true, structuredOutputs: true } }` |
|
|
82
|
+
| cualquier otro | `{ openai: {} }` |
|
|
83
|
+
|
|
84
|
+
> ⚠️ **Gotcha importante.** Esa tabla se elige por el **`Name`** del provider, no por su `Type`. El modelo
|
|
85
|
+
> sí se construye correctamente por el tipo, pero las opciones de salida estructurada no.
|
|
86
|
+
>
|
|
87
|
+
> Es decir: un provider de tipo `google` llamado `google` funciona; el **mismo** provider llamado
|
|
88
|
+
> `gemini-prod` construye el modelo bien pero **pierde** el `structuredOutputs: true`, y los triggers
|
|
89
|
+
> `artifact` pueden empezar a fallar sin motivo aparente.
|
|
90
|
+
>
|
|
91
|
+
> **Recomendación: deja el `Name` del provider igual que su `Type`** mientras no tengas que crear dos cuentas
|
|
92
|
+
> del mismo proveedor. Si necesitas varias, usa una con el nombre canónico para Pinocchio. Está recogido en
|
|
93
|
+
> [Límites conocidos](06-limits.md).
|
|
94
|
+
|
|
95
|
+
## El orden importa
|
|
96
|
+
|
|
97
|
+
El menú Config va habilitando entradas conforme cumples los requisitos:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
AI ▸ AI providers (siempre)
|
|
101
|
+
|
|
|
102
|
+
v con >= 1 provider
|
|
103
|
+
AI ▸ AI models
|
|
104
|
+
|
|
|
105
|
+
v con >= 1 LLM
|
|
106
|
+
Trigger
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Si un usuario te dice que "Trigger está en gris", no es un fallo del plugin: falta el LLM.
|
|
110
|
+
|
|
111
|
+
## Cuándo lee el canal esta configuración
|
|
112
|
+
|
|
113
|
+
**Sólo al arrancar la instancia del canal.** Si creas un provider o un LLM desde los menús *AI Providers* /
|
|
114
|
+
*AI Models* del core mientras un usuario tiene el canal abierto, ese usuario no lo verá hasta cerrar y
|
|
115
|
+
reabrir el canal.
|
|
116
|
+
|
|
117
|
+
En cambio, si lo creas **desde el propio menú Config de Pinocchio**, el canal sí se entera al momento: el
|
|
118
|
+
plugin manda un `PROVIDERSSET`/`CONFIGSET` que reescribe el almacén y recarga los modelos.
|
|
119
|
+
|
|
120
|
+
## Multi-clúster
|
|
121
|
+
|
|
122
|
+
La configuración de IA **no viaja entre clústeres**. Los menús del core escriben en el backend **local**,
|
|
123
|
+
pero un canal abierto contra otro kwirth lee el almacén de **ese** clúster. Si operas una federación, tienes
|
|
124
|
+
que configurar providers y LLMs en cada clúster, y con los **mismos ids de LLM** si quieres poder mover
|
|
125
|
+
triggers entre ellos. Ver [Límites conocidos](06-limits.md).
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Tools y pasos del agente
|
|
2
|
+
|
|
3
|
+
Las **tools** son lo que separa a Pinocchio de un chat al que le pegas un YAML. Son funciones reales,
|
|
4
|
+
ejecutadas dentro del clúster, que el modelo puede invocar por su cuenta mientras razona. Esta página
|
|
5
|
+
explica el catálogo, cómo se controla el gasto y —lo más importante— **por qué `Auto` es peligroso**.
|
|
6
|
+
|
|
7
|
+
## Cómo funciona la ejecución con tools
|
|
8
|
+
|
|
9
|
+
Cuando un trigger se dispara, el backend construye una llamada al modelo con:
|
|
10
|
+
|
|
11
|
+
- el `system` y el `prompt`,
|
|
12
|
+
- el subconjunto de tools que la versión declara,
|
|
13
|
+
- un tope de pasos: `stopWhen(stepCountIs(steps))`.
|
|
14
|
+
|
|
15
|
+
El modelo decide si necesita datos. Si los necesita, llama a una tool; el backend la ejecuta, le devuelve el
|
|
16
|
+
resultado, y eso cuenta como **un paso**. El ciclo se repite hasta que el modelo responde o se agotan los
|
|
17
|
+
pasos. Cada paso es una llamada de pago, con todo el contexto acumulado en la entrada.
|
|
18
|
+
|
|
19
|
+
> Si `steps` es 0 o está vacío, el backend **no usa 0**: usa **15**. Es el error de configuración más caro
|
|
20
|
+
> que se puede cometer en este plugin.
|
|
21
|
+
|
|
22
|
+
Todas las llamadas y respuestas de tools se registran como trazas en el log del backend
|
|
23
|
+
(`[pinocchio] tool <nombre> ...`), así que se puede auditar qué consultó el modelo.
|
|
24
|
+
|
|
25
|
+
## El catálogo
|
|
26
|
+
|
|
27
|
+
El catálogo lo aporta `kwirth-common-ai` y es el mismo para todos los plugins de IA de kwirth. El canal se lo
|
|
28
|
+
envía al front al arrancar, así que el selector siempre muestra lo que el backend soporta de verdad.
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
### Lectura — inventario y configuración
|
|
33
|
+
|
|
34
|
+
| Tool | Para qué |
|
|
35
|
+
|------|----------|
|
|
36
|
+
| `list_namespaces` | Namespaces con estado y etiquetas. |
|
|
37
|
+
| `get_cluster_data` | Nombre, sabor (AKS/EKS/GKE/k3s/k3d), vCPUs, memoria, nodos. |
|
|
38
|
+
| `get_node_data` | Configuración de los nodos (nombre, IP). |
|
|
39
|
+
| `get_workload_data` | Todos los workloads del clúster; filtrable por namespace. |
|
|
40
|
+
| `get_space_data` | Todo lo que hay en un namespace. |
|
|
41
|
+
| `list_services` / `get_service_yaml` | Services del clúster / manifiesto completo de uno. |
|
|
42
|
+
| `list_ingresses` / `get_ingress_yaml` | Ingresses / manifiesto completo de uno. |
|
|
43
|
+
| `get_pod_yaml` / `get_deployment_yaml` | Manifiesto completo de un Pod o Deployment. |
|
|
44
|
+
|
|
45
|
+
### Lectura — diagnóstico
|
|
46
|
+
|
|
47
|
+
| Tool | Para qué |
|
|
48
|
+
|------|----------|
|
|
49
|
+
| `describe_pod` | Equivalente a `kubectl describe pod`: motivo de espera/terminación, exitCode (OOMKilled, CrashLoop…), reinicios, probes. |
|
|
50
|
+
| `get_pod_logs` | Logs del contenedor. Con `previous:true`, los de la instancia que petó — la causa raíz de un CrashLoopBackOff. |
|
|
51
|
+
| `get_cluster_events` / `get_object_events` | Eventos de Kubernetes del clúster o de un objeto concreto. |
|
|
52
|
+
| `get_rollout_history` | Revisiones de un Deployment: qué imagen y qué env tenía cada una. Para ver **qué cambió** justo antes de romperse. |
|
|
53
|
+
| `get_configmap` / `get_secret` | Datos de un ConfigMap / claves de un Secret (**valores redactados**). Un cambio de valor no genera revisión de rollout: por eso hay que mirarlos aparte. |
|
|
54
|
+
| `get_workload_config_refs` | Qué ConfigMaps y Secrets consume un Deployment, con su fecha de última modificación. |
|
|
55
|
+
| `get_source_file` | Trae un fichero de un repo Git (GitHub/GitLab) en una ref, para seguir un stack trace hasta el fichero:línea culpable. |
|
|
56
|
+
| `get_certificate_info` | Detalles del certificado TLS de un host: emisor, validez, SANs, huella. |
|
|
57
|
+
|
|
58
|
+
### Lectura — uso de recursos
|
|
59
|
+
|
|
60
|
+
`get_cluster_usage`, `get_node_usage`, `get_deployment_usage` y sus versiones históricas
|
|
61
|
+
`get_prev_cluster_usage`, `get_prev_node_usage`, `get_prev_deployment_usage`, `get_prev_space_data`.
|
|
62
|
+
|
|
63
|
+
Estas tools se alimentan del **buffer de métricas** del canal: las últimas 100 lecturas del provider
|
|
64
|
+
`metrics`. Si acabas de abrir el canal, el histórico estará casi vacío.
|
|
65
|
+
|
|
66
|
+
### Escritura — ⚠️ modifican el clúster
|
|
67
|
+
|
|
68
|
+
| Tool | Qué hace |
|
|
69
|
+
|------|----------|
|
|
70
|
+
| `add_replica` / `remove_replica` | Escala un Deployment arriba o abajo (mínimo 1 réplica). |
|
|
71
|
+
| `restart_deployment` | Rollout-restart de un Deployment. |
|
|
72
|
+
| `delete_pod` | Borra un pod; su controlador lo recrea. |
|
|
73
|
+
| `add_node` / `remove_node` | Añade o quita un nodo del clúster (en k3d, vía `k3d node create/delete`). |
|
|
74
|
+
| `start_node` / `stop_node` | Arranca o para un nodo. |
|
|
75
|
+
|
|
76
|
+
### De prueba
|
|
77
|
+
|
|
78
|
+
`times_two` y `father_of` son tools de juguete para verificar que la cadena de tool-calling funciona. No las
|
|
79
|
+
pongas en un trigger real.
|
|
80
|
+
|
|
81
|
+
## El interruptor `Auto`: léelo antes de usarlo
|
|
82
|
+
|
|
83
|
+
El selector de tools tiene un interruptor **Auto**. Lo que hace **depende de dónde estés**, y la diferencia
|
|
84
|
+
es importante:
|
|
85
|
+
|
|
86
|
+
**En un trigger real** — `Auto` entrega al modelo el **catálogo completo, sin filtrar**. Sin selección previa
|
|
87
|
+
y, sobre todo, **sin quitar las tools de escritura**.
|
|
88
|
+
|
|
89
|
+
> ⚠️ **Un trigger con `Auto` activado puede escalar, reiniciar, borrar pods y quitar nodos.** El plugin no
|
|
90
|
+
> aplica ningún filtro de sólo-lectura. Si el modelo decide que la forma de arreglar lo que ve es reiniciar
|
|
91
|
+
> el Deployment, tiene la herramienta para hacerlo.
|
|
92
|
+
>
|
|
93
|
+
> **Recomendación: no uses `Auto` en triggers.** Marca a mano las tools de lectura que el análisis necesite.
|
|
94
|
+
> Es más trabajo una vez y te ahorra una sorpresa en producción.
|
|
95
|
+
|
|
96
|
+
**En el Playground** — `Auto` hace una **llamada previa** al modelo pidiéndole que elija qué tools necesita, y
|
|
97
|
+
sólo le pasa esas. Es más barato en tokens de contexto pero más caro en llamadas, y te muestra la elección
|
|
98
|
+
como `[Auto tools] selected: ...`. Sigue sin filtrar las de escritura.
|
|
99
|
+
|
|
100
|
+
## El coste, con números
|
|
101
|
+
|
|
102
|
+
El gasto de un trigger es, aproximadamente:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
coste ≈ (nº de disparos) × (nº de pasos) × (tokens de contexto acumulados)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Los tres factores se multiplican, y el primero es el que se dispara sin que te des cuenta:
|
|
109
|
+
|
|
110
|
+
| Trigger | Disparos al día en un clúster mediano |
|
|
111
|
+
|---------|----------------------------------------|
|
|
112
|
+
| `Deployment` + `ADDED` | decenas |
|
|
113
|
+
| `Deployment` + `MODIFIED` | cientos |
|
|
114
|
+
| `Pod` + `ADDED` | cientos |
|
|
115
|
+
| `Pod` + `MODIFIED` | **miles** — cada cambio de estado, cada probe, cada reinicio |
|
|
116
|
+
| `Pod` + *Any* | lo anterior, por tres |
|
|
117
|
+
|
|
118
|
+
Reglas prácticas:
|
|
119
|
+
|
|
120
|
+
1. **Empieza siempre por `ADDED`**, nunca por *Any*. Analiza lo que nace, no lo que respira.
|
|
121
|
+
2. **Evita `Pod`.** Analiza el `Deployment`, que es donde está la decisión de diseño; los pods sólo la
|
|
122
|
+
ejecutan, y hay muchos más.
|
|
123
|
+
3. **`steps` bajo para empezar** (3–5). Súbelo sólo si el campo `not_visible` de los análisis te dice que al
|
|
124
|
+
modelo le faltó información.
|
|
125
|
+
4. **Mira los tokens** en la línea de cabecera de cada análisis (`IN:`/`OUT:`). Es la medida real, no una
|
|
126
|
+
estimación.
|
|
127
|
+
5. **Prueba en el Playground.** Un prompt mal calibrado que hace 15 llamadas a tools se detecta gratis ahí,
|
|
128
|
+
no en producción.
|
|
129
|
+
|
|
130
|
+
Los campos `Input / Output cost per M tokens` del diálogo de LLM existen justo para que puedas traducir esos
|
|
131
|
+
tokens a dinero.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Plantillas de prompt
|
|
2
|
+
|
|
3
|
+
Pinocchio renderiza los prompts con **[nunjucks](https://mozilla.github.io/nunjucks/)** (la implementación
|
|
4
|
+
JavaScript de Jinja2), con `autoescape` activado. Esta página recoge qué variables hay disponibles en cada
|
|
5
|
+
ruta y cómo escribir un system que produzca findings útiles en vez de generalidades.
|
|
6
|
+
|
|
7
|
+
## Qué contexto recibe la plantilla
|
|
8
|
+
|
|
9
|
+
Esto es lo que más confusión genera, así que va primero y en tabla:
|
|
10
|
+
|
|
11
|
+
| Tipo de trigger | `promptType` | Contexto de render | El `system` |
|
|
12
|
+
|-----------------|--------------|--------------------|-------------|
|
|
13
|
+
| `artifact` | `jinja` | **El objeto de Kubernetes entero** | Se usa |
|
|
14
|
+
| `artifact` | `artifact` | *(no hay render: el prompt es `JSON.stringify(objeto)`)* | Se usa |
|
|
15
|
+
| `business` | cualquiera | **Vacío `{}`** | **Se ignora** |
|
|
16
|
+
|
|
17
|
+
El caso `business` merece repetirse: aunque rellenes el campo *Spaces*, **los datos del evento no llegan a la
|
|
18
|
+
plantilla**. Cualquier `{{ variable }}` se renderiza como cadena vacía. Un prompt `business` es, en la
|
|
19
|
+
práctica, texto fijo, y su valor está en las tools que le des al modelo, no en los datos que le pases.
|
|
20
|
+
|
|
21
|
+
## Variables disponibles en `artifact` + `jinja`
|
|
22
|
+
|
|
23
|
+
> ⚠️ **Antes de nada: el objeto es el contexto, no un adjunto.** El modelo recibe **sólo el texto
|
|
24
|
+
> renderizado**. Lo que la plantilla no interpole, no llega. Es el error más caro de esta página: escribes
|
|
25
|
+
> `Audita el Deployment {{ metadata.name }}`, el modelo recibe `Audita el Deployment api-gateway`, y te
|
|
26
|
+
> contesta que le pases el YAML. Si quieres que audite el recurso, **vuelca el recurso en la plantilla**
|
|
27
|
+
> (`{{ spec | dump(2) }}`) o usa `promptType: artifact`.
|
|
28
|
+
|
|
29
|
+
El contexto **es el objeto de Kubernetes tal cual**. No hay envoltorio ni prefijo: escribes las rutas del
|
|
30
|
+
manifiesto directamente.
|
|
31
|
+
|
|
32
|
+
```jinja
|
|
33
|
+
{{ kind }} → "Deployment"
|
|
34
|
+
{{ apiVersion }} → "apps/v1"
|
|
35
|
+
{{ metadata.name }} → "api-gateway"
|
|
36
|
+
{{ metadata.namespace }} → "prod"
|
|
37
|
+
{{ metadata.labels.app }} → "gateway"
|
|
38
|
+
{{ metadata.creationTimestamp }} → "2026-09-08T10:14:21Z"
|
|
39
|
+
{{ spec.replicas }} → 3
|
|
40
|
+
{{ spec.template.spec.serviceAccountName }} → "gateway-sa"
|
|
41
|
+
{{ status.readyReplicas }} → 2
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
La estructura cambia según el kind: en un `Pod` los contenedores están en `spec.containers`, mientras que en
|
|
45
|
+
un `Deployment` o un `StatefulSet` están en `spec.template.spec.containers`. Escribe la plantilla para el
|
|
46
|
+
kind concreto del trigger.
|
|
47
|
+
|
|
48
|
+
Bucles y condicionales funcionan como en Jinja2:
|
|
49
|
+
|
|
50
|
+
```jinja
|
|
51
|
+
Contenedores del {{ kind }} {{ metadata.name }}:
|
|
52
|
+
{% for c in spec.template.spec.containers %}
|
|
53
|
+
- {{ c.name }} → {{ c.image }}
|
|
54
|
+
{% if c.securityContext %}securityContext: {{ c.securityContext | dump }}{% else %}sin securityContext{% endif %}
|
|
55
|
+
{% if not c.resources.limits %}⚠ sin resource limits{% endif %}
|
|
56
|
+
{% endfor %}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
El filtro `| dump` serializa un subobjeto a JSON, y acepta un argumento de indentación (`{{ spec | dump(2) }}`).
|
|
60
|
+
Es la forma corta de meter un bloque entero del manifiesto en el prompt sin recorrerlo campo a campo — y,
|
|
61
|
+
como se explica arriba, **es lo que hace que el modelo vea de verdad el recurso**.
|
|
62
|
+
|
|
63
|
+
> ⚠️ `autoescape` está **activado**. Los caracteres `<`, `>`, `&`, `'` y `"` de los valores salen escapados
|
|
64
|
+
> como entidades HTML. Rara vez importa en un manifiesto de Kubernetes, pero si un valor te llega como
|
|
65
|
+
> `"` en el prompt, esta es la razón. El filtro `| safe` lo desactiva para una expresión concreta.
|
|
66
|
+
|
|
67
|
+
## `artifact` puro: cuando no necesitas plantilla
|
|
68
|
+
|
|
69
|
+
Si lo que quieres es "toma el manifiesto entero y audítalo", no escribas plantilla: pon `promptType` en
|
|
70
|
+
`artifact`, deja el prompt vacío y mete toda la instrucción en el `system`. El prompt será el objeto
|
|
71
|
+
serializado completo.
|
|
72
|
+
|
|
73
|
+
Es más robusto que una plantilla —no se te olvida ningún campo, y funciona igual para cualquier kind— a
|
|
74
|
+
cambio de gastar más tokens de entrada.
|
|
75
|
+
|
|
76
|
+
## Escribir un buen `system`
|
|
77
|
+
|
|
78
|
+
El `system` es donde de verdad se decide la calidad del análisis. Cuatro cosas que funcionan:
|
|
79
|
+
|
|
80
|
+
**1. Dale un rol y un marco de referencia concreto.**
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
Eres un auditor de seguridad de Kubernetes. Evalúas recursos contra los Pod Security
|
|
84
|
+
Standards (baseline y restricted) y contra el CIS Kubernetes Benchmark.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**2. Exige evidencia, y prohíbe explícitamente inventar.**
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
Cada finding debe citar en `evidence` el fragmento literal del manifiesto que lo demuestra.
|
|
91
|
+
Si no puedes citar evidencia, NO emitas el finding: añádelo a `not_visible` en su lugar.
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Esta instrucción es la que más reduce las alucinaciones. `not_visible` existe justo para darle al modelo una
|
|
95
|
+
salida honesta cuando no sabe algo.
|
|
96
|
+
|
|
97
|
+
**3. Dile qué hacer con las tools, no sólo que las tiene.**
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
Antes de concluir, usa `get_object_events` para comprobar si el recurso ya está fallando,
|
|
101
|
+
y `get_workload_config_refs` para ver qué ConfigMaps y Secrets consume.
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Un modelo con tools y sin instrucciones sobre cuándo usarlas tiende a no usarlas, o a usarlas todas.
|
|
105
|
+
|
|
106
|
+
**4. Calibra la severidad, o te llegará todo como `critical`.**
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
critical = explotable ahora mismo para escapar del contenedor o acceder a datos de otros tenants.
|
|
110
|
+
high = incumple `restricted` y amplía la superficie de ataque de forma significativa.
|
|
111
|
+
medium = mala práctica con impacto acotado.
|
|
112
|
+
low = higiene, sin impacto directo de seguridad.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Un system completo, listo para copiar
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
Eres un auditor de seguridad de Kubernetes. Analizas un único recurso y devuelves un
|
|
119
|
+
informe estructurado.
|
|
120
|
+
|
|
121
|
+
REGLAS
|
|
122
|
+
- Evalúa contra los Pod Security Standards (baseline y restricted).
|
|
123
|
+
- Cada finding DEBE citar en `evidence` el fragmento literal del manifiesto que lo prueba.
|
|
124
|
+
- Si no puedes probar algo con evidencia, no lo afirmes: añádelo a `not_visible`.
|
|
125
|
+
- Lista en `controls_passed` los controles que el recurso SÍ cumple.
|
|
126
|
+
- Sé específico en `remediation`: el fragmento de YAML corregido, no un consejo genérico.
|
|
127
|
+
|
|
128
|
+
SEVERIDAD
|
|
129
|
+
- critical: explotable ahora para escapar del contenedor o alcanzar otros tenants.
|
|
130
|
+
- high: incumple `restricted` y amplía la superficie de ataque de forma significativa.
|
|
131
|
+
- medium: mala práctica con impacto acotado.
|
|
132
|
+
- low: higiene, sin impacto directo.
|
|
133
|
+
|
|
134
|
+
INFORME
|
|
135
|
+
En `report`, redacta en markdown un resumen para el equipo propietario del recurso:
|
|
136
|
+
qué es, qué riesgo tiene, qué hacer primero. Español, tono directo, sin relleno.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Probar antes de activar
|
|
140
|
+
|
|
141
|
+
No afines prompts en producción. El [Playground](../user/06-playground.md) ejecuta la misma tubería contra un
|
|
142
|
+
payload pegado a mano y te enseña, en la pestaña **IN**, el prompt **ya renderizado** que recibió el modelo.
|
|
143
|
+
Es la forma más rápida de descubrir que `{{ spec.containers }}` estaba vacío porque el kind era un
|
|
144
|
+
`Deployment`.
|
|
145
|
+
|
|
146
|
+
Recuerda las dos diferencias del Playground al probar plantillas: en modo Business el **payload es el
|
|
147
|
+
prompt**, y en modo Artifact el `promptType` se deriva de si el campo Prompt está vacío o no.
|
package/admin/05-rbac.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Permisos y acceso
|
|
2
|
+
|
|
3
|
+
## Los scopes del canal
|
|
4
|
+
|
|
5
|
+
Pinocchio declara al core sólo **dos** scopes válidos para su canal, en este orden de menor a mayor:
|
|
6
|
+
|
|
7
|
+
| Scope | Nivel | Significado |
|
|
8
|
+
|-------|-------|-------------|
|
|
9
|
+
| `none` | 1 | Nivel mínimo. Suficiente para abrir el canal. |
|
|
10
|
+
| `cluster` | 2 | Nivel de administración. Cubre todo lo anterior. |
|
|
11
|
+
|
|
12
|
+
Una clave de acceso concede permiso sobre el canal `pinocchio` con uno de esos dos valores. El canal pide
|
|
13
|
+
como scope de instancia `NONE`, así que **cualquiera de los dos basta para abrirlo y usarlo entero**.
|
|
14
|
+
|
|
15
|
+
Cualquier otro literal en el scope (`view`, `restart`, `filter`, …) es **inválido para este canal**: el core
|
|
16
|
+
lo rechaza con un aviso muy visible en el log de autorización:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
***************** Inexistent scope 'view' on channel 'pinocchio' *****************
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Si un usuario no consigue abrir el canal, ese log es el primer sitio donde mirar.
|
|
23
|
+
|
|
24
|
+
## Lo que hay que entender antes de repartir claves
|
|
25
|
+
|
|
26
|
+
Pinocchio **no tiene permisos granulares**. No hay un scope para "sólo leer los findings" y otro para
|
|
27
|
+
"editar triggers". Quien puede abrir el canal, puede hacerlo todo. Esto es lo que "todo" incluye:
|
|
28
|
+
|
|
29
|
+
**1. Leer los análisis de todos los demás.** Los análisis se guardan en el canal, no por usuario. Cualquiera
|
|
30
|
+
que abra el canal recibe de golpe los últimos 50, con los manifiestos y los findings que contengan. Si un
|
|
31
|
+
trigger analiza recursos de un namespace sensible, esos datos son visibles para todos los que tengan acceso
|
|
32
|
+
al canal.
|
|
33
|
+
|
|
34
|
+
**2. Borrar los análisis de todos los demás.** El botón **Clear back** vacía la memoria del canal para todo
|
|
35
|
+
el mundo, sin confirmación adicional más allá del propio diálogo.
|
|
36
|
+
|
|
37
|
+
**3. Editar la configuración de IA compartida.** Los diálogos *Provider* y *LLM* escriben en el almacén
|
|
38
|
+
**común** de kwirth. Un usuario de Pinocchio puede añadir, modificar o **borrar** los providers y modelos que
|
|
39
|
+
usan los demás plugins de IA del clúster.
|
|
40
|
+
|
|
41
|
+
**4. Ver y cambiar las API keys.** El diálogo de provider tiene un botón de ojo que revela la clave, y un
|
|
42
|
+
botón **Export** que descarga todos los providers **con las claves en claro**.
|
|
43
|
+
|
|
44
|
+
**5. Crear triggers que ejecutan acciones de escritura.** Ver el apartado siguiente.
|
|
45
|
+
|
|
46
|
+
> **Conclusión operativa: trata el acceso al canal `pinocchio` como un permiso de administración.** No es un
|
|
47
|
+
> visor de logs. Concédelo al equipo que gobierna la plataforma, no a los usuarios de aplicación.
|
|
48
|
+
|
|
49
|
+
## Las tools de escritura y el service account del backend
|
|
50
|
+
|
|
51
|
+
Este es el punto que más atención merece.
|
|
52
|
+
|
|
53
|
+
El catálogo de tools incluye acciones que **modifican el clúster**: `add_replica`, `remove_replica`,
|
|
54
|
+
`restart_deployment`, `delete_pod`, `add_node`, `remove_node`, `start_node`, `stop_node`. Pinocchio **no
|
|
55
|
+
filtra las tools de escritura**: si una versión de trigger las tiene marcadas —o tiene el interruptor
|
|
56
|
+
**`Auto`** activado, que entrega el catálogo entero— el modelo puede invocarlas.
|
|
57
|
+
|
|
58
|
+
Y cuando las invoca, se ejecutan con el **service account del backend de kwirth**, no con los permisos del
|
|
59
|
+
usuario que configuró el trigger. Un usuario con el scope mínimo puede, a través de un trigger, provocar
|
|
60
|
+
acciones que su propio nivel de acceso no le permitiría hacer directamente.
|
|
61
|
+
|
|
62
|
+
Tres medidas concretas:
|
|
63
|
+
|
|
64
|
+
1. **Prohíbe `Auto` en triggers** como norma de equipo. Que las tools se marquen a mano, una a una.
|
|
65
|
+
2. **Revisa los triggers como revisarías código.** El diálogo *Import / Export* permite volcarlos a JSON:
|
|
66
|
+
ese fichero se puede versionar y revisar en un pull request.
|
|
67
|
+
3. **Ajusta el RBAC del service account del backend** a lo que de verdad necesites. Si en tu instalación
|
|
68
|
+
Pinocchio sólo tiene que auditar, el backend no debería poder escalar deployments ni borrar pods. Ésa es
|
|
69
|
+
la barrera que de verdad detiene el problema, y está fuera del plugin: es el RBAC de kwirth en el clúster.
|
|
70
|
+
|
|
71
|
+
## Auditoría
|
|
72
|
+
|
|
73
|
+
Todas las invocaciones de tools quedan como trazas en el log del backend:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
[pinocchio] tool get_pod_logs {"namespace":"prod","name":"api-gateway-7d9f"}
|
|
77
|
+
[pinocchio] tool get_pod_logs response: {...}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Además, cada análisis muestra en su cabecera qué LLM lo produjo y cuántos tokens costó, y la traza completa
|
|
81
|
+
de la conversación con las tools es visible en las pestañas **IN**/**OUT** del Playground cuando se ejecuta
|
|
82
|
+
desde allí.
|
|
83
|
+
|
|
84
|
+
## Superficie de red
|
|
85
|
+
|
|
86
|
+
El Playground dispara sus eventos con un `POST {clusterUrl}/provider/business`, autenticado con la clave de
|
|
87
|
+
acceso del usuario. Es el mismo endpoint por el que los sistemas externos inyectan eventos de negocio: quien
|
|
88
|
+
tenga una clave válida para ese provider puede inyectar eventos y, por tanto, disparar los triggers
|
|
89
|
+
`business`. Ténlo en cuenta al repartir claves con acceso al provider `business`.
|