Helps agents pick the best-fit tool for a given task without drowning in options.
No LLM required. No network needed. Runs in milliseconds.
Agent farms accumulate dozens of tools. Choosing the right one for a task—currency conversion, math, web search—is a constant source of friction and hallucination. This library provides a repeatable, deterministic selection layer any agent can call.
No dependencies beyond Python 3.10+. Copy the tool_selector/ directory into your
project.
from tool_selector import ToolRegistry, ToolSelector
registry = ToolRegistry.default() # 10 built-in tools
selector = ToolSelector(registry, top_n=3)
recs = selector.recommend("convert 100 USD to EUR")
for r in recs:
print(r.rank, r.tool.name, r.score, r.reason)
# or get a formatted explanation string:
print(selector.explain("calculate 17 percent of 340"))# Plain text output
python3 -m tool_selector "convert 50 EUR to USD"
# JSON output (for agent pipelines)
python3 -m tool_selector --json "run this python snippet"
# Control how many results
python3 -m tool_selector --top 5 "send an email notification"
# Use a custom registry file
python3 -m tool_selector --registry my_tools.json "parse this invoice"
# List all tools in registry
python3 -m tool_selector --list-toolsfrom tool_selector import ToolRegistry, ToolEntry, ToolSelector
registry = ToolRegistry()
registry.register(ToolEntry(
name="invoice_parser",
description="Extract line items and totals from invoice PDFs.",
capabilities=["invoice", "parsing", "extraction", "ocr"],
use_cases=["extract invoice", "parse invoice", "invoice data", "receipt"],
examples=["Extract totals from this invoice PDF"],
cost_tier="moderate",
side_effects=False,
invoke_ref="mypackage.tools.invoice_parser",
tags=["finance", "document"],
))
# Save / reload
registry.save("my_tools.json")
registry = ToolRegistry.load("my_tools.json")
selector = ToolSelector(registry)
recs = selector.recommend("parse this invoice and extract the total")The default scorer is keyword-based (fast, deterministic). You can plug in an LLM re-ranker for a second-pass ranking on ambiguous tasks:
def my_llm_reranker(task: str, candidates):
# call your LLM, return ordered list of ToolEntry
...
recs = selector.recommend(task, llm_reranker=my_llm_reranker)ToolRegistry — stores ToolEntry objects (in-memory + JSON persistence)
ToolSelector — scores entries against a task via keyword overlap
Recommendation — rank, score, matched_keywords, human-readable reason
CLI — python3 -m tool_selector <task> [--json] [--top N] [--registry file]
Each tool is scored against the task description using weighted keyword overlap:
| Source | Weight |
|---|---|
| use_cases | 2.0 |
| capabilities | 1.5 |
| tags | 1.0 |
| name tokens | 1.0 |
| description | 0.5 |
Score is normalised to [0, 1]. Ties broken by registration order.
| Tool | What it does |
|---|---|
| calculator | Arithmetic and math expressions |
| currency_converter | Currency conversion with exchange rates |
| web_search | Web search and retrieval |
| code_executor | Sandboxed Python execution |
| summarizer | Document/URL summarisation |
| unit_converter | Length, weight, temperature, etc. |
| calendar_tool | Date/time math and timezone conversion |
| file_reader | CSV, JSON, PDF, txt parsing |
| email_sender | Email composition and sending |
| database_query | SQL queries on relational databases |
python3 test_selector.pyAll 10 task-routing cases + custom registry + persistence + CLI are verified.