A new model called MiniMax H3 started moving through a small backend team's group chat on a Tuesday morning. One engineer pasted a screenshot with a long answer. Another pasted a leaderboard line with no link. A third wrote, 'we should switch.'
Nobody pasted a failure.
The team had been burned by this pattern before. A model looked good on a cherry-picked example and then failed inside a real pipeline. The cost of a bad switch was not only the API bill. It was a broken structured-output step, a multi-day rollback, and a ruined evaluation data set. The team decided not to argue about screenshots. They built a small gate.
The gate had one job: turn an unverified claim into a repeatable check. The trigger was the H3 topic signal, not verified evidence. The team kept the name in the test log and refused to let the name affect the pass threshold.
The constraints were simple. The team did not want to spend money before seeing evidence. They wanted a place to store raw outputs, not just final scores. They wanted a result a human could review in ten minutes.
They chose MonkeyCode's free model access and its free server option for the prototype. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The choice was not a permanent commitment. It was a way to remove cost as an excuse for skipping the test.
The harness stayed vendor-neutral. The only hard requirements were a model URL and a place to run one Python file.
The gate in five steps
Step 1: Build cases from real failures
The team did not start with a benchmark. They started with five failures from the previous quarter. Each case had a concrete input, a short task, and a binary rubric.
The case writer is a small script. It writes JSONL so a later run can be replayed without depending on the current model wrapper.
import json
cases = [
{
'id': 'c001',
'input': 'Normalize this street address into JSON.',
'rubric': ['json_valid', 'country_inferred', 'postal_code_preserved'],
'max_tokens': 150,
},
{
'id': 'c002',
'input': 'Turn this support ticket into a priority label and ticket type.',
'rubric': ['priority_label_present', 'ticket_type_one_of'],
'max_tokens': 120,
},
]
with open('cases.jsonl', 'w', encoding='utf-8') as f:
for item in cases:
f.write(json.dumps(item) + chr(10))
Step 2: Make the rubric binary
Every rubric item is true or false. This removes the most common eval mistake: letting a second model judge the first model and calling the result objective. The team reviewed each case by hand. The rubric only asked whether the output contained a required field or satisfied a required format.
Step 3: Run the model and keep raw output
The runner treats the model as a black box. It posts a prompt, reads a response, records latency, and saves the raw text. The team never accepted a final score without the transcript beside it.
import json
import os
import time
import uuid
from pathlib import Path
import requests
MODEL_URL = os.environ.get('MODEL_URL', 'http://127.0.0.1:8000/v1/chat/completions')
MODEL_NAME = os.environ.get('MODEL_NAME', 'local-model')
CASES = 'cases.jsonl'
RESULTS_DIR = 'results'
def load_cases(path):
with open(path, encoding='utf-8') as f:
return [json.loads(line) for line in f if line.strip()]
def run_case(case):
payload = {
'model': MODEL_NAME,
'messages': [
{
'role': 'system',
'content': 'Return a JSON object. Follow the rubric exactly. Do not add commentary.'
},
{
'role': 'user',
'content': case['input']
},
],
'temperature': 0.0,
'max_tokens': case.get('max_tokens', 200),
}
started = time.time()
response = requests.post(MODEL_URL, json=payload, timeout=120)
response.raise_for_status()
data = response.json()
content = data['choices'][0]['message']['content']
return {
'latency_ms': round((time.time() - started) * 1000),
'raw': content,
'finish_reason': data['choices'][0].get('finish_reason'),
}
def main():
Path(RESULTS_DIR).mkdir(exist_ok=True)
run_id = uuid.uuid4().hex[:8]
out_path = Path(RESULTS_DIR) / f'{run_id}.jsonl'
rows = []
for case in load_cases(CASES):
row = {'case_id': case['id'], 'run_id': run_id}
try:
row.update(run_case(case))
row['status'] = 'ok'
except Exception as exc:
row['status'] = 'error'
row['error'] = str(exc)
rows.append(row)
print(case['id'], row['status'])
with open(out_path, 'w', encoding='utf-8') as out:
for row in rows:
out.write(json.dumps(row) + chr(10))
print('run_id:', run_id)
if __name__ == '__main__':
main()
The HTTP contract above is common across many chat-completion endpoints. It is not an official MonkeyCode API definition. Swap the URL, auth header, and response path for the endpoint you actually use.
Step 4: Review, never auto-accept
The scoreboard script only aggregates. It does not make the final call.
import json
from pathlib import Path
RESULTS = Path('results')
latest = sorted(RESULTS.glob('*.jsonl'))[-1]
rows = [json.loads(line) for line in latest.open(encoding='utf-8') if line.strip()]
passed = [r for r in rows if r.get('status') == 'ok']
errors = [r for r in rows if r.get('status') == 'error']
print('run_id:', latest.stem)
print('cases:', len(rows), 'ok:', len(passed), 'error:', len(errors))
for row in rows:
print(row['case_id'], row.get('status'), row.get('latency_ms'))
Each row still needs a human reading the raw output. A fast answer that misses a required field is a failure. A slow answer that records a valid field and preserves the transcript is a pass.
Step 5: Record a decision
The decision table kept the process honest. The team did not need to remember every trade-off. They only needed to follow the table.
| Evidence | Call | Action |
|---|---|---|
| At least one case status is error | fail | fix the adapter before judging the model |
| Raw output is valid and every rubric item is present | pass | advance to a larger test set |
| Raw output is valid but one rubric item is missing | review | read the transcript before deciding |
| Two or more cases fail the rubric | fail | do not promote to production |
| Only happy-path cases pass | inconclusive | add one malformed input and one security-sensitive case |
The team ran the gate against MiniMax H3 using five internal cases. The raw outputs were stored, the scoreboard was reviewed, and the final decision was written in one Markdown file. The team did not publish a universal verdict. A model is only a good fit for a particular task, not for every screenshot.
Limitations
A five-case gate is not a benchmark. It filters obvious hype, not hidden bias. Free access may be rate-limited or changed; the team did not assume permanence. Raw output can contain sensitive data, so only synthetic or public inputs belong in this harness. The runner is a plain HTTP call. Production use needs retries, auth, and alerting.
Who should not use this
Teams with strict contracts or privacy rules should not route customer data through an unvetted endpoint. Teams that need high-availability model serving should not rely on a single free server for an SLA. Teams looking for a leaderboard to copy should look elsewhere. This gate answers a narrower question: does this model pass the task that already burned us?
If a cheap first run is the missing step in your evaluation loop, a small gate like this is a useful place to start. Keep the harness vendor-neutral and let the saved transcripts drive the next case set.
Top comments (0)