Instruction Following — does the response obey explicit constraints (judge-free).
Checks structural constraints the prompt asked for: length limits, valid JSON,
required/forbidden phrases, regex shape. Deterministic, so it is reproducible and
needs no judge. Semantic constraints ("use a professional tone") are out of scope
here — use answer_quality for those.
Scoring (judge-free): fraction of specified constraints satisfied, in [0, 1].
With the 0.5 pass threshold, a response must satisfy at least half its
constraints to pass; the reason lists which ones failed.
Dataset fields
input (required) the prompt sent to the agent
constraints (required) object with any of:
max_words / min_words (int) word-count bounds
max_chars (int) character-count upper bound
must_be_json (bool) response must parse as JSON
must_include (str | list[str]) substrings that must appear
must_exclude (str | list[str]) substrings that must NOT appear
regex (str) pattern that must match somewhere
A case with no constraints is scored inconclusive, not failed.
evaluate_response
evaluate_response(response: str, metadata: dict[str, Any]) -> dict[str, Any]
Pure scoring logic — importable for unit tests, no judge, no I/O.
Source code in datarobot_genai/eval/benchmarks/instruction_following.py
| def evaluate_response(response: str, metadata: dict[str, Any]) -> dict[str, Any]:
"""Pure scoring logic — importable for unit tests, no judge, no I/O."""
constraints = metadata.get("constraints") or {}
if not constraints:
return {"reason": "no constraints specified — cannot score"}
resp = response or ""
words = len(resp.split())
checks: list[tuple[str, bool]] = []
if "max_words" in constraints:
try:
limit = int(constraints["max_words"])
checks.append((f"max_words<={limit} (got {words})", words <= limit))
except (TypeError, ValueError):
checks.append(("max_words (invalid value)", False))
if "min_words" in constraints:
try:
limit = int(constraints["min_words"])
checks.append((f"min_words>={limit} (got {words})", words >= limit))
except (TypeError, ValueError):
checks.append(("min_words (invalid value)", False))
if "max_chars" in constraints:
try:
limit = int(constraints["max_chars"])
checks.append((f"max_chars<={limit} (got {len(resp)})", len(resp) <= limit))
except (TypeError, ValueError):
checks.append(("max_chars (invalid value)", False))
if constraints.get("must_be_json"):
checks.append(("must_be_json", _is_json(resp)))
for needle in _as_list(constraints.get("must_include")):
checks.append((f"must_include '{needle}'", needle.lower() in resp.lower()))
for needle in _as_list(constraints.get("must_exclude")):
checks.append((f"must_exclude '{needle}'", needle.lower() not in resp.lower()))
if constraints.get("regex"):
pattern = str(constraints["regex"])
try:
checks.append((f"regex /{pattern}/", re.search(pattern, resp) is not None))
except re.error:
checks.append((f"regex /{pattern}/ (invalid pattern)", False))
if not checks:
return {"reason": "no recognized constraints — cannot score"}
passed = sum(1 for _, ok in checks if ok)
value = passed / len(checks)
failed = [label for label, ok in checks if not ok]
reason = (
f"all {len(checks)} constraints satisfied"
if not failed
else f"{passed}/{len(checks)} satisfied; failed: " + "; ".join(failed)
)
return {"score": value, "instruction_following": value, "reason": reason}
|