Tool-Use Design Patterns

Learn tool-use design patterns for AI function calling. Parallel calls, sequential dependencies, error recovery, and prompt templates for reliable tool use.

May 4, 2026
tool-usefunction-callingagentsapiprompt-engineering

Tool-Use Design Patterns

Tool-use (function calling) lets AI models interact with external systems — databases, APIs, search engines, or code execution environments. The way you structure tool calls — parallel, sequential, or conditional — dramatically affects reliability and performance.

Parallel Tool Calls

Multiple independent tools execute simultaneously. Best for gathering data from unrelated sources in a single turn.

Available tools:
- get_weather(city: string, units: string) → temperature, conditions
- get_news(category: string, limit: integer) → headlines, sources
- get_stock_price(symbol: string) → price, change, volume

Query: "What's the weather in Tokyo, latest tech news, and Apple's stock price?"

Model calls all 3 tools in parallel (no dependencies between them):
→ get_weather("Tokyo", "celsius")
→ get_news("technology", 5)
→ get_stock_price("AAPL")

Real-world example — dashboard data aggregation:

A user asks for their daily briefing. The model identifies 4 independent data needs:

1. get_calendar_events(today) → today's meetings
2. get_weather(user_city) → weather forecast
3. get_unread_emails(count=5) → recent messages
4. get_stock_portfolio() → portfolio performance

All 4 fire in parallel. Results are assembled into a single response.

Handling partial results: When some parallel calls fail, still present the successful ones.

Reply with whatever data you received. For failed calls, say:
"I couldn't retrieve your calendar data, but here's your weather and email summary."

Best for: Independent data gathering, dashboard widgets, initial research scoping.

Sequential Tool Calls

Output of one tool feeds the next. Required when later steps depend on earlier results.

Available tools:
- search_restaurants(cuisine: string, location: string) → list with IDs
- get_reviews(restaurant_id: string) → reviews, ratings, price range

Query: "Find the best Italian restaurants in NYC and show me their reviews"

Model executes sequentially:
1. search_restaurants("Italian", "NYC") → [restaurant_1, restaurant_2, ...]
2. get_reviews(restaurant_1), get_reviews(restaurant_2) — parallel since they depend on step 1 but not on each other

Real-world example — e-commerce fulfillment flow:

Tools:
- check_inventory(product_id: string) → {in_stock: boolean, quantity: int}
- get_shipping_options(zip: string, weight: float) → [{carrier, cost, eta}]
- place_order(product_id: string, address: string, shipping_id: string) → order_confirmation

Flow:
1. check_inventory("PROD-123")
   → {in_stock: true, quantity: 5, weight: 2.5}
2. get_shipping_options("10001", 2.5)
   → [{carrier: "UPS", cost: 8.99, eta: "3 days"}, ...]
3. place_order("PROD-123", "10001", "ups-standard")
   → {order_id: "ORD-456", status: "confirmed", total: 8.99}

Intermediate validation: After step 1, validate before proceeding.

If the product is out of stock, do not proceed to step 2 or 3.
Instead, respond with: "This item is currently out of stock."

Best for: Multi-step research, data enrichment pipelines, transaction flows, dependency chains.

Conditional Tool Calls

The model decides which tool to call next based on previous results. This creates decision trees rather than fixed sequences.

Tools:
- check_inventory(product_id: string) → stock level
- find_alternatives(product_id: string) → similar products
- get_shipping_estimate(zip: string, weight: float) → cost, eta

User: "Can I get a coffee maker delivered to 10001?"

1. check_inventory("coffee-maker-123")
   Result: {in_stock: true, quantity: 3, weight: 4.2}
   → Continue to shipping estimate

But if:
1. check_inventory("coffee-maker-123")
   Result: {in_stock: false, quantity: 0}
   → Branch to: find_alternatives("coffee-maker-123")
   → Show user alternatives instead
You have access to these tools. Make decisions based on data:

1. Check prerequisites first
2. Based on the result, choose the correct next step:
   - Success → proceed to dependent call
   - Partial success → offer alternatives
   - Failure → explain and stop
3. Never call a tool whose inputs depend on data you don't have

Real-world example — troubleshooting diagnostic tree:

Tools:
- check_service_status(service: string) → up/down/degraded
- check_recent_deployments(service: string) → {timestamp, status}
- get_error_logs(service: string, timeframe: string) → [errors]

Flow:
1. check_service_status("api-gateway")
   → degraded
2. check_recent_deployments("api-gateway")
   → deployed 30 min ago
3. get_error_logs("api-gateway", "1h")
   → 500 errors on /checkout endpoint

Conclusion: Recent deployment caused errors. Recommend rollback.

Best for: Decision trees, checkout flows, validation chains, diagnostic systems.

Error Recovery Patterns

Tools fail. Your prompt should plan for every failure mode.

Available tools with error handling:

1. get_weather(city, units)
   - On failure: retry once
   - If still failing: search_web("weather {city}")

2. get_stock_price(symbol)
   - On failure: retry once
   - If still failing: "Stock data is temporarily unavailable"

3. send_email(to, subject, body)
   - On failure: retry once
   - If still failing: save to drafts and alert user
Error handling protocol:

Network timeout:
  → Retry once after 1 second
  → If fail again: try an alternative tool that provides similar data
  → If no alternative: report the outage to the user

Invalid parameters (400 error):
  → Check parameter format and types
  → Fix and retry
  → Report if the issue persists

Empty results (200 with no data):
  → Report "no results found"
  → Suggest broadening the search
  → Do not retry — the request was valid but nothing matched

Service unavailable (503):
  → Inform the user
  → Offer to retry later
  → Do not retry immediately

Tool Description Best Practices

Tool descriptions are prompts for the model — they determine whether tools get called correctly.

Bad description:
"get_user_data(id) — gets user data"

Good description:
"get_user_data(user_id: string) → {name, email, plan, created_at}
Fetches a user's profile information by their MongoDB ObjectId.
Example: get_user_data('507f1f77bcf86cd799439011')
Returns all fields needed for account dashboard display."

Guidelines:

  • Include return schema — the model needs to know what data comes back
  • Provide example parameter values — helps the model format calls correctly
  • Document error conditions — "Returns null if user not found"
  • Specify units, formats, and constraints — "temperature in Celsius", "max 100 results"

Rate Limiting & Token Budget

When calling tools, respect these limits:
- Max 5 concurrent tool calls
- Wait for a response before making dependent calls
- If a tool returns an empty result, do not retry more than once

Budget management:
- Each tool call costs ~100-300 tokens
- Reserve 2000 tokens for the final response
- If you have limited budget, prioritize essential tool calls

Testing Tool Calls

Test your tool-use prompts before deploying:

  • Mock all dependencies — Replace real APIs with mock functions that return controlled responses
  • Test error paths — What happens when a tool returns null, an unexpected schema, or a network error?
  • Log call sequences — Track which tools were called in what order during development
  • Validate output schemas — Ensure the model passes correct parameter types
  • Test with ambiguous inputs — What if the user says "get me that thing from yesterday"?

Selection Guide

PatternDependency TypeLatencyReliability Risk
ParallelNo dependencies between callsLowest (concurrent)Partial failure handling needed
SequentialEach step depends on previousMediumCompounding delays
ConditionalBranches based on resultsVariableDecision quality depends on data
Error recoveryAll patternsAdds retry latencyRequired for production safety

Best Practices

  • Clear tool descriptions - Describe what each tool does, its parameters, and return values
  • Limit concurrent calls - Too many parallel calls can overwhelm rate limits
  • Set timeouts - Define maximum wait time for each tool
  • Provide fallbacks - What to do when a tool is unavailable
  • Log tool calls - Track which tools were called and their results for debugging
  • Test error paths - Mock failures to verify your error recovery logic works