Dynamic Tool Injection for AI Agents
AI agent platforms often face a challenge: static tool instructions in prompts cannot adapt to available tools, user permissions, or task context. When an agent sees tools it is not authorized to use, it wastes reasoning steps attempting calls that will fail. When it does not see tools that are available, it cannot leverage capabilities that would resolve the task. This mismatch between visible tools and actual capabilities results in 30-40% task failure rates.
Dynamic tool instruction injection solves this by adapting agent prompts at runtime based on context, permissions, and task requirements. Rather than giving every agent access to every tool (which overwhelms the model’s tool selection) or hardcoding tool sets per agent (which cannot adapt to user permissions), the injector selects relevant, authorized tools per request and generates tool instructions dynamically.
Solution Architecture
Section titled “Solution Architecture”Beluga AI’s prompt package combined with tool registry enables dynamic tool injection. The system analyzes task context, filters tools by permissions, and injects relevant tool instructions into agent prompts at runtime. This ensures agents see only authorized, relevant tools for each specific task.
The architecture has three stages: context analysis (what kind of task is this?), permission filtering (what tools is this user allowed to use?), and prompt injection (generate tool instructions from the filtered set). This separation of concerns means permission policies can be updated without touching the relevance logic, and new tools can be added to the registry without modifying the injection pipeline.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐│ Agent │───▶│ Context │───▶│ Tool ││ Request │ │ Analyzer │ │ Selector │└──────────────┘ └──────────────┘ └──────┬───────┘ │ ▼┌──────────────┐ ┌──────────────┐ ┌──────────────┐│ Enhanced │◀───│ Prompt │◀───│ Permission ││ Prompt │ │ Injector │ │ Filter │└──────────────┘ └──────────────┘ └──────────────┘ ▲ │ ┌─────┴────────┐ │ Tool │ │ Registry │ └──────────────┘Tool Injection Implementation
Section titled “Tool Injection Implementation”The tool injector analyzes task context, selects relevant tools, filters by permissions, and dynamically injects tool instructions into agent prompts. It uses Beluga AI’s prompt template system (prompt.NewPromptTemplate) for structured prompt generation and the tool registry (tool.Registry) for tool discovery. The permission checker runs between selection and injection, acting as a security boundary that ensures no unauthorized tool instructions reach the agent.
package main
import ( "context" "fmt"
"github.com/lookatitude/beluga-ai/agent" "github.com/lookatitude/beluga-ai/prompt" "github.com/lookatitude/beluga-ai/tool"
_ "github.com/lookatitude/beluga-ai/tool/providers/http")
// ToolInstructionInjector dynamically injects tool instructions into prompts.type ToolInstructionInjector struct { toolRegistry tool.Registry promptTemplate *prompt.PromptTemplate permissionChecker *PermissionChecker}
func NewToolInstructionInjector(ctx context.Context) (*ToolInstructionInjector, error) { template, err := prompt.NewPromptTemplate(`You are an AI agent with access to the following tools:
{{range .tools}}- {{.Name}}: {{.Description}} Usage: {{.Usage}}{{end}}
Use these tools to complete the task: {{.task}}`) if err != nil { return nil, fmt.Errorf("create prompt template: %w", err) }
return &ToolInstructionInjector{ toolRegistry: tool.DefaultRegistry, promptTemplate: template, permissionChecker: NewPermissionChecker(), }, nil}
// InjectToolInstructions builds a context-aware prompt with relevant, authorized tools.func (t *ToolInstructionInjector) InjectToolInstructions( ctx context.Context, basePrompt string, userID string, taskContext map[string]string,) (string, error) { // Select relevant tools based on task context allTools := t.toolRegistry.List() relevantTools := t.selectRelevantTools(ctx, allTools, taskContext)
// Filter by user permissions var authorizedTools []tool.Tool for _, tl := range relevantTools { if t.permissionChecker.HasPermission(ctx, userID, tl.Name()) { authorizedTools = append(authorizedTools, tl) } }
// Generate tool instructions toolInstructions, err := t.promptTemplate.Format(map[string]any{ "tools": authorizedTools, "task": taskContext["task"], }) if err != nil { return "", fmt.Errorf("format prompt: %w", err) }
return basePrompt + "\n\n" + toolInstructions, nil}
func (t *ToolInstructionInjector) selectRelevantTools( ctx context.Context, allTools []tool.Tool, taskContext map[string]string,) []tool.Tool { var relevant []tool.Tool taskType := taskContext["task_type"]
for _, tl := range allTools { if t.isToolRelevant(tl, taskType) { relevant = append(relevant, tl) } }
return relevant}
func (t *ToolInstructionInjector) isToolRelevant(tl tool.Tool, taskType string) bool { // Implement relevance logic based on tool metadata and task type return true // Simplified}Agent Integration
Section titled “Agent Integration”Integrate the tool injector with an agent to enable dynamic tool selection:
package main
import ( "context" "fmt"
"github.com/lookatitude/beluga-ai/agent" "github.com/lookatitude/beluga-ai/llm" "github.com/lookatitude/beluga-ai/schema"
_ "github.com/lookatitude/beluga-ai/llm/providers/openai")
// ContextAwareAgent uses dynamic tool injection.type ContextAwareAgent struct { agent agent.Agent injector *ToolInstructionInjector userID string}
func NewContextAwareAgent(ctx context.Context, userID string) (*ContextAwareAgent, error) { model, err := llm.New("openai", nil) if err != nil { return nil, fmt.Errorf("create model: %w", err) }
ag, err := agent.New("base", agent.Config{ Model: model, Name: "context-aware-agent", }) if err != nil { return nil, fmt.Errorf("create agent: %w", err) }
injector, err := NewToolInjectio nInjector(ctx) if err != nil { return nil, fmt.Errorf("create injector: %w", err) }
return &ContextAwareAgent{ agent: ag, injector: injector, userID: userID, }, nil}
func (a *ContextAwareAgent) Execute(ctx context.Context, taskContext map[string]string) (string, error) { basePrompt := "Complete the following task:"
// Inject tools dynamically based on context and permissions enhancedPrompt, err := a.injector.InjectToolInstructions( ctx, basePrompt, a.userID, taskContext, ) if err != nil { return "", fmt.Errorf("inject tools: %w", err) }
// Execute agent with enhanced prompt msgs := []schema.Message{ &schema.SystemMessage{Parts: []schema.ContentPart{ schema.TextPart{Text: enhancedPrompt}, }}, &schema.HumanMessage{Parts: []schema.ContentPart{ schema.TextPart{Text: taskContext["task"]}, }}, }
resp, err := a.agent.Run(ctx, msgs) if err != nil { return "", fmt.Errorf("agent execution: %w", err) }
return resp.Parts[0].(schema.TextPart).Text, nil}Production Considerations
Section titled “Production Considerations”Permission-Based Filtering
Section titled “Permission-Based Filtering”Implement robust permission checking to ensure users only see authorized tools:
package main
import ( "context" "sync")
type PermissionChecker struct { mu sync.RWMutex permissions map[string]map[string]bool // userID -> toolName -> allowed}
func NewPermissionChecker() *PermissionChecker { return &PermissionChecker{ permissions: make(map[string]map[string]bool), }}
func (p *PermissionChecker) HasPermission(ctx context.Context, userID, toolName string) bool { p.mu.RLock() defer p.mu.RUnlock()
userPerms, ok := p.permissions[userID] if !ok { return false }
return userPerms[toolName]}
func (p *PermissionChecker) GrantPermission(userID, toolName string) { p.mu.Lock() defer p.mu.Unlock()
if p.permissions[userID] == nil { p.permissions[userID] = make(map[string]bool) } p.permissions[userID][toolName] = true}Observability
Section titled “Observability”Track tool injection metrics to monitor performance and selection quality:
import ( "github.com/lookatitude/beluga-ai/o11y" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute")
func (t *ToolInjectio nInjector) InjectWithMonitoring( ctx context.Context, basePrompt string, userID string, taskContext map[string]string,) (string, error) { tracer := otel.Tracer("tool-injection") ctx, span := tracer.Start(ctx, "tool.inject") defer span.End()
span.SetAttributes( attribute.String("user_id", userID), attribute.String("task_type", taskContext["task_type"]), )
enhancedPrompt, err := t.InjectToolInstructions(ctx, basePrompt, userID, taskContext) if err != nil { span.RecordError(err) return "", err }
// Track injection success span.SetAttributes(attribute.Bool("injection_success", true))
return enhancedPrompt, nil}Tool Selection Quality
Section titled “Tool Selection Quality”Implement relevance scoring to ensure high-quality tool selection:
func (t *ToolInstructionInjector) selectRelevantToolsWithScoring( ctx context.Context, allTools []tool.Tool, taskContext map[string]string,) []tool.Tool { type scoredTool struct { tool tool.Tool score float64 }
var scored []scoredTool for _, tl := range allTools { score := t.calculateRelevanceScore(tl, taskContext) if score > 0.5 { // Threshold scored = append(scored, scoredTool{tool: tl, score: score}) } }
// Sort by score and return top K sort.Slice(scored, func(i, j int) bool { return scored[i].score > scored[j].score })
maxTools := 10 if len(scored) < maxTools { maxTools = len(scored) }
result := make([]tool.Tool, maxTools) for i := 0; i < maxTools; i++ { result[i] = scored[i].tool }
return result}
func (t *ToolInstructionInjector) calculateRelevanceScore( tl tool.Tool, taskContext map[string]string,) float64 { // Implement scoring based on tool metadata, task type, and historical usage return 0.8 // Simplified}Results
Section titled “Results”Dynamic tool injection delivered significant improvements:
| Metric | Before | After | Improvement |
|---|---|---|---|
| Task Success Rate (%) | 60-70 | 87 | 24-45% |
| Tool Selection Accuracy (%) | 65 | 92 | 42% |
| Permission Violations | 15/month | 0 | 100% reduction |
| Context Relevance (%) | 60 | 91 | 52% |
| Agent Efficiency Score | 6/10 | 8.7/10 | 45% |
Related Resources
Section titled “Related Resources”- Few-Shot SQL Generation for prompt template patterns
- Agent Orchestration Guide for agent patterns
- Tool Integration for tool provider configuration