La parte 4 repasaba dos herramientas que intentaban medir hasta qué punto estaba seguro el juez, una dentro de una sola llamada y otra a lo largo de muchas. La siguiente estrategia deja de lado la certeza. Se plantea, de entrada, a qué pregunta debería responder el juez y le dice expresamente qué tiene que dejar fuera.
Estoy construyendo AgentAnvil, un framework de pruebas guiado por contratos para agentes basados en LLM, y leer otros evaluadores me sirve para averiguar qué deberían restringir esos contratos. El anclaje por rúbrica es la estrategia que más fácil me ha resultado reconocer y la que más me ha costado precisar. La mayoría de las herramientas que usan un LLM como juez enuncian el criterio que hay que puntuar y ahí se quedan. Hay una, TruLens, que añade una segunda cláusula con lo que queda fuera del alcance del juicio. Lo normal es que los prompts no la lleven, pero cuando aparece no está de adorno.
Aquí la leo en el código fuente de TruLens 2.8.0, que es la versión a la que apuntan las referencias a ficheros y líneas.
La intuición
Una rúbrica es un contrato con el juez («puntúa esto en esta escala») y lo que el juez lee es su redacción. Lo que el contrato no dice, el juez lo rellena con lo que ha visto: su distribución de entrenamiento, su idea preconcebida de cómo es una buena respuesta, el solapamiento implícito entre esta métrica y todas las rúbricas que haya puntuado antes. Esos supuestos actúan en silencio y se notan en la puntuación, pero en el prompt no aparecen.
La rúbrica negativa le da la vuelta a lo habitual. Escribes el criterio y enumeras lo que el juez debe pasar por alto, sin confiar en que sepa interpretarlo bien por su cuenta. Si lo que puntúas es la calidad del plan, no menciones si el plan salió bien. Si puntúas las llamadas a herramientas, la elección de la herramienta es asunto de una métrica hermana, así que no la toques. La eficiencia de la ejecución se fija en el orden de los pasos y no le incumbe que el agente llegara o no al objetivo. Así se acota a propósito lo que el juez puede sopesar.
Suena defensivo, y lo es. La cláusula negativa está ahí porque quien escribió la rúbrica ha visto al juez contar dos veces lo mismo, puntuar lo que no tocaba o contaminar una dimensión con otra. Es la vía por la que esa experiencia previa (lo que el modelo hace mal una y otra vez) entra en el prompt y se queda fijada en él.
Las herramientas de la parte 4 medían a posteriori la incertidumbre del juez, mientras que esta estrategia toca una tecla muy distinta. El juez sigue igual de seguro, pero apunta a un blanco más estrecho.
TruLens, paso a paso
Casi todas las funciones de feedback de TruLens que dependen de un juez LLM, incluidas las siete métricas de agente, acaban en uno de los dos métodos de LLMProvider, generate_score o generate_score_and_reasons (feedback/llm_provider.py:180,283). Los dos montan un par de mensajes [system, user], piden una respuesta estructurada y delegan en _create_chat_completion. El proveedor de OpenAI hace pasar la llamada por una cadena que va tanteando qué admite el modelo. Prueba primero con las salidas estructuradas de la Responses API y, si no funcionan, recurre a Chat Completions.
En los kwargs de la llamada hay un detalle que pasa desapercibido. Con los modelos sin razonamiento la temperatura es 0,0 por defecto. Además, el módulo del proveedor de OpenAI fija en el código, al principio, un SEED: int = 123 y lo añade a la llamada salvo que quien la hace pase su propia semilla (providers/openai/provider.py:23,439-443). Pero la Responses API no acepta seed, así que TruLens la quita y reintenta (provider.py:270-285). La semilla solo llega al modelo cuando se recurre a Chat Completions, donde OpenAI únicamente promete respetarla en la medida de lo posible. Por tanto, la repetibilidad descansa sobre todo en la temperatura 0 y no en la semilla. Para un harness de evaluación es un valor por defecto defendible, aunque no da el determinismo que sugiere esa constante fija.
El prompt de sistema sale de plantillas, con un fichero por dominio: templates/rag.py para las métricas de RAG, templates/quality.py para la calidad general, templates/safety.py para la seguridad y templates/agent.py para las métricas propias de agentes.
Si buscas con grep «do not / don’t / only / regardless / out-of-scope» en todos los ficheros de plantillas, salen treinta y seis coincidencias. Quince están en templates/agent.py y son las que tienen estructura, con bloques In-scope:/Out-of-scope:, remisiones a métricas hermanas y «ignore execution». Las demás, repartidas por el resto de ficheros, son sobre todo fórmulas hechas («respond only as a number»). Llevan el patrón las métricas de plan, de herramientas y de ejecución, pero casi ninguna de las de groundedness, harmfulness, correctness o sentiment.
El caso más claro es el prompt de sistema de PlanQuality (templates/agent.py:212-248). Su primer párrafo, antes de cualquier criterio de puntuación, ya incluye una frase CRITICAL: según la cual el juez fracasa en su tarea en cuanto alude a si el agente siguió el plan o a cualquier parte de la ejecución. El prompt insiste en la exclusión al menos tres veces más («Do NOT infer or fill gaps using execution steps.», «You are judging the strategy, not the outcome.», «Do NOT score on the execution quality.»). Es, además, el más largo de los siete prompts de sistema de agente, casi el doble que el siguiente.
El ejemplo más estructurado está en el prompt de sistema de ToolCalling (templates/agent.py:361-363), donde TruLens abre una sección con nombre propio:
Important scope boundaries:- In-scope: argument/schema correctness, semantic fit of query, preconditions/postconditions, grounded interpretation of outputs, explicit handling of tool-returned errors.- Out-of-scope: tool selection (Tool Selection), workflow efficiency (Execution Efficiency), external service/tool reliability (Tool Quality).Fíjate en las referencias entre paréntesis. Las cláusulas de exclusión nombran las métricas hermanas a las que el juez debe remitirse. ToolSelection (agent.py:305-308) y ToolQuality (agent.py:416-418) llevan también un bloque Important scope boundaries:, así que las tres métricas de herramientas comparten ese encabezado, aunque cada una redacta el bloque a su manera. ToolSelection, por ejemplo, usa viñetas «Do NOT penalize …». Solo ella y ToolCalling nombran las métricas hermanas en las que delegan. El terreno que juzga cada una de las siete métricas de agente se ha repartido a mano (ToolCalling, ToolSelection, ToolQuality, ExecutionEfficiency, PlanQuality, PlanAdherence, LogicalConsistency): la mayoría de las rúbricas le dicen al juez qué tiene que dejarles a las otras.
Encima de todo eso se injerta la capa de CoT, igual en las siete métricas de agente. COT_REASONS_TEMPLATE está en templates/base.py:198-206 y es un esqueleto fijo, «Criteria: …, Supporting Evidence: …, Score: …». Cada método *_with_cot_reasons de las métricas de agente sustituye la etiqueta final del prompt de usuario por ese bloque, con user_prompt.replace("<METRIC> SCORE:", COT_REASONS_TEMPLATE). La clave exacta varía según la métrica (TOOL CALLING SCORE:, LOGICAL CONSISTENCY SCORE:, etc.). El prompt de sistema, que es donde están las cláusulas negativas, no se toca. (Las métricas de criterios al estilo de LangChain, en cambio, añaden la misma plantilla al prompt de sistema.)
Después de tanto prompt, la puntuación en sí es poca cosa. El espacio de salida por defecto es una escala Likert de 0 a 3 (templates/base.py:179) y el modelo devuelve un JSON estructurado. Si falla el parseo, la ruta de CoT le pide primero al LLM que reformatee la respuesta como JSON estricto, y solo entonces el código recurre a una expresión regular que captura los tokens numéricos y se queda con los que caen dentro de la escala (generated.py:35,97). Si quedan varios números dentro del rango, elige el mínimo. Según el comentario del código, es para que «1 out of 3» se lea como 1, pero así también «between 1 and 3» se convierte en 1, un comportamiento por defecto que sesga a la baja la puntuación de las respuestas que no se mojan. Y con la escala de 0 a 3 por defecto, «5 out of 10» da error de parseo y no cuenta como un 5. Casi nunca importa, hasta el día en que importa. El número aceptado se normaliza después a un float entre 0 y 1 con (raw - min) / (max - min), y ese float es lo que ve el usuario.
Conviene ver esto en su justa medida. En TruLens la rúbrica negativa aparece solo donde hacía falta; no es un principio de diseño general que recorra todos los prompts. Es un invento de la evaluación de agentes, añadido cuando TruLens 2.x incorporó siete métricas de agente que necesitaban distinguirse entre sí. Si lees las rúbricas más antiguas (RAG, calidad, seguridad), verás que las cláusulas negativas escasean o desaparecen.
Lo que pierdes
Crecen las dos mitades del prompt. Las cláusulas negativas alargan la rúbrica y, con ello, encarecen en tokens cada llamada de puntuación. En una puerta de CI que lanza cientos de llamadas de feedback en cada evaluación, ese coste se nota, aunque pasa lo mismo con cualquier rúbrica estructurada.
agent.py:212, 225) y la capa de CoT es una versión abreviada de la plantilla de base.py. La rúbrica base y el bloque de alcance son inventados (PlanQuality no tiene bloque de alcance) y se dejan en inglés, como los prompts de TruLens. Como la rúbrica base es corta, el crecimiento sale exagerado: en el prompt de sistema real de PlanQuality, de unos 1400 tokens sin contar la traza, las frases de exclusión son más o menos una décima parte.Ver el prompt montado
El coste más grave no se ve. Si una cláusula negativa está mal, le dice al juez que ignore justo la señal que debería sopesar. La creencia previa del autor queda codificada en el prompt y la métrica entera descansa sobre ella. Si esa creencia es errónea, la métrica se equivoca igual en todos los casos de test, sin hacer ruido y sin un parser que lo detecte.
El patrón exige además que haya algo que desambiguar. TruLens lo necesitaba porque sus siete métricas de agente se solapan, mientras que un juez con un único propósito (una rúbrica, una puntuación) no tiene de qué desmarcarse. En ese caso, la cláusula negativa se queda en adorno («no penalices la verbosidad»), luego en letra muerta y en la siguiente revisión se borra. Por otro lado, el anclaje por rúbrica estrecha la pregunta que se le hace al juez, pero no su salida, que sigue siendo un Likert de 0 a 3 normalizado a un float entre 0 y 1. La varianza que haya al producir ese entero no se ha ido a ninguna parte. Has cambiado qué se puntúa, pero la puntuación es igual de estable que antes.
Cuándo encaja este enfoque
Para saber si esta estrategia te sirve basta con una pregunta.
¿Tienes más de una métrica que razonablemente podría sopesar los mismos indicios? Si es así, el anclaje por rúbrica es lo más barato que puedes hacer para que no cuenten dos veces lo mismo. Tanto la calidad del plan como la eficiencia de la ejecución tienen algo que decir sobre si el agente tuvo éxito. Si el prompt de cada una le dice al juez que ignore el terreno de la otra, las dos siguen midiendo cosas distintas. En cambio, si tu juez tiene una sola rúbrica y un solo veredicto, las cláusulas negativas no añaden nada que puedas verificar y el prompt crece en balde.
Su sitio natural es la evaluación de agentes, porque en cuanto puntúas por separado el plan, la ejecución y las herramientas, es inevitable que los criterios se solapen. Con RAG quizá se gane un poco; con calidad y seguridad, normalmente nada.
Lo que el anclaje por rúbrica no te dice es si la rúbrica es la adecuada. Las cláusulas negativas codifican la creencia previa del autor sobre dónde falla el juez, una creencia que no pasa de conjetura mientras nadie la mida. La siguiente parte repasa una herramienta que ataca el mismo problema desde el lado contrario y se pregunta en qué casos puedes prescindir del juez por completo y quedarte con comprobaciones deterministas.
Como regla práctica, el anclaje por rúbrica es lo que escribes cuando has visto al juez hacerse un lío. Si aún no te ha pasado, no lo necesitas. Si ya te ha pasado, sabrás qué tiene que ignorar.