-
-
Gemini 3.5 Flash plans 11 loads at 100% SLA in ~90s — every assignment has a reason and confidence, with the agent trace streaming live.
-
The agent pipeline — ingest, filter, reason, schedule, validate, publish — with Gemini's own trade-off reasoning from the live run.
-
Cargo routing: shipment → wagon → vessel, priority-coloured. The customs-blocked shipment is held in the yard.
-
Wagon breakdown at 09:30 — the agent re-plans only what broke: one load moved, one rebooked to the next sailing, four already done.
-
Dock schedule with the port-clock line — dashed ghosts show where cargo sat before the breakdown and how the agent re-routed it.
-
Decision log: every plan version, human approve/override with reasons, and the scorecard — the record the agent learns from.
-
Plan v2 after recovery — completed loads marked, the moved and rebooked shipments highlighted, SLA held at 91%.
Inspiration
Our inspiration comes from a real logistics environment close to home: the Port of Baku, located in Alat, Azerbaijan.
Every morning, a port dispatcher has to decide which waiting shipment should load onto which rail wagon, and in what order.
That sounds simple until you consider everything that has to be balanced at once: cargo-type rules, hazmat certifications, reefer requirements, weight and axle limits, customer SLA tiers, cold-chain windows, wagon reservations, dock-team capacity, customs clearance, and hard vessel loading cutoffs.
A manual plan takes roughly 45 minutes, and under pressure a single bad decision can mean premium cargo waits, perishables miss their cold-chain window, or a ship sails without cargo that could have made the cutoff.
But the morning plan is only half the problem.
At 10:30 AM, a wagon can fail inspection. A ship can move its cutoff earlier. A dock team can lose a crane. Suddenly, part of the plan is invalid and the dispatcher has to understand what has already happened, identify which shipments are affected, find alternative resources, and communicate a new plan quickly.
We built Port Operations Dispatch Agent to explore whether an autonomous AI agent could handle that reasoning while still respecting the hard physical and operational rules of a port.
Our core idea became:
Let Gemini reason about the best legal plan, but never let the model decide what is physically legal.
What it does
Port Operations Dispatch Agent plans the port's cargo-loading day, explains its decisions, reacts to disruptions, and keeps a human dispatcher in control.
The workflow starts by reading the current port state from Firestore: shipments, wagons, vessel schedules, dock-team calendars, reservations, customs status, and the port clock.
Before Gemini sees the problem, deterministic Python code checks every possible cargo-wagon combination against the hard constraints. In our demo scenario, this eliminates 51 illegal pairings, leaving only 37 legal options for the model to reason over.
Then Gemini 3.5 Flash through Google ADK handles the decisions that require judgment: shipment priority, wagon choice, resource scarcity, wagon reuse, SLA trade-offs, and disruption recovery.
Gemini does not control exact loading times. It proposes the order of work, while a deterministic gap-aware scheduling engine calculates the real loading windows from wagon availability, dock-team calendars, turnaround time, and vessel deadlines.
Every final plan must pass through a tool-gated submit_plan function that validates the hard constraints again. If a violation is found, the plan is rejected and the agent must correct it. The validator also makes sure every shipment is accounted for — either assigned or explicitly placed on hold.
Once accepted, the plan is versioned and saved to Firestore. Every assignment includes a confidence score and a short explanation. The system can also send the dispatcher a formatted HTML email with an XLSX attachment.
Disruption handling is a major part of the project. The demo includes three scenarios:
- Wagon breakdown
- Ship loading cutoff moved earlier
- Dock-team crane outage
When something changes, the system first performs deterministic impact analysis to identify only the affected shipments. It preserves completed and unaffected work, re-plans the impacted subset, and publishes plan v2 with a visual diff showing what was moved, retimed, rebooked, or already completed.
The dispatcher can then Approve the plan or Override it with a typed reason. Those decisions are saved to an outcomes history. On future runs, the agent reads that history through get_dispatcher_history, so previous human decisions can influence confidence and future reasoning.
The live dashboard shows the complete process: agent activity, plan board, dock-team Gantt chart, cargo-routing flow, explanations, confidence scores, disruption diffs, email delivery, and the human decision log.
How we built it
The application runs as a single container on Google Cloud Run, containing the FastAPI backend, Google ADK agent, deterministic planning tools, and React dashboard.
The reasoning layer uses Gemini 3.5 Flash on Vertex AI through Google Agent Development Kit (ADK).
We divided responsibilities between AI and deterministic software.
Gemini owns the soft decisions:
- Priority ordering
- Wagon selection among legal choices
- Resource-reuse strategy
- SLA trade-offs
- Disruption recovery
- Confidence and explanations
Deterministic Python owns the hard rules:
- Cargo compatibility
- Hazmat certification
- Reefer and cover requirements
- Weight and axle capacity
- Wagon reservations
- Availability windows
- Cold-chain rules
- Customs status
- Dock calendars
- Vessel cutoff calculations
The ADK agent uses specialized tools:
get_dispatch_snapshot— loads the current operational state.get_dispatcher_history— retrieves previous human approve/override decisions.get_valid_pairings— removes illegal shipment-wagon combinations.propose_schedule— converts the model's chosen order into concrete loading times.submit_plan— validates, persists, versions, and publishes the final plan.
The system follows a loop of:
reason → propose → schedule → validate → correct → submit
rather than trusting the model's first response.
Firestore stores shipments, wagons, vessels, plans, runs, events, and dispatcher outcomes.
Cloud Scheduler automatically triggers the morning optimization at 06:00.
Secret Manager protects SMTP credentials.
Cloud Logging stores the agent's execution trace.
Cloud Build and Artifact Registry handle container building and storage.
The dashboard is built with React 18 + Vite, while the backend uses Python, FastAPI, Pydantic, and openpyxl.
We also created a deterministic fallback planner that uses the same tools and constraint engine. If Vertex AI is unavailable, the application remains functional and clearly displays PLANNER: FALLBACK rather than pretending Gemini produced the result.
Our synthetic dataset is deliberately adversarial. It includes a premium hazmat shipment with exactly one certified wagon, two perishables competing for a reefer, a heavyweight shipment with only one suitable wagon, a reserved wagon, customs-blocked cargo, and a second reefer that is unavailable until 15:30 — but becomes the recovery option after the first reefer breaks.
Challenges we ran into
One of the biggest challenges was deciding what Gemini should control and what it should never control.
Letting an LLM directly generate a complete operational schedule would be flexible, but it would also allow hallucinated times or illegal assignments. We solved that by making Gemini responsible for reasoning and ordering while deterministic software owns feasibility and exact scheduling.
Another unexpected problem was resource reuse.
Gemini's first cloud-generated plan scheduled only eight loads and held several shipments because it treated the wagon fleet almost like a one-use resource. It had not fully internalized that a wagon could be reused later after turnaround.
We added explicit capacity and reuse facts to the prompt. That change took the system from 8 scheduled loads and 4 holds to a complete 11-load day, and Gemini began explicitly explaining wagon reuse in its reasoning.
We also discovered that model output can be incomplete rather than obviously wrong. In an early version, nothing guaranteed Gemini would account for every shipment. A shipment could disappear from the plan, dashboard, and email without producing an obvious validation error. We changed submit_plan so every in-scope shipment must be assigned or explicitly held.
Another issue involved model-generated values. A priority outside the valid 1–3 range could pass the planning stage and fail Pydantic validation later, turning a small model formatting mistake into a failed run. We now coerce and clamp model values at the tool boundary.
Production also exposed bugs that local tests did not.
Our in-memory store treated an update to a missing record as a no-op, while Firestore's merge behavior could create the record. That difference could create a phantom document and break future planning runs. We changed the Firestore implementation so both storage backends follow the same semantics.
At another point, all of our API verification requests passed while the actual React dashboard returned 401 errors. A build-time token had been compiled into the frontend and no longer matched the deployed service. We fixed this by generating a derived dashboard token at runtime instead of embedding a deploy-time secret into the client bundle.
Cloud Run also introduced an automation challenge: CPU can be throttled after a response completes, which meant a background planning thread could stall during a headless scheduled run. We added a synchronous optimization path for Cloud Scheduler and configured the service appropriately so the scheduled 06:00 run completes reliably.
Finally, our first cloud deployment referenced a Gemini model ID that was not publicly available. The deterministic fallback kept the application operational and clearly labeled while we corrected the configuration. That experience reinforced an important principle for us: design the demo so it degrades transparently instead of simply dying.
Accomplishments that we're proud of
We're proud that this project is not just a chatbot generating a schedule.
It is a complete agentic operational workflow combining:
- Gemini reasoning through Google ADK
- deterministic hard-constraint filtering
- deterministic time scheduling
- tool-gated validation and self-correction
- versioned plans in Firestore
- incremental disruption recovery
- live agent execution tracing
- per-assignment confidence and explanations
- dispatcher approval and overrides
- history-aware future reasoning
- HTML and Excel plan delivery
- automated Cloud Scheduler runs
- a resilient deterministic fallback
- a deployed React control-tower dashboard
Most importantly, we measured the system on the actual deployed Google Cloud service.
The morning run produced:
- 11 loads scheduled
- 1 shipment held because of customs
- 0 hard-constraint violations
- 100% SLA compliance
- 92% peak dock utilization
- approximately 85 seconds planning time
That is roughly 32× faster than the ~45-minute manual planning baseline.
The disruption scenario is equally important to us.
When wagon W003 breaks down, the agent does not regenerate the entire schedule from scratch. It identifies the affected shipments, recognizes five loads as already completed, preserves unaffected work, and finds the best remaining recovery.
One perishable shipment is moved to the second reefer and still clears its vessel cutoff by 90 minutes. Another shipment cannot realistically make its original sailing, so the system explicitly rebooks it onto the next vessel.
SLA compliance falls from 100% to 91%, but that failure is unavoidable — and the system makes it visible instead of hiding it.
What we learned
The biggest lesson was that pre-filtering is what makes the LLM trustworthy in an operational system.
Instead of asking Gemini to remember every safety and feasibility rule, we remove illegal options before the model ever sees them and validate the result again afterwards.
That changes the failure mode from potentially dangerous to merely suboptimal.
We also learned to:
Give the model a calculator, not a calendar.
Gemini is excellent at reasoning about priorities, scarcity, and trade-offs. Deterministic software is much better at calendars, arithmetic, capacity, and invariants. Separating those responsibilities eliminated an entire class of scheduling hallucinations.
We learned that structured model output is still untrusted input. Every generated value needs schema validation, normalization, completeness checks, and domain-level verification.
We learned that human-in-the-loop should be more than an Approve button. By storing override reasons and giving that history back to the agent, human judgment becomes operational memory rather than a one-time gate.
We also learned that incremental re-planning is more useful than starting over. By the time a disruption happens, part of the day's work has already been completed. A useful recovery agent must understand what is finished, what is still valid, and what actually needs to change.
Finally, we learned that some of the most dangerous bugs live between two implementations rather than inside either one: local storage versus Firestore, development configuration versus deployed configuration, and interactive requests versus scheduled Cloud Run execution.
What's next for Port Operations Dispatch Agent
The current dataset is synthetic so the scenarios are reproducible and the edge cases can be demonstrated consistently.
The next step is replacing those seed inputs with real operational integrations such as:
- Terminal Operating System shipment feeds
- live wagon telemetry
- vessel schedule feeds
- equipment status
- workforce and dock-team availability
We also want to move from explicit disruption buttons to continuous re-planning, where the system watches operational state changes and automatically determines when a new plan is required.
The dispatcher outcomes history is another major opportunity. We want to compare predicted versus actual load times, analyze when humans override the agent, and use those signals to calibrate assignment confidence.
We also plan to introduce explicit cost objectives so the agent can optimize not only SLA compliance but also crane hours, wagon dwell time, and resource utilization.
Finally, we want to add a Gemma cross-check. The same pre-filtered planning problem could be evaluated independently by another Google model, with disagreements flagged to the dispatcher as lower-confidence decisions.
Our longer-term vision is an AI-assisted port control layer where Gemini handles high-frequency operational reasoning, deterministic software enforces the rules of the physical system, and the human dispatcher retains final authority.