Steve AI Documentation

repository·main·Indexed 23 days ago

https://github.com/yuvdwi/steve

An autonomous AI agent system for Minecraft 1.20.1 (Forge) that allows players to control agents using natural language. Steve AI enables complex tasks such as mining, building, and combat through multi-agent coordination and LLM-driven planning. It supports OpenAI, Groq, and Gemini providers and utilizes a custom agent loop to generate action sequences for real-time gameplay.

Tokens
12.7K
Snippets
20
Records
45
Agent score
80%

What's inside Steve AI

  1. Understand the Steve AI Agent Capabilities

    main

    Steve AI is an autonomous AI agent for Minecraft (version 1.20.1) that can perform various tasks within the game loop. The system supports the following core actions:

    • Build: Constructing structures using procedural algorithms or NBT templates.
    • Mine: Extracting blocks from the environment.
    • Attack: Engaging in combat.
    • Pathfind: Navigating the terrain.
    • Follow: Tracking players or other entities.
    • Gather: Collecting resources.
    • Place: Placing specific blocks.
    • Craft: (Stubbed) Creating items.
  2. Use the ground-finding algorithm for building

    main

    When building, the agent needs to find a valid surface. The findGroundLevel algorithm scans for a suitable BlockPos using the following logic:

    1. Downward Scan: Scans up to 20 blocks below the startPos. It looks for a position where the current block is isAir() and the block directly below it isSolidGround().
    2. Upward Scan: If no ground is found below, it scans up to 10 blocks above the startPos to find a surface.
    3. Fallback: If both scans fail, it performs a continuous downward scan until it hits a solid block or reaches the world minimum Y-level (-64).

    Solid Ground Criteria: A block is considered solid ground if it is not air, water, or lava, and blockState.isSolid() returns true.

  3. How WorldKnowledge environmental scanning works

    main

    The WorldKnowledge component provides the agent with situational awareness through two main methods:

    1. Environmental Scanning (scanBlocks): Scans a 16-block radius. For performance, it samples every 2 blocks (an 8x8x8 grid) rather than every single block. It filters out air blocks to build a frequency map of nearby materials.
    2. Entity Detection (scanEntities): Uses an Axis-Aligned Bounding Box (AABB) inflated by the scanRadius to find nearby entities.

    These scans are used to generate contextual summaries (e.g., getNearbyBlocksSummary, getNearbyPlayerNames) which are then injected into the LLM prompt.

  4. How Steve AI works: High-level architecture

    main

    Steve AI operates as an autonomous agentic system integrated into the Minecraft game loop. The workflow follows a ReAct-inspired (Reason → Act → Observe) pattern:

    1. User Interface: Users interact via a sliding panel GUI (triggered by pressing K) or Minecraft chat commands (e.g., /steve spawn, /steve tell).
    2. Task Planner:
      • WorldKnowledge scans the environment within a 16-block radius.
      • PromptBuilder creates a context-rich prompt including position, nearby entities, blocks, and biome.
      • An LLM Client (Groq, OpenAI, or Gemini) processes the prompt and returns a JSON response.
      • ResponseParser extracts structured tasks from the JSON.
    3. Structured Task Queue: The parsed tasks are queued for execution.
    4. Action Executor: Manages the queue, creates BaseAction subclasses, and ticks them every game tick (50ms).
    5. Action Layer: Executes specific logic (e.g., BuildAction, MineAction, CombatAction, PathfindAction).
    6. Minecraft Integration: Actions interact directly with the game engine via level.setBlock(), PathfinderMob, doHurtTarget(), and level.getBlockState().
  5. Understand Agent Action Lifecycle and Execution

    main

    Agents operate within Minecraft's game loop using tick-based execution (50ms intervals). Actions are managed through a BaseAction abstract class and an action queue system.

    • Action Lifecycle: Every action implements three primary hooks:
      • onStart: Initialization logic.
      • onTick: Logic executed every 50ms.
      • onCancel: Cleanup logic if the action is interrupted.
    • Stuck Detection: To prevent agents from idling indefinitely, the system tracks position deltas. If an agent moves less than 0.1 blocks over 40 ticks, it is considered stuck and will be teleported.
    • Task Replanning: The action queue system validates tasks and triggers replanning automatically if an action fails.
  6. How mining actions navigate and excavate tunnels

    main

    The MineBlockAction implements intelligent mining through several key behaviors:

    • Intelligent Depth Navigation: The agent uses predefined ORE_DEPTHS to target specific Y-levels for different ores (e.g., diamond_ore at -59, coal_ore at 96).
    • Directional Tunneling: The mining direction is determined by the player's look angle. The agent starts mining 3 blocks in front of the player and finds solid ground to begin.
    • Tunnel Excavation: The agent excavates a 3-block-tall tunnel (center, above, and below the current position) to ensure clearance.
    • Ore Detection: The agent searches 20 blocks ahead in the current tunnel direction, checking the center, above, and below positions for the targetBlock.
    • Automatic Lighting: To prevent mob spawns, the agent checks the light level at its position. If it falls below MIN_LIGHT_LEVEL (8), it attempts to place a torch in an adjacent air block.
    // Ore depth mappings for intelligent mining
    private static final Map<String, Integer> ORE_DEPTHS = new HashMap<>() {{
        put("iron_ore", 64);
        put("deepslate_iron_ore", -16);
        put("coal_ore", 96);
        put("copper_ore", 48);
        put("gold_ore", 32);
        put("diamond_ore", -59);
        put("deepslate_diamond_ore", -59);
        put("redstone_ore", 16);
        put("emerald_ore", 256);
    }};
  7. How collaborative building works with BuildSection

    main

    In collaborative builds, a large construction plan is partitioned into quadrants (North-West, North-East, South-West, South-East). Each quadrant is managed by a BuildSection object.

    To ensure thread safety and prevent multiple agents from placing the same block, BuildSection uses Atomic Block Claiming. Each section maintains an AtomicInteger to track the next block index, allowing agents to claim blocks in a lock-free manner.

    Agents are assigned to sections using a two-pass logic:

    1. Primary Assignment: Find an unassigned section that is not yet complete.
    2. Load Balancing: If all sections have an agent, an additional agent can join an incomplete section to help finish it.

    Blocks within a section are sorted from bottom-to-top (Y-axis) to ensure structural integrity during construction.

    public static class BuildSection {
        public final int yLevel; // Section ID
        public final String sectionName;
        private final List<BlockPlacement> blocks;
        private final AtomicInteger nextBlockIndex; // Thread-safe counter
    
        public BlockPlacement getNextBlock() {
            int index = nextBlockIndex.getAndIncrement(); // Atomic increment
            if (index < blocks.size()) {
                return blocks.get(index);
            }
            return null; // Section complete
        }
    
        public int getBlocksPlaced() {
            return Math.min(nextBlockIndex.get(), blocks.size());
        }
    
        public boolean isComplete() {
            return nextBlockIndex.get() >= blocks.size();
        }
    }
  8. How the ground-finding algorithm locates build sites

    main

    The ground-finding algorithm ensures that structures are built on solid ground regardless of the player's current position (sky, underground, or in liquid). It uses a three-phase bidirectional scan:

    1. Downward Scan: Scans up to 20 blocks below the starting position looking for an air block sitting directly above a solid block.
    2. Upward Scan: If the player is underground, it scans up to 10 blocks above the starting position for a surface.
    3. Fallback: If neither scan succeeds, it descends until it hits a solid block or reaches the world height limit (Y > -64).

    Solid ground is validated by checking that the block is not air, water, or lava, and that Minecraft's built-in isSolid() check returns true.

    private BlockPos findGroundLevel(BlockPos startPos) {
        // Phase 1: Scan downward (most common case - player is above ground)
        for (int i = 0; i < 20; i++) {
            BlockPos checkPos = startPos.below(i);
            BlockPos belowPos = checkPos.below();
    
            if (isAir(checkPos) && isSolidGround(belowPos)) {
                return checkPos; // Found ground: air above solid block
            }
        }
    
        // Phase 2: Scan upward (player is underground)
        for (int i = 1; i < 10; i++) {
            BlockPos checkPos = startPos.above(i);
            BlockPos belowPos = checkPos.below();
    
            if (isAir(checkPos) && isSolidGround(belowPos)) {
                return checkPos; // Found surface
            }
        }
    
        // Phase 3: Fallback - keep descending until we hit something
        BlockPos fallbackPos = startPos;
        while (!isSolidGround(fallbackPos.below()) && fallbackPos.getY() > -64) {
            fallbackPos = fallbackPos.below();
        }
    
        return fallbackPos;
    }
    
    private boolean isSolidGround(BlockPos pos) {
        var blockState = level.getBlockState(pos);
        var block = blockState.getBlock();
    
        // Not solid if air or liquid
        if (blockState.isAir() || block == Blocks.WATER || block == Blocks.LAVA) {
            return false;
        }
    
        return blockState.isSolid(); // Minecraft's built-in solid check
    }
  9. How exponential backoff handles LLM API failures

    main

    To manage unpredictable LLM API failures (such as network issues, rate limits, or server errors), the system implements an exponential backoff retry logic.

    When a request returns a status code of 429 (rate limit) or 500+ (server error), the system waits for a delay calculated as INITIAL_RETRY_DELAY_MS * (2 ^ attempt). For example, with a 1s initial delay, retries occur after 1s, 2s, and 4s. This prevents hammering an overloaded server and significantly improves the success rate of API interactions.

    for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
        try {
            HttpResponse<String> response = client.send(request, ...);
    
            if (response.statusCode() == 200) {
                return parseResponse(response.body());
            }
    
            // Retry on rate limit or server error
            if (response.statusCode() == 429 || response.statusCode() >= 500) {
                if (attempt < MAX_RETRIES - 1) {
                    int delayMs = INITIAL_RETRY_DELAY_MS * (int) Math.pow(2, attempt);
                    // Retry after: 1s, 2s, 4s
                    Thread.sleep(delayMs);
                    continue;
                }
            }
    
            return null; // Non-retryable error
    
        } catch (Exception e) {
            // Network error - retry
        }
    }
  10. How Steve AI agents operate

    main

    Steve AI uses a direct action execution model designed for real-time Minecraft gameplay. Unlike traditional ReAct frameworks that use iterative observe-think-act cycles, Steve AI generates complete action sequences upfront to reduce latency and API calls.

    Core Execution Flow:

    1. Input: User input is captured via the GUI (press K).
    2. Planning: The task is sent to the TaskPlanner with conversation context.
    3. Reasoning: The configured LLM (Groq/OpenAI/Gemini) generates a structured action plan.
    4. Parsing: The ResponseParser extracts specific actions from the LLM response.
    5. Execution: The ActionExecutor processes actions through specialized action classes.
    6. Tick-based Processing: Actions execute tick-by-tick to prevent the game from freezing.
    7. Feedback: Results are fed back into the conversation memory for future context.
  11. How multi-agent coordination works via lock-free atomic operations

    main

    To prevent multiple agents from placing the same block twice when building the same structure, Steve AI uses lock-free atomic operations with spatial partitioning instead of traditional mutexes or synchronized blocks. This avoids deadlocks and reduces contention.

    Each section of a build plan uses an AtomicInteger to manage block indices. Agents use the getAndIncrement() method, which utilizes the hardware-level CMPXCHG instruction to ensure each agent receives a unique, sequential block index in $O(1)$ time complexity. This approach scales linearly with the number of agents and ensures thread safety even during parallel ticking.

    // Each section has an atomic counter
    private final AtomicInteger nextBlockIndex;
    
    public BlockPlacement getNextBlock() {
        // Atomic compare-and-swap - no locks needed
        int index = nextBlockIndex.getAndIncrement();
        if (index < blocks.size()) {
            return blocks.get(index);
        }
        return null;
    }
  12. How combat targeting and loops work in CombatAction

    main

    The CombatAction manages autonomous combat through a target acquisition and execution loop:

    • Target Acquisition: The agent searches within a 32-block radius (AABB). It filters for LivingEntity targets that are alive and not players or other SteveEntity instances.
    • Target Types: You can specify a targetType. Using any, mob, hostile, or monster will match any hostile mob. Specific entity type names can also be used.
    • Combat Loop:
      • The agent sprints towards the target using a high speed multiplier.
      • It attacks when within ATTACK_RANGE (3.5 blocks).
      • It performs an attack every 7 ticks (approximately 3 times per second).
    • Unstuck Logic: If the agent fails to move significantly for 2 seconds (40 ticks), it will teleport 4 blocks closer to the target to prevent getting stuck on terrain.
    // Target acquisition search radius
    AABB searchBox = steve.getBoundingBox().inflate(32.0);
    
    // Attack range
    if (distance <= ATTACK_RANGE) { // ATTACK_RANGE = 3.5 blocks
        steve.doHurtTarget(target);
        steve.swing(InteractionHand.MAIN_HAND, true);
    
        // Attack 3 times per second (every 6-7 ticks)
        if (ticksRunning % 7 == 0) {
            steve.doHurtTarget(target);
        }
    }