Matemática de calendario
Pregunta lo que sea sobre fechas — "what weekday is Christmas 2027?", "how many
Showcase — Matemática de calendario
Pregunta lo que sea sobre fechas — "what weekday is Christmas 2027?", "how many
days until 2026-12-31?" — y el modelo llama a una tool date_facts en lugar de
adivinar. La aritmética de día de la semana y de conteo de días es una
debilidad conocida del modelo: hacen pattern-match de calendarios en lugar de
contar. El datetime de Python sí cuenta. Todo el trabajo del modelo aquí es
extraer YYYY-MM-DD de tu oración y narrar los hechos exactos que regresan.
Córrelo
bash bootstrap-secrets.sh # reads ../../../../.env, writes secrets/
docker compose up --build # default: PROVIDER=openai
Abre http://localhost:3000.
Para correrlo contra Gemini:
PROVIDER=gemini docker compose up --build
Qué hay aquí
backend/ai_openai.py—date_facts(día de la semana, días desde hoy, semana ISO, año bisiesto) + el schema de la tool y el round tripbackend/ai_gemini.py— misma tool, la formaFunctionDeclarationde Geminibackend/main.py— loader de FastAPI idéntico; lee PROVIDER y despachafrontend/app/page.tsx— textarea + resultadodocker-compose.yml— dos servicios, secrets montados desde./secrets/
Si una pregunta nombra dos fechas, el modelo emite dos function calls en el mismo turno y ambos resultados regresan juntos — el round trip maneja una lista, no una sola llamada.
Detenlo
docker compose down
Ejecútalo en tu máquina
Descarga el proyecto como ZIP y córrelo con Docker. Levanta un backend FastAPI y un frontend Next.js en localhost:3000.
unzip calendar-math.zip
cd calendar-math
bash bootstrap-secrets.sh # one-time: pulls API keys into ./secrets
docker compose up --build # default provider: openai
# or: PROVIDER=gemini docker compose up --build
Escribe algo, elige un proveedor y ejecuta el mismo código de Código contra la API real. Requiere iniciar sesión.
Los mismos módulos que ejecuta el botón Run. El proyecto completo (frontend, Dockerfile, compose) está en el ZIP, pestaña README.
backend/ai_openai.py
"""Week 5 - Showcase 2 (OpenAI): calendar math the model can't fake.
Models are famously unreliable at weekday and date-difference arithmetic —
they pattern-match instead of counting. One date_facts tool built on
Python's datetime fixes the whole category: the model extracts the date,
the standard library does the calendar.
"""
import datetime as dt
import json
from openai import OpenAI
_client = OpenAI()
_MODEL = "gpt-5.4-nano"
def _date_facts(date: str) -> dict:
d = dt.date.fromisoformat(date)
today = dt.date.today()
return {
"date": d.isoformat(),
"weekday": d.strftime("%A"),
"days_from_today": (d - today).days,
"iso_week": d.isocalendar().week,
"day_of_year": d.timetuple().tm_yday,
"is_leap_year": (d.year % 4 == 0 and d.year % 100 != 0) or d.year % 400 == 0,
"today": today.isoformat(),
}
_TOOLS = [{
"type": "function",
"name": "date_facts",
"description": "Exact calendar facts for a date: weekday, days from today "
"(negative if past), ISO week, day of year, leap year. "
"Call it once per date mentioned.",
"parameters": {
"type": "object",
"properties": {
"date": {"type": "string", "description": "The date in YYYY-MM-DD format."},
},
"required": ["date"],
},
}]
def run(question: str) -> str:
response = _client.responses.create(
model=_MODEL,
instructions="Answer calendar questions. Always use the date_facts tool "
"for weekday and day-count math; never count days yourself.",
input=question.strip(),
tools=_TOOLS,
)
calls = [item for item in response.output if item.type == "function_call"]
if not calls:
return response.output_text
outputs = []
for call in calls:
args = json.loads(call.arguments)
try:
result = _date_facts(**args)
except ValueError as exc:
result = {"error": str(exc)}
outputs.append({
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result),
})
final = _client.responses.create(
model=_MODEL,
previous_response_id=response.id,
input=outputs,
tools=_TOOLS,
)
return final.output_text
backend/ai_gemini.py
"""Week 5 - Showcase 2 (Gemini): calendar math the model can't fake."""
import datetime as dt
import os
from google import genai
from google.genai import types
_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
_MODEL = "gemini-3.1-flash-lite"
def _date_facts(date: str) -> dict:
d = dt.date.fromisoformat(date)
today = dt.date.today()
return {
"date": d.isoformat(),
"weekday": d.strftime("%A"),
"days_from_today": (d - today).days,
"iso_week": d.isocalendar().week,
"day_of_year": d.timetuple().tm_yday,
"is_leap_year": (d.year % 4 == 0 and d.year % 100 != 0) or d.year % 400 == 0,
"today": today.isoformat(),
}
_CONFIG = types.GenerateContentConfig(
system_instruction="Answer calendar questions. Always use the date_facts tool "
"for weekday and day-count math; never count days yourself.",
tools=[types.Tool(function_declarations=[types.FunctionDeclaration(
name="date_facts",
description="Exact calendar facts for a date: weekday, days from today "
"(negative if past), ISO week, day of year, leap year. "
"Call it once per date mentioned.",
parameters=types.Schema(
type=types.Type.OBJECT,
properties={
"date": types.Schema(type=types.Type.STRING,
description="The date in YYYY-MM-DD format."),
},
required=["date"],
),
)])],
)
def run(question: str) -> str:
contents = [types.Content(role="user", parts=[types.Part(text=question.strip())])]
response = _client.models.generate_content(
model=_MODEL, contents=contents, config=_CONFIG,
)
if not response.function_calls:
return response.text or ""
contents.append(response.candidates[0].content)
parts = []
for fc in response.function_calls:
try:
result = _date_facts(**fc.args)
except ValueError as exc:
result = {"error": str(exc)}
parts.append(types.Part.from_function_response(name=fc.name, response=result))
contents.append(types.Content(role="user", parts=parts))
final = _client.models.generate_content(
model=_MODEL, contents=contents, config=_CONFIG,
)
return final.text or ""
Archivos del proyecto
.gitignoreREADME.es.mdREADME.mdbackend/Dockerfilebackend/ai_gemini.pybackend/ai_openai.pybackend/main.pybackend/requirements.txtbootstrap-secrets.shdocker-compose.ymlfrontend/Dockerfilefrontend/app/layout.tsxfrontend/app/page.tsxfrontend/next.config.tsfrontend/package.jsonfrontend/tsconfig.json