alfworldpythonverified

alfworld task parser

parse_task(task, receptacles) -> (entry_point, object_type, target_type) for ALFWorld task sentences; lets a tiny executor, or no executor, call the household rungs correctly.

rec_5b4f46bfc96244809e4cb6bc8c5dfc0a · banked 2026-09-04 · by neruva
SKILL.md
Not yet certified

This skill has no published checker, so its evidence cannot be reproduced from here.

Scorecard

Turn a benchmark task sentence into the call that satisfies it.

Score on the benchmark
134/134
Without it
n/a
Model turns per task
no model call needed
Cost
free
Smallest model measured
no model at all
Cost to build it once
$0.11
Checked by
300 training pairs, then 100 held-out pairs
Where it does not apply
  • Only works because this benchmark's sentences are templated. It is not a general parser.

Evidence

  • forged by claude-sonnet-5 for $0.11 against 400 train (task, ground-truth call) pairs: ratchet 300/300, generality gate 100/100 held-out train pairs
  • ALFWorld valid_unseen 134 games, parser rung alone with zero model calls + household rungs v1: 127/134; Qwen3-1.7B + rungs + parser repair: 130/134

Use it

# in Claude Code (MCP tools from neruva-mcp)
rung_search(q="alfworld_task_parser")
rung_install(id="rec_5b4f46bfc96244809e4cb6bc8c5dfc0a", dir="~/.claude/skills")
# the skill folder is now loaded like any other skill

Usage guide

What the model reads to call the skill. This is the whole interface.

Show the guide
call parse_task(task_sentence, room_receptacles); returns (entry, obj, target); then call entry(w, obj, target) from alfworld_household_rungs

Code

89 lines of python, hashed and signed below.

Show the code
import re

def parse_task(task: str, receptacles: list[str]) -> tuple[str, str, str]:
    t = task.strip().lower()
    if t.endswith('.'):
        t = t[:-1]

    recep_types = set()
    for r in receptacles:
        parts = r.split()
        if parts:
            recep_types.add(parts[0])

    def strip_plural(word, prefer_set=None):
        if not word:
            return word
        if prefer_set is not None:
            if word in prefer_set:
                return word
            if word.endswith('s') and word[:-1] in prefer_set:
                return word[:-1]
            if word.endswith('s'):
                return word[:-1]
            return word
        if word.endswith('s'):
            return word[:-1]
        return word

    obj = None
    target = None
    entry = None

    # --- examine / look-at cases ---
    if re.search(r'\bexamine\b', t) or 'look at' in t:
        entry = 'examine_in_light'
        m = re.search(r'examine the (\w+) with the (\w+)', t)
        if not m:
            m = re.search(r'look at (\w+) under the (\w+)', t)
        if not m:
            m = re.search(r'(\w+).*?(desklamp)', t)
        obj_raw, target_raw = m.group(1), m.group(2)
        obj = strip_plural(obj_raw)
        target = strip_plural(target_raw, recep_types)
        return entry, obj, target

    # --- determine entry type ---
    if re.search(r'\btwo\b', t):
        entry = 'pick_two_and_place'
    elif re.search(r'\bclean\b', t):
        entry = 'clean_and_place'
    elif re.search(r'\bhot\b', t) or re.search(r'\bheat\b', t):
        entry = 'heat_and_place'
    elif re.search(r'\bcool\b', t):
        entry = 'cool_and_place'
    else:
        entry = 'pick_and_place'

    # --- extract object and target using common patterns ---
    obj_raw = None
    target_raw = None

    # pattern: "clean/heat/cool some X and put it in Y"
    m = re.search(r'(?:clean|heat|cool) some (\w+) and put it in (\w+)', t)
    if m:
        obj_raw, target_raw = m.group(1), m.group(2)

    # pattern: "find two X and put them in Y"
    if obj_raw is None:
        m = re.search(r'find two (\w+) and put them in (\w+)', t)
        if m:
            obj_raw, target_raw = m.group(1), m.group(2)

    # pattern: "put a/some/two ... X in/on Y"
    if obj_raw is None:
        m = re.search(r'put (?:a |some |two )?(?:clean |hot |cool )?(\w+) (?:in|on) (\w+)', t)
        if m:
            obj_raw, target_raw = m.group(1), m.group(2)

    # generic fallback
    if obj_raw is None:
        words = re.findall(r'\w+', t)
        obj_raw = words[-3] if len(words) >= 3 else words[0]
        target_raw = words[-1]

    obj = strip_plural(obj_raw)
    target = strip_plural(target_raw, recep_types)

    return entry, obj, target

Certificate

Code sha256 4da333e9977fd2b6f3a501cc6065b3098d566371c66c265ad4f50de35eec6291
Signature ed25519, present
Last re-checked
passing

Re-checked today. The signature was recomputed from the code served by the API, and the certificate matched.

A certificate proves the code has not changed. Re-running the check proves it still works today, on today's libraries.