Módulo 8: Capstone The Andes Cargo Genai Extractor
3. Recorrido end-to-end: el camino determinista, barato, ejecutado
Descripción
Esta lección recorre, de punta a punta, la mitad izquierda del diagrama de la lección 2 — el camino determinista— con una sola meta: probar, con código corrido de verdad, que un manifiesto bien formado nunca dispara ManifestParseFailed. No es una afirmación teórica repetida por tercera vez; es parse_manifest(), exactamente el mismo código de process-shipment-manifest que el M1.3 ya presentó, corrido aquí contra un lote nuevo de manifiestos bien formados, con el mismo criterio de escalamiento que escalation_rate.py (M7.4) ya codificó. El resultado, literal: cero de diez manifiestos escalan. extract-shipment-manifest-fields nunca se invoca. Bedrock nunca se toca.
Conexión con el módulo
Esta lección es la primera mitad del recorrido que la lección 2 prometió: el camino determinista, ejecutado por completo, sin ninguna excepción representativa —a diferencia de la lección 4, que sí encuentra un límite real a mitad de camino—. Es también la prueba viva de la tesis central de ADR-001 (M1.8): el LLM es un camino de escalamiento, no el default.
Analogía: el cajero automático que nunca necesitó llamar a un humano
Volviendo a la analogía de la lección 2: un cajero automático que procesa cien retiros de efectivo seguidos, todos con una tarjeta válida y un saldo suficiente, nunca activa el timbre que llama al personal del banco — ni una sola vez, porque ninguno de esos cien casos necesitó ayuda humana. Esta lección es exactamente esa corrida de cien retiros: un lote de manifiestos, todos con la forma que Andes Cargo siempre esperó, procesados por el cajero automático (parse_manifest()), sin que el mostrador humano (extract-shipment-manifest-fields) reciba ni una sola llamada.
Paso 1 — El lote: diez manifiestos bien formados, nunca generados al azar
Reusa, sin ningún cambio, parse_manifest() (M1.3) y SHIPMENT_FIELDS_SCHEMA/would_escalate() (M4.6, M7.4). El lote de esta lección cicla entre los tres envíos ya conocidos (4471, 4472, 4473) y agrega un cuarto (4477), nuevo en esta lección, para confirmar que el resultado no depende de reusar siempre los mismos tres textos:
#!/usr/bin/env python3
"""deterministic_path_walkthrough.py -- Module 8, lesson 3 of
genai-on-aws-production-guide. Runs parse_manifest() (Module 1, lesson 3)
and would_escalate() (Module 7, lesson 4) against a batch of TEN
well-formed manifests only -- never a mixed batch like escalation_rate.py's
50 events. The claim this script proves: a well-formed manifest never
escalates, period.
Never uses random or datetime.now(). Same batch, same order, every run.
"""
from __future__ import annotations
# --- parse_manifest(): Module 1, lesson 3, unmodified ----------------------
def parse_manifest(text: str) -> dict:
fields = {}
for line in text.strip().splitlines():
if "=" in line:
key, _, value = line.partition("=")
fields[key.strip()] = value.strip()
return fields
# --- SHIPMENT_FIELDS_SCHEMA / would_escalate(): Module 4/7, unmodified -----
SHIPMENT_FIELDS_SCHEMA = (
"shipmentId",
"originCountry",
"destinationCountry",
"carrier",
"weightKg",
)
def would_escalate(parsed: dict) -> bool:
return bool(set(SHIPMENT_FIELDS_SCHEMA) - set(parsed.keys()))
# --- Cuatro envios conocidos, ciclados diez veces ---------------------------
SHIPMENTS = {
"4471": {"originCountry": "Peru", "destinationCountry": "Chile", "carrier": "AndesExpress", "weightKg": "120"},
"4472": {"originCountry": "Colombia", "destinationCountry": "Ecuador", "carrier": "AndesExpress", "weightKg": "85"},
"4473": {"originCountry": "Chile", "destinationCountry": "Peru", "carrier": "RutaSur", "weightKg": "200"},
"4477": {"originCountry": "Bolivia", "destinationCountry": "Peru", "carrier": "RutaSur", "weightKg": "60"},
}
CYCLE = ["4471", "4472", "4473", "4477"]
TOTAL_MANIFESTS = 10
def well_formed_manifest(shipment_id: str) -> str:
fields = {"shipmentId": shipment_id, **SHIPMENTS[shipment_id]}
return "\n".join(f"{k}={v}" for k, v in fields.items()) + "\n"
def main() -> int:
escalated = 0
for i in range(TOTAL_MANIFESTS):
shipment_id = CYCLE[i % len(CYCLE)]
text = well_formed_manifest(shipment_id)
parsed = parse_manifest(text)
escalates = would_escalate(parsed)
status = "ESCALATES" if escalates else "write_shipment_record()"
print(f"[{i + 1:02d}] shipmentId={shipment_id:<6s} fields={len(parsed)}/5 -> {status}")
if escalates:
escalated += 1
total = TOTAL_MANIFESTS
print()
print(f"Total well-formed manifests {total}")
print(f"Escalated (ManifestParseFailed) {escalated}")
print(f"Escalation rate {round((escalated / total) * 100, 1)}%")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Fíjate en SHIPMENTS["4477"]: un cuarto envío, nunca usado en ninguna lección anterior de esta guía — la prueba de que el resultado de esta lección no depende de reusar, una y otra vez, los mismos tres textos que 4471/4472/4473 ya hicieron familiares desde el M1.3.
Paso 2 — Corriendo el recorrido completo, de verdad
python3 deterministic_path_walkthrough.py
Qué esperar (literal — corrido de verdad, mismo entorno de esta guía; parse_manifest() y would_escalate() sin ningún cambio respecto a M1.3/M7.4):
[01] shipmentId=4471 fields=5/5 -> write_shipment_record()
[02] shipmentId=4472 fields=5/5 -> write_shipment_record()
[03] shipmentId=4473 fields=5/5 -> write_shipment_record()
[04] shipmentId=4477 fields=5/5 -> write_shipment_record()
[05] shipmentId=4471 fields=5/5 -> write_shipment_record()
[06] shipmentId=4472 fields=5/5 -> write_shipment_record()
[07] shipmentId=4473 fields=5/5 -> write_shipment_record()
[08] shipmentId=4477 fields=5/5 -> write_shipment_record()
[09] shipmentId=4471 fields=5/5 -> write_shipment_record()
[10] shipmentId=4472 fields=5/5 -> write_shipment_record()
Total well-formed manifests 10
Escalated (ManifestParseFailed) 0
Escalation rate 0.0%
Diez de diez, cero escalamientos. Cada línea muestra fields=5/5 — los cinco campos de SHIPMENT_FIELDS_SCHEMA presentes, sin excepción — y la columna final confirma, para cada uno, que el siguiente paso sería write_shipment_record(), heredado sin cambios de aws-core-services-guide, Módulo 7: nunca ManifestParseFailed, nunca extract-shipment-manifest-fields, nunca Bedrock.
Paso 3 — Contrastando con el lote mixto del M7.4, en el mismo lugar
Esta comparación existe para que el 0,0% de esta lección no se lea como una coincidencia sino como la consecuencia directa de un lote deliberadamente distinto:
M7.4 -- LOTE MIXTO (50 eventos) M8.3 -- ESTE LOTE (10 eventos)
44 bien formados + 6 que escalan 10 bien formados, 0 que escalan
(a proposito, para tener algo (a proposito, para probar la
que el SLI pueda medir) otra mitad de la tesis de ADR-001)
Resultado: 12,0% de escalamiento Resultado: 0,0% de escalamiento
Ambos lotes son CONSTRUIDOS A MANO, deterministas, nunca "random" ni
una muestra de trafico real -- la misma honestidad que el M7.4, Paso 5
ya declaro para su propio numero.
Ningún número de estos dos contradice al otro: el M7.4 midió qué proporción de un lote diseñado para incluir fallos escala; esta lección midió qué proporción de un lote diseñado para no tener ninguno escala. Los dos resultados, juntos, confirman el mismo mecanismo desde dos ángulos: would_escalate() responde False para todo manifiesto bien formado, sin excepción, y True únicamente cuando falta al menos un campo — nunca al azar, nunca por el volumen del lote.
Paso 4 — Confirmando el resultado con pytest, un caso nuevo
"""test_deterministic_path_walkthrough.py -- Module 8, lesson 3. Confirms,
as an executable assertion, the exact claim this lesson makes in prose:
an all-well-formed batch never escalates. No random, no datetime.now()."""
from deterministic_path_walkthrough import CYCLE, TOTAL_MANIFESTS, well_formed_manifest, parse_manifest, would_escalate
def test_all_ten_manifests_have_five_fields():
for i in range(TOTAL_MANIFESTS):
shipment_id = CYCLE[i % len(CYCLE)]
parsed = parse_manifest(well_formed_manifest(shipment_id))
assert len(parsed) == 5
def test_zero_of_ten_escalate():
escalated = sum(
would_escalate(parse_manifest(well_formed_manifest(CYCLE[i % len(CYCLE)])))
for i in range(TOTAL_MANIFESTS)
)
assert escalated == 0
def test_new_shipment_4477_is_well_formed():
parsed = parse_manifest(well_formed_manifest("4477"))
assert would_escalate(parsed) is False
assert parsed["originCountry"] == "Bolivia"
pytest test_deterministic_path_walkthrough.py -v -p no:randomly
Qué esperar (literal — corrido de verdad):
============================= test session starts ==============================
platform darwin -- Python 3.14.0, pytest-9.1.1, pluggy-1.6.0
collected 3 items
test_deterministic_path_walkthrough.py::test_all_ten_manifests_have_five_fields PASSED [ 33%]
test_deterministic_path_walkthrough.py::test_zero_of_ten_escalate PASSED [ 66%]
test_deterministic_path_walkthrough.py::test_new_shipment_4477_is_well_formed PASSED [100%]
============================== 3 passed in 0.01s ===============================
Paso 5 — Lo único que este recorrido no ejecuta, y por qué no es un límite nuevo
write_shipment_record() —el paso que, en el sistema real, escribiría cada uno de estos diez registros en Shipments— no corre contra una tabla DynamoDB real en esta lección, por la misma razón exacta que el M3.4 ya documentó para BedrockManifestExtractorRole: este entorno de escritura específico no tiene un LOCALSTACK_AUTH_TOKEN exportado, así que ningún servicio de LocalStack arranca aquí — ni siquiera DynamoDB, que sí está incluido en el plan Hobby gratuito. No es un límite nuevo de esta lección; es el mismo límite del entorno, ya declarado, aplicado ahora al último paso del camino determinista. En tu propia máquina, con un token de Hobby real exportado, los diez write_shipment_record() de esta lección se ejecutarían de verdad, escribiendo diez ítems reales en una tabla Shipments emulada — sin que parse_manifest() ni would_escalate(), el corazón real de esta lección, cambien una sola línea.
Errores comunes
Concluir que el 0,0% de esta lección "prueba" que Andes Cargo nunca necesitará el camino de escalamiento (de generalizar de un lote construido a propósito a todo el tráfico real). Qué pasa: alguien, viendo cero escalamientos en este lote, concluye que extract-shipment-manifest-fields es innecesario. Cómo detectarlo: si tu conclusión de esta lección es "entonces nunca hace falta IA para esto". Cómo corregirlo: este lote se construyó, a propósito, con diez manifiestos bien formados — la misma honestidad que el M7.4, Paso 5 ya aplicó al 12,0% de su propio lote mixto. El M1.3 ya documentó, con un correo real, que sí existen manifiestos de texto libre que parse_manifest() no puede leer; esta lección no niega eso, prueba la mitad complementaria: cuando el manifiesto SÍ tiene la forma correcta, el sistema nunca escala innecesariamente.
Pensar que el 4477 de esta lección es el mismo tipo de "caso nuevo" que 4475/4476 del M7.6 (de confundir dos lotes con propósitos distintos). Qué pasa: alguien busca 4477 en evals/fixtures/sample_manifests.json (M7.6) y no lo encuentra, y se pregunta si esta lección cometió un error de continuidad. Cómo detectarlo: si tu expectativa es que todo envío nuevo de esta guía aparezca en el archivo de fixtures del M7.6. Cómo corregirlo: 4477 es un envío nuevo, exclusivo de esta lección, elegido únicamente para demostrar que el resultado de 0,0% no depende de reusar los mismos tres textos de siempre — nunca formó parte del lote de fixtures del arnés de smoke test, que tiene su propio propósito distinto (probar validate_shipment_fields(), no would_escalate()).
Asumir que write_shipment_record() no correr en esta lección invalida el resto del recorrido (de tratar un paso representativo como si contaminara los pasos reales). Qué pasa: alguien, al leer el Paso 5, concluye que "entonces esta lección tampoco es 100% ejecutada". Cómo detectarlo: si tu resumen de esta lección incluye la palabra "representativo" aplicada a parse_manifest() o would_escalate(). Cómo corregirlo: la afirmación central de esta lección —un manifiesto bien formado nunca dispara ManifestParseFailed— depende exclusivamente de parse_manifest() y would_escalate(), ambos 100 % reales, corridos y verificados con pytest. Que el paso final de escritura en DynamoDB dependa de un token de LocalStack que este entorno específico no tiene es un detalle del entorno de escritura, no una grieta en la afirmación central que esta lección prueba.
Ejercicios
Ejercicio 1 — Modifica, tú mismo, SHIPMENTS["4477"] para que le falte el campo weightKg, y agrégalo al ciclo. Antes de correr el script, predice qué pasaría con la tasa de escalamiento del lote.
Ver solución
La tasa de escalamiento dejaría de ser 0,0% — con 4477 faltándole weightKg, ese manifiesto específico (que aparece dos o tres veces en un ciclo de diez, dependiendo de la posición) haría que would_escalate() devuelva True cada vez que aparece. Con TOTAL_MANIFESTS = 10 y un ciclo de cuatro elementos, 4477 aparecería en las posiciones 4 y 8 (dos veces), así que la nueva tasa sería 2/10 = 20,0%. Este ejercicio confirma, con un cambio propio, que el 0,0% de esta lección no es un valor fijo del código — depende, enteramente, de que cada manifiesto del lote esté genuinamente bien formado, exactamente como el Paso 3 de esta lección ya explicó en contraste con el M7.4.
Ejercicio 2 — Explica, con tus propias palabras, por qué esta lección eligió un lote de DIEZ manifiestos, en vez de, por ejemplo, cien o mil. ¿El tamaño del lote cambia la validez de la conclusión que esta lección demuestra?
Ver solución
No cambia la validez de la conclusión — would_escalate() es una función pura y determinista: dado un parsed con los cinco campos presentes, siempre devuelve False, sin importar cuántas veces se llame ni en qué orden. Un lote de diez es suficiente para demostrar el mecanismo con claridad legible; un lote de mil produciría exactamente el mismo 0,0%, solo que con mil líneas de salida en vez de diez, sin agregar ninguna evidencia adicional sobre la corrección del mecanismo mismo. La elección de diez es una decisión de legibilidad pedagógica, no una limitación de lo que el código puede probar — la misma distinción que el M7.4, Ejercicio 3 ya hizo entre "determinismo del cálculo" y "tamaño de la muestra".
Ejercicio 3 — Predice qué pasaría si, en el Paso 4, alguien agregara un quinto test que reusa well_formed_manifest("4471") y lo compara, campo por campo, contra SHIPMENTS["4471"] del Módulo 7, lección 4 (observability/escalation_rate.py). ¿Deberían coincidir exactamente?
Ver solución
Sí, deberían coincidir exactamente — ambos diccionarios (SHIPMENTS["4471"] en esta lección y en escalation_rate.py) describen el mismo envío 4471 de Andes Cargo (Perú → Chile, AndesExpress, 120kg), el mismo que el M1.3 introdujo originalmente. Un test que comparara ambos y encontrara una diferencia señalaría, correctamente, una inconsistencia real entre dos partes de esta guía que deberían estar contando la misma historia sobre el mismo envío — exactamente el tipo de verificación de continuidad que un ingeniero cuidadoso haría antes de presentar cualquiera de los dos scripts como parte de un mismo sistema coherente.
Resumen y siguiente paso
Esta lección probó, con código ejecutado en su totalidad —parse_manifest() y would_escalate(), sin ningún cambio respecto a M1.3/M7.4—, la mitad izquierda del diagrama de la lección 2: diez manifiestos bien formados, 0,0% de escalamiento, verificado además con tres casos de pytest. Contrastaste este resultado con el 12,0% del lote mixto del M7.4, confirmando que ambos números son consistentes con el mismo mecanismo, no contradictorios. El único paso que este recorrido no ejecuta contra un servicio real —write_shipment_record() en DynamoDB— queda representativo por la misma razón de entorno, ya conocida, que el M3.4 estableció para BedrockManifestExtractorRole.
Antes de avanzar deberías poder: correr deterministic_path_walkthrough.py con tu propio quinto envío y predecir el resultado; explicar por qué 0,0% aquí y 12,0% en el M7.4 no se contradicen; y nombrar la razón exacta —de entorno, no de servicio— por la que la escritura final en Shipments queda fuera del alcance ejecutado de esta lección.
La lección 4 recorre la otra mitad del diagrama: el camino de escalamiento, donde ManifestParseFailed sí se dispara de verdad, y donde el recorrido se vuelve, con toda intención, mixto — real hasta el punto exacto de invocar Bedrock, representativo desde ahí.
Recursos
- Este mismo curso, Módulo 1, lección 3 (
03-andes-cargos-ai-workload-when-the-deterministic-parser-is-not-enough.md) — el origen deparse_manifest(), corrido aquí sin ningún cambio. - Este mismo curso, Módulo 7, lección 4 (
04-hands-on-the-escalation-rate-metric.md) — el origen dewould_escalate()y el lote mixto que el Paso 3 de esta lección contrasta. - Este mismo curso, Módulo 3, lección 4 (
04-hands-on-bedrockmanifestextractorrole-least-privilege.md) — la fuente de la distinción "límite del entorno, no del servicio" que el Paso 5 de esta lección reaplica. - AWS Docs — DynamoDB
PutItem— referencia oficial de la operación quewrite_shipment_record()ejecutaría, heredada deaws-core-services-guide.