Building the Tool-Calling Layer: Teaching an AI Agent to Search Real Flights
How we built the system that lets the AI agent call real travel APIs — tool definition patterns, error handling mid-conversation, and reliability at scale.

The first time the agent searched real inventory and returned a bookable flight, ten things broke getting there. The API key was configured for test mode. The date format did not match the provider's expectation. The airport code mapping had gaps for secondary airports. The response parser choked on codeshare flights. The rate limiter was too aggressive. The timeout was too short for international searches. The error message for sold-out routes was confusing. The ranking system did not handle single results. The streaming pipeline did not know how to render partial tool results. And the price display truncated currencies with more than two decimal places.
Each of these was a small problem. Together, they illustrated why building a reliable tool-calling layer is the hardest part of shipping an AI agent that does real work.
Tool definition architecture

The agent's 70-plus tools are defined with structured parameter schemas. Each tool has a name, a description that helps the agent understand when to use it, a set of required and optional parameters with types and constraints, and an execution function that calls the actual API.
The description is more important than it might seem. The language model uses the description to decide when to invoke the tool. A poorly written description leads to the agent calling the wrong tool or not calling the right tool at the expected moment. "Search for flights" is too vague. "Search for one-way or round-trip flights between two airports on specific dates, returning price, duration, stops, and airline information" gives the model enough context to match traveler requests accurately.
Parameter schemas enforce type safety at the tool boundary. The agent might generate a date in various formats, but the schema normalizes it before the API call. Airport codes are validated against a known set. Cabin class values are constrained to valid options. This validation layer catches parameter errors before they reach the external API, where they would produce confusing failures.
Error handling when APIs fail mid-conversation
External APIs fail in ways that are outside our control. Timeouts, rate limits, malformed responses, inventory that disappears between search and booking. Each failure mode requires a different response strategy.
For timeouts, we retry with exponential backoff. One retry for transient network issues, two retries for persistent timeouts. After the retry budget is exhausted, the agent tells the traveler that the search is taking longer than expected and offers to try again or adjust the search parameters.
For rate limits from upstream providers, we queue the request and inform the traveler of the delay. The agent says something like "That search is in the queue. Let me try a slightly different approach in the meantime." This keeps the conversation moving rather than leaving the traveler waiting.
For malformed responses, we have a fallback parser that extracts whatever usable data exists and presents it with appropriate caveats. A response missing price data still has route and timing information that might be useful.
For disappeared inventory, where a flight that appeared in search results is no longer available at booking time, the agent explains the situation clearly and offers alternatives. This is a common scenario because live inventory changes by the minute, and the gap between search and booking decision can be minutes or hours.
Reliability at scale

A single chat message from a traveler can trigger the agent to call three to seven tools, each hitting external APIs. At scale, this amplification creates significant load on upstream providers. Rate limiting our own agent to stay within provider quotas is essential to maintaining service reliability.
We rate limit at multiple levels: per-traveler message rate, per-tool call rate, and per-provider API rate. These limits are configured independently because the bottlenecks are different. A traveler might send messages faster than we want the agent to hit a provider, and the agent might want to call a provider faster than the provider allows.
Test suites run against real APIs with retries configured for the flaky behavior that external systems inevitably exhibit. This means our tests are slower than pure unit tests, but they catch integration issues that mocks cannot reproduce. A mock that always returns a valid response in 200 milliseconds does not prepare you for a real API that sometimes returns a 503 after four seconds.
The tool-calling layer is the bridge between the agent's intelligence and the real world's data. Getting it right means the agent can do useful work. Getting it wrong means the agent is just another chatbot that talks about travel without actually being able to book anything.
Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. Plan your next trip.