Team-Prik
Deployed06.2026 - ∞|5m
An autonomous multi-agent pricing service that reprices live intercity bus trips, with a deterministic validator standing between the language model and every fare it touches.
The measurement
- Measured on
- offline backtest replaying real journeys under a linear-demand model, repricing only inventory unsold at the decision lead
- Against a baseline of
- rule baseline +0.5 to +0.75%; bounded optimizer ceiling +0.95 to +1.16%
What it does
- Six specialised agents over a shared blackboard, sequenced by a head orchestrator. Agents never call each other; they communicate only through the blackboard, which is what makes the cycle replayable.
- The language model is deliberately off the critical path. If Gemini is slow or unavailable the deterministic baseline carries the cycle, so pricing degrades rather than stalls.
- Designed the in-process tiered memory layer: an O(1) hashmap with a doubly-linked-list LRU, TTL and label invalidation, a bounded undo stack, and per-trip velocity deques. Cut per-cycle database round-trips from 4 to 0 within TTL, for a service firing every five minutes and on every booking.
- Built the 167-to-10 composite-signal pipeline that feeds the agent, fronted by a durable feature store, so a pricing decision reads precomputed signals instead of recomputing them each cycle.
- One validator is the single safety chokepoint: classification moves at most one step, adjustment stays within plus or minus 20%, price never leaves its floor or ceiling, booked seats are never touched. Fuzz-tested to zero bound violations across 38 tests.
How it is put together
Five figures, all built from the project's own source and README rather than drawn by hand. The counts in the captions are computed from the data underneath them.
the order stages run in, and which one contains a language modela single-agent design, where a slow model stalls the whole cycle
The dashed route is the argument. Stage 03 produces a usable deterministic answer before the model is consulted at all, so stage 04 is an improvement rather than a dependency - if Gemini stalls, the cycle completes on the baseline. The interesting part is allowed to fail.
Show the numbers
| # | Agent | Role | Model |
|---|---|---|---|
| 1 | Collector | Live trips, occupancy and every metric family, read through the memory layer | no |
| 2 | Planner | Gates which trips need action at all, then prioritises them | no |
| 3 | Pricing agent | Deterministic baseline. This is the fallback that makes the next stage optional | no |
| 4 | Reasoner | Gemini 2.5 Flash picks the two levers from ten composite scores | yes |
| 5 | Validator | Clamps to the price rules. The single safety chokepoint | no |
| 6 | Writer | Logs the decision, applies it on staging, pushes the undo stack | no |
database round-trips per pricing cycle, once the cache is warm4 round-trips per cycle cold, for a service firing every five minutes and on every booking
Why each datum sits in the tier it does
- 01LIVEOccupancy, seats, lead time, current fares, discount depthDirect read every cycleChanges between cycles, so caching it would be caching a stale price
- 02LRU-STATICRoute master, last-year demand, the booking curveCached, misses batchedEffectively fixed within a day; re-reading it is pure cost
- 03LRU-SLOWPrice rules, competitor fares, look-to-book funnelCached, sentinel-gatedMoves on the order of hours, not minutes
- 04DEQUEPer-trip 24h booking velocityFixed-length window across cyclesA velocity percentile needs history a single cycle does not have
- 05STACKLIFO fare-action undoBounded, with a durable escape hatchAn autonomous pricer that cannot be reversed is not shippable
- 06DURABLEBooking curve and elasticity featuresNightly rebuild, fronted by LRUToo expensive to compute on the critical path
- 07DERIVEDComposites and signalsRecomputed per cycleCheap, and always a function of the tiers above
An O(1) hashmap fronting a doubly-linked-list LRU, with TTL and label invalidation, a bounded undo stack, and per-trip deques. Within TTL it serves a whole cycle without touching a database. No Redis: the working set fits in process, and a network hop to fetch it back would have undone the point.
Show the numbers
| Tier | Holds | Policy |
|---|---|---|
| LIVE | Occupancy, seats, lead time, current fares, discount depth | Direct read every cycle |
| LRU-STATIC | Route master, last-year demand, the booking curve | Cached, misses batched |
| LRU-SLOW | Price rules, competitor fares, look-to-book funnel | Cached, sentinel-gated |
| DEQUE | Per-trip 24h booking velocity | Fixed-length window across cycles |
| STACK | LIFO fare-action undo | Bounded, with a durable escape hatch |
| DURABLE | Booking curve and elasticity features | Nightly rebuild, fronted by LRU |
| DERIVED | Composites and signals | Recomputed per cycle |
signals reaching the language model, per tripa 167-metric catalog the collector can compute in full
94% reduction
Six composite scores, three guardrail flags and one funnel signal. The reduction happens off the critical path and lands in a durable feature store, so a pricing decision reads precomputed signals instead of recomputing 167 of them every five minutes.
Show the numbers
| State | Value |
|---|---|
| Metric catalog | 167 |
| Composite scores | 10 |
trips the model discounted anomalously, out of a fixed backtest setthe same 120 trips before the prompt was tuned
96% reduction against a fixed set of 120safety-relevant
No guardrail violation was observed in validation either, but that is a weaker claim: the validator clamps every decision regardless, so guardrails holding says more about the validator than about the model. This figure is the one that says the model itself got better.
Show the numbers
| State | Value | Of | Share |
|---|---|---|---|
| Before tuning | 50 | 120 | 41.7% |
| After tuning | 2 | 120 | 1.7% |
simulated revenue liftoffline backtest replaying real journeys under a linear-demand model, repricing only inventory unsold at the decision lead
The agent beats doing nothing, but it does not beat the blunt rules here - and the project's README says so rather than burying it. The elasticity proxy is confounded and reads inelastic everywhere, which rewards blanket aggressive raises and flatters the rule baseline. A demand-controlled elasticity estimate and a live A/B are the real arbiters.
Show the numbers
| Pricer | Low | High | Kind |
|---|---|---|---|
| Rule baseline | +0.5% | +0.75% | baseline |
| Agent + Gemini, tuned | +0.4% | +0.55% | measured subject |
| Bounded optimizer ceiling | +0.95% | +1.16% | upper bound, not a competitor |
Where it falls down
The agent reliably beats doing nothing, but in this simulation the language model sits at near-parity with the blunt rules it was meant to improve on - and the project's own README says so. The reason is that the elasticity proxy is confounded and reads inelastic everywhere, which rewards blanket aggressive raises. A demand-controlled elasticity estimate and a live A/B test are the real arbiters, and neither has run yet.
Stack
- Python
- Gemini 2.5 Flash
- Multi-agent
- LRU
- PostgreSQL
- ClickHouse
- Next.js