Module 3: Dynamic Tooling (The Body)
1. Introduction: The Backpack Problem
Imagine you are going on a hike. You have a backpack (your Context Window). You have a garage full of 5,000 tools (saws, hammocks, pots, ropes, axes...).
You cannot fit all 5,000 tools in your backpack. If you try, the backpack breaks (the LLM crashes or gets confused). So, what do you do?
- Approach A (Static): You pack a saw and a rope. But what if you encounter a river and need a raft? You are stuck.
- Approach B (Dynamic): You carry a magical radio. When you see a river, you radio the garage: "Send me something for water." A drone drops a raft. You use it. Then you drop the raft and radio for a tent.
Our Agent uses Approach B. In technical terms, the "Backpack" is the System Prompt. The "Garage" is our Tool Registry.
2. The Factory: Building a Custom Body
Because every user has a different "Garage" (User A has GitHub, User B has Notion), we cannot pre-build the agent. We must build it Just-In-Time.
This happens in backend/app/services/agent/agent_factory.py.
The create_final_agent_pipeline Function
Think of this function as the Assembly Line. Every time a user starts a chat (or resumes one), this function runs:
- Scan User Config: It checks which MCP servers the user has connected.
- Fetch Tool Definitions: It asks those servers for their tool lists.
- Result: A list of 500 potential tools.
- Create Registry: It indexes these 500 tools into a search engine (BM25).
- Inject Initial Tools: It picks a small set of "always available" tools (like
search_tools) and puts them in the backpack. - Compile Graph: It takes the generic Brain (from Module 2) and gives it this specific backpack.
Result: A unique Agent instance that exists only for this specific user session.
3. The Registry: The Search Engine
The file backend/app/services/agent/tool_registry.py is the librarian. It doesn't "run" tools. It "knows about" tools.
We use an algorithm called BM25 (Best Matching 25). It is a classic keyword search algorithm (similar to what Solr or Lucene use).
How the Agent "Learns" a New Tool
Let's trace a concrete example.
Scenario: The user asks, "Restart the production database." Problem: The Agent's backpack currently only has search_tools. It doesn't know how to restart a database.
The Loop:
- Think: The Agent analyzes the request. "I need to restart a database. I don't have a tool for that. I should search."
- Act: The Agent calls
search_tools(query="restart database"). - System: The Registry runs BM25 against the 500 descriptions in the Garage.
- Found:
aws_restart_db_instance(Score: 0.95) - Found:
azure_restart_sql(Score: 0.92)
- Found:
- Observe: The Agent receives these tool definitions.
- Crucial Step: The System dynamically binds these new tools to the LLM. They are now in the backpack.
- Think: "Aha! I see
aws_restart_db_instance. I will call that."
4. What the Agent Actually Sees (JSON)
To demystify "Tool Definitions", here is exactly what we send to the LLM. It's just JSON.
When we say "we bind the tool", we are just appending this text to the System Prompt:
{
"name": "aws_restart_db_instance",
"description": "Restarts a specific RDS database instance.",
"parameters": {
"type": "object",
"properties": {
"instance_id": {
"type": "string",
"description": "The AWS identifier for the database"
}
},
"required": ["instance_id"]
}
}The LLM is trained to read this schema and output a formatted string like: Action: aws_restart_db_instance(instance_id="prod-db-1")
5. Summary
- The Problem: Context Windows are small. Tool Lists are huge.
- The Solution: The Factory builds a
ToolRegistryfor every user. - The Mechanism: The Agent starts with an empty backpack and uses
search_toolsto find what it needs on the fly.
This is what makes the agent Tool Agnostic. It doesn't need to know AWS exists when we write the code. It discovers AWS at runtime.
But wait... if the Agent can find "Restart Database" and just run it... isn't that dangerous? That brings us to Module 4: Safety.