Files
fn_registry/docs/capabilities/comfyui-skill.md
T
egutierrez bcf731275e feat(ml): cierre del bucle de mejora comfyui-skill (genera→juzga→bump)
Tres funciones nuevas que cierran el lazo skill→generación→juicio→promoción
del grupo comfyui-skill (issue 0087):

- comfyui_bump_skill_version (impura): promueve una versión nueva SOLO si el
  score del panel-juez sube (gate objetivo). Snapshot versions/vN.json
  pre-mutación, deep-merge de recipe_patch, semver↑, línea en growth_log.jsonl.
  force=True salta el gate. No usa datetime.now().
- comfyui_update_skill_score (impura): media incremental de score_mean/score_n
  reescribiendo recipe.json in-place (sin snapshot ni growth_log).
- comfyui_generate_with_skill_oneshot (pipeline): one-shot load→build→submit→
  wait→fetch→judge→score_mean. recipe_patch prueba variantes sin guardar score.
  Compone 7 funciones del registry.

Tests offline: 11 passed (gate, semver, deep-merge, media incremental, errores).
Página madre docs/capabilities/comfyui-skill.md: +3 funciones, sección "Bucle de
mejora" con diagrama, fronteras de scoring actualizadas.

Demo real verificada: skill seed portrait_cinematic_sd15 (SD1.5) generó imagen
SFW real, el panel la juzgó, una variante puntuó más alto (4.787 > 4.7276) y el
gate promovió v1.0.0→v1.1.0 con el judge_run_id como evidencia.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:09:33 +02:00

13 KiB

ComfyUI Skill — Recetas versionadas de generación reutilizables

Tag: comfyui-skill. Grupo para tratar una configuración de generación de ComfyUI como una skill: una receta versionada en disco (checkpoint + LoRAs + params + scaffold de prompt + bloques de post-proceso) que se guarda una vez y se reproduce a un workflow concreto cambiando solo el subject. Es la doctrina del issue 0087 aplicada a la generación de imágenes: el registry crece promoviendo configuraciones que funcionan a recetas reutilizables, no reescribiendo el grafo de nodos cada vez.

Construye sobre el grupo comfyui (los builders puros de workflow y el ciclo submit/wait). Una skill no es un workflow: es la receta que compila a uno.

Filtro MCP: mcp__registry__fn_search query="" tag="comfyui-skill".

Qué es una skill

Una receta vive en ~/ComfyUI/skills_library/<slug>/ y la manipulan las funciones de este grupo:

~/ComfyUI/skills_library/
  INDEX.md                      # índice regenerado de todas las skills
  <slug>/
    recipe.json                 # la receta actual
    versions/vN.json            # snapshot inmutable de cada save (N incremental)
    growth_log.jsonl            # bitácora append-only de cada save
    exports/                    # plantillas de workflow exportadas
    samples/                    # imágenes de muestra

Schema de recipe.json (canónico)

{
  "schema_version": 1,
  "slug": "portrait_cinematic_sdxl",
  "version": "1.0.0",
  "title": "Retrato cinematográfico SDXL",
  "base_workflow": "txt2img",
  "checkpoint": "juggernaut_xl_v11.safetensors",
  "loras": [{"name": "add_detail.safetensors", "strength_model": 0.6, "strength_clip": 0.6}],
  "params": {"steps": 30, "cfg": 5.5, "sampler_name": "dpmpp_2m",
             "scheduler": "karras", "width": 832, "height": 1216, "denoise": 1.0},
  "prompt_scaffold": {"positive": "cinematic portrait of {subject}, sharp focus",
                      "negative": "blurry, lowres", "trigger_words": []},
  "blocks": [{"type": "facedetailer", "params": {"denoise": 0.45}},
             {"type": "hires_fix", "params": {"upscale_by": 1.5, "denoise": 0.4}}],
  "score_mean": 0.0, "score_n": 0,
  "provenance": {"source": "manual", "nsfw": false},
  "export_template_path": "exports/portrait_cinematic_sdxl.template.json"
}

base_workflow ∈ {txt2img, flux, sdxl_refiner} (las bases que se generan desde un subject de texto). blocks[].type ∈ {facedetailer, hires_fix}.

Funciones del grupo

ID Firma corta Qué hace Purity
comfyui_build_skill_workflow_py_ml build_skill_workflow(recipe, subject, *, seed=0) -> dict Compila una receta a un workflow en API format: despacha al builder base, sustituye {subject} + trigger_words, encadena LoRAs y aplica los blocks en orden. SkillWorkflowError si la base es desconocida o requiere imagen. pura
comfyui_inject_hires_fix_py_ml comfyui_inject_hires_fix(workflow, *, upscale_by=1.5, denoise=0.4, steps=20, ...) -> dict Inyecta una 2ª pasada hires-fix (UpscaleModelLoader + UltimateSDUpscale) sobre un workflow ya construido, repuntando el SaveImage. Versión encadenable-sobre-dict del builder hermano. pura
comfyui_save_skill_py_ml comfyui_save_skill(recipe, *, library_dir=None) -> dict Valida el schema mínimo y escribe recipe.json + snapshot versions/vN.json + growth_log + INDEX.md. No muta la receta (round-trip con load). impura
comfyui_load_skill_py_ml comfyui_load_skill(slug, *, version=None, library_dir=None) -> dict Lee recipe.json (actual) o un snapshot versions/vN.json. Slug/versión inexistente → {ok:False} sin lanzar. impura
comfyui_list_skills_py_ml comfyui_list_skills(*, library_dir=None, include_nsfw=False) -> dict Lista las skills con slug/title/base_workflow/version/score/nsfw/n_versions. Oculta NSFW por defecto. impura
ask_llm_vision_py_core ask_llm_vision(prompt, image_path='', *, image_b64='', media_type='', model='claude-opus-4-8', ...) -> dict Pregunta multimodal (imagen + texto) al modelo via API directa de Anthropic (grupo claude-direct). Útil para puntuar el PNG de una skill y alimentar score_mean. impura
comfyui_generate_with_skill_oneshot_py_pipelines generate_with_skill_oneshot(slug, subject, *, server='127.0.0.1:8188', dest=None, seed=0, judge=True, recipe_patch=None, ...) -> dict One-shot del bucle: carga la skill, la compila para el subject, encola, espera, descarga el PNG y (si judge) lo puntúa con el panel comfyui-judge, acumulando el score en la media. recipe_patch prueba una variante en memoria sin guardar. pipeline (impura)
comfyui_update_skill_score_py_ml comfyui_update_skill_score(slug, new_score, *, library_dir=None) -> dict Acumula el score de un juicio en score_mean/score_n por media incremental, reescribiendo recipe.json en sitio (sin snapshot ni growth_log). impura
comfyui_bump_skill_version_py_ml comfyui_bump_skill_version(slug, change, *, score_before, score_after, judge_run_id=None, recipe_patch=None, force=False, ...) -> dict Promueve una versión nueva solo si el score sube (gate objetivo): snapshot versions/vN.json + aplica recipe_patch + sube el semver + línea en growth_log. Gate bloquea si no mejora. impura

build_skill_workflow compone los builders del grupo comfyui: comfyui_build_txt2img_workflow, comfyui_build_flux_workflow, comfyui_build_sdxl_refiner_workflow, comfyui_inject_lora, comfyui_build_facedetailer_workflow y comfyui_inject_hires_fix.

Ejemplo canónico end-to-end (receta → workflow → PNG → score)

Guardar una skill, cargarla, compilarla a un workflow para un sujeto, encolarla y puntuar el resultado con visión. Requiere el server ComfyUI en 127.0.0.1:8188 y los modelos de la receta instalados.

import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_save_skill import comfyui_save_skill
from ml.comfyui_load_skill import comfyui_load_skill
from ml.comfyui_build_skill_workflow import build_skill_workflow
from ml.comfyui_submit_workflow import comfyui_submit_workflow
from ml.comfyui_wait_result import comfyui_wait_result
from ml.comfyui_fetch_output_image import comfyui_fetch_output_image
from core.ask_llm_vision import ask_llm_vision

# 1. Definir y guardar la skill (una vez).
recipe = {
    "schema_version": 1, "slug": "portrait_cinematic_sdxl", "version": "1.0.0",
    "title": "Retrato cinematográfico SDXL", "base_workflow": "txt2img",
    "checkpoint": "dreamshaper_8.safetensors",
    "loras": [{"name": "add_detail.safetensors", "strength_model": 0.6, "strength_clip": 0.6}],
    "params": {"steps": 28, "cfg": 6.0, "sampler_name": "dpmpp_2m", "scheduler": "karras"},
    "prompt_scaffold": {"positive": "cinematic portrait of {subject}, sharp focus",
                        "negative": "blurry, lowres", "trigger_words": []},
    "blocks": [{"type": "facedetailer", "params": {"denoise": 0.45}}],
    "score_mean": 0.0, "score_n": 0, "provenance": {"source": "manual", "nsfw": False},
}
comfyui_save_skill(recipe)                                 # ~/ComfyUI/skills_library/portrait_cinematic_sdxl/

# 2. Cargar + compilar a un workflow para un sujeto concreto.
recipe = comfyui_load_skill("portrait_cinematic_sdxl")["recipe"]
wf = build_skill_workflow(recipe, "a woman with red hair", seed=42)

# 3. Encolar y esperar el PNG (camino headless del grupo comfyui).
pid = comfyui_submit_workflow(wf)["prompt_id"]
outputs = comfyui_wait_result(pid)["outputs"]
img = comfyui_fetch_output_image(outputs[0]["filename"], dest_dir="/tmp")["path"]

# 4. Puntuar el resultado con visión (alimenta el bucle de scoring de la skill).
verdict = ask_llm_vision(
    "Puntúa de 0 a 10 el realismo de este retrato. Responde solo el número.",
    image_path=img, model="claude-opus-4-8",
)
print(verdict["text"])

El paso "guardar la receta" se hace una sola vez; a partir de ahí cada generación es load → build → submit, cambiando solo el subject y la seed.

Bucle de mejora (skill → genera → juzga → bump)

La doctrina del issue 0087 cerrada en un lazo: una skill no crece inflando la receta a ciegas, crece registrando mejoras medibles. El juez (no el humano) decide qué se promueve.

   ┌─────────────────────────────────────────────────────────────────┐
   │  generate_with_skill_oneshot(slug, subject, judge=True)          │
   │     load → build → submit → wait → fetch → judge → score_mean    │  ← canónica
   └─────────────────────────────────────────────────────────────────┘
            │  score_before = score de la receta vigente
            ▼
   ┌─────────────────────────────────────────────────────────────────┐
   │  generate_with_skill_oneshot(..., recipe_patch={params:{...}})   │  ← variante (no guarda score)
   │     misma seed, un cambio plausible → judge.score = score_after  │
   └─────────────────────────────────────────────────────────────────┘
            │
            ▼  GATE objetivo
   comfyui_bump_skill_version(slug, change, score_before, score_after, judge_run_id=...)
            │
      score_after > score_before ?
        ├── sí → promueve: versions/vN.json (snapshot) + recipe_patch + semver↑ + growth_log
        └── no → {ok:False} — NO se promueve (la variante se descarta)

Pasos concretos:

  1. Genera la canónica con judge=True. El panel comfyui-judge emite un score y el pipeline lo acumula en score_mean/score_n de la skill (vía comfyui_update_skill_score). Ese score es el score_before.
  2. Genera una variante con recipe_patch (p.ej. {"params": {"steps": 32}}) y la misma seed. El patch se aplica en memoria, NO se guarda, y su score NO contamina la media. Su judge.score es el score_after, y su judge_run_id es la evidencia.
  3. Promueve con el gate: comfyui_bump_skill_version aplica el patch a recipe.json, sube el semver y deja una línea en growth_log.jsonl solo si score_after > score_before. Si no mejora, devuelve {ok:False} y la receta se queda como estaba. El gate es objetivo: lo decide el número del juez, no quien lanza la generación.

Así versions/ y growth_log reflejan versiones de receta con mejora demostrada, mientras score_mean es la telemetría de calidad media de la versión vigente.

Fronteras

  • No genera ni descarga modelos: una skill referencia checkpoints/LoRAs por nombre; deben estar ya instalados en ComfyUI (comfyui_download_model, otro flujo). build_skill_workflow es puro y no valida contra el servidor — usa comfyui_validate_workflow antes de encolar si dudas.
  • base_workflow solo de texto: txt2img, flux, sdxl_refiner. Las bases que parten de una imagen (img2img, inpaint, controlnet) lanzan SkillWorkflowError; para esas, monta el workflow con los builders del grupo comfyui directamente.
  • blocks soportados: facedetailer y hires_fix. Otros post-procesos (IPAdapter, multi-ControlNet) se añaden creando su función-inyector hermana y registrándola en el dispatcher de build_skill_workflow.
  • El juicio (comfyui-judge) vive en su grupo: este grupo lo consume (vía generate_with_skill_oneshot con judge=True), pero el panel multi-juez —estético + CLIP + LLM-vision— se documenta en comfyui-judge. Aquí solo se acumula su score en score_mean (comfyui_update_skill_score) y se usa como gate del bump.
  • El bump solo sube versiones, no genera ni juzga: comfyui_bump_skill_version aplica el patch y registra la mejora; generar la imagen y puntuarla es trabajo del pipeline + el panel-juez. Una variante que no supera a la vigente se descarta sola (el gate la rechaza).
  • La librería es metadata local: vive bajo ~/ComfyUI/skills_library (no toca el venv ni los modelos en disco). No tiene repo propio ni se indexa — es estado vivo, como un operations.db.
  • Las funciones impuras del grupo (save/load/list, ask_llm_vision) no llevan unit tests por diseño (I/O de disco / red); build_skill_workflow e inject_hires_fix son puras y sí tienen tests de estructura offline (python/functions/ml/tests/test_comfyui_build_skill_workflow.py, test_comfyui_inject_hires_fix.py).