Introduction
The difference between a tool an LLM uses correctly 50% of the time versus 95% of the time often comes down to design. Good tool design makes the LLM's job easier and your agent more reliable.
Design Principle: A well-designed tool is one the LLM would choose correctly and use correctly, even without examples. Name it clearly, describe it thoroughly, and make parameters intuitive.
Naming Conventions
Tool names should be immediately clear about what the tool does:
Verb-Noun Pattern
🐍naming_patterns.py
1# ✅ GOOD: Verb-noun clearly describes action and target
2"get_weather" # Retrieves weather
3"search_web" # Searches the web
4"read_file" # Reads a file
5"create_issue" # Creates an issue
6"send_email" # Sends an email
7"calculate_sum" # Calculates a sum
8"delete_user" # Deletes a user
9
10# ❌ BAD: Vague or unclear names
11"weather" # Is this get? set? check?
12"file_operation" # Which operation?
13"process" # Process what?
14"handler" # Handler for what?
15"do_thing" # Too vague
16
17# ✅ GOOD: Specific actions when needed
18"search_documents" # vs just "search"
19"list_directory" # vs just "list"
20"update_user_profile" # vs just "update"Consistency Matters
🐍naming_consistency.py
1# ✅ GOOD: Consistent patterns across tools
2tools = [
3 "get_user",
4 "get_order",
5 "get_product",
6 "create_user",
7 "create_order",
8 "update_user",
9 "delete_user",
10]
11
12# ❌ BAD: Inconsistent patterns
13tools = [
14 "user_get", # Noun-verb instead of verb-noun
15 "fetch_order", # "fetch" instead of "get"
16 "product", # Missing verb
17 "createUser", # CamelCase instead of snake_case
18 "new_order", # "new" instead of "create"
19]
20
21# Convention: stick with one pattern throughout
22# get_* - retrieve information
23# create_* - make something new
24# update_* - modify existing
25# delete_* - remove something
26# list_* - get multiple items
27# search_* - find with query| Action | Verb | Example |
|---|---|---|
| Retrieve single | get_ | get_user, get_order |
| Retrieve multiple | list_ | list_users, list_orders |
| Search/query | search_ | search_products, search_logs |
| Create new | create_ | create_user, create_task |
| Modify existing | update_ | update_profile, update_settings |
| Remove | delete_ | delete_user, delete_file |
| Execute action | run_, execute_ | run_test, execute_query |
| Send | send_ | send_email, send_notification |
Descriptions That Guide
Descriptions are critical - they tell the LLM when and why to use a tool:
Good Description Structure
🐍description_structure.py
1# ❌ BAD: Minimal description
2{
3 "name": "search_web",
4 "description": "Search the web"
5}
6
7# ✅ GOOD: Comprehensive description
8{
9 "name": "search_web",
10 "description": """
11Search the web for current information using a search engine.
12
13Use this tool when you need:
14- Current news or events
15- Information that may have changed since your knowledge cutoff
16- Facts you're not confident about
17- Real-time data (weather, stocks, sports scores)
18
19Do NOT use for:
20- Information you're confident about from training
21- Purely hypothetical questions
22- Personal opinions or subjective matters
23
24Returns a list of search results with titles, URLs, and snippets.
25"""
26}When to Use / When Not to Use
🐍when_to_use.py
1tools = [
2 {
3 "name": "execute_python",
4 "description": """
5Execute Python code in a sandboxed environment.
6
7USE THIS WHEN:
8- You need to perform calculations that could have errors
9- You need to process or transform data
10- You need to verify a code solution works
11- Mathematical operations beyond basic arithmetic
12- Data manipulation, parsing, or formatting
13
14DO NOT USE WHEN:
15- The answer is simple and obvious
16- You're just explaining code (use natural language)
17- The code would have side effects in production
18
19LIMITATIONS:
20- No network access
21- No file system access outside /tmp
22- 30 second timeout
23- Maximum 100MB memory
24
25RETURNS:
26- stdout: What the code printed
27- stderr: Any errors or warnings
28- return_value: The last expression's value
29"""
30 },
31 {
32 "name": "read_file",
33 "description": """
34Read the contents of a file from the filesystem.
35
36USE THIS WHEN:
37- You need to see the contents of a specific file
38- The user mentions a file path
39- You need to analyze, modify, or understand existing code
40
41PATH HANDLING:
42- Accepts absolute paths (/home/user/file.txt)
43- Accepts relative paths (./src/file.py)
44- Paths are relative to the current working directory
45
46RETURNS:
47- File contents as a string
48- Error if file doesn't exist or isn't readable
49- For binary files, returns a base64-encoded string
50"""
51 }
52]Include Examples in Descriptions
🐍description_examples.py
1{
2 "name": "search_code",
3 "description": """
4Search for code patterns in the repository.
5
6EXAMPLES:
7- search_code("def authenticate") - Find authentication functions
8- search_code("import pandas", file_pattern="*.py") - Find pandas usage
9- search_code("TODO", file_pattern="*.js") - Find TODOs in JavaScript
10
11QUERY SYNTAX:
12- Plain text: exact match
13- Regex: wrap in /pattern/
14- Case-insensitive by default
15
16Use this before modifying code to understand existing patterns.
17"""
18}Parameter Design
Parameters should be intuitive and well-documented:
Clear Parameter Descriptions
🐍parameter_descriptions.py
1# ❌ BAD: Minimal parameter info
2{
3 "name": "create_event",
4 "parameters": {
5 "type": "object",
6 "properties": {
7 "title": {"type": "string"},
8 "date": {"type": "string"},
9 "time": {"type": "string"}
10 }
11 }
12}
13
14# ✅ GOOD: Detailed parameter documentation
15{
16 "name": "create_event",
17 "description": "Create a calendar event",
18 "parameters": {
19 "type": "object",
20 "properties": {
21 "title": {
22 "type": "string",
23 "description": "Event title. Keep it concise but descriptive."
24 },
25 "date": {
26 "type": "string",
27 "description": "Event date in ISO format: YYYY-MM-DD (e.g., '2024-03-15')"
28 },
29 "time": {
30 "type": "string",
31 "description": "Start time in 24-hour format: HH:MM (e.g., '14:30' for 2:30 PM)"
32 },
33 "duration_minutes": {
34 "type": "integer",
35 "description": "Duration in minutes. Default is 60.",
36 "default": 60
37 },
38 "attendees": {
39 "type": "array",
40 "items": {"type": "string"},
41 "description": "List of attendee email addresses"
42 },
43 "location": {
44 "type": "string",
45 "description": "Physical location or video call URL"
46 }
47 },
48 "required": ["title", "date", "time"]
49 }
50}Sensible Defaults
🐍sensible_defaults.py
1# Make tools usable with minimal input
2{
3 "name": "list_files",
4 "description": "List files in a directory",
5 "parameters": {
6 "type": "object",
7 "properties": {
8 "path": {
9 "type": "string",
10 "description": "Directory path. Defaults to current directory.",
11 "default": "."
12 },
13 "include_hidden": {
14 "type": "boolean",
15 "description": "Include hidden files (starting with .)",
16 "default": False
17 },
18 "pattern": {
19 "type": "string",
20 "description": "Glob pattern to filter files (e.g., '*.py')",
21 "default": "*"
22 },
23 "recursive": {
24 "type": "boolean",
25 "description": "Search subdirectories recursively",
26 "default": False
27 },
28 "max_results": {
29 "type": "integer",
30 "description": "Maximum files to return",
31 "default": 100
32 }
33 },
34 "required": [] # All have defaults, none required
35 }
36}Enum for Constrained Values
🐍enum_parameters.py
1{
2 "name": "format_code",
3 "parameters": {
4 "type": "object",
5 "properties": {
6 "code": {
7 "type": "string",
8 "description": "The code to format"
9 },
10 "language": {
11 "type": "string",
12 "description": "Programming language",
13 "enum": ["python", "javascript", "typescript", "java", "go", "rust"]
14 },
15 "style": {
16 "type": "string",
17 "description": "Formatting style",
18 "enum": ["compact", "expanded", "minimal"]
19 },
20 "indent": {
21 "type": "integer",
22 "description": "Spaces per indent level",
23 "enum": [2, 4, 8]
24 }
25 }
26 }
27}
28
29# Enums help the LLM by:
30# 1. Showing valid options clearly
31# 2. Preventing invalid values
32# 3. Making selection easier than free-form inputTool Granularity
Finding the right level of abstraction is crucial:
Too Fine-Grained (Bad)
🐍too_fine.py
1# ❌ BAD: Too many tiny tools
2tools = [
3 "open_file",
4 "read_line",
5 "close_file",
6 "move_cursor",
7 "write_byte",
8 "flush_buffer",
9]
10
11# Problems:
12# - LLM must orchestrate many calls correctly
13# - Easy to make mistakes in ordering
14# - Lots of API calls = slow and expensive
15# - Complex state management requiredToo Coarse-Grained (Bad)
🐍too_coarse.py
1# ❌ BAD: Tools that do too much
2tools = [
3 {
4 "name": "do_everything",
5 "description": "Does file operations, web requests, code execution, etc.",
6 "parameters": {
7 "operation": {"type": "string"},
8 "params": {"type": "object"}
9 }
10 }
11]
12
13# Problems:
14# - Hard for LLM to know how to use
15# - Parameter validation is complex
16# - Errors are hard to attribute
17# - Security risks from broad permissionsJust Right
🐍good_granularity.py
1# ✅ GOOD: Focused tools at the right level
2tools = [
3 {
4 "name": "read_file",
5 "description": "Read entire file contents",
6 "parameters": {
7 "path": {"type": "string", "description": "File path"}
8 }
9 },
10 {
11 "name": "write_file",
12 "description": "Write content to a file (creates or overwrites)",
13 "parameters": {
14 "path": {"type": "string"},
15 "content": {"type": "string"}
16 }
17 },
18 {
19 "name": "edit_file",
20 "description": "Make targeted edits to an existing file",
21 "parameters": {
22 "path": {"type": "string"},
23 "edits": {
24 "type": "array",
25 "items": {
26 "type": "object",
27 "properties": {
28 "old_text": {"type": "string"},
29 "new_text": {"type": "string"}
30 }
31 }
32 }
33 }
34 },
35 {
36 "name": "list_directory",
37 "description": "List files in a directory",
38 "parameters": {
39 "path": {"type": "string"},
40 "pattern": {"type": "string", "description": "Optional glob"}
41 }
42 }
43]
44
45# Benefits:
46# - Each tool has one clear purpose
47# - Easy to understand when to use each
48# - Errors are specific and actionable
49# - Compose tools for complex tasksThe CRUD Test
For data operations, having separate Create, Read, Update, Delete tools is usually the right granularity. Combine them only if they're always used together.
Tool Design Patterns
The Query Pattern
🐍query_pattern.py
1# For searching and filtering data
2{
3 "name": "query_database",
4 "description": "Query data with filters and pagination",
5 "parameters": {
6 "type": "object",
7 "properties": {
8 "table": {
9 "type": "string",
10 "description": "Table to query",
11 "enum": ["users", "orders", "products"]
12 },
13 "filters": {
14 "type": "object",
15 "description": "Field-value pairs to filter by",
16 "additionalProperties": True
17 },
18 "fields": {
19 "type": "array",
20 "items": {"type": "string"},
21 "description": "Fields to return (default: all)"
22 },
23 "order_by": {
24 "type": "string",
25 "description": "Field to sort by"
26 },
27 "order": {
28 "type": "string",
29 "enum": ["asc", "desc"],
30 "default": "asc"
31 },
32 "limit": {
33 "type": "integer",
34 "default": 10,
35 "description": "Maximum results"
36 },
37 "offset": {
38 "type": "integer",
39 "default": 0,
40 "description": "Skip first N results"
41 }
42 },
43 "required": ["table"]
44 }
45}The Action-Result Pattern
🐍action_result_pattern.py
1# For operations that modify state
2{
3 "name": "update_user",
4 "description": "Update user profile fields",
5 "parameters": {
6 "type": "object",
7 "properties": {
8 "user_id": {
9 "type": "string",
10 "description": "User's unique identifier"
11 },
12 "updates": {
13 "type": "object",
14 "description": "Fields to update",
15 "properties": {
16 "name": {"type": "string"},
17 "email": {"type": "string"},
18 "role": {"type": "string", "enum": ["user", "admin"]}
19 }
20 }
21 },
22 "required": ["user_id", "updates"]
23 },
24 # Return format (documented in description):
25 # {
26 # "success": true,
27 # "updated_fields": ["name", "email"],
28 # "user": { ...updated user object... }
29 # }
30}The Confirmation Pattern
🐍confirmation_pattern.py
1# For dangerous operations, require confirmation
2{
3 "name": "delete_user",
4 "description": """
5Permanently delete a user and all their data.
6
7⚠️ This action is IRREVERSIBLE.
8
9Before using this tool:
101. Confirm the user ID is correct
112. Verify with the human that deletion is intended
123. Consider using deactivate_user instead
13
14Returns an error if the user has active subscriptions.
15""",
16 "parameters": {
17 "type": "object",
18 "properties": {
19 "user_id": {
20 "type": "string",
21 "description": "User's unique identifier"
22 },
23 "confirm_deletion": {
24 "type": "boolean",
25 "description": "Must be true to proceed. Set false for dry run."
26 },
27 "reason": {
28 "type": "string",
29 "description": "Reason for deletion (for audit log)"
30 }
31 },
32 "required": ["user_id", "confirm_deletion", "reason"]
33 }
34}The Batch Pattern
🐍batch_pattern.py
1# For operations on multiple items
2{
3 "name": "send_emails",
4 "description": "Send emails to multiple recipients",
5 "parameters": {
6 "type": "object",
7 "properties": {
8 "emails": {
9 "type": "array",
10 "description": "List of emails to send",
11 "items": {
12 "type": "object",
13 "properties": {
14 "to": {"type": "string"},
15 "subject": {"type": "string"},
16 "body": {"type": "string"}
17 },
18 "required": ["to", "subject", "body"]
19 },
20 "maxItems": 100
21 },
22 "send_as": {
23 "type": "string",
24 "description": "From address",
25 "default": "noreply@example.com"
26 }
27 },
28 "required": ["emails"]
29 }
30 # Returns: { "sent": 95, "failed": 5, "errors": [...] }
31}Summary
Designing effective tools:
- Name clearly: Verb-noun pattern, consistent across tools
- Describe thoroughly: Include when to use, when not to, examples
- Document parameters: Clear descriptions, sensible defaults, enums
- Right granularity: Not too fine, not too coarse
- Use patterns: Query, action-result, confirmation, batch
- Think like the LLM: Would you know how to use this tool?
Next: Let's explore JSON Schema validation for tools to ensure correct usage.