Entrevistas con IA
Una API de ejecución de código para entrevistas con IA
Un entrevistador de IA hace una pregunta de programación y el candidato escribe el código. Antes de que la IA pueda comentarlo, algo tiene que ejecutar ese código contra pruebas y decir si funciona, y las pruebas ocultas tienen que quedar lejos de la IA para que no se le escapen en la conversación. Con los problemas almacenados (stored problems), guardas cada pregunta una vez, con sus pruebas ocultas. Tu backend evalúa cada Ejecutar y cada Enviar por el id del problema, y la IA recibe solo un resumen en texto simple y los resultados de las pruebas visibles.
Por el equipo de OnlineCompiler.io. Actualizado el .
Also in EnglishTambém em português
Cómo encajan las piezas
- Guardas cada pregunta de la entrevista una vez como un problema almacenado: un enunciado, pruebas de ejemplo, pruebas ocultas y, si quieres, una solución de referencia y un verificador propio (custom checker).
- Durante la entrevista, el candidato escribe el código en tu editor. Cuando presiona Ejecutar o Enviar, o cuando tu IA decide ejecutar su código, la IA llama a una herramienta en tu backend.
- Tu backend envía el código al problema con tu clave de API. La API lo ejecuta contra las pruebas, cada una en un sandbox nuevo, y devuelve un veredicto por prueba y un resumen.
- Tu backend le pasa a la IA el resumen y los resultados de las pruebas visibles. Las entradas y las salidas esperadas de las pruebas ocultas nunca entran en la conversación.
1. Guarda el problema una vez
POST /api/problems/ crea un problema a partir de un título, un enunciado, los lenguajes que puede usar el
candidato y las pruebas. Cada prueba es una entrada y una salida esperada, y una prueba es oculta salvo que digas "hidden": false, así que las únicas pruebas que el candidato (o tu IA) puede ver son los ejemplos que
elijas. Con una reference_solution, la publicación la revisa primero: POST /api/problems/{problem}/publish/ responde 202, la versión queda en checking mientras la
solución de referencia se ejecuta contra todas las pruebas, y los candidatos solo se evalúan contra la versión cuando esta
pasó todas las pruebas y quedó en published. Si falla, la versión queda en rejected; corrige el
borrador y vuelve a publicar.
# 1. Guarda el problema. Una prueba es oculta, salvo que digas "hidden": false.
curl -X POST https://api.onlinecompiler.io/api/problems/ \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Sum of two numbers",
"slug": "interview-sum",
"statement": "Read two integers on one line and print their sum.",
"languages": ["python-3.14", "nodejs-24", "openjdk-25"],
"scoring": "percent",
"tests": [
{"name": "sample 1", "input": "1 2\n", "expected_output": "3\n", "hidden": false},
{"name": "sample 2", "input": "5 7\n", "expected_output": "12\n", "hidden": false},
{"input": "-4 4\n", "expected_output": "0\n"},
{"input": "2000000000 2000000000\n", "expected_output": "4000000000\n"}
],
"reference_solution": {"compiler": "python-3.14", "code": "a, b = map(int, input().split())\nprint(a + b)\n"}
}'
# 2. Publícalo. Con una solución de referencia, la respuesta es 202 y la versión queda en "checking"
# hasta que esa solución pase todas las pruebas. Lee el problema hasta que quede en "published".
curl -X POST https://api.onlinecompiler.io/api/problems/interview-sum/publish/ -H "Authorization: YOUR_API_KEY"
curl https://api.onlinecompiler.io/api/problems/interview-sum/ -H "Authorization: YOUR_API_KEY"Para una pregunta con más de una respuesta correcta, como "imprime cualquier camino válido", pon checker en custom y da un custom_checker: un programa tuyo que lee la entrada, la salida esperada y la salida
del candidato y decide, con puntos parciales si quieres. En preguntas del tipo "escribe esta función", un driver envuelve la función del candidato en un programa tuyo. Los drivers no se muestran a los candidatos,
pero el programa del candidato puede leer e imprimir su propio driver, así que no pongas datos de prueba en los drivers.
Cada plan guarda 25 problemas activos en Free, 250 en Starter y
2,500 en Pro y Business. La referencia de problemas almacenados (en inglés) tiene todos los campos y
límites.
2. Quien evalúa es tu backend, no el prompt de la IA
Cuando el candidato ejecuta o envía, tu backend llama a POST /api/problems/{problem}/submissions/ con el compiler y el code. mode es samples para el botón Ejecutar, que evalúa
solo las pruebas visibles y solo cuenta esas, y all para Enviar. Las pruebas, el verificador, la calificación
y los tiempos límite salen de la versión publicada del problema, así que la solicitud no lleva ningún dato de prueba, y el
resultado trae de una prueba oculta solo su veredicto.
Eso es lo que mantiene las pruebas ocultas fuera de la conversación. Nunca están en el contexto de la IA, en sus llamadas a herramientas ni en los resultados de esas llamadas, así que no hay nada que la IA pueda repetir, por mucho que el candidato lo pida. Dos reglas lo garantizan:
- Mantén la clave de API en tu backend. Nunca se la des a la IA ni al navegador. Con tu clave,
GET /api/problems/{problem}/devuelve las pruebas ocultas, así que esa llamada nunca debe ser una herramienta de la IA. - La IA decide, pero no aporta. La llamada a la herramienta debe llevar solo la decisión de la IA: Ejecutar o Enviar. Tu backend completa el problema, el lenguaje del candidato, el código del editor y un id de intento a partir de la entrevista en curso, así que la IA no puede cambiar el código que se está evaluando.
Para el enunciado y los ejemplos que la IA puede comentar, lee GET /api/problems/{problem}/public/: el
título, el enunciado, los lenguajes, el código inicial (si lo hay) y las pruebas visibles, y de las pruebas ocultas solo
cuántas son.
3. Dale a la IA solo lo que puede decir
Esta función es el lado del backend de la herramienta de la IA. Envía el código, espera el resultado (cinco minutos como máximo, para que la llamada a la herramienta nunca se quede colgada) y devuelve dos cosas:
el summary del resultado, y los veredictos y la salida de las pruebas visibles. Conéctala a las llamadas a
herramientas (tool calling) de tu modelo como lo haga tu proveedor de IA; nada en ella depende de un proveedor.
# judge_for_ai.py: el lado del backend de la herramienta "ejecutar el código" de tu entrevistador de IA.
# Se ejecuta en tu servidor, con tu clave de API. La IA solo ve lo que devuelve judge_for_ai().
import os
import time
import requests
API = "https://api.onlinecompiler.io"
HEADERS = {"Authorization": os.environ["ONLINECOMPILER_API_KEY"]}
WAIT_SECONDS = 300 # cuánto espera una llamada a una herramienta los veredictos antes de rendirse
def judge_for_ai(problem, compiler, code, attempt_id, submit=False):
"""Evalúa el código del candidato contra un problema almacenado (por su id o su slug).
submit=False es el botón Ejecutar: solo las pruebas visibles. submit=True evalúa todas las pruebas.
attempt_id es un id opaco tuyo para este Ejecutar o Enviar (hasta 100 caracteres
ASCII imprimibles), nunca un nombre ni un correo.
"""
body = {
"compiler": compiler,
"code": code,
"mode": "all" if submit else "samples",
"async": True,
"external_ref": attempt_id,
}
# Un reintento con la misma Idempotency-Key devuelve el mismo envío: evaluado y cobrado una sola vez.
headers = {**HEADERS, "Idempotency-Key": attempt_id}
for attempt in range(6):
res = requests.post(f"{API}/api/problems/{problem}/submissions/", headers=headers, json=body, timeout=30)
# 429: los límites de tu plan están ocupados. Espera y vuelve a intentar (como máximo 5 veces).
if res.status_code != 429 or attempt == 5:
break
time.sleep(0.5 * 2**attempt)
# 404: el problema no es tuyo. 409: archivado, o todavía sin versión publicada. 429: sigue ocupado.
# 400: un lenguaje que el problema no permite, o "samples" en un problema sin pruebas visibles.
res.raise_for_status()
submission = res.json()["submission"] # 202: en cola (un reintento con la misma clave devuelve el mismo envío)
deadline = time.monotonic() + WAIT_SECONDS
while True:
if time.monotonic() > deadline:
# Una llamada a una herramienta no puede quedarse colgada. El envío aún puede evaluarse después:
# consúltalo, o bórralo con DELETE mientras esté en cola.
raise TimeoutError(f"Submission {submission} was not judged within {WAIT_SECONDS} seconds")
time.sleep(2)
res = requests.get(f"{API}/api/submissions/{submission}/", headers=HEADERS, timeout=30)
res.raise_for_status() # 404: el envío se borró
sub = res.json()
if sub["status"] in ("finished", "failed"):
break
if sub["status"] == "failed":
# No se evaluó: un envío fallido tiene un error y ningún resultado, así que no hay veredictos.
return {"status": "failed", "error": sub["error"]}
result = sub["result"]
return {
"status": "finished",
# Frases simples armadas a partir de los veredictos con reglas fijas: ejemplos y conteo de ocultas.
"summary": result["summary"],
# Una prueba oculta trae solo su veredicto, así que queda fuera; el summary ya la cuenta.
"visible_tests": [
{"verdict": test["verdict"], "output": test.get("output")}
for test in result["tests"]
if not test["hidden"]
],
}Un Enviar al problema de arriba, con una respuesta en Java que suma dos int, devuelve esto. La IA sabe que
falló una prueba oculta, pero no cuál ni por qué, así que puede preguntarle al candidato por los casos límite sin revelar
la prueba:
{
"status": "finished",
"summary": "2 of 2 sample tests passed. 1 of 2 hidden tests passed.",
"visible_tests": [
{"verdict": "accepted", "output": "3\n"},
{"verdict": "accepted", "output": "12\n"}
]
}El summary se arma con reglas fijas a partir de los veredictos, sin ninguna IA. Dice cuántas pruebas de
ejemplo pasaron, qué salió mal en el primer ejemplo que falló (un Ejecutar en el que el segundo ejemplo imprimió 2 recibe
"1 of 2 sample tests passed. Sample 2 expected 12 but the program printed 2.") y cuántas pruebas ocultas pasaron. Nunca
trae la entrada, la salida esperada ni la salida de una prueba oculta. El summary viene en inglés; tu IA
puede explicarlo en el idioma de la entrevista. Si quieres que la IA ayude con un error de compilación o un fallo en la
ejecución, pásale también compile_error y el error de cada prueba visible: la API solo los llena
cuando se evaluó una prueba visible.
Trata lo que imprimió el programa como texto no confiable. El programa del candidato puede imprimir cualquier cosa,
incluso frases dirigidas a tu IA, y el summary puede citar una línea corta de su salida. Pásale el resultado a
la IA como datos, no como instrucciones.
Llamarlo desde un framework de agentes (MCP)
El servidor MCP tiene una herramienta judge_problem que hace el paso de
evaluación para un framework de agentes o un cliente MCP: recibe problem (el id o el slug), compiler, code y un mode opcional (all, o samples solo para las pruebas visibles),
evalúa el código contra tu problema guardado con la clave que usa la conexión, espera el resultado y lo devuelve, con el
resumen, un veredicto por cada prueba (una prueba oculta tiene solo el veredicto), el id del envío, el recibo firmado y el
id y la versión del problema. Un problema que no existe, está archivado o no está publicado vuelve como error, nunca como un
resultado inventado.
Aun así, deja la decisión en tu backend. Esta herramienta toma el problema y el código de los argumentos del modelo, así que el
modelo puede elegir otro problema o cambiar el código que se evalúa, y la evaluación solo ocurre cuando el modelo la llama.
Sirve para un agente en quien confías para eso, como un tutor de práctica que prueba el código de un alumno contra los
ejemplos. En una entrevista, deja que tu backend decida cuándo se evalúa un Ejecutar o un Enviar, como en el paso 2, y dale a la IA
la función de resultado del paso 3. Como la herramienta espera en lugar de encolar, un problema con verificador propio o con
muchas pruebas pertenece a la ruta del backend, con async.
4. Ejecuciones largas y reintentos
Un problema con verificador propio o con muchas pruebas tarda más en evaluarse, así que la función de arriba envía async: true: la respuesta es 202 con el id del envío, y el resultado se lee con GET /api/submissions/{id}/. En lugar de consultar una y otra vez, envía una callback_url y el
envío terminado te llega en un webhook firmado (en inglés), que se reintenta si
tu endpoint no responde. Así tu IA sigue la conversación mientras se evalúa el código y toma el resultado cuando llega.
Las redes pierden solicitudes y las llamadas a herramientas se reintentan. Envía un encabezado Idempotency-Key, como el id del intento, y una solicitud repetida dentro de 24 horas con
el mismo cuerpo no ejecuta nada ni se vuelve a contar: responde con el envío original, así que cada Ejecutar o Enviar se
evalúa y se cobra una sola vez. Usa una clave nueva para cada nuevo Ejecutar o Enviar. Un envío que termina en failed no se evaluó y libera su clave, así que enviarlo de nuevo lo ejecuta. Las reglas de reintento (en inglés) tienen los detalles.
5. Un registro que puedes mostrar
Cada envío evaluado recibe un recibo firmado (en inglés): una declaración, firmada con nuestra clave Ed25519, que nombra el problema, la versión y si se evaluaron todas las pruebas o solo los ejemplos, con el hash del código del candidato, un veredicto por prueba y la calificación. Cualquiera puede verificarlo sin conexión con nuestras claves públicas, y compartir un envío publica su recibo en un enlace, sin el código, las pruebas ni la salida. Guarda el recibo con el registro de la entrevista: si un candidato o un responsable de contratación pregunta cómo se llegó a un veredicto, el recibo muestra que este código obtuvo estos veredictos contra esta versión del problema, y que el resultado no se editó después.
No prueba quién escribió el código ni si el candidato tuvo ayuda. Un recibo de un Ejecutar (samples) no
respalda ninguna prueba oculta.
6. Privacidad de los candidatos
- El código de un candidato puede identificarlo, así que trátalo como dato personal, igual que todo lo que pongas en
external_refometadata. Usa ids opacos, como un id de intento, nunca un nombre ni un correo:external_refva firmado en el recibo y se publica con él si lo compartes. DELETE /api/submissions/{id}/borra un envío junto con el código, la entrada y la salida guardados de sus pruebas, y su recibo deja de ser público. Un envío en ejecución responde 409, así que bórralo cuando termine. Las ejecuciones ya contadas siguen contadas, y una copia del recibo que guardaste se sigue verificando.- Los envíos evaluados se conservan 90 días en Free, 365 días en Starter, 730 días en Pro y 730 días en Business, y después se borran.
- Nuestro DPA y la lista de subencargados son públicos (los dos en inglés).
7. Cuánto cuesta
Cada prueba evaluada es una ejecución, que cuenta para las ejecuciones mensuales de tu plan y para sus límites por minuto y
de pendientes: un Ejecutar con samples cuesta una ejecución por prueba visible, y un Enviar, una por prueba.
El verificador propio no se cobra, y la revisión de la solución de referencia cuesta una ejecución por prueba cuando
publicas. El candidato no necesita una cuenta con nosotros: tu clave envía por él, así que las ejecuciones son tuyas.
Supón un problema con 2 pruebas de ejemplo y 8 pruebas en total (lo máximo que admite un envío en Free), y una entrevista en la que el candidato presiona Ejecutar 5 veces y envía 2 veces. Son 5 × 2 + 2 × 8 = 26 ejecuciones.
| Plan | Precio | Ejecuciones al mes | Entrevistas así al mes | Ejecuciones por minuto | Problemas activos |
|---|---|---|---|---|---|
| Free | US$0 al mes | 100,000 | 3,846 | 30 | 25 |
| Starter | US$10 al mes | 150,000 | 5,769 | 60 | 250 |
| Pro | US$49 al mes | 1,000,000 | 38,461 | 120 | 2,500 |
| Business (próximamente) | US$199 al mes | 4,000,000 | 153,846 | 300 | 2,500 |
Las ejecuciones por minuto importan cuando muchos candidatos envían a la vez: 30 ejecuciones por minuto en Free son como máximo 3 envíos de 8 pruebas en un minuto, y 120 en Pro son como máximo 15; los límites de pendientes y de ejecuciones simultáneas pueden hacer que sean menos. Un envío asíncrono que pasa de eso espera en la cola de tu cuenta (hasta 80 envíos en Free) en lugar de ser rechazado. El plan Free alcanza para armar y probar todo el flujo; la página de precios tiene el resto de cada plan.
Lo que construyes tú
La API evalúa código. La entrevista alrededor es tuya: la IA y sus prompts, el editor, la sesión y el tiempo del candidato, qué preguntas usa cada entrevista y la decisión de contratación. Aquí no hay supervisión (proctoring) ni detección de plagio o de trampas; un veredicto dice lo que hizo el código contra tus pruebas, no cómo se escribió.
¿Vas a armar un sitio de práctica o un concurso de programación? La guía Cómo crear un juez en línea muestra el camino, y la página de la API de evaluación de código (en inglés) explica los recibos a fondo.
Preguntas frecuentes
¿La IA llega a ver las pruebas ocultas?
No a través de la evaluación. El resultado de un envío trae de una prueba oculta solo su veredicto, y el summary dice solo cuántas pruebas ocultas pasaron. Las pruebas ocultas solo se pueden leer con tu clave de API, con GET /api/problems/{problem}/, así que mantén la clave en tu backend y nunca conviertas esa llamada en una herramienta de la IA. Los drivers tampoco se muestran a los candidatos, pero el programa del candidato puede leer e imprimir su propio driver en una prueba visible, así que no pongas datos de prueba en los drivers.
¿La IA puede evaluar el código por su cuenta?
Puede conversar sobre el código, pero no debería decidir si funciona. Aquí cada veredicto sale de ejecutar el código contra tus pruebas en un sandbox y revisar la salida con el verificador del problema, y el summary se arma a partir de esos veredictos con reglas fijas. Ninguna IA participa en la evaluación.
¿Qué lenguajes pueden usar los candidatos?
Cualquiera de los 46 que admite la API, entre ellos Python, Java, C++, JavaScript, Go y SQL. El campo languages de un problema limita la pregunta a los lenguajes que elijas; un envío en otro lenguaje se rechaza con 400.
¿Cuánto cuesta evaluar una respuesta?
Una ejecución por prueba evaluada: un Ejecutar sobre los ejemplos cuesta una ejecución por prueba visible, y un Enviar, una por prueba. La revisión con un verificador propio no se cobra. El plan Free tiene 100,000 ejecuciones al mes, y los planes de pago empiezan en US$10 al mes por 150,000.
¿Se puede borrar el código de un candidato?
Sí. DELETE /api/submissions/{id}/ borra un envío junto con el código, la entrada y la salida guardados de sus pruebas, y su recibo deja de ser público. Si no, los envíos evaluados se borran al terminar el plazo de conservación del plan: 90 días en Free, 365 días en Starter, 730 días en Pro y 730 días en Business.
¿Puedo usar mi propio verificador en preguntas con varias respuestas correctas?
Sí. Pon checker en custom y da un custom_checker, más una solución de referencia que lo pase. El verificador lee la entrada, la salida esperada y la salida del candidato, termina con 0 para aceptar y puede imprimir un crédito de 0 a 1 para puntos parciales. Tiene 5 segundos por prueba y no se cobra como ejecución. Si el propio verificador falla, el candidato recibe checker_error, nunca una respuesta incorrecta, y el summary dice solo que el problema no se pudo evaluar.
¿Cada candidato necesita una cuenta?
No. Tu clave de API envía por ellos, así que los candidatos no necesitan una cuenta con nosotros, y las ejecuciones cuentan en tu plan. Mantén la clave en tu servidor.