From 8bf90c70d29a9444531d2205f9032ba6a8b112c5 Mon Sep 17 00:00:00 2001 From: Pablomonte Date: Sun, 17 May 2026 06:40:20 -0300 Subject: [PATCH] fix(policy): add craft category + narrow inventory_query keyword scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GemmaPolicy's L4 classifier had two collaborating bugs that made any craft-style intent unable to dispatch the actual crafting tools: 1. **No 'craft' category existed**. Any captain-side intent like "Craft a stone shovel using cobblestone and sticks" had nothing to match against in the keyword table, so classification fell back to whichever later category matched first. 2. **'inventory_query' keyword 'inventory' was too broad**. It matched any intent that *mentioned* the word "inventory" anywhere — including normal craft/mine/build intents that include the perfectly natural "from my inventory" clause. The 'inventory_query' tool subset is only `[get_inventory, ask_clarification, report_execution_error]`, so the captain ended up with no actionable body tools at all. Observed end-to-end stack: Captain (Kimi-K2.6) emits embodied_plan(intent="Craft a stone shovel using cobblestone and sticks from my inventory.") → policy.classify_category() → loop hits 'inventory_query' via keyword 'inventory' → allowed_tools = [ask_clarification, get_inventory, report_execution_error] → Gemma-Andy plan: ask_clarification("craft_item is not available in this turn; should I wait?") → captain explains "no tengo capacidad de craftear" to the player The fix is additive in three places: a) New 'craft' category, inserted *before* 'mining'/'build' in the keyword list so it wins on overlap. Keywords cover ES+EN imperative forms ("craft", "craftear", "fabricar", "smelt", "fundir", "cocinar", "horno", "furnace", "forge"). b) CATEGORY_TOOLS['craft'] = [get_inventory, view_craftable, craft_item, smelt_item, check_furnace, take_from_furnace, equip_item, place_block, scan_nearby, goto]. Includes place_block because the agent may need to place a crafting_table/furnace first; includes goto/scan_nearby because the agent may need to find a crafting station. c) STRATEGY_MAP['craft'] = 'embodied_plan' — same execution method as every other category; included so the dispatcher doesn't tip-toe on a missing key. Bonus cleanups in the same patch (both load-bearing on this branch): - Tighten 'inventory_query' keywords: drop bare 'inventory', keep 'inventario' (Spanish exclusive), 'show inventory', 'list inventory', etc. Pure inventory-show intents still classify correctly. - Extend 'build' tools with 'fill_volume' and 'ignite'. The agent already has 'place_block'; adding 'fill_volume' enables bulk construction without dropping out of the build category, and 'ignite' covers torch/lava placement scenarios. - Extend 'build' keywords with 'fill ', 'rellena/rellená', 'construir' (infinitive — 'construí'/'construye' covered only imperative forms). Verification Bench from the live install (Hermes-K2.6 + DaemonCraft): >>> p = GemmaPolicy() >>> for it in ["Craft a stone shovel using cobblestone and sticks…", ... "craft 4 oak planks", ... "fundir 16 arena en vidrio", ... "construir una casa de 5x5", ... "fill volume con cobblestone", ... "mostrame el inventario", ... "inventory check"]: ... cat = p.classify_category(it.lower()) ... print(cat, len(p.get_allowed_tools(cat) or []) or 'ALL') craft 12 craft 12 craft 12 build 9 build 9 inventory_query 3 default 38 Before this patch, the first three returned `inventory_query 3`, so the agent literally could not dispatch craft_item. pytest tests/test_gemma_policy.py — 29/29 pass (no test fixtures covered craft/build keyword collision yet; follow-up PR can add regression cases). --- agents/gemma_policy.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/agents/gemma_policy.py b/agents/gemma_policy.py index 199d656..9c05f91 100644 --- a/agents/gemma_policy.py +++ b/agents/gemma_policy.py @@ -52,10 +52,11 @@ class GemmaPolicy: ("pickup", ["recogé", "recoge", "pickup", "agarrá", "agarra", "levantá", "levanta", "pick up"]), ("food", ["comé", "comer", "comelo", "eat ", "drink", "bebé", "bebe", "morder", "ingerir"]), ("memory", ["acordate", "marcá", "marca ", "recordá", "remember", "volvé a", "return to", "olvidá", "forget"]), + ("craft", ["craft ", "craftear", "crafteá", "craftea", "fabricar", "fabricá", "fabrica", "smelt", "fundir", "fundí", "fundi", "cocinar", "cociná", "cocina", "horno", "furnace", "forja", "forjar", "forge"]), ("mining", ["minar", "mine ", "conseguí", "consegui", "gather", "dig "]), - ("build", ["construí", "construye", "construí ", "pongá", "place ", "build ", "make a "]), + ("build", ["construí", "construye", "construí ", "pongá", "place ", "build ", "make a ", "rellena", "rellená", "fill ", "construir"]), ("combat", ["atacá", "ataca", "attack", "defendé", "defend", "raise_shield"]), - ("inventory_query", ["inventario", "inventory", "decime qué tenés", "decime que tenes", "mostrame el inventario", "what do you have", "show inventory"]), + ("inventory_query", ["inventario", "decime qué tenés", "decime que tenes", "mostrame el inventario", "what do you have", "show inventory", "list inventory", "list my items"]), ] CATEGORY_TOOLS = { @@ -67,8 +68,9 @@ class GemmaPolicy: "inventory_query": ["get_inventory"], "memory": ["remember_here", "goto_remembered_place", "forget_place", "get_inventory"], "food": ["consume_food", "get_inventory"], - "build": ["scan_nearby", "goto", "place_block", "equip_item", "get_inventory"], + "build": ["scan_nearby", "goto", "place_block", "fill_volume", "ignite", "equip_item", "get_inventory"], "combat": ["scan_nearby", "attack_entity", "flee_from", "raise_shield", "consume_food"], + "craft": ["get_inventory", "view_craftable", "craft_item", "smelt_item", "check_furnace", "take_from_furnace", "equip_item", "place_block", "scan_nearby", "goto"], } COMMON_SAFE = ["ask_clarification", "report_execution_error"] GUARDIAN_AWARE_CATEGORIES = {"navigation", "combat", "default"} @@ -84,6 +86,7 @@ class GemmaPolicy: "memory": "embodied_plan", "food": "embodied_plan", "build": "embodied_plan", + "craft": "embodied_plan", "combat": "embodied_plan", "default": "embodied_plan", }