A high-performance hierarchical tool orchestration framework for AI-powered applications.
Traditional AI tool usage faces a critical limitation: one tool per invocation per turn.
This creates several challenges:
- High Latency: Each tool call requires a full round-trip (AI model → backend → tool execution → backend → AI model)
- Chatty Interfaces: Complex workflows need multiple exchanges between model and backend
- Inefficient Token Usage: Repeated context sending across multiple turns
- Complex State Management: Application code must track state across multiple tool invocations
AI-Toolkit enables multiple tool invocations in a single turn through a hierarchical orchestration framework:
┌─────────────────────────────────┐
│ Single API Call │
│ │
│ ┌─────────────┐ ┌─────────────┐│
│ │ Parent: DB │ │ Parent: FS ││
│ │ ┌─────────┐ │ │ ┌─────────┐ ││
│ │ │ Query │ │ │ │ ReadFile│ ││
│ │ └─────────┘ │ │ └─────────┘ ││
│ │ ┌─────────┐ │ │ ┌─────────┐ ││
│ │ │ Insert │ │ │ │ WriteFile││
│ │ └─────────┘ │ │ └─────────┘ ││
│ └─────────────┘ └─────────────┘│
└─────────────────────────────────┘
Benefits:
- 5-10x Latency Reduction: Single API call vs multiple sequential calls
- Structured Organization: Logical grouping of related tools
- Automatic Schema Generation: JSON schemas derived from Go types
- Clean Error Handling: Standardized error propagation and collection
- Parallel Execution Capability: Multiple tools execute in a single turn
AI-Toolkit is built around three core concepts:
The top-level orchestrator that manages Parent categories and handles request routing.
A category of related tools that acts as a namespace and container.
Individual tool implementations that perform specific operations.
┌───────────────────────────────────────┐
│ Toolkit │
│ ┌───────────────┐ ┌────────────────┐ │
│ │ Parent: Files │ │ Parent: Search │ │
│ │ ┌───────────┐ │ │ ┌────────────┐ │ │
│ │ │ ReadFile │ │ │ │ WebSearch │ │ │
│ │ └───────────┘ │ │ └────────────┘ │ │
│ │ ┌───────────┐ │ │ ┌────────────┐ │ │
│ │ │ WriteFile │ │ │ │ FetchURL │ │ │
│ │ └───────────┘ │ │ └────────────┘ │ │
│ └───────────────┘ └────────────────┘ │
└───────────────────────────────────────┘
┌──────────┐ ┌─────────┐ ┌──────────┐ ┌──────────┐
│ AI Model │────▶│ Toolkit │────▶│ Parent A │────▶│ Child A1 │
│ │ │ │ │ │ └──────────┘
│ │ │ │ │ │ ┌──────────┐
│ │ │ │ │ │────▶│ Child A2 │
│ │ │ │ └──────────┘ └──────────┘
│ │ │ │ ┌──────────┐ ┌──────────┐
│ │ │ │────▶│ Parent B │────▶│ Child B1 │
└──────────┘ └─────────┘ └──────────┘ └──────────┘
# Install the latest stable version
go get github.com/h-ess/ai-toolkit@v0.1.0For production use, we strongly recommend pinning to a specific version tag to ensure stability.
| Version | Go Version | Status |
|---|---|---|
| v0.1.0 | Go 1.22+ | Stable |
If you encounter module resolution issues, try clearing your module cache:
go get github.com/h-ess/ai-toolkit@v0.1.0package main
import (
"context"
"fmt"
"os"
"github.com/h-ess/ai-toolkit/toolkit"
)
// Define argument and response types
type ReadFileArgs struct {
Path string `json:"path" jsonschema:"required,description=The path to the file"`
}
type ReadFileResponse struct {
Content string `json:"content"`
Success bool `json:"success"`
}
func main() {
// Create a child tool
readFileTool := toolkit.NewChild(
"read_file",
"Reads the content of a file",
func(ctx context.Context, args ReadFileArgs) (interface{}, error) {
content, err := os.ReadFile(args.Path)
if err != nil {
return ReadFileResponse{Success: false}, err
}
return ReadFileResponse{
Content: string(content),
Success: true,
}, nil
},
)
// Create a parent to group related tools
fileOpsParent := toolkit.NewParent(
"file_operations",
"File system operations",
readFileTool,
// Add more child tools...
)
// Create the toolkit
myToolkit := toolkit.New(
"my_app_toolkit",
fileOpsParent,
// Add more parents...
)
// The toolkit is now ready to handle requests
}// Process a JSON request from an AI model
func handleToolRequest(toolkitJSON []byte) {
ctx := context.Background()
// Process the toolkit request
response, err := myToolkit.HandleToolKit(ctx, toolkitJSON)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Handle the response
fmt.Printf("Response: %+v\n", response)
}The HandleToolKit method expects a specific JSON structure that maps to the internal ToolKit type. Below is an example of a valid request format:
{
"name": "test_toolkit",
"parents": [
{
"name": "test_parent",
"childs": [
{
"name": "test_child",
"args": {
"arg1": "value1",
"arg2": 42
}
}
]
}
]
}| JSON Field | Go Struct Field | Description |
|---|---|---|
name |
ToolKit.Name |
The name of the toolkit (for identification) |
parents |
ToolKit.ToolKitParents |
Array of parent categories to invoke |
parents[].name |
ToolKitParent.Name |
Name of the parent category (must match a registered parent) |
parents[].childs |
ToolKitParent.ToolKitChilds |
Array of child tools to execute within this parent |
parents[].childs[].name |
ToolKitChild.Name |
Name of the child tool to execute (must match a registered child) |
parents[].childs[].args |
ToolKitChild.Args |
Arguments specific to this child tool (must match the tool's input schema) |
One of the key advantages of AI-Toolkit is the ability to execute multiple tools in a single request:
{
"name": "media_toolkit",
"parents": [
{
"name": "file_operations",
"childs": [
{
"name": "read_file",
"args": {
"path": "input.txt"
}
},
{
"name": "write_file",
"args": {
"path": "output.txt",
"content": "Generated content"
}
}
]
},
{
"name": "search",
"childs": [
{
"name": "web_search",
"args": {
"query": "AI toolkit best practices"
}
}
]
}
]
}Common issues when working with toolkit requests:
- Invalid JSON format: Ensure your JSON is well-formed and follows the structure above
- Missing required fields: The
namefield is required at all levels - Parent not found: Verify that the parent name matches exactly what was registered
- Child not found: Verify that the child name matches exactly what was registered
- Invalid arguments: Make sure the arguments match the schema defined for the tool
- Schema validation: If you're getting errors about required fields, check the jsonschema tags in your argument type definitions
The toolkit provides detailed error messages to help identify the source of request parsing problems.
For a complete working example, see the Claude integration example in this repository.
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/option"
"github.com/h-ess/ai-toolkit/toolkit"
)
// Create your toolkit with various tools...
// Configure Claude with the toolkit
params := anthropic.MessageNewParams{
Model: anthropic.F(anthropic.ModelClaude3_7Sonnet20250219),
System: anthropic.F([]anthropic.TextBlockParam{
anthropic.NewTextBlock(
`You are a helpful assistant. You can execute multiple tools in one invocation.`),
}),
Tools: anthropic.F([]anthropic.ToolUnionUnionParam{
anthropic.ToolParam{
Name: anthropic.F(myToolkit.GetToolkitName()),
Description: anthropic.F(myToolkit.GetToolkitDescription()),
InputSchema: anthropic.F(myToolkit.GetToolkitSchema("anthropic")),
},
}),
}
// Later, in your conversation loop, handle tool use:
for _, block := range claudeResponse.Content {
switch b := block.AsUnion().(type) {
case anthropic.ToolUseBlock:
// Handle the tool use request
toolkitResponse, err := myToolkit.HandleToolKit(ctx, b.Input)
// Send the result back to Claude
toolResultJSON, _ := json.Marshal(toolkitResponse)
toolResultBlock := anthropic.NewToolResultBlock(b.ID, string(toolResultJSON), err != nil)
// Add result to conversation history...
}
}AI-Toolkit excels in scenarios requiring complex, multi-step tool workflows:
- Knowledge Work: Query multiple data sources, filter results, generate summaries
- Content Creation: Research topics, fetch references, generate and publish content
- Data Analysis: Extract data, transform it, analyze patterns, visualize results
- Process Automation: Create multi-step workflows with conditional branches and parallel execution
AI-Toolkit uses a builder pattern with Go generics to create type-safe tools:
// Create a strongly-typed tool with automatic schema generation
toolkit.NewChild[MyArgType](
"tool_name",
"Tool description",
func(ctx context.Context, args MyArgType) (interface{}, error) {
// Implement tool logic here
return result, nil
},
)All toolkit operations accept and propagate context.Context for cancellation support, timeouts, and value passing:
// Context flows from request handling down to individual tools
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// The toolkit propagates this context to all tools
response, err := myToolkit.HandleToolKit(ctx, requestJSON)The toolkit automatically generates JSON schemas from Go types using struct tags:
type UserArgs struct {
Name string `json:"name" jsonschema:"required,description=The user's name"`
Age int `json:"age" jsonschema:"description=The user's age in years"`
Location string `json:"location" jsonschema:"description=The user's location"`
}Standardized error handling with structured error types:
// Return structured errors from tools
if err != nil {
return nil, toolkit.NewError("file_not_found", fmt.Sprintf("File %s not found", path))
}
// Error responses are included in the response structure
response, err := myToolkit.HandleToolKit(ctx, requestJSON)
// Even if err != nil, response contains structured error information| Feature | Traditional Approach | AI-Toolkit |
|---|---|---|
| Latency | High (multiple round trips) | Low (single round trip) |
| Complexity | Complex state management | Simple hierarchical structure |
| Extensibility | Ad-hoc | Structured parent/child system |
| Error Handling | Inconsistent | Standardized |
| Schema Generation | Manual | Automatic from Go types |
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request