Skip to main content

Overview

Tools are the building blocks that enable agents to interact with external systems, execute code, generate images, or perform any custom operation. Qwen-Agent provides a flexible tool system based on the BaseTool class.

Quick Start

Here’s a simple custom tool example from the codebase (examples/assistant_add_custom_tool.py:30):
from qwen_agent.tools.base import BaseTool, register_tool
import json

@register_tool('my_image_gen')
class MyImageGen(BaseTool):
    description = 'AI painting service that generates images from text descriptions.'
    parameters = [{
        'name': 'prompt',
        'type': 'string',
        'description': 'Detailed description of the desired image content, in English',
        'required': True,
    }]

    def call(self, params: str, **kwargs) -> str:
        import json5
        prompt = json5.loads(params)['prompt']
        # Your implementation here
        return json.dumps(
            {'image_url': f'https://image.pollinations.ai/prompt/{prompt}'},
            ensure_ascii=False,
        )

Understanding BaseTool

The BaseTool class (qwen_agent/tools/base.py:109) defines the interface all tools must implement:
class BaseTool(ABC):
    name: str = ''  # Tool identifier
    description: str = ''  # What the tool does
    parameters: Union[List[dict], dict] = []  # Tool parameters schema
    
    @abstractmethod
    def call(self, params: Union[str, dict], **kwargs) -> Union[str, list, dict]:
        """Execute the tool"""
        raise NotImplementedError

Creating a Custom Tool

1
Step 1: Define the Tool Class
2
Inherit from BaseTool and use the @register_tool decorator:
3
from qwen_agent.tools.base import BaseTool, register_tool

@register_tool('weather_query')
class WeatherTool(BaseTool):
    description = 'Get current weather for a city'
    parameters = [{
        'name': 'city',
        'type': 'string',
        'description': 'City name',
        'required': True
    }, {
        'name': 'units',
        'type': 'string',
        'description': 'Temperature units (celsius/fahrenheit)',
        'required': False
    }]
4
Step 2: Implement the call Method
5
    def call(self, params: str, **kwargs) -> str:
        # Parse parameters
        params_dict = self._verify_json_format_args(params)
        city = params_dict['city']
        units = params_dict.get('units', 'celsius')
        
        # Your logic here
        weather_data = fetch_weather(city, units)
        
        # Return result as string or JSON
        return json.dumps(weather_data, ensure_ascii=False)
6
Step 3: Use in an Agent
7
from qwen_agent.agents import Assistant

agent = Assistant(
    llm={'model': 'qwen-max'},
    function_list=['weather_query']  # Use your registered tool
)

messages = [{'role': 'user', 'content': 'What is the weather in Beijing?'}]
for response in agent.run(messages):
    print(response)

Parameter Schema Formats

Qwen-Agent supports two parameter schema formats:

List Format (Simple)

parameters = [{
    'name': 'param_name',
    'type': 'string',
    'description': 'Parameter description',
    'required': True
}]

OpenAI JSON Schema Format (Advanced)

parameters = {
    'type': 'object',
    'properties': {
        'location': {
            'type': 'string',
            'description': 'The city and state, e.g. San Francisco, CA'
        },
        'unit': {
            'type': 'string',
            'enum': ['celsius', 'fahrenheit']
        }
    },
    'required': ['location']
}
The OpenAI JSON Schema format provides better validation and is recommended for complex tools. It must conform to the OpenAI function calling specification.

Real-World Examples

Example 1: Database Query Tool

import json
import sqlite3
from qwen_agent.tools.base import BaseTool, register_tool

@register_tool('database_query')
class DatabaseQuery(BaseTool):
    description = 'Execute SQL queries on the database'
    parameters = {
        'type': 'object',
        'properties': {
            'query': {
                'type': 'string',
                'description': 'SQL query to execute'
            }
        },
        'required': ['query']
    }
    
    def __init__(self, cfg=None):
        super().__init__(cfg)
        self.db_path = cfg.get('db_path', 'database.db') if cfg else 'database.db'
    
    def call(self, params: str, **kwargs) -> str:
        params_dict = self._verify_json_format_args(params)
        query = params_dict['query']
        
        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            cursor.execute(query)
            results = cursor.fetchall()
            conn.close()
            
            return json.dumps({
                'success': True,
                'data': results
            })
        except Exception as e:
            return json.dumps({
                'success': False,
                'error': str(e)
            })

Example 2: API Integration Tool

import requests
from qwen_agent.tools.base import BaseTool, register_tool

@register_tool('github_search')
class GitHubSearch(BaseTool):
    description = 'Search GitHub repositories'
    parameters = [{
        'name': 'query',
        'type': 'string',
        'description': 'Search query',
        'required': True
    }, {
        'name': 'language',
        'type': 'string',
        'description': 'Programming language filter',
        'required': False
    }]
    
    def call(self, params: str, **kwargs) -> str:
        import json5
        params_dict = json5.loads(params)
        query = params_dict['query']
        language = params_dict.get('language', '')
        
        search_query = f"{query} language:{language}" if language else query
        
        response = requests.get(
            'https://api.github.com/search/repositories',
            params={'q': search_query, 'sort': 'stars', 'order': 'desc'},
            headers={'Accept': 'application/vnd.github.v3+json'}
        )
        
        if response.status_code == 200:
            data = response.json()
            repos = [{
                'name': repo['full_name'],
                'stars': repo['stargazers_count'],
                'url': repo['html_url']
            } for repo in data['items'][:5]]
            return json.dumps(repos, ensure_ascii=False)
        else:
            return f"Error: {response.status_code}"

Example 3: File Processing Tool

import os
from qwen_agent.tools.base import BaseToolWithFileAccess, register_tool

@register_tool('csv_analyzer')
class CSVAnalyzer(BaseToolWithFileAccess):
    description = 'Analyze CSV files and return statistics'
    parameters = [{
        'name': 'file_path',
        'type': 'string',
        'description': 'Path to CSV file',
        'required': True
    }]
    
    def call(self, params: str, files=None, **kwargs) -> str:
        # BaseToolWithFileAccess automatically handles file downloads
        super().call(params, files=files)
        
        params_dict = self._verify_json_format_args(params)
        file_path = params_dict['file_path']
        
        # Process file in self.work_dir
        local_path = os.path.join(self.work_dir, os.path.basename(file_path))
        
        import pandas as pd
        df = pd.read_csv(local_path)
        
        stats = {
            'rows': len(df),
            'columns': len(df.columns),
            'column_names': df.columns.tolist(),
            'summary': df.describe().to_dict()
        }
        
        return json.dumps(stats, ensure_ascii=False, indent=2)

Tool Configuration

Pass configuration when initializing tools:
# Use default configuration
agent = Assistant(
    llm={'model': 'qwen-max'},
    function_list=['code_interpreter']
)

Built-in Tools

Qwen-Agent includes several powerful built-in tools:
  • code_interpreter: Execute Python code in a sandboxed environment
  • image_gen: Generate images (integration point)
  • doc_parser: Parse and extract content from documents
See the Code Interpreter guide for detailed usage.

Helper Methods

The BaseTool class provides useful helper methods (qwen_agent/tools/base.py:140):

_verify_json_format_args(params, strict_json=False)

Validate and parse tool parameters:
def call(self, params: str, **kwargs) -> str:
    # Automatically validates against self.parameters schema
    params_dict = self._verify_json_format_args(params)
    # Use params_dict...

function Property

Get OpenAI-compatible function definition:
tool = MyTool()
function_def = tool.function
# Returns: {'name': '...', 'description': '...', 'parameters': ...}

Best Practices

Tool Design Guidelines
  • Keep tools focused on a single responsibility
  • Provide clear, detailed descriptions for better LLM understanding
  • Use descriptive parameter names and descriptions
  • Return structured JSON for complex data
  • Handle errors gracefully and return informative error messages
Security Considerations
  • Validate all inputs thoroughly
  • Use BaseToolWithFileAccess for tools that need file operations
  • Be cautious with tools that execute code or make system calls
  • Consider rate limiting for API-based tools
  • Never expose sensitive credentials in tool responses

Troubleshooting

Tool Not Found Error

# Make sure to import the file where @register_tool is called
import my_tools  # This registers the tool

agent = Assistant(
    function_list=['my_custom_tool']  # Now it's available
)

Parameter Validation Errors

# Use json5 for more flexible parsing
import json5

def call(self, params: str, **kwargs):
    params_dict = json5.loads(params)  # Handles trailing commas, comments, etc.

Next Steps

Build docs developers (and LLMs) love