#Overview
Learn how to programmatically inspect and understand the tools available to your agents. This example covers tool introspection, schema exploration, parameter validation, and format conversion - essential skills for debugging, testing, and building advanced agent systems.
#What You'll Learn
- Inspecting available tools and their metadata
- Understanding tool parameters and schemas
- Converting tool formats (OpenAI, Anthropic, etc.)
- Using agent health checks
- Debugging tool integration issues
- Programmatically generating tool documentation
#Prerequisites
- Understanding of PostgreSQL Tools basics
- Familiarity with tool registration
- Basic Python programming
#Why Tool Introspection Matters
Tool introspection enables:
- Debugging: Understand why tools aren't being called correctly
- Testing: Validate tool configurations programmatically
- Documentation: Auto-generate tool docs from schemas
- Integration: Convert tool formats for different LLM providers
- Monitoring: Track which tools are available and working
#Step 1: List Available Tools
See what tools an agent has access to:
from daita.agents.agent import Agent
from daita.plugins.postgresql import postgresql
import asyncio
async def main():
# Create plugin and agent
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(
name="introspection_agent",
tools=[db]
)
# List all tool names
print(f"Agent has {len(agent.tool_names)} tools:")
for tool_name in agent.tool_names:
print(f" - {tool_name}")
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())Output:
Agent has 4 tools:
- list_tables
- get_table_schema
- query_database
- execute_sql#Step 2: Inspect Tool Metadata
Access detailed information about each tool:
async def main():
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(name="introspection_agent", tools=[db])
# Get tool from registry
tool = agent.tool_registry.get("query_database")
print(f"Tool: {tool.name}")
print(f"Description: {tool.description}")
print(f"Category: {tool.category}")
print(f"Source: {tool.source}")
print(f"Plugin: {tool.plugin_name}")
print(f"Timeout: {tool.timeout_seconds}s")
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())Output:
Tool: query_database
Description: Execute SELECT queries on the PostgreSQL database
Category: database
Source: plugin
Plugin: postgresql
Timeout: 30sTool metadata includes:
name: Tool identifierdescription: What the tool doescategory: Logical grouping (database, storage, compute, etc.)source: Where it came from (plugin, custom, framework)plugin_name: Parent plugin nametimeout_seconds: Maximum execution time
#Step 3: Explore Tool Parameters
Understand what parameters a tool accepts:
async def main():
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(name="introspection_agent", tools=[db])
# Inspect parameters for a specific tool
tool = agent.tool_registry.get("query_database")
print(f"Tool: {tool.name}\n")
print("Parameters:")
for param_name, param_info in tool.parameters.items():
required = "required" if param_info.get("required") else "optional"
param_type = param_info.get("type", "unknown")
description = param_info.get("description", "No description")
print(f"\n {param_name} ({param_type}, {required})")
print(f" {description}")
# Show default value if present
if "default" in param_info:
print(f" Default: {param_info['default']}")
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())Output:
Tool: query_database
Parameters:
sql (string, required)
The SQL SELECT query to execute
params (array, optional)
Query parameters for parameterized queries (e.g., [$1, $2])
Default: None#Step 4: Convert to OpenAI Function Format
Convert tool schemas to OpenAI's function calling format:
async def main():
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(name="introspection_agent", tools=[db])
# Convert tool to OpenAI format
tool = agent.tool_registry.get("query_database")
openai_format = tool.to_openai_function()
print("OpenAI Function Format:")
print(f"Name: {openai_format['name']}")
print(f"Description: {openai_format['description']}")
print(f"\nParameters:")
print(f" Type: {openai_format['parameters']['type']}")
print(f" Properties: {list(openai_format['parameters']['properties'].keys())}")
print(f" Required: {openai_format['parameters'].get('required', [])}")
# Full format (for API calls)
import json
print(f"\nFull OpenAI Format:")
print(json.dumps(openai_format, indent=2))
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())Output:
OpenAI Function Format:
Name: query_database
Description: Execute SELECT queries on the PostgreSQL database
Parameters:
Type: object
Properties: ['sql', 'params']
Required: ['sql']
Full OpenAI Format:
{
"name": "query_database",
"description": "Execute SELECT queries on the PostgreSQL database",
"parameters": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "The SQL SELECT query to execute"
},
"params": {
"type": "array",
"description": "Query parameters for parameterized queries",
"default": null
}
},
"required": ["sql"]
}
}#Step 5: Convert to Anthropic Tool Format
Convert tool schemas to Anthropic's tool format:
async def main():
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(name="introspection_agent", tools=[db])
# Convert tool to Anthropic format
tool = agent.tool_registry.get("query_database")
anthropic_format = tool.to_anthropic_tool()
print("Anthropic Tool Format:")
print(f"Name: {anthropic_format['name']}")
print(f"Description: {anthropic_format['description']}")
print(f"\nInput Schema:")
print(f" Type: {anthropic_format['input_schema']['type']}")
print(f" Properties: {list(anthropic_format['input_schema']['properties'].keys())}")
print(f" Required: {anthropic_format['input_schema'].get('required', [])}")
# Full format
import json
print(f"\nFull Anthropic Format:")
print(json.dumps(anthropic_format, indent=2))
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())Output:
Anthropic Tool Format:
Name: query_database
Description: Execute SELECT queries on the PostgreSQL database
Input Schema:
Type: object
Properties: ['sql', 'params']
Required: ['sql']
Full Anthropic Format:
{
"name": "query_database",
"description": "Execute SELECT queries on the PostgreSQL database",
"input_schema": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "The SQL SELECT query to execute"
},
"params": {
"type": "array",
"description": "Query parameters for parameterized queries",
"default": null
}
},
"required": ["sql"]
}
}#Step 6: Use Agent Health Checks
Monitor agent status including tool availability:
async def main():
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(name="introspection_agent", tools=[db])
# Get agent health status
health = agent.health
print("Agent Health Status:")
print(f" Status: {health['status']}")
print(f" Agent Name: {health['name']}")
print(f"\nTools:")
print(f" Count: {health['tools']['count']}")
print(f" Setup: {health['tools']['setup']}")
print(f" Available: {', '.join(health['tools']['names'])}")
if 'llm' in health:
print(f"\nLLM:")
print(f" Provider: {health['llm']['provider']}")
print(f" Model: {health['llm']['model']}")
print(f" Connected: {health['llm']['connected']}")
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())Output:
Agent Health Status:
Status: healthy
Agent Name: introspection_agent
Tools:
Count: 4
Setup: True
Available: list_tables, get_table_schema, query_database, execute_sql#Step 7: Comprehensive Tool Inspector
Build a complete tool inspection utility:
async def inspect_all_tools(agent):
"""Comprehensive tool inspection utility"""
print("="*70)
print(f"TOOL INSPECTION REPORT - Agent: {agent.name}")
print("="*70)
for tool_name in agent.tool_names:
tool = agent.tool_registry.get(tool_name)
print(f"\n{'='*70}")
print(f"Tool: {tool.name}")
print(f"{'='*70}")
# Basic info
print(f"\nBasic Information:")
print(f" Description: {tool.description}")
print(f" Category: {tool.category}")
print(f" Source: {tool.source}")
print(f" Plugin: {tool.plugin_name}")
print(f" Timeout: {tool.timeout_seconds}s")
# Parameters
print(f"\nParameters:")
if tool.parameters:
for param_name, param_info in tool.parameters.items():
required = "✓ required" if param_info.get("required") else "○ optional"
print(f" • {param_name} ({param_info['type']}) - {required}")
print(f" {param_info.get('description', 'No description')}")
if "default" in param_info:
print(f" Default: {param_info['default']}")
else:
print(" No parameters")
# Format examples
print(f"\nOpenAI Function Format:")
openai_format = tool.to_openai_function()
print(f" Name: {openai_format['name']}")
print(f" Has parameters: {bool(openai_format['parameters']['properties'])}")
print(f"\nAnthropic Tool Format:")
anthropic_format = tool.to_anthropic_tool()
print(f" Name: {anthropic_format['name']}")
print(f" Has input schema: {bool(anthropic_format['input_schema']['properties'])}")
print(f"\n{'='*70}")
print(f"Total Tools: {len(agent.tool_names)}")
print(f"{'='*70}\n")
async def main():
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(name="introspection_agent", tools=[db])
# Run comprehensive inspection
await inspect_all_tools(agent)
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())#Step 8: Generate Tool Documentation
Automatically create markdown documentation from tool schemas:
async def generate_tool_docs(agent, output_file="tools_documentation.md"):
"""Generate markdown documentation for all tools"""
with open(output_file, "w") as f:
f.write(f"# Tool Documentation - {agent.name}\n\n")
f.write(f"Total tools: {len(agent.tool_names)}\n\n")
for tool_name in agent.tool_names:
tool = agent.tool_registry.get(tool_name)
# Tool header
f.write(f"## {tool.name}\n\n")
f.write(f"**Description:** {tool.description}\n\n")
f.write(f"**Category:** {tool.category}\n\n")
f.write(f"**Source:** {tool.source} ({tool.plugin_name})\n\n")
f.write(f"**Timeout:** {tool.timeout_seconds}s\n\n")
# Parameters table
if tool.parameters:
f.write("### Parameters\n\n")
f.write("| Parameter | Type | Required | Description |\n")
f.write("|-----------|------|----------|-------------|\n")
for param_name, param_info in tool.parameters.items():
required = "✓" if param_info.get("required") else "○"
param_type = param_info.get("type", "unknown")
description = param_info.get("description", "No description")
f.write(f"| {param_name} | {param_type} | {required} | {description} |\n")
f.write("\n")
# Usage example
f.write("### Usage Example\n\n")
f.write("```python\n")
f.write(f'result = await agent.call_tool("{tool_name}", {{\n')
if tool.parameters:
for param_name, param_info in tool.parameters.items():
if param_info.get("required"):
example = '"example_value"' if param_info["type"] == "string" else "[]"
f.write(f' "{param_name}": {example},\n')
f.write("})\n")
f.write("```\n\n")
f.write("---\n\n")
print(f"Documentation generated: {output_file}")
async def main():
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(name="introspection_agent", tools=[db])
# Generate documentation
await generate_tool_docs(agent)
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())#Complete Example
Full tool introspection example:
from daita.agents.agent import Agent
from daita.plugins.postgresql import postgresql
import asyncio
import json
async def main():
# Create plugin and agent
db = postgresql(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
agent = Agent(name="introspection_agent", tools=[db])
print("\n" + "="*70)
print("TOOL INTROSPECTION EXAMPLE")
print("="*70)
# 1. List all tools
print(f"\n1. Available Tools ({len(agent.tool_names)}):")
for tool_name in agent.tool_names:
print(f" - {tool_name}")
# 2. Inspect each tool
print("\n2. Detailed Tool Information:\n")
for tool_name in agent.tool_names:
tool = agent.tool_registry.get(tool_name)
print(f"\nTool: {tool.name}")
print(f" Description: {tool.description}")
print(f" Category: {tool.category}")
print(f" Source: {tool.source}")
print(f" Plugin: {tool.plugin_name}")
print(f" Timeout: {tool.timeout_seconds}s")
print(f" Parameters:")
for param_name, param_info in tool.parameters.items():
required = "required" if param_info.get("required") else "optional"
print(f" - {param_name} ({param_info['type']}, {required})")
print(f" {param_info.get('description', 'No description')}")
# Format conversion examples
print(f"\n OpenAI Function Format:")
openai_format = tool.to_openai_function()
print(f" {json.dumps(openai_format, indent=6)}")
print(f"\n Anthropic Tool Format:")
anthropic_format = tool.to_anthropic_tool()
print(f" Name: {anthropic_format['name']}")
print(f" Input Schema: {anthropic_format['input_schema']['type']}")
print("\n" + "-"*70)
# 3. Agent health check
print("\n3. Agent Health Status:")
health = agent.health
print(f" Tools: {health['tools']['count']} tools")
print(f" Setup: {health['tools']['setup']}")
print(f" Available: {', '.join(health['tools']['names'])}")
print("\n" + "="*70)
await agent.stop()
if __name__ == "__main__":
asyncio.run(main())#Framework Internals
Tool Registry Architecture:
- Tool Registry: Central store for all tools (
agent.tool_registry) - Tool Metadata: Each tool has rich metadata (name, description, params, etc.)
- Format Converters: Built-in conversion to LLM-specific formats
- Health Monitoring: Runtime status of tool availability
- Dynamic Registration: Tools can be added/removed at runtime
Tool Schema Structure:
{
"name": "tool_name",
"description": "What it does",
"category": "database",
"source": "plugin",
"plugin_name": "postgresql",
"timeout_seconds": 30,
"parameters": {
"param1": {
"type": "string",
"description": "Param description",
"required": True
}
}
}#Use Cases for Tool Introspection
- Debugging: Verify tools are registered correctly
- Testing: Validate tool schemas in CI/CD
- Documentation: Auto-generate tool docs
- Monitoring: Track tool availability in production
- LLM Integration: Convert to provider-specific formats
- Tool Discovery: Let agents inspect their own capabilities
#Key Takeaways
- Tool introspection provides complete visibility into agent capabilities
- Tool metadata is rich including descriptions, parameters, and constraints
- Format conversion is built-in for OpenAI and Anthropic
- Agent health checks monitor tool availability
- Programmatic access enables automation and testing
- Tool schemas are self-documenting - use them for docs generation
#Next Steps
- PostgreSQL Custom Handlers to build complex logic with tools
- Multi-Plugin Architecture to combine multiple tool sources
- Database Query Agent for LLM-powered database operations