Skip to content

Commit 269f8e4

Browse files
committed
feat: Add intelligent tool matcher with semantic embeddings
- Implement ToolMatcher class using sentence transformers for semantic similarity - Add fallback mechanisms: TF-IDF vectorization and simple keyword matching - Support configurable similarity thresholds and maximum tool results - Include comprehensive error handling and logging - Provide methods for tool matching, similarity calculation, and tool management - Enable intelligent task-tool matching based on names and descriptions
1 parent 635f1c9 commit 269f8e4

File tree

4 files changed

+430
-2
lines changed

4 files changed

+430
-2
lines changed

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ packaging==25.0
4848
pandas==2.2.3
4949
pillow==11.2.1
5050
psutil==7.0.0
51+
sentence-transformers==2.5.1
52+
scikit-learn==1.5.2
5153
pycparser==2.22
5254
pydantic==2.11.3
5355
pydantic-settings==2.10.1

slaver/config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
tool:
22
# Has the model undergone targeted training on tool_calls
33
support_tool_calls: true
4+
# Tool matching configuration
5+
matching:
6+
# Maximum number of tools to match for each task
7+
max_tools: 3
8+
# Minimum similarity score threshold (0.0 to 1.0)
9+
min_similarity: 0.1
410

511
# Cloud Server Infos
612
model:

slaver/run.py

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from mcp.client.stdio import stdio_client
2020
from mcp.client.streamable_http import streamablehttp_client
2121
from tools.utils import Config
22+
from tools.tool_matcher import ToolMatcher
2223

2324
config = Config.load_config()
2425
collaborator = Collaborator.from_config(config=config["collaborator"])
@@ -39,6 +40,22 @@ def __init__(self):
3940
self.threads = []
4041
self.loop = asyncio.get_event_loop()
4142
self.robot_name = None
43+
44+
# Initialize tool matcher with configuration
45+
print(f"\n{'='*60}")
46+
print(f"⚙️ INITIALIZING TOOL MATCHER")
47+
print(f"{'='*60}")
48+
print(f"Configuration loaded from config.yaml:")
49+
print(f" max_tools: {config['tool']['matching']['max_tools']}")
50+
print(f" min_similarity: {config['tool']['matching']['min_similarity']}")
51+
52+
self.tool_matcher = ToolMatcher(
53+
max_tools=config["tool"]["matching"]["max_tools"],
54+
min_similarity=config["tool"]["matching"]["min_similarity"]
55+
)
56+
57+
print(f"✅ Tool matcher initialized successfully")
58+
print(f"{'='*60}\n")
4259

4360
signal.signal(signal.SIGINT, self._handle_signal)
4461
signal.signal(signal.SIGTERM, self._handle_signal)
@@ -108,8 +125,53 @@ async def _execute_task(self, task_data: Dict) -> None:
108125
return
109126

110127
os.makedirs("./.log", exist_ok=True)
128+
129+
# Use tool matcher to find relevant tools for the task
130+
task = task_data["task"]
131+
print(f"\n{'='*60}")
132+
print(f"🔍 TOOL MATCHING FOR TASK")
133+
print(f"{'='*60}")
134+
print(f"Task: {task}")
135+
print(f"Available tools: {len(self.tools)}")
136+
137+
# Get matched tools with detailed logging
138+
matched_tools = self.tool_matcher.match_tools(task)
139+
140+
# Filter tools based on matching results
141+
if matched_tools:
142+
matched_tool_names = [tool_name for tool_name, _ in matched_tools]
143+
filtered_tools = [tool for tool in self.tools
144+
if tool.get("function", {}).get("name") in matched_tool_names]
145+
146+
print(f"\n✅ MATCHING RESULTS:")
147+
print(f"Matched tools count: {len(matched_tools)}")
148+
149+
# Print tool names and scores only
150+
for i, (tool_name, similarity_score) in enumerate(matched_tools, 1):
151+
print(f" {i}. {tool_name} (Score: {similarity_score:.4f})")
152+
153+
print(f"\n📊 TOOL FILTERING:")
154+
print(f"Original tools: {len(self.tools)}")
155+
print(f"Filtered tools: {len(filtered_tools)}")
156+
print(f"Reduction: {len(self.tools) - len(filtered_tools)} tools filtered out")
157+
158+
# Show which tools were filtered out
159+
if len(filtered_tools) < len(self.tools):
160+
filtered_names = {tool.get("function", {}).get("name") for tool in filtered_tools}
161+
all_names = {tool.get("function", {}).get("name") for tool in self.tools}
162+
filtered_out_names = all_names - filtered_names
163+
if filtered_out_names:
164+
print(f"Filtered out tools: {', '.join(sorted(filtered_out_names))}")
165+
else:
166+
filtered_tools = self.tools
167+
print(f"\n⚠️ NO SPECIFIC TOOLS MATCHED")
168+
print(f"Using all {len(self.tools)} available tools")
169+
print(f"Consider adjusting min_similarity threshold in config")
170+
171+
print(f"{'='*60}\n")
172+
111173
agent = ToolCallingAgent(
112-
tools=self.tools,
174+
tools=filtered_tools,
113175
verbosity_level=2,
114176
model=self.model,
115177
model_path=self.model_path,
@@ -118,7 +180,7 @@ async def _execute_task(self, task_data: Dict) -> None:
118180
collaborator=self.collaborator,
119181
tool_executor=self.session.call_tool,
120182
)
121-
task = task_data["task"]
183+
122184
result = await agent.run(task)
123185
self._send_result(
124186
robot_name=self.robot_name,
@@ -195,6 +257,26 @@ async def connect_to_robot(self):
195257
for tool in response.tools
196258
]
197259
print("Connected to robot with tools:", str(self.tools))
260+
261+
# Train the tool matcher with the available tools
262+
print(f"\n{'='*60}")
263+
print(f"🔄 TRAINING TOOL MATCHER")
264+
print(f"{'='*60}")
265+
print(f"Available tools: {len(self.tools)}")
266+
267+
# Show tool details
268+
for i, tool in enumerate(self.tools, 1):
269+
name = tool.get("function", {}).get("name", "Unknown")
270+
description = tool.get("function", {}).get("description", "No description")
271+
print(f" {i}. {name}")
272+
print(f" {description}")
273+
274+
# Train the matcher
275+
self.tool_matcher.fit(self.tools)
276+
print(f"\n✅ Tool matcher training completed")
277+
print(f"Training data: {len(self.tools)} tools")
278+
print(f"Configuration: max_tools={self.tool_matcher.max_tools}, min_similarity={self.tool_matcher.min_similarity}")
279+
print(f"{'='*60}\n")
198280

199281
"""Complete robot registration with thread management"""
200282
robot_name = config["robot"]["name"]

0 commit comments

Comments
 (0)