Inspiration

AI trip planners are confident and often wrong, and they can't safely transact. They hallucinate hotels that don't exist, blow your budget, ignore that a typhoon is forecast for your hiking day, and — when it's time to actually book — either hand you off to ten tabs or, worse, would happily spend your money on a fabricated itinerary with no real consent.

We measured it: the same large model, asked to book a trip solo, fails to book a perfectly feasible trip in up to ~25% of runs (4/16 in our isolation baseline, benchmark/REPORT.md) — and never the same run twice. You cannot put a credit card behind that. We wanted to know: what does it actually take to make an autonomous agent safe to spend money on your behalf — not "safe" as a prompt instruction, but safe as an architectural guarantee?

What it does

One sentence — "6 days in Tokyo, food + culture, $3,500" — goes to a society of 12 specialized Qwen agents that negotiate a real, bookable, budget-honest, safety-aware itinerary, then execute a prepaid checkout that needs exactly one human consent.

You see it as a clean web dashboard: plan → review the held package → one-click "Confirm & Book" → a keepable preview you save to your phone. Nothing is charged until that one click.

Try it live — staging.itinerario.io runs the deterministic seeded snapshot (same request → same plan, every time — judging-safe, variance ≈ 0).

Five structural checks make this different from a single well-prompted agent:

  1. Can't overspend — a real Go merchant enforces the budget via an HTTP 403 the agents cannot override, not an LLM's opinion.
  2. Can't hallucinate a charge — a Critic agent re-verifies every price against the backend before any commit.
  3. Can't silently partial-book — all-or-none checkout; an incomplete package returns an honest cannot_satisfy, never a partial surprise.
  4. Can't spend without authorization — one human consent per package, bound to a signed AP2 mandate; any swap or recovery needs fresh re-consent.
  5. Can't flake — LLM reasoning is clamped behind deterministic validators, so correctness variance ≈ 0 across runs, even though the same underlying model is ~25% unreliable solo.

It also carries an honest safety layer most planners skip: per-leg, per-hazard risk (typhoon / flood / wildfire, each its own line), a date-aware live emergency feed (a storm active today won't false-flag a trip months away), and a post-booking risk watch that alerts you — in your language — if conditions shift, suggesting a minimal swap instead of a painful re-plan.

How we built it

  • Qwen / DashScope (qwen3.x) at exactly 3 clamped LLM edges — intent parsing, vibe→area ranking, accommodation ranking — plus the itinerary narrator. Every edge sits behind a deterministic validator and a closed-set fallback, so the LLM never has the final word over money or facts.
  • The agent society — 11 specialists (budget/finance veto, destination, accommodation, transport, critic, risk, insurance, compliance, health, fraud, day-planner) negotiating over A2A (peer propose/critique/veto), MCP (tool/memory access), UCP (the Go merchant channel), and AP2 (the consent mandate) — plus a human-in-the-loop consent gate as the architecture's 12th safety property.
  • A deterministic core: multi-leg budget allocation under precedence constraints is genuinely NP-hard (a multiple-choice knapsack problem); we run an exact DP over the assembled candidates rather than trusting an LLM (or a greedy heuristic) to get the arithmetic right.
  • The Go UCP merchant (ucp-merchant/) — a real, separate service enforcing RFC 9421 signed requests, a W3C-VC AP2 mandate envelope, and a prepaid wallet in integer cents. This is the piece that turns "the agent promised not to overspend" into "the server will HTTP 403 it regardless of what the agent says."
  • Web dashboard: Svelte + TypeScript + MapLibre GL, talking to the backend over HTTP/SSE.
  • AliCloud-native deployment: DashScope for Qwen, ECS for the running backend, with SLS telemetry and AMap map-data seams wired (both key-gated, activating at demo time) and a KMS-hardening seam for the agent's signing key (on-disk today, KMS-envelope activation is a ~30-line swap once credentials are provisioned — see ALICLOUD-PROOF.md).
  • Data: a catalog seeded from OSM / Wikidata / SimpleMaps / OpenFlights, deepened with grounded Gemini seeding — thousands of cities and growing, honestly labeled as beta coverage where thin.

Challenges we ran into

  • Taming an LLM into variance ≈ 0 without neutering it. The interesting engineering problem wasn't "add an LLM" — it was deciding exactly which 3 decision points actually need generative reasoning (intent parsing, vibe-matching, ranking) versus which need to be deterministic law (budget, checkout, consent), then proving the boundary holds under adversarial testing.
  • NP-hard, for real. Multi-leg itinerary assembly under a shared budget is a multiple-choice knapsack problem with precedence constraints. A single LLM call asked to "plan within budget" is really being asked to solve an NP-hard optimization problem via next-token prediction — which is exactly where hallucinated arithmetic comes from. We split it: agents propose feasible candidates, an exact DP allocator composes the globally budget-optimal package from them.
  • Making "safe" mean something the agent can't talk its way around. It would have been easy to ship a system prompt that says "never exceed the budget" and call it safety. We built a real HTTP 403 at a server the agents don't control, specifically so no amount of prompt-injection or model confidence could override it.
  • A real correctness bug class, found by testing, not assumed away. Our per-city risk-region mapping had a subtle bug: city names that exist in more than one country (Richmond CA/US, Salamanca ES/MX, and others) could silently inherit the wrong country's hazard profile if nobody caught the collision. We built a standing collision checker and wired it into CI so the next homonym introduced by our daily catalog seeding gets caught automatically instead of shipping silently.
  • Deciding what to simulate, and saying so loudly. Real Alipay settlement needs a business-account keypair we don't have as individual hackathon entrants. Rather than fake it quietly, every simulated response is explicitly labeled "simulated": true in the API and called out in the architecture diagram — the enforcement machinery (signing, budget veto, mandate verification) is real; only the money rail is a sandbox.
  • Knowing when to stop chasing a green checkmark. We set out to activate real AliCloud KMS envelope-encryption for the agent's signing key. The first integration, built against the generic KMS SDK, failed outright — it turned out our KMS 3.0 instance is a Dedicated KMS instance, which needs an entirely different SDK and a mutual-TLS client-certificate auth model, not AccessKey auth. We rewrote the integration correctly against the real (verified, not guessed) SDK API in both Python and Go, got it independently audited safe, and confirmed via a live connection attempt with real credentials that the code and auth were right — the request reached AliCloud's own gateway and was rejected for a documented, specific reason (InvalidHeader, then UnsupportedOperation), not a vague timeout. What we couldn't do in the time remaining was get genuine private-network access to the instance to complete a live encrypt→decrypt round-trip. Rather than flip the activation flag on unverified code the night before submission, we shipped the honest state: the seam is built and audited, activation is deferred, and the existing on-disk key path — already in production — is untouched.

Accomplishments that we're proud of

  • Variance ≈ 0 where it actually counts, measured, not asserted. N=20 real runs across four tight-budget scenarios: a single-agent baseline stays within budget 70–100% of the time (same model, both arms, no tip-off); the society stays within budget 100% of the time, every leg, every run. We didn't relax the baseline to make this look good — same qwen3-max on both sides.
  • A budget veto the agents genuinely cannot talk their way around. Not a system prompt — a real Go service that returns HTTP 403 with the exact cents figures when a checkout would exceed budget. We can point at the literal server response, not describe an intention.
  • Solving the allocation problem instead of hand-waving it. Multi-leg budget assembly is NP-hard; rather than let an LLM eyeball an "optimal" package, we built an exact DP allocator that's provably budget-optimal for every instance size the system actually sees.
  • Catching a real correctness bug class through testing discipline, not luck. The city→country/region homonym bug (Richmond CA/US, Salamanca ES/MX, and others) is exactly the kind of thing that ships silently in most travel data pipelines. We built a standing collision checker, wired it into CI, and it now guards every future day's catalog growth automatically.
  • A genuinely honest degradation path. No fabricated hotels, no invented prices, no silent partial bookings — when the system can't satisfy a request it says cannot_satisfy and explains why, across thousands of tests against a catalog that's grown past 13,000 cities.
  • Four real protocols, coherently layered, not buzzword-collected. MCP for tool access, A2A for peer negotiation, UCP for the signed commerce channel, AP2 for the human consent mandate — each one doing a job the others structurally can't, verified end-to-end in the Go test suite (signed round-trips, replay-attack rejection, tamper detection).

What we learned

  • Determinism has to be architected, not requested. You cannot prompt your way to variance ≈ 0. The only way we found to get there was deciding, up front, exactly which 3 decisions genuinely need LLM judgment (intent, vibe-matching, ranking) and making every other decision — money, checkout, consent — a deterministic, server-enforced fact.
  • "Safe agentic commerce" needs an economic backstop, not just a well-behaved model. A budget veto is only real if it lives somewhere the agent can't reach — a separate signed service, not a clause in the system prompt.
  • Adversarial verification finds real bugs that building-and-self-checking misses. Every non-trivial fix in this project went through an independent audit pass before shipping, and it kept paying off — including on the architecture diagram itself, where an adversarial pass caught that a "telemetry" edge visually implied data flowing back into planning, which would have quietly contradicted our own var-0 determinism claim if it had shipped as drawn.
  • Even "which country is this city in" is a real, recurring engineering problem at scale. It sounds trivial until your catalog has multiple Richmonds, multiple San Pedros, multiple Victorias — and the wrong answer silently degrades a safety-relevant hazard assessment. Travel data correctness is a genuinely deep problem, not a solved one.
  • A growing dataset needs a permanent regression gate, not a one-time fix. Our daily seeding cron adds a thousand-plus new cities on a good day; any fix to a data-correctness bug has to be a standing CI check, or the next day's growth just reintroduces the same class of problem.

What's next for Travel Guild

  • This submission is the precursor. What's here is the safe-execution layer — the agent society, the deterministic core, the one-consent booking flow. The natural destination is the real consumer product built on top of it: itinerario.io, an AI travel-planning webpage for actual bookings, carrying forward the same safety guarantees this submission demonstrates.
  • A real settlement rail. Alipay integration is simulated today because it needs a registered business-account PSP keypair we don't have as individual entrants — the enforcement machinery (signing, mandate verification, budget veto) is already built to swap in a real rail without re-architecting; this is the most concrete near-term integration opportunity.
  • Take the pattern beyond travel. The actual contribution here — clamp the LLM to judgment calls, make money and commitments a deterministic, server-enforced fact — isn't travel-specific. Any domain where an agent needs to spend money or make a binding commitment on a human's behalf has the same shape of problem.

Built With

  • a2a-(agent2agent)
  • alibaba-cloud-ecs
  • alibaba-cloud-kms
  • alibaba-cloud-sls
  • amap/gaode
  • fastapi
  • go
  • maplibre-gl
  • mcp-(model-context-protocol)
  • openstreetmap
  • playwright
  • pytest
  • python
  • qwen3-(dashscope-api)
  • rfc-9421-(http-message-signatures)
  • simplemaps
  • sqlite
  • svelte
  • typescript
  • w3c-verifiable-credentials
  • wikidata
Share this project:

Updates