Serie: Aprender a programar con IA

Esbozando en papel la estructura de carpetas del SDK

El esqueleto del SDK dibujado antes de que existiera código alguno.

La estructura de carpetas es la especificación Dibuja las casillas en las que los agentes tendrán que escribir /sdk /apps /modules /assets /docs /config arena_demo/ coach_demo/ hud_designer/ ...8 demos hud gesture gaze sync fx voice widgets audio gesture icons README guías referencias de API layouts roles Los stubs fijan el contrato antes de que exista la implementación
La estructura es la disciplina: los agentes la heredan a través de las casillas en las que escriben.

Una base de código construida por agentes o bien se convierte en una papilla en tres meses o se mantiene coherente durante años, y la diferencia se decide antes de que se escriba una sola línea. RakuAI dibujó las casillas primero para que los agentes siempre cayeran en el lugar correcto.

Hay un tipo de trabajo de diseño que es invisible desde fuera y estructural desde dentro. La estructura de carpetas de una base de código es una de esas cosas. Parece gestión de archivos. En realidad es el organigrama del código. Acertar temprano hace que cada PR de los próximos dos años caiga en el lugar correcto. Equivocarse temprano hace que cada PR de los próximos dos años tenga que discutir dónde pertenece.

Este sábado se dedicó a acertar temprano.

Lo que la estructura tiene que soportar

Antes de dibujar casillas, hice una lista de restricciones que la estructura tiene que satisfacer.

Cada demo tiene su propio hogar. Las ocho demos que especifiqué el mes pasado necesitan cada una una carpeta donde su código, sus assets y su documentación vivan juntos. Un desarrollador que lea cualquiera de las demos no debería tener que saltar entre cuatro partes distintas del SDK para entenderla.

Los módulos compartidos son separables de las demos. Cualquier cosa que se reutilice entre demos (renderizado de HUD, entrada por gestos, seguimiento de mirada, sincronización multijugador, control por voz, efectos visuales) vive en su propio lugar y es consumida por las demos a través de una superficie pública.

Los assets se organizan por tipo, no por demo. Un PNG de barra de salud es un widget de HUD, no un asset de la Arena Demo. Los assets visuales reutilizables viven en una carpeta de assets compartida y las demos que los necesitan enlazan a ellos.

La documentación está ubicada junto a lo que describe. La documentación de instalación del SDK vive junto al SDK. La guía de demos vive junto a las demos. La documentación de la API de HUD vive junto al módulo de HUD. La descubribilidad de la documentación es sobre todo cuestión de dónde vive.

La configuración es su propia preocupación. Los archivos de configuración de HUD, los layouts por defecto, las configuraciones de roles multijugador. No son código, no son assets, no son documentación. Tienen su propio hogar.

La estructura

Después de una mañana de pizarra, el diseño se ve así.

La carpeta raíz es el SDK en sí. Bajo la raíz:

/apps/ es donde viven las demos. Cada demo tiene su propia subcarpeta. arena_demo/ para la arena en solitario. coach_demo/ para el coaching de RA. hud_designer/ para el diseñador de superposiciones de HUD. streamer_demo/ para el modo streamer. pos_demo/ para la superposición de punto de servicio. companion_demo/ para el HUD complementario de RA. aimlab_demo/ para el entrenador de puntería de RA. hudsync_demo/ para la sincronización de HUD multijugador. Cada carpeta de demo contiene su propio código fuente, su propia configuración, sus propios assets específicos de la demo, y su propio README.

/modules/ es donde vive el código compartido. Seis módulos al inicio. El renderizador de HUD, que consume cada demo. La detección de entrada por gestos, que consumen la mayoría de las demos. El seguimiento de mirada, usado por las demos que lo necesitan. La sincronización multijugador, usada por las demos multijugador en LAN. El animador de superposiciones para efectos visuales. La interfaz de control por voz para las demos que reciben entrada de voz. Cada módulo expone una superficie pública pequeña y estable a la que las demos se enlazan.

/assets/ es la biblioteca de assets compartida. Widgets de HUD (barras de salud, contadores de munición, superposiciones de brújula) como PNG y SVG. Efectos de audio para impactos, alertas, locuciones. Íconos de gestos que le muestran al usuario cuándo se ha detectado un gesto. Las demos enlazan a los assets aquí en lugar de duplicarlos.

/docs/ es el hogar de la documentación. Un README que presenta el SDK y guía la instalación. Una guía de demos que explica cómo ejecutar cada una de las ocho demos. Una referencia de API para el módulo de HUD. Una referencia de API para el módulo de control por voz. La documentación está deliberadamente ubicada junto al código para que un desarrollador que quiera saber cómo funciona el HUD pueda leer el código fuente y la documentación en la misma carpeta.

/config/ es la configuración. El layout de HUD por defecto usado como punto de partida para proyectos nuevos. Layouts de ejemplo exportados desde el diseñador de HUD. Definiciones de roles multijugador. Mantener la configuración separada del código mantiene las demos limpias y le permite a un desarrollador cambiar el comportamiento sin recompilar.

Los stubs

Una estructura de carpetas por sí sola es solo un sistema de archivos vacío. Lo otro que redacté hoy es un conjunto de stubs de código que muestran a los agentes (cuando eventualmente empiecen a escribir código contra esta estructura) cómo debería verse una implementación real en cada punto de entrada.

Los stubs están en pseudocódigo Python por ahora. Se traducirán al lenguaje de producción (C++ para el runtime, con bindings hacia Python y otros ecosistemas) una vez que se abra el repositorio del motor. La forma en Python es la que me permite razonar sobre las interfaces sin perderme en detalles de implementación.

Un stub de ejemplo, para el módulo de entrada por gestos. Aproximadamente:

def detect_gesture(frame):
    """Detect gestures like swipe, point, raise, duck in a single sensor frame."""
    if is_swipe(frame):
        return "swipe"
    elif is_raise(frame):
        return "raise_arm"
    else:
        return None

Eso no es el código de producción. Es el contrato. El código de producción reemplazará is_swipe e is_raise con pipelines reales de visión por computadora. La interfaz se mantiene igual. La firma es lo que las demos van a codificar contra. Fijar la firma temprano significa que puedo lanzar las demos antes de que el pipeline de CV sea infalible, porque las demos no dependen de la implementación, dependen de la interfaz.

Redacté stubs de esta forma para cada módulo de la carpeta /modules/. Seis archivos Python pequeños. Ninguno de ellos implementa nada. Todos definen el contrato que las implementaciones eventuales tendrán que honrar.

Por qué los agentes necesitan esto

Esta es la conexión de vuelta a la plantilla de agentes de hace unos sábados.

Cuando llegue el momento de escribir el código real, los agentes no estarán inventando la arquitectura. La arquitectura está en papel. La estructura de carpetas les dice dónde va el código nuevo. Los stubs de módulo les dicen qué interfaz implementar. La organización de assets les dice dónde buscar lo que necesitan. La estructura de documentación les dice dónde poner la documentación que escriben.

Esa es la diferencia entre una base de código construida por agentes que se convierte en papilla después de tres meses y una que se mantiene coherente durante años. La estructura es la disciplina. Los agentes heredan la disciplina a través de la estructura.

Sin este trabajo previo, un agente al que se le pidiera “implementar la entrada por gestos” inventaría una carpeta, inventaría una API, escribiría el código, y produciría algo que funcionara de forma aislada. Multiplicado por seis módulos, ese enfoque produce seis mini-motores incompatibles. La estructura previene ese modo de fallo dibujando las casillas en las que los agentes tienen que escribir.

Lo difícil de esto

Algunas cosas honestas.

Las estructuras de carpetas quieren crecer. Dentro de seis meses voy a querer añadir un séptimo módulo. La estructura tiene que permitir eso sin convertirse en un cajón de sastre. La regla que me estoy imponiendo: cualquier carpeta de nivel superior nueva necesita justificar por qué no es una subcarpeta de una ya existente. La mayoría de las cosas nuevas terminan siendo subcarpetas.

Los stubs necesitan mantenimiento. A medida que las implementaciones reales lleguen, los stubs se vuelven obsoletos. Los agentes que lean los stubs como “el contrato” heredarán cualquier desviación entre el stub y la implementación real. La solución es hacer que los stubs sean la fuente de verdad, y exigir que las implementaciones reales los actualicen cuando el contrato cambie.

Algunas de estas carpetas son especulativas. La carpeta /config/ es una apuesta a que los archivos de configuración de runtime serán una categoría real de artefacto en este SDK. Si resulta que no lo son, la carpeta se reduce o se fusiona con otra cosa. Estoy bien con eso. Es más barato eliminar una carpeta después que inventar una dentro de un año cuando la convención ya se haya desviado.

Qué sigue

El próximo sábado es el diseño de la superficie de API. Cada uno de los seis módulos anteriores necesita su superficie pública especificada. Eso significa decidir qué tipos de datos cruzan el límite, qué códigos de error se devuelven, qué eventos se emiten. Con la estructura de carpetas terminada, el diseño de la API tiene dónde aterrizar.

Después viene la especificación demo por demo de qué módulos consume cada demo y en qué orden. Luego la arquitectura del runtime. Luego, finalmente, se abre el repositorio del motor.

El patrimonio de patentes ha estado esperando una década. Unas semanas más de trabajo de diseño para hacerlo bien no será lo que nos cueste la ventana de oportunidad.

Estructura de carpetas terminada. La base de código tiene ahora una forma, aunque todavía no haya código en ella.

Un SDK que no se moverá bajo tus pies

Una estructura de carpetas que no cambiará, una superficie de API fijada antes de la implementación: construye sobre un runtime espacial diseñado para el largo plazo.

← Todas las entradas