Building a Freight Quote Agent: Multi-Carrier Pricing and Booking
Learn how to build an AI agent that fetches freight rates from multiple carriers, compares pricing based on transit time and service level, books shipments, and generates required documentation.
Why Freight Quoting Needs Automation
Shipping managers spend hours every day requesting quotes from multiple freight carriers, comparing rates, and booking the best option. A single LTL (Less Than Truckload) shipment might require checking five different carriers, each with their own rate structure, accessorial charges, and transit time estimates. The process is repetitive, error-prone, and time-sensitive since rates can change daily.
An AI freight quote agent automates the entire workflow: it collects shipment details, fetches quotes from multiple carriers simultaneously, presents a ranked comparison, books the selected carrier, and generates the Bill of Lading.
Shipment and Rate Data Models
from dataclasses import dataclass
from typing import Optional
@dataclass
class ShipmentDetails:
origin_zip: str
destination_zip: str
weight_lbs: float
freight_class: int
pieces: int
length_in: float
width_in: float
height_in: float
is_hazmat: bool = False
liftgate_required: bool = False
residential: bool = False
@dataclass
class FreightQuote:
carrier: str
service_level: str
rate: float
fuel_surcharge: float
accessorials: float
total_cost: float
transit_days: int
guaranteed: bool
quote_id: str
valid_until: str
Multi-Carrier Rate Fetching Tool
The rate tool simulates calling multiple carrier APIs and returns normalized quotes:
from agents import function_tool
import hashlib
from datetime import date, timedelta
def _generate_quote_id(carrier: str, origin: str, dest: str) -> str:
raw = f"{carrier}-{origin}-{dest}-{date.today()}"
return f"Q-{hashlib.md5(raw.encode()).hexdigest()[:8].upper()}"
CARRIER_RATES = {
"FedEx Freight": {"base_per_cwt": 28.50, "fuel_pct": 0.32, "transit_base": 3},
"XPO Logistics": {"base_per_cwt": 24.75, "fuel_pct": 0.29, "transit_base": 4},
"Old Dominion": {"base_per_cwt": 31.00, "fuel_pct": 0.30, "transit_base": 2},
"Estes Express": {"base_per_cwt": 22.50, "fuel_pct": 0.35, "transit_base": 5},
"SAIA": {"base_per_cwt": 26.00, "fuel_pct": 0.31, "transit_base": 3},
}
@function_tool
def get_freight_quotes(
origin_zip: str,
destination_zip: str,
weight_lbs: float,
freight_class: int = 70,
liftgate: bool = False,
residential: bool = False,
) -> str:
"""Get freight quotes from multiple carriers for an LTL shipment."""
quotes = []
cwt = weight_lbs / 100
for carrier, rates in CARRIER_RATES.items():
base_rate = cwt * rates["base_per_cwt"]
# Adjust for freight class
class_multiplier = 1.0 + (freight_class - 70) * 0.008
base_rate *= class_multiplier
fuel = base_rate * rates["fuel_pct"]
accessorials = 0.0
if liftgate:
accessorials += 75.00
if residential:
accessorials += 85.00
total = base_rate + fuel + accessorials
valid_date = (date.today() + timedelta(days=3)).isoformat()
quotes.append(FreightQuote(
carrier=carrier,
service_level="LTL Standard",
rate=round(base_rate, 2),
fuel_surcharge=round(fuel, 2),
accessorials=round(accessorials, 2),
total_cost=round(total, 2),
transit_days=rates["transit_base"],
guaranteed=carrier in ("Old Dominion", "FedEx Freight"),
quote_id=_generate_quote_id(carrier, origin_zip, destination_zip),
valid_until=valid_date,
))
quotes.sort(key=lambda q: q.total_cost)
lines = [f"Freight quotes for {weight_lbs} lbs, Class {freight_class}:"]
lines.append(f"Route: {origin_zip} -> {destination_zip}\n")
for i, q in enumerate(quotes, 1):
guaranteed_tag = " [GUARANTEED]" if q.guaranteed else ""
lines.append(
f"{i}. {q.carrier}{guaranteed_tag}\n"
f" Base: ${q.rate:.2f} | Fuel: ${q.fuel_surcharge:.2f} | "
f"Accessorials: ${q.accessorials:.2f}\n"
f" Total: ${q.total_cost:.2f} | Transit: {q.transit_days} days\n"
f" Quote ID: {q.quote_id} (valid until {q.valid_until})"
)
return "\n".join(lines)
Booking Tool
Once the shipper selects a quote, the agent books the shipment:
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
BOOKED_SHIPMENTS = {}
@function_tool
def book_freight_shipment(
quote_id: str,
shipper_name: str,
shipper_address: str,
consignee_name: str,
consignee_address: str,
pickup_date: str,
special_instructions: Optional[str] = None,
) -> str:
"""Book a freight shipment using a previously generated quote ID."""
# In production, validate quote_id against cached quotes
booking_ref = f"BK-{quote_id[2:]}"
BOOKED_SHIPMENTS[booking_ref] = {
"quote_id": quote_id,
"shipper": shipper_name,
"consignee": consignee_name,
"pickup_date": pickup_date,
"status": "confirmed",
}
return (
f"Shipment booked successfully!\n"
f"Booking Reference: {booking_ref}\n"
f"Pickup Date: {pickup_date}\n"
f"Shipper: {shipper_name} ({shipper_address})\n"
f"Consignee: {consignee_name} ({consignee_address})\n"
f"{'Special Instructions: ' + special_instructions if special_instructions else ''}"
f"\nBill of Lading will be emailed to the shipper."
)
Documentation Generation Tool
@function_tool
def generate_bol(booking_reference: str) -> str:
"""Generate a Bill of Lading summary for a booked shipment."""
booking = BOOKED_SHIPMENTS.get(booking_reference)
if not booking:
return f"Booking {booking_reference} not found."
return (
f"=== BILL OF LADING ===\n"
f"BOL Number: BOL-{booking_reference[3:]}\n"
f"Date: {date.today().isoformat()}\n"
f"Shipper: {booking['shipper']}\n"
f"Consignee: {booking['consignee']}\n"
f"Pickup Date: {booking['pickup_date']}\n"
f"Carrier Quote: {booking['quote_id']}\n"
f"Status: {booking['status'].upper()}\n"
f"========================\n"
f"This BOL is ready for printing and driver signature."
)
Assembling the Freight Agent
from agents import Agent, Runner
freight_agent = Agent(
name="Freight Broker",
instructions="""You are a freight quoting and booking assistant. Help shippers:
1. Get competitive quotes from multiple LTL carriers
2. Compare rates by cost, transit time, and service guarantees
3. Book shipments with the selected carrier
4. Generate Bills of Lading for booked shipments
Always recommend the best value option and note guaranteed service when relevant.""",
tools=[get_freight_quotes, book_freight_shipment, generate_bol],
)
result = Runner.run_sync(
freight_agent,
"I need to ship 1200 lbs of Class 85 freight from 90210 to 10001 with liftgate. "
"Show me the cheapest options."
)
print(result.final_output)
FAQ
How do I connect to real carrier rate APIs?
Use aggregate APIs like ShipEngine, Freightview, or SMC3 which provide a single interface to multiple LTL carriers. Each requires a shipper account and API credentials. Rate requests typically need origin/destination zips, weight, freight class, and dimensions. Cache quotes with a TTL matching each carrier's validity window (usually 3-7 days).
What about FTL (Full Truckload) quotes?
FTL pricing is lane-based rather than per-hundredweight. Add a separate tool that queries load boards or FTL rate APIs. FTL quotes depend on origin-destination lane, equipment type (dry van, reefer, flatbed), and market conditions. The agent should ask the user about equipment needs before fetching FTL rates.
How do I handle accessorial charges that vary by carrier?
Build an accessorial fee table per carrier. Common accessorials include liftgate delivery, residential delivery, inside delivery, notify before delivery, and hazmat surcharges. When the user mentions special requirements, the agent should include relevant accessorial codes in the rate request and show them as separate line items in the comparison.
#Freight #CarrierPricing #ShippingQuotes #BookingAutomation #Python #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.