ToWow / Notas de ingeniería

Confirmar el éxito después de verificar la identidad

Un elemento identificó mal su tarea cuando el lote ya constaba como exitoso. La corrección cambió el orden de confirmación.

En una evaluación por lotes, una respuesta cambió case_id de gold-pair-078 a gold-pair-079. Su identificador de tarea seguía apuntando al caso original. Estos son los campos reconstruidos a partir del registro del fallo:

task_id:      gold-pair-078::gold-pair-078-candidate-127
candidate_id: gold-pair-078-candidate-127
case_id:      gold-pair-079

La respuesta pertenecía a un lote de 20 candidatos. El JSON se podía analizar y superó la validación estructural. La comprobación por elemento encontró después el error, pero el recibo del lote ya decía status: ok. Registro original del fallo y ubicación del código

El resultado no llegó a formar una evaluación formal completa, pero dejó un riesgo de recuperación: el «éxito» del archivo antiguo contradecía el registro de fallo posterior. Un recuperador que importara solo según los recibos exitosos aceptaría de nuevo el lote.

La corrección era clara. El estado de éxito debe escribirse después de verificar la identidad de todos los elementos. La tarea a la que pertenece cada solicitud es un hecho que el llamador ya conoce. El programa debe contrastarlo, en lugar de confiar al modelo su garantía.

Validar todo el lote antes de escribir el éxito

Un esquema limitado a tipos y estructura admite dos cadenas diferentes como válidas. Un esquema dinámico puede restringir además los valores de identidad; este sistema también compara la respuesta con la solicitud efectiva para aceptar el lote.

La implementación actual comprueba, en orden, el case_id superior, la cobertura de tareas, la estructura de los elementos y su identidad. La cobertura compara tanto cantidad como conjunto para detectar ausencias, duplicados y tareas adicionales. Después se contrastan task_id, case_id, candidate_id y el tipo de candidato.

Este es un esquema didáctico del orden central. La implementación de producción añade esquemas por fase, recibos y gestión de reintentos.

expected = {task["task_id"]: task for task in requests}
if len(expected) != len(requests):
    raise InvalidBatch("请求自身包含重复任务")

ids = [item["task_id"] for item in response["items"]]
if len(ids) != len(expected) or set(ids) != set(expected):
    raise InvalidBatch("任务缺失、重复或新增")

for item in response["items"]:
    task = expected[item["task_id"]]
    for key in ("task_id", "case_id", "candidate_id"):
        if item[key] != task[key]:
            raise InvalidBatch(f"身份字段不一致:{key}")

commit_success(response)

Relacionar por identificador permite variar el orden de respuesta. Contrastar los campos redundantes evita que distintos consumidores asignen el mismo objeto a tareas diferentes: uno encuentra 078 por task_id, mientras otro lo agrupa en 079 por case_id.

El programa no corrigió simplemente 079 a 078. El desajuste no revela si el modelo copió mal el número o entendió otra pregunta. Rechazar el lote y conservar la respuesta original mantiene abierta la investigación de ambas posibilidades.

Aceptar el lote como unidad tiene un coste: tampoco se reutilizan los otros 19 elementos aparentemente normales. A cambio, establece una frontera de confirmación sencilla. La aceptación parcial necesitaría definir validación independiente, reposición de ausencias y manejo de duplicados para cada elemento: otro diseño de recuperación.

Volver a validar los antiguos archivos exitosos

Tras corregir las nuevas llamadas, seguían existiendo los recibos históricos. Por ello, la importación vuelve a comprobar cada elemento. Si encuentra el error de identidad asociado al fallo antiguo, conserva el lote completo para diagnóstico y no incorpora ningún elemento al punto de control reutilizable.

El coste de las llamadas ya realizadas sigue contabilizado. Reiniciar puede cambiar el directorio de ejecución, pero no borrar los intentos anteriores. De lo contrario, el «coste por resultado utilizable» solo incluiría el último tramo exitoso.

El tamaño de lote debe elegirse con ese coste total. Los lotes grandes amortizan las entradas comunes, pero reintentar todo amplifica un error individual. Posteriormente, el proyecto habilitó división determinista solo para lotes designados que ya habían agotado sus reintentos, manteniendo la posibilidad de terminar la ejecución por un fallo individual. Dividir es una recuperación acotada, no una razón para reintentar indefinidamente.

La prueba de regresión altera deliberadamente la identidad de un elemento en la primera respuesta y comprueba tres cosas: se escribe un recibo de fallo, la ruta del punto de control exitoso queda vacía y solo una respuesta correcta posterior escribe el éxito. Prueba precisamente la abertura por la que pasó este fallo.

El contador de lotes completados debe crecer desde este punto: respuesta y solicitud coinciden, terminó la validación del lote y solo entonces se confirma el éxito.

Next step

If your team is putting agents into real work and wants to talk about Flowness or enterprise AI deployment, email us your industry and the problem you want to solve. hi@towow.ai