tools

Production

The tools module spans internal/apitools (re-exported via sdk/api as tools) (the public Tool interface, Registry, ToolFunc, and re-exports of the planner), internal/tools/resources (the internal core.Registry and builtin tool implementations), and internal/tools/planner (the intent-based capability planner and execution bridge).

Responsibility

  • Define the 5-method Tool interface (Name, Description, Parameters, Execute, Capabilities) and the ToolFunc adapter for turning a plain function into a tool.
  • NewRegistry() auto-registers all 9 built-in tools on creation so callers get a ready registry with zero setup.
  • Ship 9 built-in tools: calculator, hash_tool, string_utils, pdf_tool, id_generator (from internal/tools/resources/builtin/*) plus regex, json_tools, web_search, file_tools (self-contained legacy tools).
  • Bridge to the internal core.Registry via toolAdapter/coreAdapter so the same tools work in both API surfaces.
  • Provide a capability Planner (intent analyze, decompose, resolve, score, plan) and a Bridge that executes by name with planner fallback.

Architecture

flowchart TD
    Caller([Caller]) --> Reg["tools.NewRegistry()"]
    Reg --> Auto["RegisterBuiltinTools"]
    Auto --> BI1["internal: calculator, hash_tool, string_utils, pdf_tool, id_generator"]
    Auto --> BI2["legacy: regex, json_tools, web_search, file_tools"]
    Reg -->|"Get/Execute"| Tool["Tool interface"]
    Tool --> Adapter["toolAdapter / coreAdapter"]
    Adapter --> CoreReg["internal core.Registry"]
    Caller --> Plan["tools.NewPlanner(reg)"]
    Plan --> Analyze["SemanticAnalyzer"]
    Analyze --> CapPlan["CapabilityPlanner"]
    CapPlan --> Resolver["ToolResolver"]
    Resolver --> Score["EvidenceScorer"]
    Score --> ExecPlan["ExecutionPlanner"]
    Caller --> Bridge["tools.NewBridge(reg, plan)"]
    Bridge -->|"named tool found"| Direct["direct Execute"]
    Bridge -->|"not found"| Plan
    Bridge --> Store["EvidenceStore (feedback loop)"]

External interfaces

// api/tools
type Result struct {
    Success bool `json:"success"`
    Data    any  `json:"data,omitempty"`
}

type Tool interface {
    Name() string
    Description() string
    Parameters() map[string]any
    Execute(ctx context.Context, params map[string]any) (Result, error)
    Capabilities() []string
}

type ToolFunc struct {
    ToolName   string
    ToolDesc   string
    ToolParams map[string]any
    Fn         func(ctx context.Context, params map[string]any) (any, error)
}

type ToolInfo struct {
    Name        string `json:"name"`
    Description string `json:"description"`
}

type Registry struct { /* unexported */ }
func NewRegistry() *Registry
func NewEmptyRegistry() *Registry
func RegisterBuiltinTools(r *Registry) error
func (r *Registry) Register(tool Tool) error
func (r *Registry) Unregister(name string) error
func (r *Registry) Get(name string) (Tool, bool)
func (r *Registry) Execute(ctx context.Context, name string, params map[string]any) (Result, error)
func (r *Registry) List() []string
func (r *Registry) ListTools() []ToolInfo
func (r *Registry) ListToolNames() []string
func (r *Registry) GetToolCapabilities(name string) ([]string, error)
func (r *Registry) PlannerProvider() *RegistryPlannerProvider

// Planner / Bridge (re-exports from internal/tools/planner)
type Planner = planner.Planner
type ExecutionPlan = planner.ExecutionPlan
type Bridge = planner.ToolExecutionBridge

func NewPlanner(r *Registry) (*Planner, error)
func NewBridge(r *Registry, p *Planner) (*Bridge, error)

Key types and methods

TypeMethodPurpose
tools.Toolinterface (5 methods)Contract every tool implements.
tools.ToolFuncfield FnAdapter: wrap func(ctx, params) (any, error) as a Tool.
tools.Resultfields Success, DataExecution outcome; errors are returned as Result{Success:false}.
tools.RegistryNewRegistry()Pre-populated with all 9 builtins via RegisterBuiltinTools.
tools.RegistryNewEmptyRegistry()Empty registry for custom-only environments.
tools.RegistryRegister(Tool) / Unregister(name)Add/remove tools; syncs the cached core.Registry.
tools.RegistryExecute(ctx, name, params)Look up by name and dispatch.
tools.RegistryGetToolCapabilities(name)Read declared capabilities for the planner.
tools.RegistryPlannerProvider()Returns an adapter satisfying planner.ToolProvider.
tools.RegisterBuiltinTools(r)functionRegisters the 9 builtins; idempotent helper.
tools.PlannerNewPlanner(r)Build a planner from a registry (analyzer, capability planner, resolver, scorer, execution planner).
tools.PlannerPlan(ctx, request)Intent-based plan: analyze, decompose, resolve, score, build DAG.
tools.BridgeNewBridge(r, p)Wrap registry with planner fallback + evidence store.
tools.BridgeExecute(ctx, name, params, intent)Named-tool direct path; falls back to planner when name is empty/unknown.
calculatorbuiltinArithmetic expression evaluator (expression param).
hash_toolbuiltinHashing utilities (from internal/tools/resources/builtin/hash).
string_utilsbuiltinString manipulation (from .../builtin/stringutils).
pdf_toolbuiltinPDF text extraction (from .../builtin/pdf).
id_generatorbuiltinUnique ID generation (from .../builtin/system).
regexbuiltinRegex match/extract/replace.
json_toolsbuiltinJSON parse/transform/validate.
web_searchbuiltinHTTP fetch with SSRF-safe dialer (isPrivateIP check).
file_toolsbuiltinFile read/write/list/exists/delete with path validation (WithAllowedDir).

Module collaboration

  • api/tools.Registry is the public surface. NewRegistry() immediately calls RegisterBuiltinTools, which first registers the 5 internal-powered tools (via fromCore -> coreAdapter) and then the 4 self-contained legacy tools (regexTool, jsonTool, webSearchTool, fileTool).
  • toolAdapter wraps a public tools.Tool so it satisfies the internal core.Tool interface (Category, Capabilities returning core.Capability, Parameters returning *core.ParameterSchema). coreAdapter wraps the reverse direction. The two adapters let the same tool instance live in both the public Registry and the cached core.Registry used by the planner.
  • Registry.PlannerProvider() returns a RegistryPlannerProvider that satisfies planner.ToolProvider via structural typing (ListTools, GetToolCapabilities), so the planner can resolve capabilities without importing internal/apitools (re-exported via sdk/api as tools).
  • tools.NewPlanner(r) wires NewRuleBasedAnalyzer -> NewCapabilityPlanner -> NewToolResolver -> NewEvidenceScorer -> NewExecutionPlanner, backed by a MemoryEvidenceStore.
  • tools.NewBridge(r, p) wraps the internal core.Registry with the planner and an evidence store: a named call hits the registry directly, while an unnamed or unknown tool triggers planner-based intent resolution. Every execution writes evidence back into the store, refining future scoring.
  • web_search uses ssrfSafeDialContext and isPrivateIP to block RFC1918/loopback/link-local addresses; file_tools validates paths via validatePath and supports WithAllowedDir to confine I/O to a sandbox.

Extension points

  1. Register a custom tool. Implement tools.Tool (5 methods) or build a tools.ToolFunc{Name, Desc, Params, Fn}, then call registry.Register(myTool). Parameters() should return a JSON Schema map so the LLM can call it.
  2. Declare capabilities for the planner. Return non-empty []string from Tool.Capabilities(). The planner maps these to planner.CapabilityDef entries via ToolCapabilityMap for intent-based resolution.
  3. Start from an empty registry. Call tools.NewEmptyRegistry(), then selectively Register only the tools you want. Re-add builtins later with tools.RegisterBuiltinTools(r) if needed.
  4. Use intent-based execution. Build tools.NewPlanner(reg) and tools.NewBridge(reg, planner), then call bridge.Execute(ctx, "", nil, "compute 1+1") to let the planner resolve the intent to calculator and execute it. Evidence is auto-recorded.
  5. Constrain file_tools to a sandbox. Construct the registry, then re-register file_tools built from a fileToolOption like WithAllowedDir("/var/ares/sandbox") so validatePath rejects any path outside the allow-list.

Bilingual status

This page is maintained in both English (tools.en.md) and Chinese (tools.zh.md). Code blocks, type names, method names, and identifiers are kept verbatim across both languages; prose is translated.

Maturity

Production. The module is covered by tools_test.go, planner_test.go, bridge_test.go, evidence_test.go, dag_test.go, and integration_test.go, is integrated into the SDK via sdk.WithTool/runtime.RegisterTool, and ships no experimental markers.

file_tools sandbox (serve path)

tools.file_sandbox_dir in ares.yaml roots the file_tools path-traversal sandbox — the directory agents may read/write. Empty (default) falls back to a process-PRIVATE temp dir via ResolveFileToolsAllowedDir, NOT the working directory: agents served from a repo cannot touch that repo (or any project data) until the key points at the intended workspace. Denied paths fail with an error naming the config key. ares init docs and quick-start cover the key.

Production