Page:
Data Flow 06 Tool Execution
Pages
Architecture Commands and API
Architecture Context System
Architecture Core Engine
Architecture Event Sourcing
Architecture Generative Reflection
Architecture Helpers
Architecture Journal System
Architecture LLM Interaction
Architecture LLM Providers
Architecture Logging
Architecture Memory and Sleep
Architecture Overview
Architecture Persona Protection
Architecture Prompt System
Architecture RAG Implementation
Architecture Resilience System
Architecture Safety System
Architecture Self Management
Architecture Sub Agent Delegation
Architecture Task Assessment
Architecture Token Management
Architecture Tool System
Configuration Reference
Context and Memory Flow Analysis
Data Flow 01 Context Compaction
Data Flow 02 ReAct Loop
Data Flow 03 Memory Consolidation
Data Flow 04 Message Classification
Data Flow 05 Entity Profile System
Data Flow 06 Tool Execution
Data Flow 07 Sleep Mode Transitions
Data Flow 08 LLM Provider Interaction
Data Flow 09 Self Management Operations
Home
LLM Decision Patterns
Research Foundations
User Guide 00 Index
User Guide 01 Getting Started
User Guide 02 Configuration and Customization
User Guide 03 Advanced Capabilities
User Guide 04 Troubleshooting
No results
1
Data Flow 06 Tool Execution
blightbow edited this page 2025-12-07 00:47:17 +00:00
Table of Contents
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.
Related Documents
| 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