Skip to main content
When you ask for tools=["read_notes"], PraisonAI checks four places in a fixed order and stops at the first match.
from praisonaiagents import Agent
import os

os.environ["PRAISONAI_ALLOW_LOCAL_TOOLS"] = "true"

agent = Agent(
    name="Notes Helper",
    instructions="Use local tools when available.",
    tools=["read_notes"],
)
agent.start("Read my latest notes")
The user lists a tool by name on Agent(…); PraisonAI resolves it through local files, built-ins, praisonai-tools, then plugins.

How It Works

Quick Start

1

Enable Local Tools

Set the environment variable to allow loading local tool files:
export PRAISONAI_ALLOW_LOCAL_TOOLS=true
2

Create a Local Tool

Create tools.py in your project directory:
def read_notes(query: str) -> str:
    """Read local notes matching the query."""
    return f"Notes about: {query}"
3

Declare tools by name on your Agent

from praisonaiagents import Agent
import os

os.environ["PRAISONAI_ALLOW_LOCAL_TOOLS"] = "true"

agent = Agent(
    name="Research assistant",
    instructions="Use available tools to answer questions.",
    tools=["read_notes", "internet_search"],
)
agent.start("What did I write about caching and what's new in the news?")
4

PraisonAI resolves through the 4-layer pipeline

PraisonAI finds read_notes from your local tools.py (Tier 1) and internet_search from the built-in SDK (Tier 2) — no extra configuration needed.

How It Works

The user names a tool; PraisonAI walks the four tiers in order and stops at the first match.

The Four Tiers

From the tool_resolver.py module docstring — first match wins:
TierSourceWhen it wins
1Local tools.py (backward compat, custom tools, custom variables)Your own function with that name exists in tools.py or tools/
2praisonaiagents.tools.TOOL_MAPPINGS (built-in SDK tools)The name is a built-in like internet_search, execute_code, etc.
3praisonai-tools package (external tools, optional)The name exists in the optional praisonai-tools install
4Tool registry (plugins via entry_points)A third-party plugin registered via praisonai.tool_sources entry point

Runtime string-name resolution inside an Agent

The four tiers above cover config-time resolution — tools.py/YAML names loaded by the CLI’s praisonai_code.tool_resolver. When you pass a tool name as a string to Agent(tools=[...]), the agent runtime uses a separate 3-tier resolver in praisonaiagents.tools.resolver at execution time.
from praisonaiagents import Agent

agent = Agent(
    name="Research assistant",
    instructions="Use available tools to answer questions.",
    tools=["internet_search", "my_registered_tool"],
)
agent.start("Search the news")
internet_search resolves from built-in TOOL_MAPPINGS; my_registered_tool resolves from the tool registry if you registered it. With only built-ins installed, the first name works out of the box. The runtime resolver walks three tiers — first match wins:
TierSourceSDK location
1Tool registry (explicitly registered tools)praisonaiagents.tools.registry.get_registry().get(name)
2Built-in lazy toolspraisonaiagents.tools.TOOL_MAPPINGS[name]
3praisonai-tools package (optional)praisonai_tools.<name>
If a name resolves nowhere, the resolver logs a warning and skips it — the agent keeps running. A partially-installed or broken praisonai-tools no longer crashes the run either: the resolver flips its “available” flag off and continues.
The runtime 3-tier resolver is safe to use even if praisonai_tools is only partially installed — a broken import no longer takes the agent down.
Use the 4-tier CLI resolver for tools.py/YAML config-time names, and the 3-tier runtime resolver for Agent(tools=["name"]) string names at execution time. They are different modules that solve different problems.

Precedence Example

If you define def search(...) in your local tools.py and there is also a search in praisonai-tools, the local one wins — Tier 1 always beats Tier 3. To see which tier resolved a tool, run with LOGLEVEL=INFO:
LOGLEVEL=INFO PRAISONAI_ALLOW_LOCAL_TOOLS=true praisonai-code run --tools tools.py "Search for something"
The log shows: Resolved 'search' from source 'local-tools.py'

When praisonai-tools Is Not Installed

As of PR #2550, failures from the praisonai-tools package are logged, not silently skipped. You’ll see them at LOGLEVEL=INFO:
Tool 'some_tool' exists in TOOL_MAPPINGS but failed to load: <error>
This means tool resolution failures are now visible and debuggable, rather than passing silently through to an unresolved tool error at runtime.

YAML tools: Blocks

resolve_all_from_yaml walks both the top-level tools: list and the agents: shape, so both formats work:
# Top-level tools list
tools:
  - internet_search
  - read_notes

# Per-agent tools (also supported)
agents:
  researcher:
    tools:
      - internet_search
  note_taker:
    tools:
      - read_notes
Tasks nested under agents can also declare their own tools: lists — the resolver walks all of them.

Backward Compatibility

The praisonai.* import paths for tool_resolver, _safe_loader, tool_registry, and _framework_availability stay live via identity-preserving sys.modules shims. Each shim does:
import praisonai_code.tool_resolver as _impl
sys.modules[__name__] = _impl
This means praisonai.tool_resolver is praisonai_code.tool_resolver — both names point at the same object. The same identity holds for _framework_availability, _safe_loader, and tool_registry, as asserted in test_c5_backward_compat.py::test_module_identity.

Best Practices

Use string names like tools=["internet_search"] rather than inline lambdas. Named tools are resolved through the full 4-tier pipeline, making them easier to override and debug.
# ✅ Named tool — discoverable and overridable
agent = Agent(tools=["internet_search"])

# ⚠️ Inline lambda — bypasses discovery pipeline
agent = Agent(tools=[lambda q: q.upper()])
Before writing a custom tools.py, check if PraisonAI already has the tool built-in. Run praisonai tools list to see all available built-in tools.
Set PRAISONAI_ALLOW_LOCAL_TOOLS=true only in environments where you trust the local tools.py. This opt-in prevents accidental execution of untrusted code.
The PRAISONAI_ALLOW_LOCAL_TOOLS environment variable must be set to true before Tier 1 (local tools.py) is consulted. This is an opt-in security gate — without it, tools.py is not loaded and the resolver logs why it was skipped (visible at LOGLEVEL=INFO). Only SDK built-ins and plugins are then considered.
export PRAISONAI_ALLOW_LOCAL_TOOLS=true
When the same name appears in multiple tiers, Tier 1 always wins. If you define def search(...) in tools.py and a plugin also registers search, your local version takes precedence. Use this intentionally to shadow or override built-in tools.
Plugins should register under the praisonai.tool_sources group in their pyproject.toml. This makes them discoverable at Tier 4 without modifying agent code.
Set LOGLEVEL=INFO to see exactly which tier resolved each tool. The log line Resolved 'search' from source 'local-tools.py' confirms which source won.
LOGLEVEL=INFO PRAISONAI_ALLOW_LOCAL_TOOLS=true python agent.py

Configuration Options

OptionTypeDefaultDescription
PRAISONAI_ALLOW_LOCAL_TOOLSenv var (bool-like string)falseOpt-in flag that enables loading local tools.py or tools/ directory (Tier 1).
LOGLEVELenv var (string)unsetSet to INFO to log which tier resolved each tool name.

Tool Resolver Python Reference

Python reference for the tool resolver module

Local Tools Loading

How to load your own tools.py safely with the PRAISONAI_ALLOW_LOCAL_TOOLS opt-in.

Approval Backends

Choose who approves tool calls — terminal, plan mode, or a chat channel.

Plugins

Register tools and hooks that extend the resolver’s tiers.

Config File

Turn plugins on from [plugins] in config.toml.