From 77e6804a4f02e019c455e720661f22ee97cb713b Mon Sep 17 00:00:00 2001 From: Alejandro Lembke Barrientos Date: Thu, 30 Jul 2026 06:35:11 +0000 Subject: [PATCH] Fase 5: gate3 - subir max_tokens a 2048 y guardar texto completo de respuestas Mejoras permanentes al test, no solo para esta corrida: 1. max_tokens: 512 -> 2048 (configurable via GATE3_MAX_TOKENS). Con --reasoning-parser activo, un modelo de razonamiento puede agotar 512 tokens pensando antes de emitir el contenido final -- la respuesta queda cortada a mitad de razonamiento y check_adherencia() no encuentra ninguna substring esperada, un FAIL por presupuesto de tokens agotado, no por adherencia real. El caso que fallaba en las corridas NVFP4 de Fase 5 (aleleba-pr/adherencia, el prompt mas abierto de los tres) es sospechoso de este defecto -- el JSON de resultados no guardaba el texto de la respuesta, asi que no se podia auditar. 2. Guardar content+reasoning completos en cada fila del JSON de resultados (antes solo se guardaba passed/hits/tool_calls). Permite auditar con criterio humano cualquier fallo futuro sin tener que re-correr el test. --- scripts/33_gate3_adherencia.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/scripts/33_gate3_adherencia.py b/scripts/33_gate3_adherencia.py index 14dc129..222b96f 100644 --- a/scripts/33_gate3_adherencia.py +++ b/scripts/33_gate3_adherencia.py @@ -33,6 +33,11 @@ EVAL_URL = os.environ.get("VLLM_EVAL_URL", "http://localhost:8001") EVAL_MODEL = os.environ.get("VLLM_EVAL_MODEL", "qwen3.6-35b-a3b-mcp-bf16") PROD_URL = os.environ.get("VLLM_PROD_URL", "http://localhost:8000") PROD_MODEL = os.environ.get("VLLM_PROD_MODEL", "qwen3.6-35b-a3b") +# 512 dejaba cortar la respuesta a mitad de razonamiento en los prompts mas +# abiertos (modelo de razonamiento con --reasoning-parser activo) antes de +# emitir el contenido final -- un FAIL por presupuesto de tokens agotado, no +# por adherencia real. Ver hallazgo de Fase 5. +GATE3_MAX_TOKENS = int(os.environ.get("GATE3_MAX_TOKENS", "2048")) # Checklists por skill: (skill, tipo, prompt, [substrings esperados en la respuesta, any-of]) CHECKLISTS = [ @@ -103,10 +108,10 @@ def call_model(base_url, model_name, prompt): payload = { "model": model_name, "messages": [{"role": "user", "content": prompt}], - "max_tokens": 512, + "max_tokens": GATE3_MAX_TOKENS, "temperature": 0.0, } - resp = requests.post(f"{base_url}/v1/chat/completions", json=payload, timeout=120) + resp = requests.post(f"{base_url}/v1/chat/completions", json=payload, timeout=240) resp.raise_for_status() data = resp.json() message = data["choices"][0]["message"] @@ -147,9 +152,20 @@ def run_checklist(base_url, model_name, label): print(f" [ERROR] {skill}/{kind}: {e}") continue + # Se guarda siempre el texto completo (content + reasoning) para poder auditar + # con criterio humano los casos que fallan -- antes solo se guardaba passed/hits, + # lo que hacia imposible revisar despues que dijo realmente el modelo. if kind == "adherencia": passed, hits = check_adherencia(response, expected) - rows.append({"skill": skill, "kind": kind, "prompt": prompt, "passed": passed, "hits": hits}) + rows.append({ + "skill": skill, + "kind": kind, + "prompt": prompt, + "passed": passed, + "hits": hits, + "content": response["content"], + "reasoning": response["reasoning"], + }) print(f" {'OK ' if passed else 'FAIL'} {skill:20s} adherencia hits={hits}") else: passed = check_no_activacion(response) @@ -159,6 +175,8 @@ def run_checklist(base_url, model_name, label): "prompt": prompt, "passed": passed, "tool_calls": [tc["function"]["name"] for tc in response["tool_calls"]], + "content": response["content"], + "reasoning": response["reasoning"], }) print(f" {'OK ' if passed else 'FAIL'} {skill:20s} {kind:20s} tool_calls={len(response['tool_calls'])}") return rows