Files
Mortdecai/agent/tools/tool_schemas.py
T
Seth ee764cd22a Tool-calling training: 1,159 multi-turn examples with error correction
Tool schemas (agent/tools/tool_schemas.py):
- rcon.execute: execute commands, get success/error results
- minecraft.wiki_lookup: look up syntax and item info
- world.player_info: player health, position, inventory
- world.server_state: time, weather, online players
- 10 RCON error patterns with corrections
- 12 common error scenarios for training

Training data generator (training/scripts/generate_tool_training.py):
- Converts seed dataset to multi-turn tool conversations
- Error correction: model tries wrong command → gets error → self-corrects
- Wiki/player/server lookups for uncertainty scenarios
- Qwen3 native tool-calling format with <tool_call> tags

1,159 examples: 1043 success, 79 error correction, 24 error scenarios,
13 tool lookups. Ready for v4 training.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 18:49:08 -04:00

409 lines
17 KiB
Python

"""
Tool schemas for the Minecraft AI God model.
Defines all tools the model can call during inference, plus common RCON error
patterns used for generating error-correction training data.
Exports:
TOOL_SCHEMAS — Python list of dicts (name, description, parameters in JSON Schema)
QWEN3_TOOLS — Same tools formatted for Qwen3 chat template
RCON_ERROR_PATTERNS — Dict mapping RCON error substrings to correction metadata
"""
from typing import List, Dict, Any
# ---------------------------------------------------------------------------
# Tool definitions (canonical source of truth)
# ---------------------------------------------------------------------------
TOOL_SCHEMAS: List[Dict[str, Any]] = [
{
"name": "rcon.execute",
"description": (
"Execute a Minecraft command via RCON on the server. "
"Returns whether the command succeeded and the server's response text. "
"Commands should NOT start with a leading slash."
),
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The Minecraft command to execute (no leading slash)."
}
},
"required": ["command"],
"additionalProperties": False
},
"returns": {
"type": "object",
"properties": {
"success": {"type": "boolean"},
"result": {"type": "string"}
}
}
},
{
"name": "minecraft.wiki_lookup",
"description": (
"Look up command syntax, item info, or game mechanics from the "
"Minecraft Wiki. Use this when you are unsure about exact syntax, "
"item IDs, enchantment names, effect names, or entity types."
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query (e.g. 'give command syntax', 'trident enchantments')."
}
},
"required": ["query"],
"additionalProperties": False
},
"returns": {
"type": "object",
"properties": {
"content": {"type": "string"},
"url": {"type": "string"}
}
}
},
{
"name": "world.player_info",
"description": (
"Get a player's current state including health, position, and a "
"summary of their inventory. Requires the player to be online."
),
"parameters": {
"type": "object",
"properties": {
"player": {
"type": "string",
"description": "The player's in-game name (case-sensitive)."
}
},
"required": ["player"],
"additionalProperties": False
},
"returns": {
"type": "object",
"properties": {
"health": {"type": "number"},
"position": {
"type": "object",
"properties": {
"x": {"type": "integer"},
"y": {"type": "integer"},
"z": {"type": "integer"}
}
},
"inventory_summary": {"type": "string"}
}
}
},
{
"name": "world.server_state",
"description": (
"Get the current server state: time of day, weather, online players, "
"and world border size. No parameters required."
),
"parameters": {
"type": "object",
"properties": {},
"required": [],
"additionalProperties": False
},
"returns": {
"type": "object",
"properties": {
"time_of_day": {"type": "string"},
"weather": {"type": "string"},
"online_players": {
"type": "array",
"items": {"type": "string"}
},
"world_border": {"type": "number"}
}
}
},
]
# ---------------------------------------------------------------------------
# Qwen3 tool format (for chat template injection)
# ---------------------------------------------------------------------------
def _to_qwen3_tool(schema: Dict[str, Any]) -> Dict[str, Any]:
"""Convert a tool schema dict to the Qwen3 function-calling format."""
return {
"type": "function",
"function": {
"name": schema["name"],
"description": schema["description"],
"parameters": schema["parameters"],
}
}
QWEN3_TOOLS: List[Dict[str, Any]] = [_to_qwen3_tool(t) for t in TOOL_SCHEMAS]
def qwen3_tools_block() -> str:
"""Return the tools block string to inject into the system prompt for Qwen3."""
import json
lines = ["# Tools", "", "You may call one or more functions to assist.",
"The available tools are:", ""]
for tool in QWEN3_TOOLS:
lines.append(json.dumps(tool, indent=2))
lines.append("")
return "\n".join(lines)
# ---------------------------------------------------------------------------
# Common RCON error patterns and their corrections
# ---------------------------------------------------------------------------
RCON_ERROR_PATTERNS: Dict[str, Dict[str, Any]] = {
# --- Syntax / structural errors ---
"Unknown or incomplete command": {
"type": "syntax_error",
"description": "Command is malformed, misspelled, or missing subcommands.",
"common_causes": [
"Missing 'give' subcommand in effect (e.g. 'effect player' instead of 'effect give player')",
"Invalid weather value (e.g. 'weather storm' instead of 'weather thunder')",
"Old NBT syntax that the parser cannot parse",
"Missing minecraft: namespace prefix",
],
"example_wrong": "effect slingshooter08 minecraft:strength 300 2",
"example_right": "effect give slingshooter08 minecraft:strength 300 2",
},
"Incorrect argument for command": {
"type": "wrong_argument",
"description": "A command argument has an invalid type or value.",
"common_causes": [
"Wrong gamemode string (e.g. 's' instead of 'survival')",
"Non-integer where integer expected",
"Invalid enchantment or effect name",
],
"example_wrong": "gamemode s slingshooter08",
"example_right": "gamemode survival slingshooter08",
},
"Expected whitespace": {
"type": "missing_space",
"description": "Parser expected a space between tokens but found something else.",
"common_causes": [
"Missing space before count in give command",
"Squashed arguments with no separator",
],
"example_wrong": "give slingshooter08 minecraft:diamond_sword1",
"example_right": "give slingshooter08 minecraft:diamond_sword 1",
},
"Unknown item 'minecraft:": {
"type": "invalid_item",
"description": "The item ID does not exist in 1.21.",
"common_causes": [
"Using generic name (bed -> white_bed, log -> oak_log, wood -> oak_planks)",
"Misspelled item ID",
"Using a removed or renamed item",
],
"corrections": {
"minecraft:bed": "minecraft:white_bed",
"minecraft:log": "minecraft:oak_log",
"minecraft:wood": "minecraft:oak_planks",
"minecraft:plank": "minecraft:oak_planks",
"minecraft:stone_brick": "minecraft:stone_bricks",
"minecraft:wooden_sword": "minecraft:wooden_sword",
"minecraft:iron": "minecraft:iron_ingot",
"minecraft:gold": "minecraft:gold_ingot",
"minecraft:diamond": "minecraft:diamond",
"minecraft:notch_apple": "minecraft:enchanted_golden_apple",
"minecraft:gapple": "minecraft:golden_apple",
"minecraft:grass": "minecraft:short_grass",
"minecraft:boat": "minecraft:oak_boat",
},
"example_wrong": "give slingshooter08 minecraft:bed 1",
"example_right": "give slingshooter08 minecraft:white_bed 1",
},
"No player was found": {
"type": "player_not_found",
"description": "The specified player is not online or the name is wrong.",
"common_causes": [
"Player logged off",
"Case-sensitive name mismatch",
"Typo in player name",
],
"example_wrong": "give Slingshooter08 minecraft:diamond 1",
"example_right": "give slingshooter08 minecraft:diamond 1",
},
"That position is not loaded": {
"type": "unloaded_chunk",
"description": "The target coordinates are in an unloaded chunk.",
"common_causes": [
"Filling/placing blocks far from any player",
"Teleporting to unloaded area without a player there",
],
"example_wrong": "setblock 50000 64 50000 minecraft:diamond_block",
"example_right": "execute at slingshooter08 run setblock ~ ~1 ~ minecraft:diamond_block",
},
"Invalid or unknown enchantment": {
"type": "invalid_enchantment",
"description": "The enchantment name is wrong or incompatible with the item.",
"common_causes": [
"Typo in enchantment name",
"Using an enchantment on an incompatible item (e.g. sharpness on a bow)",
"Old enchantment names",
],
"example_wrong": "give slingshooter08 minecraft:bow[enchantments={sharpness:5}] 1",
"example_right": "give slingshooter08 minecraft:bow[enchantments={power:5}] 1",
},
"Could not parse the remainder": {
"type": "nbt_parse_error",
"description": "The NBT/component data could not be parsed.",
"common_causes": [
"Old {Enchantments:[{id:...,lvl:...}]} NBT syntax instead of 1.21 component syntax",
"Mismatched brackets or braces",
"Extra trailing data",
],
"example_wrong": "give slingshooter08 minecraft:diamond_sword{Enchantments:[{id:sharpness,lvl:5}]} 1",
"example_right": "give slingshooter08 minecraft:diamond_sword[enchantments={sharpness:5}] 1",
},
"Unknown effect 'minecraft:": {
"type": "invalid_effect",
"description": "The effect ID does not exist.",
"common_causes": [
"Misspelled effect name",
"Using display name instead of ID (e.g. 'haste' is correct, not 'fast_digging')",
],
"corrections": {
"minecraft:fast_digging": "minecraft:haste",
"minecraft:slow_digging": "minecraft:mining_fatigue",
"minecraft:confusion": "minecraft:nausea",
"minecraft:damage_boost": "minecraft:strength",
"minecraft:harm": "minecraft:instant_damage",
"minecraft:heal": "minecraft:instant_health",
},
"example_wrong": "effect give slingshooter08 minecraft:fast_digging 300 2",
"example_right": "effect give slingshooter08 minecraft:haste 300 2",
},
"Unknown entity type": {
"type": "invalid_entity",
"description": "The entity type does not exist in 1.21.",
"common_causes": [
"Missing minecraft: prefix",
"Old entity name (e.g. 'PigZombie' -> 'zombified_piglin')",
],
"corrections": {
"minecraft:pigzombie": "minecraft:zombified_piglin",
"minecraft:pig_zombie": "minecraft:zombified_piglin",
"minecraft:zombie_pigman": "minecraft:zombified_piglin",
},
"example_wrong": "summon minecraft:pig_zombie ~ ~ ~",
"example_right": "summon minecraft:zombified_piglin ~ ~ ~",
},
}
# ---------------------------------------------------------------------------
# Specific error scenarios for training data generation
# ---------------------------------------------------------------------------
ERROR_SCENARIOS: List[Dict[str, Any]] = [
{
"id": "missing_prefix",
"description": "Missing minecraft: prefix on item ID",
"wrong_command": "give {player} diamond_sword 1",
"error_message": "Unknown or incomplete command, see below for error at position ...",
"correct_command": "give {player} minecraft:diamond_sword 1",
"reasoning": "Item IDs require the minecraft: namespace prefix in 1.21+.",
},
{
"id": "old_nbt_enchantments",
"description": "Old NBT enchantment syntax instead of 1.21 component syntax",
"wrong_command": 'give {player} minecraft:diamond_sword{{Enchantments:[{{id:"minecraft:sharpness",lvl:5}}]}} 1',
"error_message": "Could not parse the remainder of the data tag",
"correct_command": "give {player} minecraft:diamond_sword[enchantments={{sharpness:5,unbreaking:3,fire_aspect:2}}] 1",
"reasoning": "1.21 uses component syntax item[enchantments={name:level}] not old NBT {Enchantments:[...]}.",
},
{
"id": "invalid_effect_name",
"description": "Invalid or old effect name",
"wrong_command": "effect give {player} minecraft:fast_digging 300 2",
"error_message": "Unknown effect 'minecraft:fast_digging'",
"correct_command": "effect give {player} minecraft:haste 300 2",
"reasoning": "The effect is called 'haste' in 1.21, not 'fast_digging'.",
},
{
"id": "wrong_item_bed",
"description": "Generic 'bed' instead of color-specific bed",
"wrong_command": "give {player} minecraft:bed 1",
"error_message": "Unknown item 'minecraft:bed'",
"correct_command": "give {player} minecraft:white_bed 1",
"reasoning": "In 1.13+, beds require a color prefix. 'bed' is not a valid item; use 'white_bed', 'red_bed', etc.",
},
{
"id": "wrong_item_log",
"description": "Generic 'log' instead of wood-specific log",
"wrong_command": "give {player} minecraft:log 64",
"error_message": "Unknown item 'minecraft:log'",
"correct_command": "give {player} minecraft:oak_log 64",
"reasoning": "In 1.13+, logs require a wood type prefix. Use 'oak_log', 'birch_log', etc.",
},
{
"id": "count_wrong_position",
"description": "Count placed before item in give command",
"wrong_command": "give {player} 64 minecraft:diamond",
"error_message": "Incorrect argument for command at position ...: Expected item, got '64'",
"correct_command": "give {player} minecraft:diamond 64",
"reasoning": "Give command syntax is: give <player> <item> [count]. Count comes after item, not before.",
},
{
"id": "effect_missing_give",
"description": "Missing 'give' subcommand in effect command",
"wrong_command": "effect {player} minecraft:speed 300 2",
"error_message": "Unknown or incomplete command, see below for error at position ...",
"correct_command": "effect give {player} minecraft:speed 300 2",
"reasoning": "In 1.21, effect requires a subcommand: 'effect give', 'effect clear'. Bare 'effect <player>' is invalid.",
},
{
"id": "weather_storm",
"description": "Invalid weather value 'storm'",
"wrong_command": "weather storm",
"error_message": "Unknown or incomplete command, see below for error at position ...",
"correct_command": "weather thunder",
"reasoning": "Valid weather values are: clear, rain, thunder. 'storm' is not valid; use 'thunder'.",
},
{
"id": "gamemode_abbreviation",
"description": "Gamemode abbreviation instead of full name",
"wrong_command": "gamemode c {player}",
"error_message": "Incorrect argument for command at position ...: Invalid game mode 'c'",
"correct_command": "gamemode creative {player}",
"reasoning": "Gamemode requires full names in 1.21: survival, creative, adventure, spectator. Abbreviations are invalid.",
},
{
"id": "wrong_item_grass",
"description": "Old 'grass' item renamed to 'short_grass'",
"wrong_command": "give {player} minecraft:grass 64",
"error_message": "Unknown item 'minecraft:grass'",
"correct_command": "give {player} minecraft:short_grass 64",
"reasoning": "In 1.20.3+, 'grass' was renamed to 'short_grass'. The block 'grass_block' is separate.",
},
{
"id": "summon_no_prefix",
"description": "Summon without minecraft: prefix",
"wrong_command": "summon zombie ~ ~ ~",
"error_message": "Unknown entity type: zombie",
"correct_command": "summon minecraft:zombie ~ ~ ~",
"reasoning": "Entity types require the minecraft: namespace prefix.",
},
{
"id": "old_zombie_pigman",
"description": "Old zombie pigman name",
"wrong_command": "summon minecraft:zombie_pigman ~ ~ ~",
"error_message": "Unknown entity type: minecraft:zombie_pigman",
"correct_command": "summon minecraft:zombified_piglin ~ ~ ~",
"reasoning": "Zombie pigmen were renamed to zombified piglins in 1.16.",
},
]