Flujo de agentes (S13)
El pipeline de estimación reexpresado como un grafo LangGraph:
una estado compartido tipado que fluye por agentes
especializados. Dos handovers explícitos
(Command(goto)), dos gates humanos
(interrupt() + resume) y un fan-out
(Send) que estima cada tarea en paralelo. Recurso de solo
lectura — no ejecuta nada.
Clasifica la complejidad y reformula el brief.
Lee la transcripción cruda y produce una ComplexityClassification: una etiqueta de complejidad (low / medium / high) y un brief reformulado y limpio. La complejidad decide luego el reasoning effort del agente de estructura. Es el nodo de entrada y hace el primer handover (Command(goto) → structure), sin arista estática.
Descompone el brief en módulos → tareas (sin horas).
Reutiliza el agente hecho a mano de la S12 (bucle Responses API) para convertir el brief en un árbol de módulos y tareas, todavía sin horas. El esfuerzo de razonamiento se elige según la complejidad del classifier (GRAPH_STRUCTURE_EFFORT_BY_COMPLEXITY).
Pausa para revisión humana de los módulos y tareas.
Human-in-the-loop: interrupt() detiene el grafo y expone la estructura para que una persona la edite o apruebe. Al reanudar (Command(resume=…)) devuelve los módulos aprobados. Disciplina interrupt-first: no escribe en el estado antes de pausar, porque el resume re-ejecuta el nodo entero.
interrupt() — reanuda con Command(resume=…)
Horas por tarea vía búsqueda vectorial determinista.
No usa LLM: es un fan-out con la Send API (una rama en paralelo por cada tarea aprobada). Cada rama busca las tareas históricas más cercanas y deriva las horas por consenso ponderado por distancia (reutiliza la lógica de la S10). Sin análogo por debajo del umbral → tarea marcada en rojo.
Junta las ramas, recupera tareas dudosas y construye la estimación.
Nodo de join del fan-out. Detecta tareas dudosas (sin match, rango contradictorio o fiabilidad baja) y, si las hay, lanza un bucle agéntico de recuperación (S12). Fusiona las horas y construye el Estimate consolidado. Termina con el segundo handover (Command(goto) → analysis).
Redacta el informe de fiabilidad (no toca los números).
Lee la estimación y escribe un ReliabilityReport: confianza global, ratio de tareas fundamentadas (calculado de forma determinista, sobrescribe el del LLM), puntos débiles y un resumen. No modifica ninguna cifra; solo evalúa.
Pausa para la validación humana del informe y la estimación.
Segundo human-in-the-loop: interrupt() expone la estimación y el informe de fiabilidad. Al reanudar, aplica los overrides, fija el status (validated / needs_review) y registra si se pidió una propuesta comercial (want_proposal).
interrupt() — reanuda con Command(resume=…)
Bonus: redacta la propuesta comercial.
Nodo condicional: solo se ejecuta si GRAPH_PROPOSAL_ENABLED está activo Y el humano pidió propuesta en el gate 2. Redacta una CommercialProposal en markdown, anclada estrictamente a la estimación validada. En caso contrario, el grafo termina en END.