Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungRefreshed repository pull requests page generally available(22.09.2026 um 03:25 Uhr)
Sichere ProgrammierungThe Joy of Learning the Basics Again(22.09.2026 um 03:28 Uhr)
Sichere ProgrammierungZero-Code OpenTelemetry Tracing for Dagster(22.09.2026 um 03:39 Uhr)
Linux Tipps & Hardening`prime-all`(22.09.2026 um 02:28 Uhr)
IT Security Toolsopensoho v0.15.2(22.09.2026 um 03:33 Uhr)
IT Security NachrichtenUS Proposes AI Incident Alert System in Talks With China, Bessent Says(22.09.2026 um 04:01 Uhr)
Sichere ProgrammierungRefreshed repository pull requests page generally available(22.09.2026 um 03:25 Uhr)
Sichere ProgrammierungThe Joy of Learning the Basics Again(22.09.2026 um 03:28 Uhr)
Sichere ProgrammierungZero-Code OpenTelemetry Tracing for Dagster(22.09.2026 um 03:39 Uhr)
Linux Tipps & Hardening`prime-all`(22.09.2026 um 02:28 Uhr)
IT Security Toolsopensoho v0.15.2(22.09.2026 um 03:33 Uhr)
IT Security NachrichtenUS Proposes AI Incident Alert System in Talks With China, Bessent Says(22.09.2026 um 04:01 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Building a Ride-Share Zone-Balancing Agent with LangGraph — Part 1: A Rule-Based Agent, No LLM Yet

It's 5pm on a weekday, and you're standing outside a stadium after an event just wrapped up. You open your ride-share app. Estimated wait: 12 minutes. Meanwhile, three neighborhoods over, drivers are circling empty streets with nobody…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

It's 5pm on a weekday, and you're standing outside a stadium after an event just wrapped up. You open your ride-share app. Estimated wait: 12 minutes. Meanwhile, three neighborhoods over, drivers are circling empty streets with nobody requesting a ride.



This happens constantly on any ride-share platform. It's not a bug — it's just supply and demand failing to line up on their own.



Someone, or something, has to watch each part of the city. It needs to notice when a zone is out of balance, then decide what to do:




  • Raise prices, to pull in more drivers

  • Pay drivers a bonus to reposition

  • Redirect riders to a calmer zone

  • Or just leave it alone, if it'll sort itself out



That decision-maker is what I'm building in this series, using LangGraph — one capability at a time.



This is Part 1 of a 5-part series. Each part adds exactly one thing the previous one genuinely couldn't do, so the agent gets closer to how this would actually have to work in production:





  • Part 1 (this one): a rule-based agent for a single zone — no LLM, everything is structured numbers


  • Part 2: an LLM, but only for the two jobs a lookup table can't do — reading a human's free-text note, and explaining the decision back


  • Part 3: memory, so the agent remembers what it already tried on a zone instead of deciding once and forgetting


  • Part 4: a human-in-the-loop pause, so a risky or unusual decision doesn't just execute unattended


  • Part 5: coordinating two zones at once, since pulling a driver from one zone is a decision that affects both



Nothing above is built yet in Part 1. That's deliberate. Every one of those additions is real complexity, and real complexity should only show up once the problem actually needs it — not because it sounds impressive.



So Part 1 stays as plain as the problem allows: one zone, one decision cycle, and rules a human ops person could write on a whiteboard.






The Data: A Snapshot of a City



Before there's an agent, there's a city to look at. I generate a synthetic snapshot of 8 zones — downtown, an airport, a couple of suburbs, a university district, and so on.



Each zone has:




  • A driver count

  • A rider request count

  • An average wait time

  • A couple of context flags — is it raining, is there an event nearby




zones = generate_all_zones(hour=17, rain=False, event=True, seed=42)

df = pd.DataFrame(zones)[[
'zone_name', 'zone_type', 'driver_count',
'rider_request_count', 'avg_wait_time', 'rain_flag', 'event_flag', 'traffic'
]]
df['ratio'] = (df['rider_request_count'] / df['driver_count']).round(2)
df














































zone_name driver_count rider_request_count avg_wait_time ratio
Downtown Core 15 29 6.3 1.93
Airport 8 17 6.7 2.12
Midtown 6 14 8.9 2.33
Stadium Area 6 15 9.2 2.50


The ratio column is rider requests divided by drivers. It's the whole signal the agent needs:




  • Above 1.3 → deficit (too few drivers)

  • Below 0.5 → surplus (too many idle drivers)

  • In between → balanced



Downtown Core is at 1.93 — a real deficit, made worse by a nearby event. That's the zone I'll walk through.






The Graph



Here's the shape of the decision, end to end:




detect_imbalance

classify_severity
↓ (conditional edge)
┌────┴────────────┬──────────────┐
↓ ↓ ↓
demand_action supply_action do_nothing
└────────┬────────┴──────────────┘

resolved_imbalance

choose_best_policy

simulate_and_report






Two nodes run first:





  • detect_imbalance computes the ratio.


  • classify_severity labels it mild, moderate, or critical. That label is for reporting only — it doesn't affect the decision.



Then comes the conditional edge, where the actual branching happens. It reads imbalance_type and decides which policies are even worth trying:





  • deficitsurge_pricing, driver_bonus, demand_redirect, or do_nothing


  • surplusreallocation_nudge or do_nothing (a zone with too many idle drivers doesn't need demand-side incentives)


  • balanceddo_nothing



Two more nodes run after that:





  • resolved_imbalance evaluates every candidate policy once. It records each one's profit, and whether it actually fixes the imbalance.


  • choose_best_policy picks the most profitable policy among those that resolve it. If none of them resolve it, it falls back to picking the most profitable one overall.



One more thing worth naming: every policy here is zone-local. None of them look past the selected zone's own numbers.



Pulling a driver in from a neighboring zone is a real idea. But it needs a second zone in the picture first — that's Part 5, not Part 1.






The State



LangGraph's StateGraph runs on a single shared piece of state. Every node reads from it and writes to it. In Python, that's just a TypedDict:




class ZoneState(TypedDict):
zone: dict # the single zone this graph run studies
imbalance_ratio: float
imbalance_type: str # "deficit" | "surplus" | "balanced"
severity: str # "none" | "mild" | "moderate" | "critical"
candidate_policies: list # which policies are worth evaluating, set by the routed branch
policy_evaluations: dict # {policy_name: profit} for every candidate considered
policy_resolutions: dict # {policy_name: bool} — did this candidate resolve the imbalance
recommended_policy: str
outcome: dict
outcome_delta: dict
report: str






Nothing here is LangGraph-specific magic. It's a plain dictionary shape. The framework's only job is making sure every node agrees on it.






Wiring the Graph



Each node is a plain Python function. It takes the current state in, and returns whichever fields it changed. Nodes don't call each other directly — the graph's edges decide what runs next.



Wiring them together takes three steps:




  • Register each node under a name.

  • Connect the straightforward ones with add_edge.

  • Use add_conditional_edges for the one spot where the next step depends on the state.




g = StateGraph(ZoneState)

g.add_node("detect_imbalance", detect_imbalance)
g.add_node("classify_severity", classify_severity)
g.add_node("demand_action", demand_action)
g.add_node("supply_action", supply_action)
g.add_node("do_nothing", do_nothing)
g.add_node("resolved_imbalance", resolved_imbalance)
g.add_node("choose_best_policy", choose_best_policy)
g.add_node("simulate_and_report", simulate_and_report)

g.add_edge(START, "detect_imbalance")
g.add_edge("detect_imbalance", "classify_severity")

g.add_conditional_edges("classify_severity", route_action, {
"demand_action": "demand_action",
"supply_action": "supply_action",
"do_nothing": "do_nothing",
})

g.add_edge("demand_action", "resolved_imbalance")
g.add_edge("supply_action", "resolved_imbalance")
g.add_edge("do_nothing", "resolved_imbalance")
g.add_edge("resolved_imbalance", "choose_best_policy")
g.add_edge("choose_best_policy", "simulate_and_report")
g.add_edge("simulate_and_report", END)

app = g.compile()






route_action is the function behind that conditional edge. It reads imbalance_type off the state, and returns the name of the branch to take.



That's the entire routing logic for this stage — one if-shaped decision, expressed as data instead of a chain of if statements.






Running It on Downtown Core



With the graph compiled, running it takes one call: invoke(), with Downtown Core's snapshot as the starting state.




result = app.invoke(initial_state)






Here's what came back:

































Policy Profit Resolved?

surge_pricing ← chosen
$230.78 No
demand_redirect $202.46 No
driver_bonus $181.83 No
do_nothing $177.84 No


surge_pricing won on profit — same as it would have without the resolution check. But notice that nothing resolved the imbalance in one pass.



That's not a bug. resolved_imbalance only measures a policy's effect over one 15-minute window. A deficit this size — 29 riders against 15 drivers — genuinely needs more than one nudge to close.



The agent still made the best call it could. It just can't promise a full fix in a single shot, and it says so honestly (resolved=NO) instead of pretending otherwise. Giving it the ability to try again, and remember what it already attempted, is exactly what Part 3 adds.






Fast-Forwarding the Outcome



The last node is simulate_and_report. It plays the chosen policy forward 15 minutes.



The simulation is stochastic. Drivers respond to incentives probabilistically, the way real people do — not on a fixed schedule.
































Metric Before After Change
driver_count 15 16 +1
rider_request_count 29 25 −4
avg_wait_time (min) 6.3 5.5 −0.8


Directionally correct: more drivers, fewer pending requests, a shorter wait. Just not enough to flip Downtown Core out of deficit territory in one cycle.






What's Missing



Everything the agent looked at here was already structured — numbers and boolean flags. That's exactly why plain rules could handle it. There was no ambiguity for a language model to resolve.



But an ops person doesn't write structured fields. They write something like "the stadium event just got cancelled, and there's an accident backing up traffic near the venue." Stage 1's agent has no way to read that sentence. No lookup table fixes that — it needs an LLM, used for exactly one narrow job. That's where Part 2 picks up.






Code for this series: github.com/ebiarian/zone-balancing-ridesharing-langgraph-agent



Next — Part 2: teaching the agent to read a human's ops note, and to explain its own decision back in plain English — plus a head-to-head comparison of five local LLMs to see which one actually holds up at the job.

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-49449 | Joplin is an open source note-taking and to-do application that organise…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick