By Sagar Shankaran, Founder of CallSphere
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.
Key takeaways
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.
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
The rate tool simulates calling multiple carrier APIs and returns normalized quotes:
Hear it before you finish reading
Talk to a live CallSphere AI voice agent for logistics in your browser — 60 seconds, no signup.
flowchart TD
USAGE{"Monthly call<br/>volume?"}
STARTER["Starter<br/>under 500 calls per month"]
GROWTH["Growth<br/>500 to 5,000 per month"]
SCALE["Scale<br/>5,000 plus per month"]
ENT["Enterprise<br/>dedicated infra and SLA"]
USAGE -->|Light| STARTER
USAGE -->|Mid| GROWTH
USAGE -->|High| SCALE
USAGE -->|Custom| ENT
STARTER --> NEXT(["Pick monthly plan"])
GROWTH --> NEXT
SCALE --> NEXT
ENT --> NEXT
style USAGE fill:#4f46e5,stroke:#4338ca,color:#fff
style NEXT fill:#059669,stroke:#047857,color:#fff
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)
Once the shipper selects a quote, the agent books the shipment:
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."
)
@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."
)
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)
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).
Still reading? Stop comparing — try CallSphere live.
See the logistics AI agent handle a real call — complete, industry-specific, and live in your browser. No signup.
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.
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

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Freight forwarders, clearing agents and transport firms around Owendo and Port-Gentil use CallSphere AI agents to answer shipper and driver calls 24/7 in French, Fang and English, and keep every consignment moving.
Kyrgyz freight forwarders, Dordoi traders, and Middle Corridor logistics firms field calls at all hours. CallSphere answers 24/7 in Kyrgyz, Russian, and English and captures every shipment enquiry.
Freight forwarders and transport firms in Hai Phong and Ho Chi Minh City are buried under tracking calls and after-hours booking enquiries. How CallSphere AI voice + chat agents handle the repetitive load 24/7.
Step-by-step build of a working agent with the OpenAI Agents SDK — Agent class, tools, handoffs, tracing — plus an eval pipeline that catches regressions before merge.
Smolagents lets agents write Python instead of JSON. Why code-as-action reduces tool errors and where the security trade-offs are for production deployments.
Modal turns a Python function into autoscaling serverless compute with optional GPU. Deploy a LiveKit Agent with one command and get pay-per-second billing.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco