Entrevistas com IA
Uma API de execução de código para entrevistas com IA
Um entrevistador de IA faz uma pergunta de programação, e o candidato escreve o código. Antes que a IA possa comentar a resposta, alguém precisa rodar esse código contra testes e dizer se ele funciona, e os testes ocultos precisam ficar longe da IA, para que ela não os deixe escapar na conversa. Com os problemas armazenados (stored problems), você guarda cada pergunta uma vez, com os testes ocultos. O seu backend julga cada Executar e cada Enviar pelo id do problema, e a IA recebe de volta só um resumo em texto simples e os resultados dos testes visíveis.
Pela equipe do OnlineCompiler.io. Atualizado em .
Also in EnglishTambién en español
Como as peças se encaixam
- Você guarda cada pergunta da entrevista uma vez como um problema armazenado: um enunciado, testes de exemplo, testes ocultos e, se quiser, uma solução de referência e um verificador próprio (custom checker).
- Durante a entrevista, o candidato escreve o código no seu editor. Quando ele clica em Executar ou Enviar, ou quando a sua IA decide rodar o código, a IA chama uma ferramenta no seu backend.
- O seu backend envia o código para o problema com a sua chave de API. A API roda o código contra os testes, cada um num sandbox novo, e devolve um veredito para cada teste e um resumo.
- O seu backend entrega à IA o resumo e os resultados dos testes visíveis. As entradas e as saídas esperadas dos testes ocultos nunca entram na conversa.
1. Guarde o problema uma vez
POST /api/problems/ cria um problema a partir de um título, um enunciado, as linguagens que o candidato
pode usar e os testes. Cada teste é uma entrada e uma saída esperada, e um teste é oculto, a não ser que você
diga "hidden": false, então os únicos testes que o candidato (ou a sua IA) consegue ver são os
exemplos que você escolher. Com uma reference_solution, a publicação confere essa solução antes: POST /api/problems/{problem}/publish/ responde 202, a versão fica checking enquanto a solução
de referência roda contra todos os testes, e os candidatos só são julgados contra a versão depois que ela passa em todos
os testes e fica published. Se falhar, a versão fica rejected; corrija o rascunho e publique de
novo.
# 1. Guarde o problema. Um teste é oculto, a não ser que você diga "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. Publique. Com uma solução de referência, a resposta é 202 e a versão fica "checking"
# até essa solução passar em todos os testes. Leia o problema até ele ficar "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 uma pergunta com mais de uma resposta certa, como "imprima qualquer caminho válido", defina checker como custom e dê um custom_checker: um programa seu que lê a entrada, a saída esperada e a saída
do candidato e decide, com nota parcial se você quiser. Em perguntas do tipo "escreva esta função", um driver envolve a função do candidato num programa seu. Os drivers não aparecem para os candidatos, mas o
programa do candidato consegue ler e imprimir o próprio driver, então não coloque dados de teste nos drivers. Cada plano
guarda 25 problemas ativos no Free, 250 no Starter e
2.500 no Pro e no Business. A referência dos problemas armazenados (em inglês) tem todos os campos e limites.
2. Quem julga é o seu backend, não o prompt da IA
Quando o candidato executa ou envia, o seu backend chama POST /api/problems/{problem}/submissions/ com o compiler e o code. mode é samples para o botão Executar, que julga só os
testes visíveis e só conta esses, e all para o Enviar. Os testes, o verificador, a pontuação e os tempos limite
vêm da versão publicada do problema, então a requisição não leva nenhum dado de teste, e o resultado traz de um teste
oculto só o veredito.
É isso que mantém os testes ocultos fora da conversa. Eles nunca estão no contexto da IA, nas chamadas de ferramenta dela nem nos resultados dessas chamadas, então não há nada para a IA repetir, por mais que o candidato peça. Duas regras garantem isso:
- Mantenha a chave de API no seu backend. Nunca a entregue à IA nem ao navegador. Com a sua chave,
GET /api/problems/{problem}/devolve os testes ocultos, então essa chamada nunca pode ser uma ferramenta da IA. - A IA decide, mas não fornece. A chamada de ferramenta da IA deve levar só a decisão dela: Executar ou Enviar. O seu backend preenche o problema, a linguagem do candidato, o código do editor e um id de tentativa a partir da entrevista em andamento, então a IA não consegue mudar o código que está sendo julgado.
Para o enunciado e os exemplos que a IA pode comentar, leia GET /api/problems/{problem}/public/: o
título, o enunciado, as linguagens, o código inicial (se houver) e os testes visíveis, e, dos testes ocultos, só quantos
são.
3. Entregue à IA só o que ela pode dizer
Esta função é o lado do backend da ferramenta da IA. Ela envia o código, espera o resultado (por no máximo cinco minutos, para que a chamada de ferramenta nunca trave) e devolve duas coisas: o summary do resultado e os vereditos e a saída dos testes visíveis. Ligue a função à chamada de ferramentas
(tool calling) do seu modelo do jeito que o seu provedor de IA faz; nada nela depende de um provedor específico.
# judge_for_ai.py: o lado do backend da ferramenta "rodar o código" do seu entrevistador de IA.
# Roda no seu servidor, com a sua chave de API. A IA só vê o que judge_for_ai() devolve.
import os
import time
import requests
API = "https://api.onlinecompiler.io"
HEADERS = {"Authorization": os.environ["ONLINECOMPILER_API_KEY"]}
WAIT_SECONDS = 300 # quanto tempo uma chamada de ferramenta espera pelos vereditos antes de desistir
def judge_for_ai(problem, compiler, code, attempt_id, submit=False):
"""Julga o código do candidato contra um problema armazenado (pelo id ou pelo slug).
submit=False é o botão Executar: só os testes visíveis. submit=True julga todos os testes.
attempt_id é um id opaco seu para este Executar ou Enviar (até 100 caracteres
ASCII imprimíveis), nunca um nome ou um e-mail.
"""
body = {
"compiler": compiler,
"code": code,
"mode": "all" if submit else "samples",
"async": True,
"external_ref": attempt_id,
}
# Repetir com a mesma Idempotency-Key devolve a mesma submissão: julgada e cobrada uma vez só.
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: os limites do seu plano estão ocupados. Espere e tente de novo (no máximo 5 vezes).
if res.status_code != 429 or attempt == 5:
break
time.sleep(0.5 * 2**attempt)
# 404: o problema não é seu. 409: arquivado, ou ainda sem versão publicada. 429: ainda ocupado.
# 400: uma linguagem que o problema não permite, ou "samples" num problema sem teste visível.
res.raise_for_status()
submission = res.json()["submission"] # 202: na fila (repetir com a mesma chave devolve a mesma submissão)
deadline = time.monotonic() + WAIT_SECONDS
while True:
if time.monotonic() > deadline:
# Uma chamada de ferramenta não pode travar. A submissão ainda pode ser julgada depois:
# consulte-a, ou apague-a com DELETE enquanto estiver na fila.
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: a submissão foi apagada
sub = res.json()
if sub["status"] in ("finished", "failed"):
break
if sub["status"] == "failed":
# Não foi julgada: uma submissão com falha tem um erro e nenhum resultado, então não há vereditos.
return {"status": "failed", "error": sub["error"]}
result = sub["result"]
return {
"status": "finished",
# Frases simples montadas a partir dos vereditos por regras fixas: exemplos e contagem dos ocultos.
"summary": result["summary"],
# Um teste oculto traz só o veredito, então fica de fora; a contagem dele já está no summary.
"visible_tests": [
{"verdict": test["verdict"], "output": test.get("output")}
for test in result["tests"]
if not test["hidden"]
],
}Um Enviar para o problema acima, com uma resposta em Java que soma dois int, devolve isto. A IA sabe que um
teste oculto falhou, mas não qual nem por quê, então pode perguntar ao candidato sobre casos-limite sem entregar o teste:
{
"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"}
]
}O summary é montado por regras fixas a partir dos vereditos, sem nenhuma IA. Ele diz quantos testes de
exemplo passaram, o que deu errado no primeiro exemplo que falhou (um Executar em que o segundo exemplo imprimiu 2 recebe
"1 of 2 sample tests passed. Sample 2 expected 12 but the program printed 2.") e quantos testes ocultos passaram. Ele
nunca traz a entrada, a saída esperada nem a saída de um teste oculto. O summary vem em inglês; a sua IA pode
explicá-lo no idioma da entrevista. Se você quiser que a IA ajude com um erro de compilação ou uma falha na execução,
passe também o compile_error e o error de cada teste visível: a API só preenche esses campos
quando um teste visível foi julgado.
Trate o que o programa imprimiu como texto não confiável. O programa do candidato pode imprimir qualquer coisa,
inclusive frases dirigidas à sua IA, e o summary pode citar uma linha curta da saída. Passe o resultado à IA
como dado, não como instrução.
Chamando por um framework de agentes (MCP)
O servidor MCP tem uma ferramenta judge_problem que faz a etapa de julgamento
para um framework de agentes ou um cliente MCP: ela recebe problem (o id ou o slug), compiler, code e um mode opcional (all, ou samples só para os testes visíveis), julga o
código contra o seu problema guardado com a chave que a conexão usa, espera o resultado e o devolve, com o resumo, um veredito
para cada teste (um teste oculto tem só o veredito), o id da submissão, o recibo assinado e o id e a versão do problema. Um
problema que não existe, está arquivado ou não foi publicado volta como erro, nunca como um resultado inventado.
Mesmo assim, deixe a decisão no seu backend. Essa ferramenta recebe o problema e o código dos argumentos do modelo, então o
modelo pode escolher outro problema ou mudar o código que está sendo julgado, e o julgamento só acontece quando o modelo a
chama. Serve para um agente em quem você confia para isso, como um tutor de prática que testa o código de um aluno contra os
exemplos. Em uma entrevista, deixe o seu backend decidir quando um Executar ou um Enviar é julgado, como na etapa 2, e entregue à
IA a função de resultado da etapa 3. Como a ferramenta espera em vez de enfileirar, um problema com verificador próprio ou
com muitos testes pertence ao caminho do backend, com async.
4. Execuções longas e novas tentativas
Um problema com verificador próprio ou com muitos testes demora mais para ser julgado, então a função acima envia async: true: a resposta é 202 com o id da submissão, e o resultado é lido com GET /api/submissions/{id}/. Em vez de consultar o resultado várias vezes, envie uma callback_url, e a submissão pronta chega até você num webhook assinado (em inglês), reenviado se o seu endpoint não responder.
Assim a sua IA segue com a conversa enquanto o código é julgado e pega o resultado quando ele chegar.
Redes perdem requisições, e chamadas de ferramenta são repetidas. Envie um cabeçalho Idempotency-Key, como o
id da tentativa, e uma requisição repetida em até 24 horas com o mesmo corpo não roda nada nem é
contada de novo: ela responde com a submissão original, então cada Executar ou Enviar é julgado e cobrado uma vez só. Use
uma chave nova a cada novo Executar ou Enviar. Uma submissão que termina failed não foi julgada e libera a
chave, então enviá-la de novo roda o código. As regras de
repetição (em inglês) têm os detalhes.
5. Um registro que você pode mostrar
Toda submissão julgada recebe um recibo assinado (em inglês): uma declaração, assinada com a nossa chave Ed25519, que traz o problema, a versão e se todos os testes ou só os exemplos foram julgados, com o hash do código do candidato, um veredito para cada teste e a nota. Qualquer pessoa pode verificar o recibo offline com as nossas chaves públicas, e compartilhar uma submissão publica o recibo num link, sem o código, os testes nem a saída. Guarde o recibo junto com o registro da entrevista: se um candidato ou um gestor perguntar como se chegou a um veredito, ele mostra que este código teve estes vereditos contra esta versão do problema, e que o resultado não foi editado depois.
Ele não prova quem escreveu o código, nem se o candidato teve ajuda. Um recibo de um Executar (samples) não
atesta nenhum teste oculto.
6. Privacidade dos candidatos
- O código de um candidato pode identificá-lo, então trate esse código como dado pessoal, assim como tudo o que você
colocar em
external_refoumetadata. Use ids opacos, como um id de tentativa, nunca um nome ou um e-mail: oexternal_refvai assinado no recibo e é publicado com ele se você o compartilhar. DELETE /api/submissions/{id}/apaga uma submissão junto com o código, a entrada e a saída guardados dos testes dela, e o recibo deixa de ser público. Uma submissão em andamento responde 409, então apague-a depois que ela terminar. As execuções já contadas continuam contadas, e uma cópia do recibo que você guardou continua passando na verificação.- As submissões julgadas ficam guardadas por 90 dias no Free, 365 dias no Starter, 730 dias no Pro e 730 dias no Business, e depois são apagadas.
- O nosso DPA e a lista de subprocessadores são públicos (os dois em inglês).
7. Quanto custa
Cada teste julgado é uma execução, contada na cota mensal do seu plano e nos limites por minuto e de pendentes: um
Executar com samples custa uma execução por teste visível, e um Enviar, uma por teste. O verificador próprio
não é cobrado, e a conferência da solução de referência custa uma execução por teste quando você publica. O candidato não
precisa de conta conosco: a sua chave envia por ele, então as execuções são suas.
Imagine um problema com 2 testes de exemplo e 8 testes no total (o máximo que uma submissão aceita no Free), e uma entrevista em que o candidato clica em Executar 5 vezes e envia 2 vezes. São 5 × 2 + 2 × 8 = 26 execuções.
| Plano | Preço | Execuções por mês | Entrevistas assim por mês | Execuções por minuto | Problemas ativos |
|---|---|---|---|---|---|
| Free | US$ 0 por mês | 100.000 | 3.846 | 30 | 25 |
| Starter | US$ 10 por mês | 150.000 | 5.769 | 60 | 250 |
| Pro | US$ 49 por mês | 1.000.000 | 38.461 | 120 | 2.500 |
| Business (em breve) | US$ 199 por mês | 4.000.000 | 153.846 | 300 | 2.500 |
As execuções por minuto pesam quando muitos candidatos enviam ao mesmo tempo: 30 execuções por minuto no Free são no máximo 3 envios de 8 testes num minuto, e 120 no Pro são no máximo 15; os limites de pendentes e de execuções simultâneas podem deixar esse número menor. Uma submissão assíncrona acima disso espera na fila da sua conta (até 80 submissões no Free) em vez de ser recusada. O plano Free basta para montar e testar o fluxo inteiro; a página de preços tem o resto de cada plano.
O que fica por sua conta
A API julga código. A entrevista em volta dele é sua: a IA e os prompts dela, o editor, a sessão e o tempo do candidato, quais perguntas cada entrevista usa e a decisão de contratação. Não há supervisão da prova (proctoring) nem detecção de plágio ou de cola aqui; um veredito diz o que o código fez contra os seus testes, não como ele foi escrito.
Vai montar um site de treino ou uma maratona de programação? O guia Como criar um juiz online mostra o caminho, e a página da API de avaliação de código (em inglês) explica os recibos em detalhe.
Perguntas frequentes
A IA chega a ver os testes ocultos?
Não pelo julgamento. O resultado de uma submissão traz de um teste oculto só o veredito, e o summary diz só quantos testes ocultos passaram. Os testes ocultos só podem ser lidos com a sua chave de API, por GET /api/problems/{problem}/, então mantenha a chave no seu backend e nunca transforme essa chamada numa ferramenta da IA. Os drivers também não aparecem para os candidatos, mas o programa do candidato consegue ler e imprimir o próprio driver num teste visível, então não coloque dados de teste nos drivers.
A IA pode julgar o código sozinha?
Ela pode conversar sobre o código, mas não deve decidir se ele funciona. Aqui todo veredito vem de rodar o código contra os seus testes num sandbox e conferir a saída com o verificador do problema, e o summary é montado a partir desses vereditos por regras fixas. Nenhuma IA participa do julgamento.
Quais linguagens os candidatos podem usar?
Qualquer uma das 46 que a API suporta, entre elas Python, Java, C++, JavaScript, Go e SQL. O campo languages de um problema limita a pergunta às linguagens que você escolher; uma submissão em outra linguagem é recusada com 400.
Quanto custa uma resposta julgada?
Uma execução por teste julgado: um Executar nos exemplos custa uma execução por teste visível, e um Enviar, uma por teste. A conferência com um verificador próprio não é cobrada. O plano Free tem 100.000 execuções por mês, e os planos pagos começam em US$ 10 por mês para 150.000.
Dá para apagar o código de um candidato?
Sim. DELETE /api/submissions/{id}/ apaga uma submissão junto com o código, a entrada e a saída guardados dos testes dela, e o recibo deixa de ser público. Fora isso, as submissões julgadas são apagadas depois do prazo de guarda do plano: 90 dias no Free, 365 dias no Starter, 730 dias no Pro e 730 dias no Business.
Posso usar o meu próprio verificador em perguntas com várias respostas certas?
Sim. Defina checker como custom e dê um custom_checker, mais uma solução de referência que passe nele. O verificador lê a entrada, a saída esperada e a saída do candidato, sai com 0 para aceitar e pode imprimir um crédito de 0 a 1 para nota parcial. Ele tem 5 segundos por teste e não é cobrado como execução. Se o próprio verificador falhar, o candidato recebe checker_error, nunca uma resposta errada, e o summary diz só que o problema não pôde ser julgado.
Cada candidato precisa de uma conta?
Não. A sua chave de API envia por eles, então os candidatos não precisam de conta conosco, e as execuções contam no seu plano. Mantenha a chave no seu servidor.