Lección 2 de 6Gratis18 min

Taller 2: catálogo, herramienta y adaptación local

Lee el contrato de solo lectura y crea una copia local del catálogo sin tocar el fixture del kit.

Última actualización: 14 de septiembre de 2026

Tienda Luna repite las mismas preguntas: precio, disponibilidad y qué productos hay. El núcleo resuelve esas preguntas con una gramática pequeña, no con autonomía. Aquí leerás el código existente y harás una adaptación local comprobable.

Objetivo: leer el código existente y hacer una adaptación local comprobable de tu copia del catálogo, sin tocar el fixture del kit.

Antes de tocar el JSON

Necesitas el directorio extraído en el taller 1, Python 3.10+ y nociones de funciones, diccionarios/listas, JSON y terminal. catalogo.json es un diccionario con version, currency e items; cada artículo es otro diccionario. Un SKU identifica un artículo, price_cents es dinero entero en centavos y stock es una cantidad almacenada.

Una herramienta es una función con un contrato estrecho. respond(text, catalog=...) recibe texto y devuelve un diccionario con text, status, items y trace. La herramienta consulta; no reserva, compra ni cambia inventario. La trace solo describe operaciones realizadas, nunca una acción imaginada.

Cinco consultas y estados

python3 catalogo_core.py "ayuda"
python3 catalogo_core.py "listar"
python3 catalogo_core.py "buscar periféricos"
python3 catalogo_core.py "ficha A-100"
python3 catalogo_core.py "stock C-300"

status puede ser ok, out_of_stock, not_found, unsupported o invalid. La comparación útil es:

  • stock C-300out_of_stock, porque el SKU existe y su stock es 0.
  • stock X-999not_found, porque no hay registro.
  • reserva A-100unsupported, porque la demo no tiene escrituras.

Ejercicio: tu copia, no el fixture

Ahora crea mi_catalogo.json mediante la biblioteca estándar. Cambia el nombre y stock de un artículo y agrega un SKU ficticio. Después carga esa copia y pásala explícitamente a respond. El laboratorio no muta el catalogo.json incluido ni reconfigura los valores predeterminados de Telegram/Kapso. Guarda antes cualquier versión propia de mi_catalogo.json y mi_bot.py: este bloque escribe esos dos archivos. assert es una comprobación: si su condición es falsa, Python detiene la ejecución con AssertionError.

# Estos dos archivos quedan visibles en tu carpeta de trabajo.
cp catalogo.json mi_catalogo.json
python3 - <<'PY'
import json
path = "mi_catalogo.json"
data = json.loads(open(path, encoding="utf-8").read())
data["items"][0]["name"] = "Teclado Luna edición local"
data["items"][0]["stock"] = 7
data["items"].append({
    "sku": "L-900", "name": "Taza Luna", "category": "regalos",
    "description": "Taza ficticia para el laboratorio", "price_cents": 1200, "stock": 4,
})
with open(path, "w", encoding="utf-8") as handle:
    json.dump(data, handle, ensure_ascii=False, indent=2)
PY
cat > mi_bot.py <<'PY'
from pathlib import Path
from catalogo_core import load_catalog, respond

CATALOG_PATH = Path(__file__).with_name("mi_catalogo.json")

def consultar_mi_catalogo(query):
    return respond(query, catalog=load_catalog(CATALOG_PATH))

if __name__ == "__main__":
    result = consultar_mi_catalogo("stock L-900")
    assert result["status"] == "ok" and result["items"] == ["L-900"]
    assert "4 unidades" in result["text"]
    print(result["text"])
PY
python3 mi_bot.py
python3 - <<'PY'
from catalogo_core import load_catalog
original = load_catalog()
assert not any(item["sku"] == "L-900" for item in original["items"])
assert original["items"][0]["stock"] == 23
print("Laboratorio local OK: mi_bot.py, mi_catalogo.json y fixture intacto")
PY
printf 'Archivos guardados en: %s\n' "$PWD"

Este patrón es una función/adaptador local: carga datos propios y los entrega al núcleo. No sincroniza una tienda, no consulta inventario real y no configura canales. Los archivos mi_catalogo.json y mi_bot.py quedan visibles en tu carpeta de trabajo; conserva esa copia si quieres seguir adaptándola.

Tu cambio, antes y después

Cambia stock de L-900 a 0 en mi_catalogo.json y ejecuta python3 mi_bot.py sin cambiar sus expectativas: debe fallar con AssertionError. La respuesta ahora tiene estado out_of_stock. Para volver a pasar, corrige ambas comprobaciones: estado out_of_stock y texto que contiene agotado, no 4 unidades.

Este bloque reproduce el fallo y la corrección partiendo de la versión inicial de mi_bot.py. Si ya la corregiste a mano, no esperes otro fallo inicial. No necesita red:

python3 - <<'PY'
import json
import subprocess
import sys
from pathlib import Path
from mi_bot import consultar_mi_catalogo

path = Path("mi_catalogo.json")
data = json.loads(path.read_text(encoding="utf-8"))
next(item for item in data["items"] if item["sku"] == "L-900")["stock"] = 0
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
failed = subprocess.run([sys.executable, "mi_bot.py"], capture_output=True, text=True)
assert failed.returncode != 0 and "AssertionError" in failed.stderr
assert consultar_mi_catalogo("stock L-900")["status"] == "out_of_stock"
script = Path("mi_bot.py")
source = script.read_text(encoding="utf-8")
source = source.replace('result["status"] == "ok"', 'result["status"] == "out_of_stock"')
source = source.replace('"4 unidades" in result["text"]', '"agotado" in result["text"]')
script.write_text(source, encoding="utf-8")
subprocess.run([sys.executable, "mi_bot.py"], check=True)
print("Comprobado: expectativa antigua falla; estado y texto corregidos pasan")
PY

Reto sin solución copiada: añade otro SKU ficticio y escribe una comprobación de su precio en ficha. Justifica por qué compruebas ese resultado.

Para usar datos propios como predeterminados de un canal, trabaja en una copia extraída separada, reemplaza allí catalogo.json por tu archivo válido y reinicia el proceso. No es una sincronización con una tienda. El kit limpio conserva sus pruebas ligadas al fixture original.

Hecho guardado frente a inventario fresco

Antes de mostrar «hay 7 unidades», pregunta: ¿cuándo se escribió el JSON y quién lo actualiza? El núcleo puede verificar que stock es un entero no negativo, pero no puede verificar frescura. En tu adaptación escribe un campo updated_at solo como metadato de tu proceso si lo necesitas; no lo presentes como sincronización sin una fuente externa comprobada. No hagas afirmaciones de ahorro, ventas o retorno económico.

Resultado observable

Puedes leer un diccionario JSON, distinguir SKU desconocido de agotado, explicar la traza y ejecutar comprobaciones sobre tu adaptación. El código trabajado sí es local y reproducible; conectar un catálogo real queda fuera y no está probado.

Error habitual y recuperación

Si cambiar el estado esperado no basta, revisa también el texto esperado: «4 unidades» ya no es correcto cuando el artículo está agotado. No borres la comprobación para obtener verde; identifica qué resultado cambió. Si el JSON deja de cargar, revisa comas, tipos y SKU repetidos; usa una copia de respaldo sin sobrescribir el kit limpio.

Fuentes