Welcome back! In the previous chapter, Skills System, we taught our agent how to use tools by giving it instruction manuals (Markdown files).
But here is the problem: Reading a manual on how to hammer a nail doesn't actually hammer the nail.
The AI (the brain) exists only in the cloud or on a GPU. It outputs text like: "Please run the command: echo hello". It cannot physically reach into your computer's operating system to execute that command.
We need a pair of "Hands" to do the actual work. In PicoClaw, this is the Tool Registry & Execution layer.
Imagine you are at a restaurant.
If the restaurant had a menu but no kitchen, you would starve. The Tool Registry is the kitchen. It takes the "Order" (text from the AI) and turns it into "Food" (executed code).
The Registry is a simple dictionary (or Map) that connects a Name (string) to a Go Function.
The AI doesn't know about Go functions. It only knows names.
exec."exec... Ah, that corresponds to the ExecTool struct in the code."Every tool in PicoClaw must look the same so the Registry can handle them comfortably. They all adhere to a specific interface (shape).
// The standard shape for any tool
type Tool interface {
Name() string
Description() string
// The actual work happens here
Execute(ctx context.Context, args map[string]interface{}) *ToolResult
}
When PicoClaw starts, we put our tools into the toolbox.
// pkg/tools/registry.go (Conceptual)
func SetupRegistry() *ToolRegistry {
registry := NewToolRegistry()
// Add the "Hand" that runs shell commands
shellTool := NewExecTool("/home/user", true)
// Put it in the box
registry.Register(shellTool)
return registry
}
Explanation: We create the tool once, configure it (setting the working directory), and store it. Now the system knows that the tool exists.
Giving an AI access to your computer's shell is powerful, but dangerous. What if the AI gets confused and tries to delete your files?
The Tool system includes Safety Guards. Before running any code, the tool inspects the arguments.
Let's look at the ExecTool (Shell). It checks the command against a "Deny List" before running it.
// pkg/tools/shell.go (Simplified)
func (t *ExecTool) guardCommand(command string) string {
// Check for dangerous patterns
if strings.Contains(command, "rm -rf") {
return "Command blocked: dangerous pattern detected"
}
if strings.Contains(command, "format c:") {
return "Command blocked: disk format attempt"
}
return "" // Empty string means it's safe
}
Why is this important? The AI is probabilisticโit makes guesses. Sometimes it guesses wrong. The Registry ensures that a wrong guess doesn't destroy your computer.
So, how do we get from a text message to a running program?
exec with arguments command='date'".exec.tool.Execute()."Fri Oct 27 10:00:00 UTC 2023".
Let's look under the hood at how PicoClaw manages this inside pkg/tools/registry.go and pkg/tools/toolloop.go.
The ExecuteWithContext function is the main entry point. It adds logging and handles errors gracefully so the whole program doesn't crash if a tool fails.
// pkg/tools/registry.go (Simplified)
func (r *ToolRegistry) Execute(name string, args map[string]interface{}) *ToolResult {
// 1. Look up the tool in our map
tool, found := r.tools[name]
if !found {
return ErrorResult("Tool not found")
}
// 2. Run the tool's Execute function
// We time it to see how long it takes
start := time.Now()
result := tool.Execute(context.Background(), args)
// 3. Log the result
logger.Info("Tool finished in", time.Since(start))
return result
}
This is a critical piece of logic found in pkg/tools/toolloop.go.
When an AI uses a tool, it usually expects to see the result so it can give a final answer.
ls)file1.txt, file2.txt)The Tool Loop automates this back-and-forth.
// pkg/tools/toolloop.go (Simplified Logic)
func RunToolLoop(messages []Message, registry *ToolRegistry) {
for {
// 1. Ask the AI what to do
response := CallAI(messages)
// 2. If AI wants to use a tool...
if len(response.ToolCalls) > 0 {
// 3. Execute the tool
result := registry.Execute(response.ToolName, response.Args)
// 4. Add the result to memory (Context)
messages = append(messages, Message{
Role: "tool",
Content: result.Output,
})
// 5. Loop executes again! The AI will now see the tool output.
continue
}
// If no tool calls, we are done.
break
}
}
This chapter connects everything we've built so far:
exec."exec.In this chapter, we learned:
Now our agent can talk, think, remember, and run computer commands. But what if we want it to interact with the physical world? What if we want it to move a robot arm or read a physical sensor?
In the final chapter, we will connect our software brain to hardware.
Next: Chapter 7 - Hardware Interface
Generated by Code IQ