Cook_with_a_LLM / Strategy /arquitectura.html
FredinVázquez
add strategy plan
bad5d84
Raw
History Blame Contribute Delete
35.6 kB
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cocina Conmigo — Plan visual del proyecto</title>
<style>
:root {
--bg: #f5ecd9;
--card: #fffbf0;
--ink: #2b2018;
--accent: #a85c2a; /* terracotta */
--accent-soft: #f6dccc;
--accent2: #6b4a2a;
--gold: #c9962b;
--green: #3f7a3a;
--green-soft: #dbe9d8;
--red: #b03a2e;
--red-soft: #f4d6d2;
--gray: #8a7e6f;
--line: #d8c9ad;
}
* { box-sizing: border-box; }
body {
font-family: 'Inter', -apple-system, sans-serif;
background: var(--bg);
color: var(--ink);
margin: 0;
padding: 32px 16px 80px;
line-height: 1.55;
}
.wrap { max-width: 1240px; margin: 0 auto; }
h1 { font-family: 'Lora', Georgia, serif; font-size: 46px; margin: 0 0 4px;
letter-spacing: -0.5px; font-weight: 700; }
h1 em { color: var(--accent); font-style: italic; }
.subtitle { color: var(--accent2); font-style: italic; margin-bottom: 28px; font-size: 17px; }
h2 {
margin-top: 56px; border-top: 1px dashed var(--line); padding-top: 24px;
font-size: 26px; font-family: 'Lora', Georgia, serif; letter-spacing: 0.3px;
}
h2 .num {
color: var(--accent); font-family: ui-monospace, monospace;
font-size: 20px; margin-right: 10px;
}
h3 { font-size: 18px; margin-top: 28px; color: var(--accent2); font-family: 'Lora', Georgia, serif; }
/* Hero */
.hero {
background: var(--card); border: 2px solid var(--ink); border-radius: 14px;
padding: 30px 32px; display: grid; grid-template-columns: 1fr; gap: 18px;
}
@media(min-width: 760px){ .hero { grid-template-columns: 2fr 1fr; align-items: center; } }
.hero h2 { border:0; margin:0 0 6px; padding:0; font-size: 22px; }
.hero .quote {
font-style: italic; font-size: 17px; color: var(--accent2);
border-left: 3px solid var(--accent); padding-left: 14px; margin: 6px 0 0;
}
.hero .target {
background: #fff3cf; border-radius: 12px; padding: 14px 16px;
font-size: 13px; border: 1px solid var(--line); line-height: 1.55;
}
.hero .target strong { color: var(--accent); }
/* Pills */
.pill {
display: inline-block; padding: 2px 9px; border-radius: 12px;
color: white; font-size: 12px; margin: 2px 4px 2px 0; font-family: ui-monospace, monospace;
}
.pill.user { background: var(--gray); }
.pill.gradio { background: var(--accent); }
.pill.hf { background: var(--gold); }
.pill.modal { background: var(--green); }
.pill.flux { background: #111; }
.pill.openbmb { background: #075e54; }
.pill.cohere { background: #5e3aa3; }
.pill.openai { background: #2c5e8a; }
.pill.llama { background: #6a3d8a; }
/* Phone/recipe card mockup */
.phone-row {
display: grid; grid-template-columns: 1fr; gap: 18px; margin-top: 16px;
}
@media(min-width: 760px){ .phone-row { grid-template-columns: repeat(4, 1fr); } }
.phone {
background: #111; border-radius: 24px; padding: 8px;
box-shadow: 0 8px 22px rgba(0,0,0,0.18);
}
.phone .screen {
background: #fffbf0; border-radius: 18px; overflow: hidden;
height: 380px; display: flex; flex-direction: column;
}
.phone .topbar {
background: var(--accent); color: white; padding: 10px 14px;
font-size: 13px; font-family: 'Lora', serif;
}
.phone .body { padding: 12px; flex: 1; overflow-y: auto; font-size: 12px; }
.phone .body .illu {
width: 100%; aspect-ratio: 4/3; border-radius: 8px;
background: linear-gradient(135deg, #ffd28b 0%, #c97a3e 100%);
display: flex; align-items: center; justify-content: center;
font-size: 48px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin-bottom: 8px;
}
.phone .body p { margin: 6px 0; line-height: 1.5; }
.phone .body .voice {
background: var(--green-soft); border-radius: 6px; padding: 6px 10px;
margin-top: 8px; font-size: 11px; color: var(--green);
}
.phone .body .tip {
background: var(--red-soft); border-radius: 6px; padding: 6px 10px;
margin-top: 6px; font-size: 11px; color: var(--red);
}
.scenario-label {
text-align: center; font-size: 13px; color: var(--accent2);
margin-top: 8px; font-style: italic;
}
/* SVG */
svg { width: 100%; height: auto; display: block; }
.node-box { fill: var(--card); stroke: var(--ink); stroke-width: 1.5; }
.node-text { font-family: 'Inter', sans-serif; font-size: 14px; fill: var(--ink); }
.node-title { font-weight: 700; font-size: 15px; }
.node-sub { font-size: 11px; fill: var(--accent2); font-style: italic; }
.arrow { stroke: var(--ink); stroke-width: 1.8; fill: none; }
.arrow-label { font-size: 11px; fill: var(--accent2); font-family: ui-monospace, monospace; }
.dashed { stroke-dasharray: 6 4; }
.arrow-loop { stroke: var(--accent); stroke-width: 2.2; fill: none; }
/* Cards */
.grid-2 { display: grid; grid-template-columns: 1fr; gap: 18px; margin-top: 16px; }
@media(min-width: 880px){ .grid-2 { grid-template-columns: 1fr 1fr; } }
.grid-3 { display: grid; grid-template-columns: 1fr; gap: 14px; margin-top: 14px; }
@media(min-width: 760px){ .grid-3 { grid-template-columns: repeat(3, 1fr); } }
.card {
background: var(--card); border: 1px solid var(--line);
border-radius: 10px; padding: 18px 20px;
}
.card.pick { border: 2px solid var(--accent); }
.pick-tag {
display: inline-block; background: var(--accent); color: white;
font-family: ui-monospace, monospace; font-size: 11px;
padding: 1px 7px; border-radius: 10px; margin-bottom: 6px;
}
table {
width: 100%; border-collapse: collapse; background: var(--card);
border: 1px solid var(--line); margin-top: 14px; font-size: 14px;
}
th, td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top; }
th { background: #efe4cb; font-size: 13px; letter-spacing: 0.5px; text-transform: uppercase; }
code {
background: #efe4cb; border-radius: 3px; padding: 1px 5px; font-size: 13px;
}
/* Forbidden zone */
.forbidden {
background: var(--red-soft); border: 1px solid var(--red);
border-radius: 8px; padding: 14px 18px; margin-top: 14px;
}
.forbidden strong { color: var(--red); }
.forbidden ul {
columns: 2; column-gap: 28px; margin: 8px 0 0; padding-left: 18px; font-size: 14px;
}
/* Timeline */
.timeline { position: relative; padding-left: 36px; margin-top: 20px; }
.timeline::before {
content: ""; position: absolute; left: 12px; top: 6px; bottom: 6px;
width: 3px; background: var(--accent); border-radius: 2px;
}
.day {
position: relative; margin-bottom: 14px; background: var(--card);
border: 1px solid var(--line); border-radius: 8px; padding: 12px 16px;
}
.day::before {
content: ""; position: absolute; left: -29px; top: 16px;
width: 13px; height: 13px; background: var(--accent);
border: 2px solid var(--card); border-radius: 50%;
}
.day .lbl {
display: inline-block; background: var(--accent); color: white;
font-family: ui-monospace, monospace; font-size: 11px;
padding: 1px 7px; border-radius: 10px; margin-right: 8px;
}
.day strong { font-size: 15px; }
.day .what { font-size: 13px; color: var(--accent2); margin-top: 2px; }
/* Award rows */
.award-row {
display: flex; justify-content: space-between;
padding: 8px 12px; border-bottom: 1px solid var(--line); font-size: 14px;
}
.award-row:last-child { border-bottom: 0; }
.prob {
font-family: ui-monospace, monospace; font-size: 12px;
padding: 1px 8px; border-radius: 10px; color: white;
}
.prob-h { background: #2e7d32; }
.prob-m { background: #ef9c2c; }
.prob-l { background: #b03a2e; }
/* Badges grid */
.badges-grid {
display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px; margin-top: 14px;
}
.badge-card {
background: var(--card); border: 1px solid var(--line);
border-radius: 8px; padding: 12px 14px;
}
.badge-card.skip { opacity: 0.45; border-style: dashed; }
.badge-card .tag {
display: inline-block; background: var(--accent); color: white;
font-family: ui-monospace, monospace; font-size: 11px;
padding: 1px 7px; border-radius: 10px; margin-bottom: 6px;
}
.badge-card.skip .tag { background: var(--gray); }
.badge-card strong { font-size: 14px; }
.badge-card p { font-size: 13px; color: var(--accent2); margin: 4px 0 0; }
.footnote {
margin-top: 30px; padding: 14px 18px;
border-left: 4px solid var(--accent);
background: var(--card); font-size: 14px; border-radius: 4px;
}
</style>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="wrap">
<h1><em>Cocina Conmigo</em></h1>
<div class="subtitle">Sous-chef multimodal con visión, voz y Flux.2 — para cocinar con tu mamá sin tener las manos libres</div>
<div class="hero">
<div>
<h2>La idea en una frase</h2>
<p>Tu mamá toma foto del refri, la app le propone qué cocinar, le <strong>muestra cómo se debe ver cada paso</strong> con Flux.2, y la <strong>narra por voz</strong> mientras ella cocina con las manos llenas.</p>
<p class="quote">"Mi mamá me pidió que le enseñara a hacer ramen. Le construí un sous-chef que vive en su tablet."</p>
<div style="margin-top: 14px;">
<span class="pill flux">Flux.2 Klein 9B</span>
<span class="pill openbmb">MiniCPM-V + voice</span>
<span class="pill cohere">Cohere voice</span>
<span class="pill gradio">Gradio Workflows</span>
<span class="pill modal">Modal-powered</span>
<span class="pill llama">llama.cpp</span>
</div>
</div>
<div class="target">
<strong>Track:</strong> Backyard AI<br/>
<strong>Persona:</strong> tu mamá / pareja / vecino<br/>
<strong>Idioma:</strong> español-mexicano<br/>
<strong>Total params:</strong> ~17B (≤ 32B ✓)<br/>
<strong>Cocina:</strong> mexicana tradicional<br/>
<strong>Storyline:</strong> "Para que mi mamá deje de googlear"
</div>
</div>
<h2><span class="num">01</span>Por qué esta idea, y no las anteriores</h2>
<table>
<thead><tr><th>Iteración</th><th>Idea</th><th>Por qué se descartó</th></tr></thead>
<tbody>
<tr><td>v1</td><td>Abuelita (parent phone helper)</td><td>En la lista pre-cocinada de OpenBMB → 5-15 equipos lo harán</td></tr>
<tr><td>v2</td><td>Cuentacuentos (voice storyteller)</td><td>También en la lista pre-cocinada de OpenBMB</td></tr>
<tr style="background:#fff3cf;"><td><strong>v3 (ésta)</strong></td><td><strong>Cocina Conmigo</strong></td><td>Refinamiento de tu idea #1 · NO está en ninguna lista pre-cocinada · usa Flux.2 + Workflows + voces · diaria + universal</td></tr>
</tbody>
</table>
<div class="forbidden">
<strong>⛔ Las 12 ideas en zona prohibida (clúster OpenBMB):</strong>
<ul>
<li>parent phone helper</li>
<li>receipt / bill explainer</li>
<li>shop menu / repair manual</li>
<li>offline personal assistant / voice companion</li>
<li>voice storyteller</li>
<li>visual mystery box</li>
<li>AI museum (≈ tu idea #4)</li>
<li>doodle creature</li>
<li>dream postcard gen</li>
<li>omni-modal adventure</li>
<li>tiny local NPC / character agent</li>
<li>cortes de cabello (tu idea #3, ya saturada)</li>
</ul>
</div>
<h2><span class="num">02</span>Las 4 historias del demo</h2>
<div class="phone-row">
<div>
<div class="phone"><div class="screen">
<div class="topbar">📸 Tengo esto en el refri</div>
<div class="body">
<div class="illu">🍅🌶🐔🧅</div>
<p><strong>Veo:</strong> pollo, jitomate, cebolla, cilantro, tortillas, queso.</p>
<p style="background:#fff3cf;border-radius:6px;padding:6px 10px;">
<strong>3 opciones:</strong><br/>
🌮 Tinga · 🌯 Enchiladas · 🧀 Quesadillas
</p>
<div class="voice">🔊 "¿Qué traes ganas?"</div>
</div>
</div></div>
<div class="scenario-label">1. Visión + Planner</div>
</div>
<div>
<div class="phone"><div class="screen">
<div class="topbar">👩‍🍳 Paso 2 de 5</div>
<div class="body">
<div class="illu">🍳✨</div>
<p><strong>Acitrona la cebolla en aceite caliente.</strong></p>
<p style="font-size:11px;color:var(--gray);">⏱ 4 minutos · hasta que esté transparente</p>
<div class="voice">🔊 OpenBMB voice narra…</div>
</div>
</div></div>
<div class="scenario-label">2. Voz + imagen objetivo</div>
</div>
<div>
<div class="phone"><div class="screen">
<div class="topbar">📸 ¿Voy bien?</div>
<div class="body">
<div class="illu">🍳👀</div>
<p style="background:var(--green-soft);border-radius:6px;padding:6px 10px;color:var(--green);">
<strong>✅ Va perfecto.</strong> La cebolla ya se ve transparente.
</p>
<div class="tip">🔊 Cohere voice: "¡Súbele 1 minuto más, está bien!"</div>
</div>
</div></div>
<div class="scenario-label">3. Closed-loop visual</div>
</div>
<div>
<div class="phone"><div class="screen">
<div class="topbar">🔄 Replan</div>
<div class="body">
<p>Usuario: <em>"No tengo cilantro."</em></p>
<div class="illu" style="background: linear-gradient(135deg,#ffd28b,#a85c2a);">🌮</div>
<p>"No pasa nada. Le ponemos perejil o nada. Sigue siendo tinga."</p>
<div class="voice">🔊 Receta regenera · plato final actualizado</div>
</div>
</div></div>
<div class="scenario-label">4. Adaptación en vivo</div>
</div>
</div>
<h2><span class="num">03</span>Arquitectura — 5 agentes en un Gradio Workflow</h2>
<svg viewBox="0 0 1240 540" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M0,0 L10,5 L0,10 z" fill="#2b2018"/>
</marker>
<marker id="aro" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M0,0 L10,5 L0,10 z" fill="#a85c2a"/>
</marker>
</defs>
<!-- User input area -->
<rect x="20" y="40" width="200" height="240" rx="10" fill="#fff3cf" stroke="#d8c9ad" stroke-dasharray="4 3"/>
<text x="40" y="62" class="node-text node-title" fill="#6b4a2a">USUARIO (cocina)</text>
<rect class="node-box" x="40" y="80" width="160" height="50" rx="6" fill="#ddd1bd"/>
<text x="120" y="102" class="node-text node-title" text-anchor="middle">📸 Foto del refri</text>
<text x="120" y="118" class="node-text node-sub" text-anchor="middle">trigger inicial</text>
<rect class="node-box" x="40" y="140" width="160" height="50" rx="6" fill="#ddd1bd"/>
<text x="120" y="162" class="node-text node-title" text-anchor="middle">🎙️ Pregunta voz</text>
<text x="120" y="178" class="node-text node-sub" text-anchor="middle">"¿voy bien?"</text>
<rect class="node-box" x="40" y="200" width="160" height="50" rx="6" fill="#ddd1bd"/>
<text x="120" y="222" class="node-text node-title" text-anchor="middle">📸 Foto progreso</text>
<text x="120" y="238" class="node-text node-sub" text-anchor="middle">closed-loop</text>
<!-- Output area -->
<rect x="20" y="320" width="200" height="180" rx="10" fill="#fff3cf" stroke="#d8c9ad" stroke-dasharray="4 3"/>
<text x="40" y="342" class="node-text node-title" fill="#6b4a2a">SALIDA</text>
<rect class="node-box" x="40" y="360" width="160" height="50" rx="6" fill="#dbe9d8"/>
<text x="120" y="382" class="node-text node-title" text-anchor="middle">🍽️ Plato final + receta</text>
<text x="120" y="398" class="node-text node-sub" text-anchor="middle">imagen + texto</text>
<rect class="node-box" x="40" y="420" width="160" height="50" rx="6" fill="#dbe9d8"/>
<text x="120" y="442" class="node-text node-title" text-anchor="middle">🔊 Voz por paso</text>
<text x="120" y="458" class="node-text node-sub" text-anchor="middle">narrador + tips</text>
<!-- Pipeline center -->
<rect x="260" y="40" width="700" height="460" rx="10" fill="#fffaf0" stroke="#d8c9ad" stroke-width="1.5"/>
<text x="610" y="62" class="node-text node-title" text-anchor="middle" fill="#6b4a2a">HF SPACE — Gradio Workflow (5 agentes)</text>
<!-- Vision (Mise en Place) -->
<rect class="node-box" x="280" y="90" width="200" height="80" rx="6" fill="#e6d5ed"/>
<text x="380" y="110" class="node-text node-title" text-anchor="middle">👁️ MISE EN PLACE</text>
<text x="380" y="126" class="node-text node-sub" text-anchor="middle">MiniCPM-V (Q4)</text>
<text x="380" y="142" class="node-text node-sub" text-anchor="middle">~2-4B</text>
<text x="380" y="160" class="node-text node-sub" text-anchor="middle">identifica ingredientes</text>
<!-- Recipe Planner -->
<rect class="node-box" x="510" y="90" width="200" height="80" rx="6" fill="#fbe4d3"/>
<text x="610" y="110" class="node-text node-title" text-anchor="middle">🧠 RECIPE PLANNER</text>
<text x="610" y="126" class="node-text node-sub" text-anchor="middle">MiniCPM-4 (LoRA mx)</text>
<text x="610" y="142" class="node-text node-sub" text-anchor="middle">~4B</text>
<text x="610" y="160" class="node-text node-sub" text-anchor="middle">arma receta JSON · replan</text>
<!-- Step Illustrator -->
<rect class="node-box" x="740" y="90" width="200" height="80" rx="6" fill="#f6dccc"/>
<text x="840" y="110" class="node-text node-title" text-anchor="middle">🎨 STEP ILLUSTRATOR</text>
<text x="840" y="126" class="node-text node-sub" text-anchor="middle">Flux.2 Klein 9B</text>
<text x="840" y="142" class="node-text node-sub" text-anchor="middle">en Modal GPU L4</text>
<text x="840" y="160" class="node-text node-sub" text-anchor="middle">imagen-objetivo por paso</text>
<!-- Sous-Chef Narrator -->
<rect class="node-box" x="510" y="200" width="200" height="70" rx="6" fill="#cfe0ee"/>
<text x="610" y="222" class="node-text node-title" text-anchor="middle">🔊 SOUS-CHEF NARRATOR</text>
<text x="610" y="238" class="node-text node-sub" text-anchor="middle">OpenBMB voice (~1B)</text>
<text x="610" y="254" class="node-text node-sub" text-anchor="middle">tono cálido</text>
<!-- Tip Giver -->
<rect class="node-box" x="740" y="200" width="200" height="70" rx="6" fill="#e9d6f5"/>
<text x="840" y="222" class="node-text node-title" text-anchor="middle">🎭 TIP GIVER</text>
<text x="840" y="238" class="node-text node-sub" text-anchor="middle">Cohere voice (~1B)</text>
<text x="840" y="254" class="node-text node-sub" text-anchor="middle">warnings · enérgico</text>
<!-- Progress Validator (closed loop) -->
<rect class="node-box" x="280" y="290" width="220" height="90" rx="6" fill="#dbe9d8" stroke="#3f7a3a" stroke-width="2"/>
<text x="390" y="312" class="node-text node-title" text-anchor="middle" fill="#3f7a3a">✅ PROGRESS VALIDATOR</text>
<text x="390" y="328" class="node-text node-sub" text-anchor="middle">MiniCPM-V (reuso)</text>
<text x="390" y="344" class="node-text node-sub" text-anchor="middle">compara foto usuario vs</text>
<text x="390" y="360" class="node-text node-sub" text-anchor="middle">imagen-objetivo</text>
<text x="390" y="376" class="node-text node-sub" text-anchor="middle">CLOSED LOOP 🔄</text>
<!-- STT -->
<rect class="node-box" x="280" y="200" width="200" height="70" rx="6" fill="#cfe0ee"/>
<text x="380" y="222" class="node-text node-title" text-anchor="middle">🎙️ STT (opcional)</text>
<text x="380" y="238" class="node-text node-sub" text-anchor="middle">Whisper-tiny (~40M)</text>
<text x="380" y="254" class="node-text node-sub" text-anchor="middle">"¿voy bien?" hands-free</text>
<!-- Recipe State -->
<rect class="node-box" x="510" y="290" width="430" height="90" rx="6" fill="#fff3cf"/>
<text x="725" y="312" class="node-text node-title" text-anchor="middle" fill="#8a6a18">📖 RECIPE STATE (dataclass)</text>
<text x="725" y="328" class="node-text node-sub" text-anchor="middle">name · final_dish_image · steps · current_step ·</text>
<text x="725" y="344" class="node-text node-sub" text-anchor="middle">missing_ingredients · substitutes · user_progress_photos</text>
<text x="725" y="362" class="node-text node-sub" text-anchor="middle">cada agente lee y escribe sobre este objeto</text>
<!-- Page assembler -->
<rect class="node-box" x="280" y="400" width="660" height="60" rx="6" fill="#f6dccc"/>
<text x="610" y="422" class="node-text node-title" text-anchor="middle">📖 RECIPE CARD ASSEMBLER</text>
<text x="610" y="438" class="node-text node-sub" text-anchor="middle">renderiza la tarjeta de receta + cards por paso + audio reproducible</text>
<!-- Modal box -->
<rect x="990" y="40" width="240" height="460" rx="10" fill="#dbe9d8" stroke="#3f7a3a" stroke-width="1.5"/>
<text x="1110" y="62" class="node-text node-title" text-anchor="middle" fill="#3f7a3a">MODAL</text>
<rect class="node-box" x="1010" y="90" width="200" height="80" rx="6" fill="#fff"/>
<text x="1110" y="112" class="node-text node-title" text-anchor="middle">Flux endpoint</text>
<text x="1110" y="128" class="node-text node-sub" text-anchor="middle">runtime · @app.cls L4</text>
<text x="1110" y="144" class="node-text node-sub" text-anchor="middle">scaledown 180s</text>
<text x="1110" y="160" class="node-text node-sub" text-anchor="middle">~1-3s/imagen</text>
<rect class="node-box" x="1010" y="190" width="200" height="80" rx="6" fill="#fff"/>
<text x="1110" y="212" class="node-text node-title" text-anchor="middle">Dataset cocina mx</text>
<text x="1110" y="228" class="node-text node-sub" text-anchor="middle">offline · 200 recetas</text>
<text x="1110" y="244" class="node-text node-sub" text-anchor="middle">Codex API genera</text>
<text x="1110" y="260" class="node-text node-sub" text-anchor="middle">~$5</text>
<rect class="node-box" x="1010" y="290" width="200" height="80" rx="6" fill="#fff"/>
<text x="1110" y="312" class="node-text node-title" text-anchor="middle">LoRA Planner</text>
<text x="1110" y="328" class="node-text node-sub" text-anchor="middle">offline · A10G ~30 min</text>
<text x="1110" y="344" class="node-text node-sub" text-anchor="middle">push GGUF a HF</text>
<text x="1110" y="360" class="node-text node-sub" text-anchor="middle">~$1</text>
<rect class="node-box" x="1010" y="390" width="200" height="80" rx="6" fill="#fff"/>
<text x="1110" y="412" class="node-text node-title" text-anchor="middle">Eval pipeline</text>
<text x="1110" y="428" class="node-text node-sub" text-anchor="middle">consistencia visual</text>
<text x="1110" y="444" class="node-text node-sub" text-anchor="middle">% ingredientes correctos</text>
<text x="1110" y="460" class="node-text node-sub" text-anchor="middle">~$1</text>
<!-- Arrows: input → vision -->
<path class="arrow" d="M200 105 L278 130" marker-end="url(#ar)"/>
<text x="200" y="100" class="arrow-label">refri</text>
<!-- input → STT -->
<path class="arrow" d="M200 165 L278 235" marker-end="url(#ar)"/>
<text x="205" y="200" class="arrow-label">audio</text>
<!-- input progress → validator -->
<path class="arrow arrow-loop" d="M200 225 L278 330" marker-end="url(#aro)"/>
<text x="200" y="270" class="arrow-label" style="fill:#a85c2a;">progreso</text>
<!-- Vision → Planner -->
<path class="arrow" d="M480 130 L508 130" marker-end="url(#ar)"/>
<text x="482" y="120" class="arrow-label">ingredientes</text>
<!-- Planner → Illustrator -->
<path class="arrow" d="M710 130 L738 130" marker-end="url(#ar)"/>
<text x="712" y="120" class="arrow-label">visual prompt</text>
<!-- Illustrator → Modal -->
<path class="arrow dashed" d="M940 130 L1008 130" marker-end="url(#ar)"/>
<text x="945" y="120" class="arrow-label">.remote()</text>
<!-- Planner → narrator -->
<path class="arrow" d="M610 170 L610 198" marker-end="url(#ar)"/>
<!-- Planner → tip giver -->
<path class="arrow" d="M710 145 C 760 170, 800 180, 800 198" marker-end="url(#ar)"/>
<!-- Validator → Planner (loop) -->
<path class="arrow arrow-loop" d="M390 290 C 390 240, 470 190, 510 145" marker-end="url(#aro)"/>
<text x="395" y="240" class="arrow-label" style="fill:#a85c2a;">verdict · feedback</text>
<!-- STT → Validator -->
<path class="arrow dashed" d="M380 270 L380 288" marker-end="url(#ar)"/>
<!-- Recipe state ↔ all agents -->
<path class="arrow dashed" d="M725 290 L725 270" marker-end="url(#ar)"/>
<path class="arrow dashed" d="M610 290 L610 270" marker-end="url(#ar)"/>
<!-- All → Assembler -->
<path class="arrow" d="M610 380 L610 398" marker-end="url(#ar)"/>
<!-- Assembler → output -->
<path class="arrow" d="M280 425 C 240 425, 220 410, 200 385" marker-end="url(#ar)"/>
<path class="arrow" d="M280 440 C 240 440, 220 445, 200 445" marker-end="url(#ar)"/>
<!-- Modal → Planner (LoRA pesos offline) -->
<path class="arrow dashed" d="M1010 330 C 870 330, 750 280, 710 165" marker-end="url(#ar)"/>
<text x="900" y="280" class="arrow-label">LoRA pesos</text>
</svg>
<p style="font-size: 13px; color: var(--accent2); margin-top: 10px;">
<strong>Flecha naranja</strong> = closed-loop visual (la innovación). El usuario toma foto del progreso, MiniCPM-V valida vs imagen-objetivo, el Planner ajusta o avanza. Ningún recipe app del mercado lo hace.
</p>
<h2><span class="num">04</span>El truco innovador: closed-loop visual cocinero</h2>
<div class="grid-3">
<div class="card">
<h3>1. Imagen-objetivo por paso</h3>
<p style="font-size:13px;">Flux.2 genera "así debe verse el sartén/plato/olla en el paso N". No es texto, no es stock photo: es generación context-aware del estado deseado.</p>
</div>
<div class="card">
<h3>2. Validación con foto del usuario</h3>
<p style="font-size:13px;">El usuario sube foto de cómo va. MiniCPM-V compara contra la imagen-objetivo y devuelve verdict: <code>go</code> · <code>wait</code> · <code>fix</code>.</p>
</div>
<div class="card">
<h3>3. Replan adaptativo</h3>
<p style="font-size:13px;">"No tengo cilantro." → Planner regenera receta + Flux regenera imagen final. El plan no es estático, evoluciona con el estado real.</p>
</div>
</div>
<p style="margin-top:14px; font-size:14px;">
<strong>Esta es la sección destacada del README</strong> y el blog post de Field Notes badge: <em>"How visual closed-loop cooking guidance works."</em>
</p>
<h2><span class="num">05</span>Badges objetivo (5/6)</h2>
<div class="badges-grid">
<div class="badge-card"><span class="tag">LLAMA.CPP</span><br/><strong>Llama Champion</strong><p>Vision + Planner via <code>llama-cpp-python</code> con GGUF Q4.</p></div>
<div class="badge-card"><span class="tag">FINE-TUNED</span><br/><strong>Well-Tuned</strong><p>LoRA en cocina mexicana · publicado en HF.</p></div>
<div class="badge-card"><span class="tag">CUSTOM UI</span><br/><strong>Off-Brand</strong><p>UI tarjeta de receta · serif · paleta cálida · modo cocina XL.</p></div>
<div class="badge-card"><span class="tag">OPEN TRACE</span><br/><strong>Sharing is Caring</strong><p>Dataset 150 recetas mx + traces + recetas generadas al Hub.</p></div>
<div class="badge-card"><span class="tag">TENTATIVE</span><br/><strong>Field Notes</strong><p>Blog: "Le construí un sous-chef a mi mamá".</p></div>
<div class="badge-card skip"><span class="tag">LOCAL-FIRST</span><br/><strong>Off the Grid</strong><p>Sacrificado: Flux.2 corre en Modal por calidad.</p></div>
</div>
<h2><span class="num">06</span>Premios objetivo</h2>
<div class="card">
<div class="award-row"><span><strong>Backyard AI Track</strong> · $1K–$4K</span><span class="prob prob-h">ALTA</span></div>
<div class="award-row"><span><strong>Modal Awards</strong> · $3K–$10K credits</span><span class="prob prob-h">ALTA</span></div>
<div class="award-row"><span><strong>OpenBMB Award</strong> · $1K–$2.5K</span><span class="prob prob-h">ALTA</span></div>
<div class="award-row"><span><strong>Best Demo</strong> · $1K</span><span class="prob prob-h">ALTA</span></div>
<div class="award-row"><span><strong>Community Choice</strong> · $2K</span><span class="prob prob-h">ALTA</span></div>
<div class="award-row"><span><strong>Best Agent</strong> · $1K</span><span class="prob prob-h">ALTA — closed-loop multi-agente real</span></div>
<div class="award-row"><span><strong>Bonus Quest Champion</strong> · $2K</span><span class="prob prob-m">MEDIA-ALTA · 5/6 badges</span></div>
<div class="award-row"><span><strong>Off-Brand</strong> · $1.5K</span><span class="prob prob-m">MEDIA</span></div>
<div class="award-row"><span><strong>Tiny Titan</strong> · $1.5K</span><span class="prob prob-l">BAJA · Flux 9B saca del rango</span></div>
</div>
<p style="font-size: 14px; margin-top: 8px;"><strong>Cota razonable acumulada: $5K–$12K cash + $3K–$10K Modal credits.</strong></p>
<h2><span class="num">07</span>Timeline de 10 días</h2>
<div class="timeline">
<div class="day"><span class="lbl">D1</span><strong>Setup + Modal Flux endpoint</strong><div class="what">"Hola Flux": prompt → imagen de un platillo. Space vacío deployado.</div></div>
<div class="day"><span class="lbl">D2</span><strong>Vision: identificación de ingredientes</strong><div class="what">MiniCPM-V Q4 · prueba con 5 fotos reales del refri.</div></div>
<div class="day"><span class="lbl">D3</span><strong>Recipe Planner LLM</strong><div class="what">MiniCPM-4 · JSON estructurado · 3 opciones a partir de ingredientes.</div></div>
<div class="day"><span class="lbl">D4</span><strong>Step Illustrator (Flux + consistencia)</strong><div class="what">Imagen del plato final + 5 imágenes-objetivo por paso · i2i suave.</div></div>
<div class="day"><span class="lbl">D5</span><strong>Voz: narrador + tip-giver</strong><div class="what">OpenBMB voice + Cohere voice · audio pre-renderizado por paso.</div></div>
<div class="day"><span class="lbl">D6</span><strong>UI Off-Brand: recipe card</strong><div class="what">gr.Blocks + CSS serif tierra · modo cocina XL hands-free.</div></div>
<div class="day"><span class="lbl">D7</span><strong>Gradio Workflows showcase</strong><div class="what">Pipeline reescrita como Workflow visible · pestaña separada.</div></div>
<div class="day"><span class="lbl">D8</span><strong>Fine-tune del Planner en cocina mx</strong><div class="what">200 recetas sintéticas · LoRA · GGUF · push HF.</div></div>
<div class="day"><span class="lbl">D9</span><strong>STT + Progress Validator + eval</strong><div class="what">Whisper · closed-loop activo · Sharing is Caring badge.</div></div>
<div class="day"><span class="lbl">D10</span><strong>Demo + README + blog + submit</strong><div class="what">Mamá real cocinando · 60-90s · subtítulos EN · Field Notes blog.</div></div>
</div>
<h2><span class="num">08</span>Plan B (corte de scope)</h2>
<table>
<thead><tr><th>#</th><th>Cortar</th><th>Pierdes</th><th>Conservas</th></tr></thead>
<tbody>
<tr><td>1</td><td>STT (preguntas voz)</td><td>comodidad demo</td><td>texto + foto</td></tr>
<tr><td>2</td><td>2da voz (Cohere tip-giver)</td><td>1 sponsor voice</td><td>narrador único</td></tr>
<tr><td>3</td><td>Progress Validator (closed-loop)</td><td><strong>Best Agent</strong> + innovación principal</td><td>demo lineal</td></tr>
<tr><td>4</td><td>Fine-tune del Planner</td><td><strong>Well-Tuned</strong></td><td>resto badges</td></tr>
<tr><td>5</td><td>Gradio Workflows showcase</td><td>diferenciador "fresh"</td><td>pipeline Python</td></tr>
<tr><td>6</td><td>UI super-custom</td><td><strong>Off-Brand</strong></td><td>UI default</td></tr>
<tr style="background:#fff3cf;"><td></td><td><strong>NUNCA</strong></td><td colspan="2">Vision + Planner + Illustrator + Narrator + UI mínima + video con persona real cocinando</td></tr>
</tbody>
</table>
<h2><span class="num">09</span>Riesgos clave</h2>
<table>
<thead><tr><th>Riesgo</th><th>Mitigación</th></tr></thead>
<tbody>
<tr><td>Flux.2 Klein no tiene API/pesos públicos cuando lo necesitas</td><td>Plan B: Flux.1-schnell o SDXL-Lightning. Pierdes posicionamiento sponsor pero idea sobrevive.</td></tr>
<tr><td>MiniCPM-V no identifica ingredientes mexicanos (chile poblano, nopales)</td><td>Few-shot en prompt; eventualmente fine-tune ligero del visión sobre 50 fotos etiquetadas</td></tr>
<tr><td>Flux.2 genera comida poco apetitosa</td><td>Itera prompts ("recipe magazine, warm light, top-down"); usa imagen final como ref para los pasos</td></tr>
<tr><td>Progress Validator da false positives</td><td>Conservador: solo dice "vas bien" si similitud es alta; default es "sigue" sin juicio fuerte</td></tr>
<tr><td>Latencia receta &gt; 30s</td><td>Streaming progresivo; paraleliza Flux + TTS</td></tr>
<tr><td>Modal cold start ~30-60s en Flux</td><td>Pre-warm 30s antes de filmar · <code>keep_warm=1</code> el día del demo</td></tr>
<tr><td>Persona del demo se quema/cocina mal</td><td>Practica la receta una vez antes · 2-3 candidatos de receta listos</td></tr>
<tr><td>Otro equipo presenta "recipe app con AI"</td><td>Diferéncialo con: closed-loop visual + español + cocina mx + dataset publicado + persona real</td></tr>
</tbody>
</table>
<h2><span class="num">10</span>Cómo gastar los créditos</h2>
<div class="grid-2">
<div class="card">
<h3>Modal · $250</h3>
<table>
<tr><td>Flux dev (días 1-9)</td><td>$5-15</td></tr>
<tr><td>Dataset cocina mx</td><td>$3-8</td></tr>
<tr><td>LoRA + sweeps</td><td>$4-5</td></tr>
<tr><td>Eval</td><td>$1</td></tr>
<tr><td>Inferencia grading jueces</td><td>$10-25</td></tr>
<tr><th>Subtotal</th><th>$25-65</th></tr>
<tr><th>+ Buffer</th><th>$30</th></tr>
<tr><th>Proyectado</th><th><strong>~$55-95 / $250</strong></th></tr>
</table>
</div>
<div class="card">
<h3>OpenAI Codex · $100</h3>
<table>
<tr><td>Codex CLI pair-programmer</td><td>$20-40</td></tr>
<tr><td>200 recetas mx sintéticas</td><td>$10-25</td></tr>
<tr><td>Prompts Flux por paso</td><td>$5-10</td></tr>
<tr><td>Reserva</td><td>$30</td></tr>
<tr><th>Proyectado</th><th><strong>~$65-105 / $100</strong></th></tr>
</table>
</div>
</div>
<div class="footnote">
<strong>Mantra del proyecto:</strong> "Una mamá cocinando frente a la cámara. Un platillo que se ve apetitoso. Una voz que la acompaña sin juzgar. Un paso a la vez."
</div>
</div>
</body>
</html>