Skip to main content
Tools can now be referenced by name directly in YAML files without creating a local tools.py file.

How It Works

When you specify tools in your YAML configuration, PraisonAI automatically resolves them from multiple sources. Only tools you list under tools: in your YAML (under each role and each task) are loaded. Misspelled or unknown tool names now produce a warning log line, not a silent skip.
from praisonaiagents import Agent

agent = Agent(name="Researcher", tools=["tavily_search"])
agent.start("Summarise today's AI headlines")
The user lists tools in agents.yaml; PraisonAI resolves built-ins and runs the configured agent.

Quick Start

1

Create YAML File

Reference tools by name in your agents.yaml:
agents.yaml
framework: praisonai
topic: "AI News"

roles:
  researcher:
    role: AI Researcher
    goal: Search for the latest AI news
    backstory: Expert researcher
    tools:
      - tavily_search
      - internet_search
    tasks:
      search_task:
        description: Search for {topic}
        expected_output: Summary of findings
2

Run Your Agents

No tools.py needed - tools are auto-resolved:
praisonai run agents.yaml

Tool Sources

Built-in Tools

70+ tools from praisonaiagents.tools
  • tavily_search
  • internet_search
  • execute_command
  • read_file
  • And more…

Local tools.py

Custom tools you create
  • Takes precedence
  • Full control
  • Custom variables

External Tools

120+ tools from praisonai-tools
  • EmailTool
  • SlackTool
  • GitHubTool
  • And more…

Resolution Order

Tools are resolved in this order (first match wins):
Local tools.py always takes precedence. This lets you override built-in tools with custom implementations.

Available Built-in Tools

ToolDescription
tavily_searchAI-powered web search
tavily_extractExtract content from URLs
internet_searchDuckDuckGo search
exa_searchExa semantic search
search_webUnified search (auto-fallback)
ToolDescription
read_fileRead file contents
write_fileWrite to files
list_filesList directory contents
copy_fileCopy files
delete_fileDelete files
ToolDescription
execute_commandRun shell commands
list_processesList running processes
kill_processTerminate processes
get_system_infoSystem information
ToolDescription
crawl4aiAsync web crawling
crawl4ai_extractExtract with CSS selectors
scrape_pageSpider page scraping
extract_linksExtract page links

CLI Commands

List Available Tools

praisonai tools list
┌─────────────────────────────────────────────────┐
│               Available Tools                    │
├──────────────────┬──────────┬───────────────────┤
│ Tool Name        │ Source   │ Description       │
├──────────────────┼──────────┼───────────────────┤
│ crawl4ai         │ builtin  │ Built-in tool...  │
│ execute_command  │ builtin  │ Built-in tool...  │
│ internet_search  │ builtin  │ Built-in tool...  │
│ tavily_search    │ builtin  │ Built-in tool...  │
│ my_custom_tool   │ local    │ My custom tool... │
└──────────────────┴──────────┴───────────────────┘

Validate YAML Tools

praisonai tools validate agents.yaml
✓ All tools in agents.yaml are valid!
Tools found: internet_search, tavily_search

Get Tool Info

praisonai tools info tavily_search

Custom Tools (tools.py)

For custom logic or variables, create a tools.py file:
tools.py
# Custom tool with your own logic
def my_custom_tool(query: str) -> str:
    """Search using my custom API."""
    # Your implementation
    return f"Results for: {query}"

# Custom variables
API_KEY = "your-key"
BASE_URL = "https://api.example.com"

def api_search(query: str) -> dict:
    """Search with custom API."""
    import requests
    response = requests.get(f"{BASE_URL}/search?q={query}&key={API_KEY}")
    return response.json()
Then reference in YAML:
agents.yaml
roles:
  researcher:
    tools:
      - my_custom_tool    # From local tools.py
      - api_search        # From local tools.py
      - tavily_search     # Built-in (still works!)
Local tools with the same name as built-in tools will override them.

Examples

framework: praisonai
topic: "Latest AI developments"

roles:
  researcher:
    role: AI Researcher
    goal: Find and summarize AI news
    backstory: Expert in AI research
    tools:
      - tavily_search
    tasks:
      research:
        description: Search for {topic}
        expected_output: Detailed summary
framework: praisonai
topic: "Python web scraping"

roles:
  developer:
    role: Developer
    goal: Research and implement solutions
    backstory: Senior Python developer
    tools:
      - tavily_search
      - internet_search
      - execute_command
      - read_file
      - write_file
    tasks:
      research:
        description: Research {topic}
        expected_output: Implementation guide
framework: praisonai
topic: "Company data"

roles:
  analyst:
    role: Data Analyst
    goal: Analyze company data
    backstory: Expert analyst
    tools:
      - my_database_query  # From tools.py
      - tavily_search      # Built-in
    tasks:
      analyze:
        description: Analyze {topic}
        expected_output: Analysis report

Troubleshooting

  1. Check the tool name spelling
  2. Run praisonai tools list to see available tools
  3. If using external tools, ensure praisonai-tools is installed:
    pip install praisonai-tools
    
  1. Check if the tool requires an API key (e.g., TAVILY_API_KEY)
  2. Run praisonai tools info <tool_name> for details
  3. Test the tool: praisonai tools test <tool_name>
Create a tools.py file with a function of the same name:
def tavily_search(query: str) -> str:
    """My custom tavily_search implementation."""
    # Your code here
    return "custom result"

Per-Agent Tool Resolution

from praisonaiagents import Agent
from praisonai.tool_resolver import ToolResolver

# Single agent with default resolver
agent = Agent(
    name="ResearchAgent",
    instructions="Research AI topics using tools",
    tools=["tavily_search", "web_scraper"]
)

agent.start("Search for latest AI developments")

Multi-agent: one resolver per agent

When agents need different tools_py_path values, construct separate resolvers:
from praisonai.tool_resolver import ToolResolver, resolve_tool

resolver_a = ToolResolver(tools_py_path="agents/a/tools.py")
resolver_b = ToolResolver(tools_py_path="agents/b/tools.py")

tool_a = resolve_tool("my_tool", resolver=resolver_a)
tool_b = resolve_tool("my_tool", resolver=resolver_b)
Thread Safety: Each ToolResolver instance is safe to share across threads — its local-tools cache is loaded once under a lock and exposed as an immutable MappingProxyType.Default resolver: When you call resolve_tool(name) without passing resolver=, PraisonAI uses a single process-level cached resolver to avoid recreating it on every call. This cached resolver is anchored to the working directory at first call.When to pass an explicit resolver: For test isolation, multi-project CLIs, or per-agent tools_py_path values, construct your own ToolResolver(...) and pass it as resolver= to the convenience functions.

Caching Behaviour

The convenience functions (resolve_tool, resolve_tools, list_available_tools, has_tool, validate_yaml_tools) share a single cached ToolResolver for the whole process. This means:
  • Fast repeated callstools.py is only read once, praisonai-tools is only probed once.
  • CWD-anchored — the cached resolver picks up ./tools.py from whatever directory was active when the first call happened.
  • Need isolation? Pass an explicit resolver=ToolResolver(...) instance — the cached default is bypassed.
from praisonai.tool_resolver import resolve_tool, ToolResolver

# Uses cached default — fast, anchored to current working directory
tool = resolve_tool("tavily_search")

# Explicit resolver — isolated, useful in tests or multi-project CLIs
tool = resolve_tool("tavily_search", resolver=ToolResolver(tools_py_path="agents/a/tools.py"))

Configuration Options

Convenience Functions (with optional resolver parameter)

FunctionSignatureDescription
resolve_toolresolve_tool(name: str, resolver: Optional[ToolResolver] = None) -> Optional[Callable]Resolve single tool
resolve_toolsresolve_tools(names: List[str], resolver: Optional[ToolResolver] = None) -> List[Callable]Resolve multiple tools
list_available_toolslist_available_tools(resolver: Optional[ToolResolver] = None) -> Dict[str, str]List all available tools
has_toolhas_tool(name: str, resolver: Optional[ToolResolver] = None) -> boolCheck if tool exists
validate_yaml_toolsvalidate_yaml_tools(yaml_config: Dict[str, Any], resolver: Optional[ToolResolver] = None) -> List[str]Validate YAML tools

ToolResolver Class

MethodDescriptionThread Safe
resolve(name, instantiate=False)Resolve single tool name. Pass instantiate=True to get an instance of class tools (BaseTool / langchain) instead of the raw class.
resolve_many(names)Resolve multiple tool names
list_available()List available tools
has_tool(name)Check if tool exists
clear_cache()Clear local tools cache✅ (lock-protected)

Python Usage

from praisonai.tool_resolver import ToolResolver, resolve_tool

# Option 1: Use convenience functions with default resolver
tool = resolve_tool("tavily_search")
tools = resolve_tools(["tavily_search", "internet_search"])
available = list_available_tools()

# Option 2: Use convenience functions with custom resolver
resolver = ToolResolver(tools_py_path="custom/tools.py")
tool = resolve_tool("tavily_search", resolver=resolver)
tools = resolve_tools(["tavily_search"], resolver=resolver)

# Option 3: Use resolver instance directly
resolver = ToolResolver()
tool = resolver.resolve("tavily_search")
tools = resolver.resolve_many(["tavily_search", "internet_search"])

Custom Tools

Build your own agent tools

Tools Overview

Browse PraisonAI tool documentation