源文件:chapter4/active-tool-selection/README.md Active Tool Selection An educational implementation of active tool discovery for LLM agents, inspired by the MCP-Zero paper (arXiv:2506.01056). 🎯 Overview Traditional LLM agents inject all available tool schemas into prompts, creating massive context overhead and reducing agents to passive tool
源文件:chapter4/active-tool-selection/README.md
An educational implementation of active tool discovery for LLM agents, inspired by the MCP-Zero paper (arXiv:2506.01056).
Traditional LLM agents inject all available tool schemas into prompts, creating massive context overhead and reducing agents to passive tool selectors. This project demonstrates active tool discovery, where agents autonomously identify capability gaps and request specific tools on-demand.
Current tool integration approaches face critical limitations:
This project implements three core mechanisms from MCP-Zero:
本实验把"工具选择"问题落到可度量的基准上,对比三种策略在同一批任务上的表现:
| 策略 | 说明 | 上下文里的工具 |
|---|---|---|
all-tools |
一次性注入全部工具(传统被动式基线) | 全部 N 个 |
retrieval |
按任务语义检索 top-k 个工具后再注入(工具检索 / RAG 式,RetrievalToolAgent) |
仅 top-k 个 |
active |
MCP-Zero 式主动发现:模型迭代地请求所需工具(ActiveToolAgent) |
按需增长 |
评测入口是 demo_comparison.py,带完整的 argparse 命令行:
# 仅离线对比(确定性,无需 API Key):召回率 vs token 成本 vs 随规模的扩展性 python demo_comparison.py --offline # 把工具目录扩充到 200 个(合成干扰工具补齐),观察 token 成本的分化 python demo_comparison.py --offline --num-tools 200 # 三种策略端到端对比(需要 API Key):模型是否真的调用了正确的工具、token、延迟 python demo_comparison.py --strategy compare # 只对单条查询运行某种策略 python demo_comparison.py --query "Deploy version 2.0 to production" --strategy retrieval # 保存结果为 JSON python demo_comparison.py --offline --output results.json
运行 python demo_comparison.py --help 查看全部参数(--strategy / --query / --num-tools / --top-k / --model / --output / --offline / --legacy-demos)。
benchmark.py 提供了一个带标准答案工具的小型基准集(10 个任务,每个任务标注了应当被选中的
工具),并在不调用任何 API 的情况下度量两件事:
下表是 python demo_comparison.py --offline 的实测输出(top-k=5,10 个任务):
| 策略 | 上下文工具数 | Schema tokens | 召回率(标准答案可达) |
|---|---|---|---|
| all-tools(全部注入) | 35 | 3,857 | 100% |
| retrieval(top-5) | 5 | 551 | 100% |
随着工具目录增长,all-tools 的 token 成本线性膨胀,而 retrieval 基本持平(实测):
| 目录规模 | all-tools tokens | retrieval(top-5) tokens | retrieval 召回率 |
|---|---|---|---|
| 35 | 3,857 | 551 | 100% |
| 100 | 10,292 | 539 | 100% |
| 200 | 20,258 | 540 | 100% |
| 400 | 40,258 | 540 | 100% |
结论:检索式按需选择在保持 100% 召回率的同时,把工具描述的 token 成本从数千压到数百,且不随
生态规模膨胀。这正是本章"把工具选择转化为知识检索"的量化体现。上述数字由--offline路径
确定性生成,可直接复现。
在配置 OPENAI_API_KEY 后,--strategy compare 会真正调用模型,度量每种策略下模型是否调用了
标准答案工具(accuracy)、平均 token 与平均延迟。这一部分需要联网与 API,故不在离线路径中运行。
┌─────────────────────────────────────────────────────────────┐ │ Active Tool Agent │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 1. Analyze Task & Identify Capability Gaps │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 2. Generate Structured Tool Request │ │ │ │ <tool_request> │ │ │ │ server: GitHub for repository operations │ │ │ │ tool: search repositories by keyword │ │ │ │ </tool_request> │ │ │ └────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ Hierarchical Semantic Router │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Stage 1: Server-Level Routing │ │ │ │ • Match request to relevant servers/domains │ │ │ │ • Filter by platform requirements │ │ │ │ • Return top-K servers │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Stage 2: Tool-Level Routing │ │ │ │ • Rank tools within selected servers │ │ │ │ • Semantic similarity matching │ │ │ │ • Return relevant tools │ │ │ └────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ Tool Knowledge Base │ │ │ │ 8 Servers × 35 Tools (optionally padded with distractors): │ │ • GitHub: Repository management (5 tools) │ │ • Filesystem: File operations (5 tools) │ │ • Database: SQL operations (5 tools) │ │ • Web: HTTP requests (4 tools) │ │ • Analytics: Data analysis (4 tools) │ │ • Communication: Email/messaging (4 tools) │ │ • DevOps: Deployment/monitoring (4 tools) │ │ • Cloud: Infrastructure management (4 tools) │ └─────────────────────────────────────────────────────────────┘
agent.py: Three agent implementations (one per strategy)
ActiveToolAgent: MCP-Zero style on-demand discovery (active)RetrievalToolAgent: one-shot semantic retrieval of top-k tools (retrieval)PassiveToolAgent: traditional approach with all tools pre-loaded (all-tools)benchmark.py: Labeled benchmark + offline evaluation
build_catalog(num_tools): real catalog, optionally padded with distractorsevaluate_offline(...): deterministic recall@k / token-cost measurement (no API)tool_knowledge_base.py: Comprehensive tool catalog
semantic_router.py: Hierarchical tool discovery
config.py: Configuration settings
quickstart.py: Quick demonstration (⭐ Start here!)demo_comparison.py: Comprehensive comparisonexamples.py: Multiple use case examplespip install -r requirements.txt
cp env.example .env # Edit .env and add your API key
Universal OpenRouter fallback: if
OPENAI_API_KEYis not set butOPENROUTER_API_KEYis,config.pyautomatically routes through OpenRouter
(base_url=https://openrouter.ai/api/v1) and maps the model id toprovider/modelform (gpt-*→openai/…,claude-*→anthropic/claude-opus-4.8). ExistingOPENAI_BASE_URL/OPENAI_MODEL
overrides are preserved.
python quickstart.py
This will demonstrate:
See the measured, reproducible numbers in 三种策略对比 above.
The offline table (schema-token cost + retrieval recall) is generated deterministically bypython demo_comparison.py --offline — no API key required. End-to-end accuracy/latency across
the three strategies requires an API key (--strategy compare).
Note: earlier drafts of this README quoted round illustrative figures for token savings. Those
have been replaced with the actual measured output of the offline benchmark to avoid fabricated
numbers.
Instead of pre-loading tools, agents explicitly request what they need:
<tool_request> server: GitHub for repository management tool: search repositories by stars and language </tool_request>
Two-stage algorithm reduces search complexity:
Stage 1: Match to relevant servers (platforms)
Stage 2: Match to specific tools within servers
github_search_reposfs_read_fileTools are discovered progressively:
Turn 1: Need GitHub access → Load GitHub tools Turn 2: Need to analyze downloaded files → Additionally load filesystem tools Turn 3: Need to visualize results → Additionally load analytics tools
Toolchain grows with task complexity, not ecosystem size.
from agent import ActiveToolAgent agent = ActiveToolAgent() result = agent.execute_task("Search for Python ML repositories on GitHub") # Agent discovers and loads only GitHub tools print(f"Tools loaded: {result['metrics']['tools_loaded']}") # 2-3 tools print(f"Tokens used: {result['metrics']['tokens_used']}") # ~2,000
task = """ 1. Query database for user data 2. Analyze with statistics 3. Create visualization 4. Email report to team """ result = agent.execute_task(task) # Agent builds cross-domain toolchain: # Database → Analytics → Communication print(f"Tools: {result['tools_loaded']}")
from agent import ActiveToolAgent, PassiveToolAgent # Active approach active = ActiveToolAgent() active_result = active.execute_task(task) # Passive approach passive = PassiveToolAgent() passive_result = passive.execute_task(task) # Compare efficiency reduction = (1 - active_result['metrics']['tokens_used'] / passive_result['metrics']['tokens_used']) * 100 print(f"Token reduction: {reduction:.1f}%") # Typically 90-98%
python quickstart.py
Shows basic active vs passive comparison.
python demo_comparison.py --offline # deterministic, no API key needed python demo_comparison.py # + end-to-end accuracy/latency if API key present
By default it prints:
all-tools, retrieval, activeThe original narrative demos (semantic-routing walk-through, iterative discovery, etc.) are still
available via --legacy-demos. See the Strategy Comparison
section and python demo_comparison.py --help for all flags.
python examples.py
Demonstrates:
Edit config.py or set environment variables:
# LLM Configuration OPENAI_API_KEY = "your-api-key" OPENAI_BASE_URL = "https://api.openai.com/v1" OPENAI_MODEL = "gpt-5.6-luna" # Routing Configuration SIMILARITY_THRESHOLD = 0.15 # Min similarity for tool match TOP_K_SERVERS = 3 # Number of servers to search TOP_K_TOOLS = 5 # Tools to return per server # Agent Configuration MAX_TOOL_REQUESTS = 5 # Max discovery iterations
This project demonstrates:
Uses TF-IDF vectorization with cosine similarity:
# Server-level server_vector = vectorizer.transform([request]) similarities = cosine_similarity(server_vector, server_embeddings) # Tool-level tool_vector = vectorizer.transform([request]) tool_similarities = cosine_similarity(tool_vector, tool_embeddings) # Combined score final_score = 0.3 * server_score + 0.7 * tool_score
Approximates tokens in tool schemas:
def count_tokens_in_schema(schema): # Rough estimation: 1 token ≈ 4 characters schema_str = json.dumps(schema) return len(schema_str) // 4
# In tool_knowledge_base.py new_tool = ToolDefinition( name="your_tool_name", description="What the tool does", parameters={...}, server="server_name" )
# Create tools for the server tools = [...] # Add server servers.append(ServerDefinition( name="your_server", description="Server description", tools=tools ))
# In config.py SIMILARITY_THRESHOLD = 0.5 # More strict matching TOP_K_SERVERS = 5 # Search more servers
# Verify .env file cat .env # Should contain: OPENAI_API_KEY=sk-...
# Reinstall dependencies pip install -r requirements.txt --upgrade
# Lower threshold in config.py SIMILARITY_THRESHOLD = 0.2
Potential improvements:
This is an educational project. Feel free to:
MIT License - See LICENSE file for details
Ready to get started? Run python quickstart.py to see active tool discovery in action!