Tools

Tools are functions that agents can call to interact with the world. Hugin provides built-in tools for common operations and makes it easy to create custom tools.

Tool Execution Flow

  1. Agent asks oracle what to do
  2. Oracle responds with a tool call
  3. Tool executes and returns result
  4. Result is pushed to stack
  5. Agent continues with new context

Built-in Tools

Located in gimle.hugin.tools.builtins:

Tool Description
finish Complete the current task
save_insight Save information to long-term memory (artifacts)
query_artifacts Search saved artifacts
get_artifact_content Retrieve a specific artifact
ask_human Request input from a human
create_branch Create a parallel exploration branch
call_agent Invoke another agent

Creating Custom Tools

A tool consists of two files:

Python Implementation

tools/greet.py:

def greet(stack, name: str, formal: bool = False) -> str:
    """
    Greet someone by name.

    Args:
        stack: The agent's interaction stack (auto-injected)
        name: The name of the person to greet
        formal: Whether to use formal greeting

    Returns:
        A greeting string
    """
    if formal:
        return f"Good day, {name}."
    return f"Hello, {name}!"

YAML Definition

tools/greet.yaml:

name: greet
description: Greet someone by name
parameters:
  - name: name
    type: string
    description: The name of the person to greet
    required: true
  - name: formal
    type: boolean
    description: Whether to use formal greeting
    required: false
    default: false
implementation: greet:greet

Parameter Types

Type Description
string Text value
integer Whole number
number Decimal number
boolean True/false
array List of values
object Nested structure

Adding Tools to Agents

Reference tools in your agent config:

# configs/my_agent.yaml
name: my_agent
tools:
  # Built-in tools
  - builtins.finish:finish
  - builtins.save_insight:save_insight

  # Custom tools from tools/ directory
  - greet:greet
  - query_database:query_database

Accessing Context in Tools

The stack parameter provides full access to agent context:

def my_tool(stack, param: str) -> str:
    # Access the agent
    agent = stack.agent

    # Access environment variables
    api_key = agent.environment.env_vars.get("API_KEY")

    # Access shared state
    state = stack.get_shared_state("my_namespace")

    # Access storage
    storage = agent.environment.storage

    # Access other registries
    config_registry = agent.environment.config_registry

    return "result"

Tool Chaining

Tools can trigger other tools using next_tool:

# tools/process_data.yaml
name: process_data
description: Process and then analyze data
implementation: process_data:process_data
next_tool: analyze_results  # Automatically call this tool next

This creates deterministic pipelines where one tool always leads to another.

Interactive Tools

Some tools require human interaction:

def approve_action(stack, action: str) -> str:
    """Request human approval for an action."""
    # This will pause and wait for human input
    stack.push(AskHuman(
        prompt=f"Do you approve this action: {action}?",
        options=["yes", "no"]
    ))
    # Execution continues when human responds
    return "Waiting for approval..."

Error Handling

Tools should handle errors gracefully:

def query_database(stack, query: str) -> str:
    try:
        result = execute_query(query)
        return f"Query returned {len(result)} rows"
    except DatabaseError as e:
        return f"Error executing query: {e}"

The result (including errors) is always pushed to the stack, allowing the agent to adapt its behavior.