Code Evaluator
Code Evaluator is the deterministic member of the AgentLoop Evaluator family. Instead of model inference, it hands the judging logic to a piece of Python or JavaScript code that you write and runs that code record by record in a sandbox. Evaluate the same data repeatedly, and the result is identical every time, unaffected by model temperature, version upgrades, or prompt wording.
Unlike the Agent evaluator and the LLM evaluator, which rely on model inference, a Code evaluator has no reasoning overhead and consumes no model calls. When a quality standard can be expressed in code, a Code evaluator is often a better fit, and it is well suited to the pass-or-fail checks in a regression safety net.
Use cases
Typical scenarios for a Code evaluator:
Structured output validation — The most common use. Require the agent to return valid JSON that contains specified fields, and score 0 whenever a field is missing.
Numeric answer comparison — Compare amounts, quantities, or calculation results. Define a tolerance range in code instead of asking a model to sense whether a value is close enough.
Denylist scanning — Scan the output for sensitive words and prohibited phrasing.
Output length and format checks — Check the output length, whether the output contains a Markdown table, or whether it starts with a specified prefix.
Tool calling sequence verification — Verify that the required tools were called in the expected order.
Citation completeness checks — Verify that every citation number in the answer can be found in the retrieved passages.
If your quality standard depends on subjective or semantic judgment that cannot be expressed in code, use the Agent evaluator or the LLM evaluator, which rely on model inference.
Prerequisites
Code Evaluator is currently available in the following regions:
| Region name | Region ID |
| China (Hangzhou) | cn-hangzhou |
| China (Shanghai) | cn-shanghai |
| China (Beijing) | cn-beijing |
| China (Shenzhen) | cn-shenzhen |
| China (Hong Kong) | cn-hongkong |
| Singapore | ap-southeast-1 |
Create an evaluator
Log on to the AgentLoop console.
In the left-side navigation pane, click Evaluation.
In the tab navigation bar at the top of the page, click the Evaluator tab.
In the upper-right corner, click Create Evaluator.
On the creation form, fill in the basic information. Set Evaluator Type to Code. Display name, description, and tags are optional. Tags let you retrieve evaluators by dimension in the evaluator list, so give your Code evaluators a consistent tag that is easy to filter on.
| Configuration item | Required | Description |
| Evaluator name | Yes | A globally unique identifier, used for API calls and system indexing. It must start with a letter and can contain only letters, digits, underscores, and hyphens. You cannot change it after creation. |
| Evaluator type | Yes | Select Code. |
| Display name | No | A user-friendly name shown to users. If you leave it blank, the evaluator name is displayed. |
| Description | No | Explains the judging convention of this evaluator so that your team can reuse it. |
| Tags | No | Enter a tag and press Enter to add it. You can retrieve evaluators by tag. |
After you complete the form, click Create Evaluator to submit it. The edit page opens with two tabs: Basic Information and Evaluation Code. On the Basic Information tab, the evaluator name and evaluator type are read-only, and the remaining fields stay editable. The core configuration happens on the Evaluation Code tab.
Write the evaluation code
In the upper-right corner of the Evaluation Code tab, switch between Python and JavaScript. The two languages are mutually exclusive, and the file name in the editor changes to code.py or code.js accordingly. Click Reset to Template to load the official default template as a starting point.
Your evaluation code runs in a sandbox. The following constraints apply:
Dependencies — Implement the evaluation logic with the language standard library only.
Determinism — Keep the code in the form of a pure function: the same input produces the same result.
Isolation — The code cannot access external services or depend on the state of the execution environment.
This is both a practical constraint of the sandbox and the precondition for evaluation results that are reproducible and trustworthy.
The only contract for your code is this: define a function named evaluate that takes data fields as input parameters and returns an object that contains score and explanation.
The Python default template is as follows.
def evaluate(input, output, context=None):
# Evaluate one record and return score and explanation.
return {
"score": 0,
"explanation": "Describe why this score was assigned",
}The JavaScript default template is as follows.
function evaluate(input, output, context) {
// Evaluate one record and return score and explanation.
return {
score: 0,
explanation: 'Describe why this score was assigned',
}
}
module.exports = evaluateThe trailing module.exports line in the JavaScript template is required. Without it, the platform cannot load the function.
Return value contract
The evaluate function processes one data item per call. The returned result must contain the following two fields.
| Field | Type | Description |
score | Number | The score of this data item. The value range is up to your code. |
explanation | String | The scoring rationale. It appears as the Evaluation Rationale of the evaluation results. |
When you assign scores and write explanations, follow these conventions:
Keep the scoring convention fixed — The evaluation results page shows the raw score, the score range, and the normalized score at the same time. One evaluator must return scores in the same range across all data so that results remain comparable across versions. Use 0 and 1 for binary judgments, and a fixed percentage scale from 0 to 100 or a continuous value from 0 to 1 for graded judgments. Do not mix the two. Otherwise, cross-version comparison and dashboard aggregation both become distorted.
Make explanations specific — State the specific reason behind each score and carry the key evidence behind the judgment: the missing field name, the sensitive word that was matched, or the actual value against the expected one. Do not return vague messages such as "does not meet the requirement". This information is your main clue when you troubleshoot a misjudgment or revisit the evaluation results later.
Variable mapping rules
The Variable Mapping table below the editor is generated automatically. The platform parses the input parameters of your evaluate function, so you declare nothing by hand. The rules:
A parameter without a default value becomes a required variable.
A parameter with a default value becomes an optional variable.
In the Python template, input and output have no default value, so they are marked as required. context has the default value None, so it is marked as optional. In JavaScript, when default value syntax is not used, all three parameters are treated as required. To make a variable optional, write function evaluate(input, output, context = null).
Parameter names are not limited to input, output, and context. You can define your own parameter names. The returned object must always contain the score and explanation fields.
Your variables appear in Field Mapping when you create an evaluation task, where you specify which data field each variable reads from. You can also add a description for each variable in the Variable Mapping table to explain what content the variable expects.
When you finish, click Save in the upper-right corner.
Code samples
Format validation
Require the agent output to be valid JSON that contains both title and items, with at least one item.
def evaluate(input, output, context=None):
import json
text = output if isinstance(output, str) else json.dumps(output, ensure_ascii=False)
try:
data = json.loads(text)
except Exception as e:
return {"score": 0, "explanation": "Output is not valid JSON: %s" % e}
if not isinstance(data, dict):
return {"score": 0, "explanation": "The top level of the output JSON is not an object"}
missing = [k for k in ("title", "items") if k not in data]
if missing:
return {"score": 0, "explanation": "Missing required fields: %s" % ", ".join(missing)}
if not isinstance(data["items"], list) or len(data["items"]) == 0:
return {"score": 0, "explanation": "items is not an array, or it is an empty array"}
return {"score": 1, "explanation": "JSON is valid and contains title and %d items" % len(data["items"])}Numeric tolerance comparison
Compare the numeric answer from the agent with the ground truth, allowing a relative error of one thousandth. expected is a custom variable that you map to the ground truth column of the dataset when you create the evaluation task.
def evaluate(output, expected, tolerance=0.001):
import re
def to_number(v):
if isinstance(v, (int, float)):
return float(v)
m = re.search(r"-?\d+(?:\.\d+)?", str(v).replace(",", ""))
return float(m.group()) if m else None
got, want = to_number(output), to_number(expected)
if want is None:
return {"score": 0, "explanation": "No number found in the ground truth"}
if got is None:
return {"score": 0, "explanation": "No number found in the model output"}
denominator = abs(want) if want != 0 else 1.0
diff = abs(got - want) / denominator
if diff <= tolerance:
return {"score": 1, "explanation": "Numbers match: expected %g, actual %g" % (want, got)}
return {"score": 0, "explanation": "Deviation exceeds the limit: expected %g, actual %g, relative error %.2f%%" % (want, got, diff * 100)}tolerance has a default value, so it is marked as optional in the variable mapping. If you do not map this variable in the task, the code uses the default value.
Tool calling verification
Check whether the agent called the required tools. The sample serializes the tool context into a string and checks whether each expected tool name appears in that string. Because this is a substring match over the whole context, a tool name that appears only in call parameters or return results also matches. context is usually mapped to trace.tool_context of the trace data.
def evaluate(output, context=None, required_tools="search,summarize"):
import json
expected = [t.strip() for t in str(required_tools).split(",") if t.strip()]
if not expected:
return {"score": 1, "explanation": "No required tools declared, so the record passes by default"}
raw = context if isinstance(context, str) else json.dumps(context or {}, ensure_ascii=False)
called = set()
for name in expected:
if name in raw:
called.add(name)
missing = [t for t in expected if t not in called]
hit_rate = len(called) / len(expected)
if not missing:
return {"score": 1, "explanation": "All required tools were called: %s" % ", ".join(expected)}
return {
"score": round(hit_rate, 2),
"explanation": "Missing tool calls: %s (%d of %d matched)" % (", ".join(missing), len(called), len(expected)),
}Deduction-based quality check
A JavaScript sample that uses a percentage-scale deduction model. It checks prohibited phrasing, output length, and format requirements in one pass, and returns both a combined score and the deduction details.
function evaluate(input, output, context = null) {
const text = typeof output === 'string' ? output : JSON.stringify(output)
const banned = ['guaranteed profit', 'guaranteed returns', 'one hundred percent', 'absolutely safe']
let score = 100
const reasons = []
const hits = banned.filter((w) => text.includes(w))
if (hits.length > 0) {
score -= 40 * hits.length
reasons.push(`Prohibited phrasing matched: ${hits.join(', ')}`)
}
if (text.trim().length < 20) {
score -= 30
reasons.push(`Response too short: only ${text.trim().length} characters`)
}
if (!/risk disclaimer|for reference only/.test(text)) {
score -= 20
reasons.push('Missing risk disclaimer')
}
score = Math.max(0, score)
return {
score,
explanation: reasons.length === 0 ? 'Passed all quality checks' : reasons.join('; '),
}
}
module.exports = evaluateUse the evaluator in an evaluation task
After you save the evaluator, apply it to real data through an evaluation task. On the Evaluation Task tab of the Evaluation module, click Create Task. The flow has two steps.
Step 1: Data Configuration
Enter the task name and description, and select the data source. Supported data sources are traces, agent trajectories, logs, and datasets. If you select traces, also specify the evaluation granularity: single-turn conversation or multi-turn conversation. Choose whether to include the input and output of previous turns, and narrow the evaluation scope with the Agent drop-down list and a data filter expression.
Step 2: Select Evaluator
This is the critical step. The Preview Data area at the top of the page shows all available fields of one real sample. Click Switch Sample to load another sample and confirm the shape of your data. In the evaluator list on the left, switch to the Custom tab and select the Code evaluator you just created. The Field Mapping area on the right lists all variables of that evaluator. Select the corresponding data field for each variable.
The fields available for mapping depend on the data source. For a trace data source, the following fields are available for mapping.
| Field | Content |
trace.input | The user's original query |
trace.output | The agent's final answer |
trace.tool_context | Tool call parameters, return results, and context |
trace.agent_trajectory | The agent's internal execution chain |
trace.rag_context | RAG retrieval context |
trace.rag_retrieval_queries | The query statements used for RAG retrieval |
trace.rag_retrieval_documents | The document passages retrieved by RAG |
trace.rag_llm_output | The model output in the RAG chain |
traceId | The unique identifier of the trace |
After you map all required variables, the evaluator card shows the Ready tag.
Run a preview test and view the results
Run a preview test before the formal run. Click Run Test at the bottom of the page. A drawer opens on the right and shows the field values of the current sample. Click Run Test in the drawer to trial-run the selected evaluators in batch. After execution finishes, the Evaluation Results area shows the status, score, and evaluation rationale of each evaluator. This step exposes most problems before the formal run consumes quota, such as a wrong field mapping, code that throws an exception on the real data shape, or a score that does not match your intended convention.
After you confirm the results, click Create and Run to submit the task. The task details page has three tabs: Overview, Run Records, and Evaluation Results.
Run Records — Shows the total count, completion rate, succeeded and failed counts, duration, and average score of each execution.
Evaluation Results — Provides a detail view and a data item view. The detail view lists the following information record by record: the data item content, score, score range, normalized score, status, evaluator, evaluation duration, and generation time. You can filter the results by status, evaluator, score range, and time window.
If your code throws an uncaught exception on a record, that record appears as an execution-failure record instead of receiving a score. Filter the evaluation results by status to locate these records. Common causes are a wrong field mapping and a data shape that your code does not handle.
The execution duration of a single Code evaluator record is usually a little over ten seconds. Records within one task use concurrent execution, so a task of about 100 records takes roughly one minute in total.
What's next
Use the evaluator name to reference this evaluator in API calls.