Backend Tool System API
Technical documentation for the backend tool creation and prompt management system based on tools/model_tools.py and tools/tool_prompts.py.
Overview
The backend tool system provides a sophisticated framework for creating, configuring, and managing AI model tools. It integrates with the VAC Service Architecture and Tool Context to provide dynamic tool capabilities for AI assistants.
Key Components:
- Dynamic tool creation for Google Gemini models
- Langfuse-based prompt management and caching
- UI/non-UI tool filtering and configuration
- Markdown formatting for tool contexts
- Integration with permission and access control systems
Model Tools (tools/model_tools.py)
Handles creation of Google GenAI Tool objects for AI model integration.
Core Function
create_model_tools(tools: List[str]) -> List[types.Tool]
Creates Google GenAI Tool objects from tool name strings.
Supported Tools:
google_search_retrieval→ Creates Google Search toolcode_execution→ Creates Code Execution toolurl_processing→ Creates URL Context tool
Implementation:
def create_model_tools(tools):
"""
Create Google GenAI Tool objects from tool names.
Args:
tools: List of tool names or None
Returns:
List of types.Tool objects
"""
if not tools:
return []
model_tools = []
for tool in tools:
if tool == "google_search_retrieval":
model_tools.append(types.Tool(google_search={}))
elif tool == "code_execution":
model_tools.append(types.Tool(code_execution={}))
elif tool == "url_processing":
model_tools.append(types.Tool(url_context={}))
# Unknown tools are silently ignored
log.info(f"Created model_tools={model_tools}")
return model_tools
Usage Example:
# Create tools for assistant
requested_tools = ["google_search_retrieval", "code_execution"]
model_tools = create_model_tools(requested_tools)
# Pass to VAC service
result = await vac_stream(
question=user_question,
tools_to_use=model_tools,
# ... other parameters
)
Error Handling:
- Unknown tools are silently ignored (no exceptions thrown)
- Empty or None input returns empty list
- Duplicates are preserved (no deduplication)
- Logs created tools for debugging
Integration Points:
- VAC Service: Tools passed directly to AI model calls
- Permission System: Tool names validated against user permissions
- Tool Context: Tool configurations merged with model tools
Tool Prompts (tools/tool_prompts.py)
Manages dynamic loading and formatting of tool-specific prompts from Langfuse.
Core Functions
add_tool_prompts(tools, prefix="aitana3", ui_only=None) -> str
Loads and formats tool-specific prompts with caching and filtering.
Parameters:
tools: List of tool names (strings) or tool objects (dicts)prefix: Langfuse prompt prefix (default: “aitana3”)ui_only: Filter for UI tools (True=UI only, False=non-UI only, None=all)
Process:
- Tool Name Extraction: Handle both string and dict tool formats
- UI Filtering: Apply UI-only filter if specified
- Prompt Loading: Load from Langfuse with caching (300s TTL)
- Error Handling: Continue on individual prompt failures
- Content Assembly: Join successful prompts with double newlines
Implementation:
def add_tool_prompts(tools, prefix="aitana3", ui_only=None):
"""
Load tool prompts from Langfuse and format them.
Args:
tools: List of tool names or tool objects
prefix: Langfuse prompt prefix
ui_only: Filter for UI tools (True/False/None)
Returns:
Formatted prompt string
"""
if not tools:
return ""
prompts = []
for tool in tools:
# Extract tool name
if isinstance(tool, str):
tool_name = tool
elif isinstance(tool, dict):
tool_name = tool.get("name")
else:
raise ValueError(f"What is tool? {tool}")
# Apply UI filter
if ui_only is True and tool_name not in UI_PROMPTS:
continue
elif ui_only is False and tool_name in UI_PROMPTS:
continue
# Load prompt from Langfuse
try:
prompt = langfuse.get_prompt(f"{prefix}-{tool_name}", cache_ttl_seconds=300)
compiled_prompt = prompt.compile()
prompts.append(compiled_prompt)
except Exception as e:
log.warning(f"Failed to load prompt for tool {tool_name}: {e}")
continue
return "\n\n".join(prompts)
UI Tools Configuration:
UI_PROMPTS = [
"tooltips",
"alerts",
"preview",
"plots",
"artifact",
"dynamic-ui",
"assistantresponse"
]
Usage Examples:
Basic Usage:
# Load all tool prompts
tools = ["google_search_retrieval", "code_execution", "tooltips"]
prompts = add_tool_prompts(tools)
UI-Only Filtering:
# Load only UI tool prompts
ui_tools = ["tooltips", "alerts", "search", "plots"]
ui_prompts = add_tool_prompts(ui_tools, ui_only=True)
# Returns prompts for: tooltips, alerts, plots (excludes search)
Non-UI Filtering:
# Load only non-UI tool prompts
mixed_tools = ["google_search_retrieval", "tooltips", "code_execution"]
backend_prompts = add_tool_prompts(mixed_tools, ui_only=False)
# Returns prompts for: google_search_retrieval, code_execution (excludes tooltips)
Custom Prefix:
# Use custom Langfuse prompt prefix
prompts = add_tool_prompts(tools, prefix="custom-assistant")
# Loads prompts: custom-assistant-tool1, custom-assistant-tool2, etc.
Prompt Management Features
Langfuse Integration
Caching Strategy:
- 300-second TTL for prompt caching
- Reduces API calls and improves performance
- Cache key based on prompt name
Prompt Naming Convention:
{prefix}-{tool_name}
Examples:
aitana3-google_search_retrievalaitana3-code_executioncustom-assistant-tooltips
Error Resilience
Individual Failure Handling:
# If one prompt fails, others continue loading
tools = ["valid_tool", "invalid_tool", "another_valid_tool"]
prompts = add_tool_prompts(tools)
# Returns prompts for valid tools only, logs warning for invalid_tool
Graceful Degradation:
- Failed prompts are logged but don’t stop processing
- Empty result if all prompts fail
- Partial success returns available prompts
Format Utilities
format_as_markdown(context: Dict) -> str
Converts context dictionaries to structured markdown format for AI consumption.
Features:
- Recursive processing of nested dictionaries
- List handling with item enumeration
- Section headers with markdown formatting
- XML-style tag wrapping for structured data
Example Usage:
context = {
"user_info": {
"name": "John Doe",
"email": "john@example.com"
},
"search_results": [
{"title": "Result 1", "url": "https://example.com/1"},
{"title": "Result 2", "url": "https://example.com/2"}
],
"settings": {
"theme": "dark",
"notifications": True
}
}
formatted = format_as_markdown(context)
Output:
### user_info
<name>
John Doe
</name>
<email>
john@example.com
</email>
---
### search_results
<item>
<title>
Result 1
</title>
<url>
https://example.com/1
</url>
</item>
<item>
<title>
Result 2
</title>
<url>
https://example.com/2
</url>
</item>
---
### settings
<theme>
dark
</theme>
<notifications>
True
</notifications>
Processing Rules:
- Top-level keys →
### {key}headers - Dictionary values → Nested
<key>value</key>tags - List values →
<item>wrappers with nested structure - Sections → Separated by
---dividers - Primitives → String conversion with proper formatting
Integration with Core Systems
VAC Service Integration
Tool system integrates with VAC Service Architecture:
# Tool preparation for VAC service
def prepare_vac_tools(emissary_config):
"""Prepare tools for VAC service processing."""
# Extract tool names from config
tool_names = emissary_config.get("tools", [])
# Create model tools for AI
model_tools = create_model_tools(tool_names)
# Load tool prompts for context
tool_prompts = add_tool_prompts(tool_names, ui_only=False)
# Combine for VAC service
return {
"model_tools": model_tools,
"tool_context": tool_prompts,
"tool_configs": emissary_config.get("toolConfigs", {})
}
# Usage in VAC service call
vac_tools = prepare_vac_tools(emissary_config)
result = await vac_stream(
question=user_question,
tools_to_use=vac_tools["model_tools"],
instructions=f"{base_instructions}\n\n{vac_tools['tool_context']}",
emissaryConfig=emissary_config
)
Email Integration
Tool system supports Email Integration processing:
# Email-specific tool processing
async def process_email_tools(assistant_config, current_user):
"""Process tools for email context."""
# Get permitted tools for user
requested_tools = assistant_config.get('tools', [])
tool_configs = assistant_config.get('toolConfigs', {})
user_permissions, allowed_configs = permitted_tools(
current_user, requested_tools, tool_configs
)
# Create model tools for permitted tools only
email_model_tools = create_model_tools(user_permissions)
# Load non-UI prompts (UI tools not relevant for email)
email_tool_prompts = add_tool_prompts(user_permissions, ui_only=False)
return {
"tools": user_permissions,
"model_tools": email_model_tools,
"tool_prompts": email_tool_prompts,
"configs": allowed_configs
}
Permission System Integration
Tool system respects Tool Context permissions:
# Permission-aware tool loading
def load_user_tools(user, assistant_tools):
"""Load tools based on user permissions."""
# Check permissions (from tool_permissions.py)
permitted_tool_names, permitted_configs = permitted_tools(
user, assistant_tools, {}
)
# Filter tools by permissions
filtered_tools = [
tool for tool in assistant_tools
if tool in permitted_tool_names
]
# Create model tools for permitted tools
user_model_tools = create_model_tools(filtered_tools)
# Load prompts for permitted tools
user_tool_prompts = add_tool_prompts(filtered_tools)
return {
"permitted_tools": filtered_tools,
"model_tools": user_model_tools,
"tool_prompts": user_tool_prompts
}
Testing and Validation
Model Tools Testing
Tool Creation Validation:
def test_create_model_tools_google_search():
"""Test Google Search tool creation."""
tools = ["google_search_retrieval"]
result = create_model_tools(tools)
assert len(result) == 1
assert isinstance(result[0], types.Tool)
assert hasattr(result[0], 'google_search')
def test_create_model_tools_unknown_tool():
"""Test unknown tool handling."""
tools = ["unknown_tool", "google_search_retrieval"]
result = create_model_tools(tools)
# Should only create known tools
assert len(result) == 1
assert hasattr(result[0], 'google_search')
Prompt System Testing
Prompt Loading with Mocking:
@patch('tools.tool_prompts.langfuse')
def test_add_tool_prompts_string_tools(mock_langfuse):
"""Test prompt loading for string tools."""
mock_prompt = MagicMock()
mock_prompt.compile.return_value = "Tool prompt content"
mock_langfuse.get_prompt.return_value = mock_prompt
tools = ["tool1", "tool2"]
result = add_tool_prompts(tools)
assert "Tool prompt content" in result
assert mock_langfuse.get_prompt.call_count == 2
Error Handling Testing:
@patch('tools.tool_prompts.langfuse')
def test_add_tool_prompts_error_handling(mock_langfuse):
"""Test error handling for failed prompt loads."""
mock_langfuse.get_prompt.side_effect = Exception("Prompt not found")
tools = ["tool1", "tool2"]
result = add_tool_prompts(tools)
# Should return empty string when all prompts fail
assert result == ""
UI Filtering Testing:
def test_add_tool_prompts_ui_filter():
"""Test UI-only filtering."""
tools = ["tooltips", "google_search_retrieval", "alerts"]
# UI-only should include tooltips and alerts
ui_result = add_tool_prompts(tools, ui_only=True)
# Non-UI should include only google_search_retrieval
non_ui_result = add_tool_prompts(tools, ui_only=False)
Performance Considerations
Prompt Caching
Langfuse Cache Configuration:
# 300-second TTL reduces API calls
prompt = langfuse.get_prompt(prompt_name, cache_ttl_seconds=300)
Cache Effectiveness:
- Reduces API latency for repeated tool combinations
- Improves response times for common assistants
- Decreases Langfuse API usage costs
Tool Processing Optimization
Batch Processing:
# Process multiple tools in single function call
tools = ["search", "code", "analysis", "ui"]
all_prompts = add_tool_prompts(tools) # Single batch operation
# Avoid individual calls
# search_prompt = add_tool_prompts(["search"]) # Inefficient
# code_prompt = add_tool_prompts(["code"]) # Inefficient
Memory Efficiency:
- String concatenation with
join()for large prompt sets - Lazy evaluation for unused tool combinations
- Efficient filtering with early returns
Configuration and Deployment
Environment Variables
Langfuse Configuration:
# Required for prompt loading
LANGFUSE_SECRET_KEY=your_secret_key
LANGFUSE_PUBLIC_KEY=your_public_key
LANGFUSE_HOST=https://your-langfuse-instance.com
Tool-Specific Settings:
# Google Search API
GOOGLE_SEARCH_API_KEY=your_api_key
GOOGLE_SEARCH_ENGINE_ID=your_engine_id
# Code execution settings
CODE_EXECUTION_TIMEOUT=30
CODE_EXECUTION_MEMORY_LIMIT=512MB
Prompt Management Best Practices
Naming Conventions:
- Use consistent prefix patterns:
{assistant_type}-{tool_name} - Version prompts with suffixes:
aitana3-search-v2 - Environment-specific prefixes:
dev-aitana3-search
Content Guidelines:
- Keep prompts focused and specific to tool functionality
- Include example usage and expected outputs
- Maintain backward compatibility when updating prompts
- Test prompt changes with representative tool combinations
Monitoring:
- Track prompt loading success rates
- Monitor cache hit ratios
- Log prompt compilation times
- Alert on prompt loading failures
Error Handling and Debugging
Common Issues
Prompt Loading Failures:
# Debug prompt loading
try:
prompt = langfuse.get_prompt(f"{prefix}-{tool_name}")
log.info(f"Successfully loaded prompt for {tool_name}")
except Exception as e:
log.error(f"Failed to load prompt for {tool_name}: {e}")
# Check: prompt exists in Langfuse, API keys correct, network connectivity
Tool Creation Issues:
# Debug tool creation
model_tools = create_model_tools(tool_names)
log.info(f"Created {len(model_tools)} tools from {len(tool_names)} requested")
# Check for silently ignored tools
created_tool_types = [type(tool).__name__ for tool in model_tools]
log.debug(f"Created tool types: {created_tool_types}")
Monitoring and Observability
Key Metrics:
- Tool creation success rate
- Prompt loading latency
- Cache hit ratios
- Tool usage patterns by assistant
- Error rates by tool type
Logging Strategy:
# Structured logging for tool operations
log.info("tool_operation", extra={
"operation": "create_model_tools",
"requested_tools": tool_names,
"created_count": len(model_tools),
"processing_time": processing_time,
"user_id": user.get("uid"),
"assistant_id": assistant_id
})
Related Documentation
- VAC Service Architecture - Core AI processing pipeline
- Tool Context - User-facing tool configuration
- Backend Email API - Email integration using tools
- Email Integration Testing - Tool system testing strategies
- Langfuse Tracing - Observability and prompt management