Módulo 1: Por qué multi-agente (y cuándo no)

La comparación ejecutada

Descripción

Las cuatro lecciones anteriores construyeron cada pieza por separado: qué es un sistema multi-agente (02), cuánto cuesta coordinar, con una fórmula (03), y el árbitro entre un agente con más tools y varios agentes con menos tools cada uno (04). Esta lección junta las tres piezas sobre un caso real, de punta a punta, sin fórmulas ni estimaciones: la MISMA tarea de Reservo — "cotiza Focus pro 3h y resérvala para Ana"— resuelta primero por un agente con las cuatro tools canónicas (exactamente el runner de agent-fundamentals M5, sin cambios) y después por un sistema de dos agentes (un supervisor y un booking_agent), y vas a contar, con el historial completo impreso en pantalla, cuántas llamadas al modelo y cuántos mensajes le costó a cada camino.

El resultado no es una opinión: es un número. Para esta tarea concreta —una que un solo agente ya resuelve bien— vas a ver que el sistema de dos agentes usa más llamadas al modelo y paga hops de coordinación que el agente único no necesita, sin ganar nada a cambio: la misma respuesta final, con las mismas dos tool calls. Esta es la pieza central del módulo — la que convierte "coordinar cuesta" de una afirmación en una medición.

Conexión con el módulo

Esta lección es la síntesis ejecutada de las lecciones 02 a 04: usa la definición formal de sistema multi-agente (02), aplica el vocabulario de costo —llamadas, hops— de la fórmula de la lección 03 al caso n=1 exacto (un solo especialista consultado), y confirma con un caso real el árbitro de la lección 04. El runner que vas a usar es literalmente el de agent-fundamentals M5 —run_agent_parallel + dispatch_parallel—, sin ninguna modificación: la orquestación nueva de esta guía no reemplaza ese runner, lo envuelve con un mecanismo de delegación por encima.


Analogía: pedirle ayuda a alguien para una tarea que ya sabes hacer

Retoma la analogía de la lección 01. Si ya sabes redactar un correo, pedirle a un colega que lo revise antes de enviarlo no lo hace mejor de forma automática — agrega el tiempo de explicarle qué necesitas, el tiempo de que lo lea, y el tiempo de que te devuelva su versión. Si el correo ya estaba bien, ese ciclo completo fue una vuelta extra sin ningún beneficio. Eso es exactamente lo que esta lección mide: un supervisor que delega una tarea completa a un solo especialista, cuando ese especialista es —tool por tool— exactamente lo que un agente único ya tenía.


Ejemplo trabajado, Sistema A: un agente con las 4 tools canónicas

Reusamos el runner de agent-fundamentals M5 sin ningún cambio: dispatch_parallel despacha los bloques tool_use de un turno (uno o varios, en paralelo con hilos reales); run_agent_parallel es el while que pide, despacha, alimenta y repite. Agregamos solo dos funciones de conteo, puramente de contabilidad —no tocan la lógica del runner—.

import concurrent.futures
import reservo_tools as rt

TOOLS = {
    "list_rooms": rt.list_rooms,
    "get_quote": rt.get_quote,
    "book_room": rt.book_room,
    "cancel_booking": rt.cancel_booking,
}


def dispatch_parallel(tool_use_blocks, tools):
    """El de agent-fundamentals M5, sin cambios."""
    with concurrent.futures.ThreadPoolExecutor(max_workers=len(tool_use_blocks)) as pool:
        futures = [pool.submit(tools[b["name"]], **b["input"]) for b in tool_use_blocks]
        results = [f.result() for f in futures]
    return [
        {"type": "tool_result", "tool_use_id": b["id"], "content": str(r)}
        for b, r in zip(tool_use_blocks, results)
    ]


def run_agent_parallel(question, model_script, tools, max_iterations=10):
    """El de agent-fundamentals M4/M5, sin cambios."""
    messages = [{"role": "user", "content": question}]
    for step in range(max_iterations):
        turn = model_script[step]
        messages.append({"role": "assistant", "content": turn["content"]})
        if turn["stop_reason"] != "tool_use":
            return turn, messages
        tool_result_blocks = dispatch_parallel(turn["content"], tools)
        messages.append({"role": "user", "content": tool_result_blocks})
    raise RuntimeError(f"max_iterations alcanzado ({max_iterations})")


def count_model_calls(history):
    """NUEVO de esta lección: cada mensaje 'assistant' es una llamada al
    modelo consumida del guion (concepto) -- pedir una tool, o el texto final."""
    return sum(1 for m in history if m["role"] == "assistant")


def count_tool_calls(history):
    """NUEVO de esta lección: cuenta los bloques tool_use reales despachados."""
    total = 0
    for m in history:
        if isinstance(m["content"], list):
            total += sum(1 for b in m["content"] if b["type"] == "tool_use")
    return total


TASK = "Cotiza Focus pro 3h y resérvala para Ana"

# Guion (concepto, claude-sonnet-5): cotiza, después reserva, después responde.
model_script_single = [
    {"stop_reason": "tool_use", "content": [
        {"type": "tool_use", "id": "toolu_01", "name": "get_quote",
         "input": {"room": "Focus", "tier": "pro", "hours": 3}}]},
    {"stop_reason": "tool_use", "content": [
        {"type": "tool_use", "id": "toolu_02", "name": "book_room",
         "input": {"room": "Focus", "tier": "pro", "hours": 3, "member": "Ana"}}]},
    {"stop_reason": "end_turn", "content": [
        {"type": "text", "text": (
            "Focus pro 3h cuesta 6000 centavos. Reservé la sala para Ana "
            "(confirmación #1)."
        )}]},
]

final_single, history_single = run_agent_parallel(TASK, model_script_single, TOOLS)

print("--- historial completo (Sistema A) ---")
for i, m in enumerate(history_single):
    role, content = m["role"], m["content"]
    if isinstance(content, str):
        print(f"  [{i}] {role:<9} pregunta: {content!r}")
        continue
    for block in content:
        if block["type"] == "tool_use":
            print(f"  [{i}] {role:<9} tool_use({block['name']}): {block['input']}")
        elif block["type"] == "tool_result":
            print(f"  [{i}] {role:<9} tool_result: {block['content']}")
        elif block["type"] == "text":
            print(f"  [{i}] {role:<9} texto final: {block['text']!r}")

calls_single = count_model_calls(history_single)
tools_single = count_tool_calls(history_single)
print()
print("respuesta final:", final_single["content"][0]["text"])
print("llamadas al modelo:", calls_single)
print("llamadas a tools:  ", tools_single)
print("hops entre agentes:", 0)

Qué esperar (sobre un Reservo desechable, recién iniciado):

--- historial completo (Sistema A) ---
  [0] user      pregunta: 'Cotiza Focus pro 3h y resérvala para Ana'
  [1] assistant tool_use(get_quote): {'room': 'Focus', 'tier': 'pro', 'hours': 3}
  [2] user      tool_result: {'price_cents': 6000}
  [3] assistant tool_use(book_room): {'room': 'Focus', 'tier': 'pro', 'hours': 3, 'member': 'Ana'}
  [4] user      tool_result: {'booking_id': 1, 'confirmed': True}
  [5] assistant texto final: 'Focus pro 3h cuesta 6000 centavos. Reservé la sala para Ana (confirmación #1).'

respuesta final: Focus pro 3h cuesta 6000 centavos. Reservé la sala para Ana (confirmación #1).
llamadas al modelo: 3
llamadas a tools:   2
hops entre agentes: 0

Tres llamadas al modelo (turnos [1], [3], [5] del historial), dos tool calls (get_quote seguido de book_room, en orden — book_room necesita el precio confirmado antes de reservar, así que no pueden ir en paralelo), y cero hops, porque no hay ningún otro agente a quien consultar. El 6000 que cita la respuesta final está anclado en el tool_result del turno [2] —el mismo hábito de grounding de agent-fundamentals M5 L07—.


Ejemplo trabajado, Sistema B: supervisor + booking_agent

Ahora la misma tarea, exactamente la misma pregunta, resuelta por un sistema de dos agentes: un supervisor que decide (concepto) delegar toda la tarea a un booking_agent, y ese booking_agent que la resuelve corriendo el mismo runner, sobre el mismo guion, que el agente único de arriba. El paso de mensajes entre los dos es real y ejecutado —un AgentMessage mínimo, con quién envía, quién recibe, la tarea y el payload—.

from dataclasses import dataclass


@dataclass
class AgentMessage:
    sender: str
    receiver: str
    task: str
    payload: dict


ROUTE_CALLS = 1     # concepto: el supervisor decide a quién delegar
COMPOSE_CALLS = 1   # concepto: el supervisor redacta la respuesta final para el socio

# Paso 1 (ejecutado): el supervisor arma el paquete y lo envía a booking_agent.
hop_1 = AgentMessage(sender="supervisor", receiver="booking_agent", task=TASK, payload={})
print("hop 1:", hop_1.sender, "->", hop_1.receiver, "| tarea:", hop_1.task)

# Paso 2 (ejecutado): booking_agent resuelve la tarea delegada con el MISMO
# runner y el MISMO guion que usó el agente único del Sistema A.
final_booking, history_booking = run_agent_parallel(hop_1.task, model_script_single, TOOLS)

print()
print("--- historial de booking_agent, internamente ---")
for i, m in enumerate(history_booking):
    role, content = m["role"], m["content"]
    if isinstance(content, str):
        print(f"  [{i}] {role:<9} pregunta: {content!r}")
        continue
    for block in content:
        if block["type"] == "tool_use":
            print(f"  [{i}] {role:<9} tool_use({block['name']}): {block['input']}")
        elif block["type"] == "tool_result":
            print(f"  [{i}] {role:<9} tool_result: {block['content']}")
        elif block["type"] == "text":
            print(f"  [{i}] {role:<9} texto final: {block['text']!r}")

booking_calls = count_model_calls(history_booking)
booking_tool_calls = count_tool_calls(history_booking)

# Paso 3 (ejecutado): booking_agent devuelve su resultado al supervisor.
hop_2 = AgentMessage(
    sender="booking_agent", receiver="supervisor", task="resultado",
    payload={"text": final_booking["content"][0]["text"]},
)
print()
print("hop 2:", hop_2.sender, "->", hop_2.receiver, "| payload:", hop_2.payload["text"])

# Paso 4 (concepto): el supervisor compone la respuesta final para el socio.
calls_multi = ROUTE_CALLS + booking_calls + COMPOSE_CALLS
hops_multi = 2

print()
print("llamadas al modelo:", calls_multi,
      f"({ROUTE_CALLS} ruteo + {booking_calls} booking_agent + {COMPOSE_CALLS} síntesis)")
print("llamadas a tools:  ", booking_tool_calls)
print("hops entre agentes:", hops_multi)

Qué esperar (sobre SU PROPIO Reservo desechable, recién iniciado — por eso book_room le asigna booking_id: 1 de nuevo, igual que en el Sistema A):

hop 1: supervisor -> booking_agent | tarea: Cotiza Focus pro 3h y resérvala para Ana

--- historial de booking_agent, internamente ---
  [0] user      pregunta: 'Cotiza Focus pro 3h y resérvala para Ana'
  [1] assistant tool_use(get_quote): {'room': 'Focus', 'tier': 'pro', 'hours': 3}
  [2] user      tool_result: {'price_cents': 6000}
  [3] assistant tool_use(book_room): {'room': 'Focus', 'tier': 'pro', 'hours': 3, 'member': 'Ana'}
  [4] user      tool_result: {'booking_id': 1, 'confirmed': True}
  [5] assistant texto final: 'Focus pro 3h cuesta 6000 centavos. Reservé la sala para Ana (confirmación #1).'

hop 2: booking_agent -> supervisor | payload: Focus pro 3h cuesta 6000 centavos. Reservé la sala para Ana (confirmación #1).

llamadas al modelo: 5 (1 ruteo + 3 booking_agent + 1 síntesis)
llamadas a tools:   2
hops entre agentes: 2

Fíjate en algo importante: el historial de booking_agent, línea por línea, es idéntico al del Sistema A —mismo get_quote, mismo price_cents: 6000, mismo book_room, mismo booking_id: 1—. booking_agent no hizo nada distinto ni mejor que el agente único; hizo exactamente lo mismo, con el mismo runner y el mismo guion. Lo único que cambió es que un supervisor tuvo que decidir enviarle la tarea (hop_1, más ROUTE_CALLS) y después tuvo que componer la respuesta final para el socio a partir de lo que booking_agent le devolvió (hop_2, más COMPOSE_CALLS).


La comparación final

print(f"{'':22}{'Sistema A (1 agente)':>22}{'Sistema B (2 agentes)':>24}")
print(f"{'llamadas al modelo':22}{calls_single:>22}{calls_multi:>24}")
print(f"{'llamadas a tools':22}{tools_single:>22}{booking_tool_calls:>24}")
print(f"{'hops entre agentes':22}{0:>22}{hops_multi:>24}")

extra_calls = calls_multi - calls_single
pct = extra_calls / calls_single * 100
print()
print(f"diferencia: Sistema B usa {extra_calls} llamadas al modelo MÁS que Sistema A "
      f"({pct:.0f}% más), y {hops_multi} hops de coordinación que Sistema A no necesita "
      f"-- para la MISMA tarea, con las MISMAS {tools_single} tool calls, y la misma "
      f"respuesta final.")

Qué esperar:

                      Sistema A (1 agente)  Sistema B (2 agentes)
llamadas al modelo                       3                       5
llamadas a tools                         2                       2
hops entre agentes                       0                       2

diferencia: Sistema B usa 2 llamadas al modelo MÁS que Sistema A (67% más), y 2 hops de
coordinación que Sistema A no necesita -- para la MISMA tarea, con las MISMAS 2 tool calls, y la
misma respuesta final.

Ahí está la medición completa. Las mismas 2 tool calls en los dos sistemas —get_quote seguido de book_room, calculando el mismo 6000 centavos, generando el mismo booking_id: 1— y la misma respuesta final, palabra por palabra. La única diferencia real entre los dos caminos es el costo de coordinación: 2 llamadas al modelo de más (67% más) y 2 hops que el agente único no pagó, porque nunca tuvo que consultar a nadie. Para esta tarea concreta —una que no se separa en dos expertises distintas; cotizar y reservar viven, las dos, en el registro de booking_agent— el sistema multi-agente no aportó ninguna capacidad que el agente único no tuviera. Solo agregó coordinación.

Este es el número que sostiene la advertencia de la lección 01: para una tarea que un agente con las tools correctas ya resuelve, sumar un supervisor y un especialista no la resuelve mejor — la resuelve más caro. La lección 06 muestra el caso contrario: una tarea donde SÍ hace falta consultar a dos especialistas distintos, y donde el costo medido aquí se justifica.


Errores comunes

  1. Correr el Sistema A y el Sistema B en el mismo proceso, sin reiniciar el estado. Si ejecutas el código de los dos sistemas en el mismo intérprete, sin reiniciarlo entre uno y otro, book_room se ejecuta dos veces sobre el mismo BOOKINGS compartido y el segundo booking_id sale 2, no 1 — un desajuste real con el texto del guion (que dice "confirmación #1"), no un error del runner. Cada sistema de esta lección asume su propio Reservo desechable, recién iniciado.

  2. Pensar que el booking_agent "hizo un trabajo distinto o mejor". El historial de arriba lo desmiente línea por línea: es exactamente el mismo camino, mismo runner, mismo guion. La única diferencia entre los dos sistemas está afuera del trabajo en sí —en la coordinación que lo rodea—.

  3. Restar solo las llamadas y olvidar los hops. Los hops no son "gratis" solo porque no se cuentan como llamadas al modelo — son mensajes reales que hay que armar, enviar y procesar. Un sistema con muchos hops, aunque tenga pocas llamadas de más, sigue teniendo más piezas móviles que pueden fallar (retomado en la lección 03, la superficie de error).

  4. Generalizar "multi-agente siempre cuesta 67% más" de este único caso. El 67% es el resultado de ESTA tarea específica (n=1 especialista, 3 llamadas internas) — con una tarea más grande, o con más especialistas genuinamente necesarios, el porcentaje cambia. Lo que sí generaliza es el patrón: coordinación agrega costo fijo (ruteo + síntesis) sobre el trabajo que un especialista ya hacía.

  5. Concluir que el patrón supervisor "nunca sirve" a partir de este ejemplo. Este ejemplo fue elegido a propósito porque la tarea NO necesitaba dos especialistas — es el caso de "cuándo NO orquestar". El Módulo 2 construye el patrón supervisor completo, para casos donde SÍ hace falta elegir entre varios especialistas genuinamente distintos.


Ejercicios

Ejercicio 1: Repite la comparación con Studio (Fácil)

Repite el Sistema A y el Sistema B de esta lección, pero con la tarea "Cotiza Studio basic 2h y resérvala para Carlos" (Studio basic 2h = 4000 * 2 = 8000 centavos, sin descuento). Confirma que la diferencia de costo (llamadas de más, hops) es exactamente la misma que con Focus.

Ver solución
TASK_STUDIO = "Cotiza Studio basic 2h y resérvala para Carlos"

model_script_studio = [
    {"stop_reason": "tool_use", "content": [
        {"type": "tool_use", "id": "toolu_01", "name": "get_quote",
         "input": {"room": "Studio", "tier": "basic", "hours": 2}}]},
    {"stop_reason": "tool_use", "content": [
        {"type": "tool_use", "id": "toolu_02", "name": "book_room",
         "input": {"room": "Studio", "tier": "basic", "hours": 2, "member": "Carlos"}}]},
    {"stop_reason": "end_turn", "content": [
        {"type": "text", "text": "Studio basic 2h cuesta 8000 centavos. Reservé la sala para Carlos (confirmación #1)."}]},
]

final_a, history_a = run_agent_parallel(TASK_STUDIO, model_script_studio, TOOLS)
print("Sistema A -- llamadas:", count_model_calls(history_a), "| tools:", count_tool_calls(history_a))

final_b, history_b = run_agent_parallel(TASK_STUDIO, model_script_studio, TOOLS)
calls_b = 1 + count_model_calls(history_b) + 1
print("Sistema B -- llamadas:", calls_b, "| tools:", count_tool_calls(history_b), "| hops:", 2)

Salida esperada:

Sistema A -- llamadas: 3 | tools: 2
Sistema B -- llamadas: 5 | tools: 2 | hops: 2

Explicación: los números son idénticos a los de Focus —3 vs. 5 llamadas, 2 vs. 2 tools, 0 vs. 2 hops— porque el costo de coordinación no depende de qué sala se cotiza ni de cuánto cuesta: es la MISMA estructura de tarea (cotizar → reservar, dos pasos secuenciales dentro de una sola expertise), sin importar los valores concretos. Eso es exactamente lo que hace que el resultado de esta lección sea un patrón, no una coincidencia de Focus pro 3h.

Ejercicio 2: Mide una tarea de tres pasos internos (Medio)

Extiende el guion del Sistema A para que, antes de cotizar, booking_agent primero llame a list_rooms() (para confirmar que "Focus" existe, el hábito de grounding de agent-fundamentals M1 L08). Ejecuta el Sistema A y el Sistema B con este guion de 4 pasos internos y confirma cómo cambia la diferencia de llamadas entre los dos sistemas.

Ver solución
model_script_4steps = [
    {"stop_reason": "tool_use", "content": [
        {"type": "tool_use", "id": "toolu_00", "name": "list_rooms", "input": {}}]},
    {"stop_reason": "tool_use", "content": [
        {"type": "tool_use", "id": "toolu_01", "name": "get_quote",
         "input": {"room": "Focus", "tier": "pro", "hours": 3}}]},
    {"stop_reason": "tool_use", "content": [
        {"type": "tool_use", "id": "toolu_02", "name": "book_room",
         "input": {"room": "Focus", "tier": "pro", "hours": 3, "member": "Ana"}}]},
    {"stop_reason": "end_turn", "content": [
        {"type": "text", "text": "Focus pro 3h cuesta 6000 centavos. Reservé la sala para Ana (confirmación #1)."}]},
]

final_a, history_a = run_agent_parallel(TASK, model_script_4steps, TOOLS)
calls_a = count_model_calls(history_a)
print("Sistema A -- llamadas:", calls_a)

final_b, history_b = run_agent_parallel(TASK, model_script_4steps, TOOLS)
calls_b = 1 + count_model_calls(history_b) + 1
extra = calls_b - calls_a
print("Sistema B -- llamadas:", calls_b, "| llamadas de más:", extra)

Salida esperada:

Sistema A -- llamadas: 4
Sistema B -- llamadas: 6 | llamadas de más: 2

Explicación: el Sistema A pasó de 3 a 4 llamadas (una más por el paso nuevo de list_rooms), y el Sistema B pasó de 5 a 6 —también una más—, porque el paso nuevo se suma dentro de booking_agent, no en el supervisor. La diferencia entre los dos sistemas se mantiene en 2 llamadas de más, exactamente como predijo la fórmula supervised_cost de la lección 03: el costo fijo del supervisor (1 ruteo + 1 síntesis) no depende de cuántos pasos internos tenga el especialista — solo depende de cuántos especialistas se consultan (n=1 en todo este módulo).

Ejercicio 3: ¿Qué pasaría con dos especialistas reales? (Difícil)

Sin ejecutar código todavía, usa la fórmula supervised_cost(n_specialists=2, calls_per_specialist=3) de la lección 03 para predecir cuántas llamadas y hops tendría un sistema donde el supervisor SÍ necesita consultar a dos especialistas distintos (por ejemplo, booking_agent y policy_agent) para una tarea compuesta. Después, diseña —en prosa, sin ejecutar el runner completo todavía, eso es la lección 06— qué tendría que tener esa tarea compuesta para que la diferencia de costo frente a un solo agente con las 5 tools juntas (4 de booking + search_docs) valiera la pena.

Ver solución

La predicción con la fórmula:

def supervised_cost(n_specialists, calls_per_specialist=3):
    route_calls = 1
    compose_calls = 1
    return {
        "model_calls": route_calls + calls_per_specialist * n_specialists + compose_calls,
        "hops": 2 * n_specialists,
    }

print(supervised_cost(2))
{'model_calls': 8, 'hops': 4}

Para que valga la pena: la tarea tendría que necesitar genuinamente las dos expertises al mismo tiempo —no solo una de ellas, como el caso de esta lección, donde booking_agent ya podía resolver todo solo—. Un ejemplo real: "cotiza Focus pro 3h y dime la política de cancelación" — ninguna de las dos preguntas depende de la otra, y ninguna de las dos vive dentro del dominio de la otra (números de reserva vs. texto de políticas). En ese caso, el costo de 8 llamadas y 4 hops no se compara contra las 3 llamadas de un agente único que YA podía resolver todo —porque un agente único con 5 tools juntas (booking + search_docs) sí podría, en teoría, resolver la tarea completa también—, sino que se compara contra los beneficios que la lección 06 mide aparte: la posibilidad de correr las dos sub-preguntas en paralelo (no secuencial, porque son independientes) y el aislamiento del contexto de cada especialista. La lección 06 hace esa cuenta completa, no solo el costo.


Resumen y siguiente paso

  • Ejecutamos, de punta a punta, la MISMA tarea de Reservo —"cotiza Focus pro 3h y resérvala para Ana"— con un agente único y con un sistema de dos agentes (supervisor + booking_agent).
  • Los números reales: Sistema A usó 3 llamadas al modelo, 2 tool calls, 0 hops; Sistema B usó 5 llamadas al modelo (67% más), las mismas 2 tool calls, 2 hops — para producir exactamente la misma respuesta final, palabra por palabra.
  • El booking_agent del Sistema B no hizo nada distinto ni mejor que el agente único: corrió el mismo runner (run_agent_parallel, sin cambios de agent-fundamentals M4/M5) sobre el mismo guion, y produjo un historial idéntico. El costo extra vino enteramente de la coordinación alrededor —el ruteo del supervisor y la síntesis final—, no del trabajo en sí.
  • Esta es la evidencia central de "cuándo NO orquestar": una tarea que ya vive completa dentro de la expertise de un solo especialista no se beneficia de sumar un coordinador encima.

Siguiente lección: 06 — Cuándo multi-agente SÍ ayuda. Con el costo ya medido en números reales, vemos el otro lado: una tarea que sí necesita dos expertises genuinamente distintas, y por qué ahí el mismo costo de coordinación sí se justifica.


Recursos adicionales

  1. Anthropic — Multi-agent research system — El reporte de Anthropic sobre el costo real (más tokens, más llamadas) de un sistema multi-agente frente a uno de un solo agente, la misma clase de medición que esta lección ejecuta a mano.
  2. Anthropic — Building effective agents — El principio rector detrás de esta lección: empezar simple, y sumar agentes solo cuando la medición lo justifica.
  3. Anthropic — Messages API reference — La forma exacta de tool_use/tool_result/stop_reason que el runner de esta lección respeta, sin cambios de agent-fundamentals.
  4. Python — dataclasses — El módulo usado para AgentMessage, la forma mínima del paquete que un agente le pasa a otro en esta guía.
  5. Python 3.14 — What's New — La versión con la que se ejecutó cada línea de esta comparación.