1 Data Flow 06 Tool Execution
blightbow edited this page 2025-12-07 00:47:17 +00:00

Data Flow 06: Tool Execution

Engineering documentation series - Data flows in the AI Assistant system


Overview

Tool execution handles the actual invocation of tools with timeout handling, retry logic, validation, caching, and conversation history management.

Document Description
Data-Flow-02-ReAct-Loop Loop that calls tool execution

1. Tool Execution Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ TOOL EXECUTION START                                                        │
│ tool_execution.py:execute_tool_call() line 52                               │
│ Input: script, character, tool_call                                         │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 1: EXTRACT TOOL INFO                                                   │
│ ─────────────────────────────────────────────────────────────────────────── │
│ tool_name = tool_call.get("tool")                                           │
│ parameters = tool_call.get("parameters", {})                                │
│ reasoning = tool_call.get("reasoning", "")                                  │
│                                                                             │
│ Defensive parsing:                                                          │
│   If parameters empty, check for top-level params (LLM format error)        │
│   Auto-correct and log warning                                              │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 2: LOG DECISION                                                        │
│ ─────────────────────────────────────────────────────────────────────────── │
│ logger.log_info(f"Assistant decision: {tool_name} - {reasoning}")           │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 3: UPDATE CONVERSATION HISTORY                                         │
│ ─────────────────────────────────────────────────────────────────────────── │
│ Skip if: in_reflection mode (meta-level, shouldn't pollute history)         │
│                                                                             │
│ Native tool calling format:                                                 │
│   Add assistant message with tool_calls array                               │
│                                                                             │
│ Fallback format:                                                            │
│   Add assistant message with JSON in content                                │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 4: LOOKUP TOOL                                                         │
│ ─────────────────────────────────────────────────────────────────────────── │
│ tool = TOOL_REGISTRY.get_tool(tool_name)                                    │
│                                                                             │
│ If not found: return error result                                           │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 5: VALIDATE PARAMETERS                                                 │
│ ─────────────────────────────────────────────────────────────────────────── │
│ validation = tool.validate_parameters(parameters)                           │
│                                                                             │
│ If not valid: return error with validation.errors                           │
│                                                                             │
│ Validation checks:                                                          │
│   - Required parameters present                                             │
│   - Type correctness                                                        │
│   - Value constraints (enums, ranges)                                       │
│   - Parameter dependencies                                                  │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 6: CHECK CACHE (if cacheable)                                          │
│ ─────────────────────────────────────────────────────────────────────────── │
│ If tool.cacheable:                                                          │
│   cache_key = get_cache_key(tool_name, parameters)                          │
│   cached = tool_cache.get(cache_key, scope=tool.cache_scope)                │
│   If cached: return cached result (skip execution)                          │
│                                                                             │
│ Cache scopes:                                                               │
│   - "tick": Valid only during current tick                                  │
│   - "session": Valid until server restart                                   │
│   - "persistent": Stored in database                                        │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 7: EXECUTE WITH TIMEOUT + RETRY                                        │
│ ─────────────────────────────────────────────────────────────────────────── │
│                                                                             │
│ RETRY LOOP (max 4 attempts):                                                │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 1. Filter parameters (remove unexpected keys)                           │ │
│ │                                                                         │ │
│ │ 2. Execute with timeout:                                                │ │
│ │    tool_d = defer.maybeDeferred(tool.execute, character, **params)      │ │
│ │    tool_d.addTimeout(timeout, reactor, onTimeoutCancel=on_timeout)      │ │
│ │                                                                         │ │
│ │ 3. Check result:                                                        │ │
│ │    If success: break loop, return result                                │ │
│ │    If retryable error: wait with backoff, retry                         │ │
│ │    If non-retryable: break loop, return error                           │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│ Retry config:                                                               │
│   max_attempts: 4                                                           │
│   backoff_base: 1.0s                                                        │
│   backoff_max: 10.0s                                                        │
│   backoff_multiplier: 2.0                                                   │
│   jitter: True                                                              │
│                                                                             │
│ Retryable errors:                                                           │
│   - timeout, connection, network                                            │
│   - temporary, rate limit, try again                                        │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 8: POST-EXECUTION                                                      │
│ ─────────────────────────────────────────────────────────────────────────── │
│ If success:                                                                 │
│   - Increment activity_since_wake (sleep safeguard)                         │
│   - Cache result if tool.cacheable                                          │
│   - Store in script.db.current_tool_call                                    │
│                                                                             │
│ If failure:                                                                 │
│   - Return error with retry count                                           │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 9: ADD TOOL RESULT TO HISTORY                                          │
│ ─────────────────────────────────────────────────────────────────────────── │
│ Native format:                                                              │
│   Add tool message with tool_call_id reference                              │
│                                                                             │
│ Fallback format:                                                            │
│   Add user message with [TOOL RESULT] prefix                                │
└─────────────────────────────────────────────────────────────────────────────┘

2. Tool Categories

┌─────────────────────────────────────────────────────────────────────────────┐
│ TOOL CATEGORIES                                                             │
│ tools/base.py:ToolCategory                                                  │
│ ─────────────────────────────────────────────────────────────────────────── │
│                                                                             │
│ SAFE_CHAIN                                                                  │
│   - Can be chained freely in ReAct loop                                     │
│   - No side effects or low-risk                                             │
│   - Examples: inspect_location, recall_memories, search_objects             │
│                                                                             │
│ TERMINAL                                                                    │
│   - Ends ReAct loop to await response                                       │
│   - Outputs to players/channels                                             │
│   - Examples: say, page, whisper, emote                                     │
│                                                                             │
│ DANGEROUS                                                                   │
│   - Single execution per tick                                               │
│   - Modifies game state                                                     │
│   - Examples: execute_command, set_attribute                                │
│                                                                             │
│ ASYNC_REQUIRED                                                              │
│   - Network calls or long operations                                        │
│   - Continues in loop (doesn't terminate)                                   │
│   - Examples: store_memory, delegate_task, web_search                       │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

3. Tool Validation

ValidationResult Structure

@dataclass
class ValidationResult:
    valid: bool
    errors: list[str] = field(default_factory=list)
    warnings: list[str] = field(default_factory=list)
    cleaned_params: dict = field(default_factory=dict)

Validation Stages

┌─────────────────────────────────────────────────────────────────────────────┐
│ PARAMETER VALIDATION                                                        │
│ ─────────────────────────────────────────────────────────────────────────── │
│                                                                             │
│ Stage 1: Required Check                                                     │
│   For each param in tool.parameters where required=True:                    │
│     If param not in provided: add error                                     │
│                                                                             │
│ Stage 2: Type Validation                                                    │
│   For each provided param:                                                  │
│     Check type matches schema (string, integer, boolean, etc.)              │
│     Handle type coercion where safe (e.g., "5" → 5)                         │
│                                                                             │
│ Stage 3: Constraint Validation                                              │
│   - Enum: value in allowed list                                             │
│   - Range: min <= value <= max                                              │
│   - Pattern: regex match                                                    │
│   - Custom: tool-specific validation                                        │
│                                                                             │
│ Stage 4: Dependency Check                                                   │
│   - Conditional requirements (if X then Y required)                         │
│   - Mutual exclusion (X or Y, not both)                                     │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

4. Tool Result Validation

┌─────────────────────────────────────────────────────────────────────────────┐
│ RESULT VALIDATION                                                           │
│ tool_execution.py:validate_tool_result()                                    │
│ ─────────────────────────────────────────────────────────────────────────── │
│                                                                             │
│ Check 1: Result is not None                                                 │
│ Check 2: Result is dict type                                                │
│ Check 3: "success" field is bool (if present)                               │
│ Check 4: "error" field is string (if present)                               │
│                                                                             │
│ Standard result structure:                                                  │
│   {                                                                         │
│     "success": True/False,                                                  │
│     "error": "message" (if failed),                                         │
│     "data": {...} (tool-specific output)                                    │
│   }                                                                         │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

5. Tool Registry

┌─────────────────────────────────────────────────────────────────────────────┐
│ TOOL REGISTRY                                                               │
│ tools/base.py:ToolRegistry                                                  │
│ ─────────────────────────────────────────────────────────────────────────── │
│                                                                             │
│ Registration:                                                               │
│   TOOL_REGISTRY.register(MyTool)                                            │
│   - Validates tool has required attributes                                  │
│   - Stores by name for lookup                                               │
│                                                                             │
│ Lookup:                                                                     │
│   tool = TOOL_REGISTRY.get_tool("tool_name")                                │
│   tools = TOOL_REGISTRY.get_tool_names_by_category(ToolCategory.SAFE_CHAIN) │
│   all_tools = TOOL_REGISTRY.get_all_tools()                                 │
│                                                                             │
│ Schema Generation:                                                          │
│   schemas = TOOL_REGISTRY.get_tool_schemas()                                │
│   - Returns OpenAI-compatible function schemas                              │
│   - Used in LLM tool calling                                                │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

6. Tool Base Class

# tools/base.py

class Tool:
    """Base class for all tools."""

    name: str                    # Unique identifier
    description: str             # Human-readable description
    parameters: dict             # JSON Schema for parameters
    category: ToolCategory       # SAFE_CHAIN, TERMINAL, etc.

    # Optional attributes
    cacheable: bool = False      # Can results be cached?
    cache_scope: str = "tick"    # tick, session, persistent
    cache_ttl: int = None        # Cache time-to-live (seconds)
    requires_character: bool = True  # Needs character context?

    def execute(self, character, **params) -> dict:
        """Execute the tool. Override in subclass."""
        raise NotImplementedError

    def validate_parameters(self, params: dict) -> ValidationResult:
        """Validate parameters against schema."""
        # Default implementation validates against self.parameters

7. Conversation History Format

Native Tool Calling

# Assistant message with tool call
{
    "role": "assistant",
    "content": None,
    "tool_calls": [
        {
            "id": "call_abc123",
            "type": "function",
            "function": {
                "name": "inspect_location",
                "arguments": "{\"detail_level\": \"full\"}"
            }
        }
    ]
}

# Tool result message
{
    "role": "tool",
    "tool_call_id": "call_abc123",
    "content": "{\"success\": true, \"location\": \"Town Square\", ...}"
}

Fallback Format

# Assistant message with JSON
{
    "role": "assistant",
    "content": "{\"tool\": \"inspect_location\", \"parameters\": {...}, \"reasoning\": \"...\"}"
}

# Tool result as user message
{
    "role": "user",
    "content": "[TOOL RESULT: inspect_location]\n{\"success\": true, ...}"
}

8. Key Files

File Purpose
tool_execution.py:52-277 execute_tool_call()
tool_execution.py:280-310 is_retryable_error()
tool_execution.py:325-378 validate_tool_result()
tools/base.py Tool, ToolRegistry, ToolCategory, ValidationResult
tools/__init__.py TOOL_REGISTRY, check_loop_continuation()

Document created: 2025-12-06