Skip to content

Latest commit

 

History

History
206 lines (167 loc) · 11.3 KB

File metadata and controls

206 lines (167 loc) · 11.3 KB

TimeWeave — Counterfactual Journey Engine

American Express · Cross-Channel Journey Stitching

"Everyone else built a better rear-view mirror. We built a flight simulator for the customer relationship."

TimeWeave stitches a customer's fragmented touches across four channels — app, web, call center, in-person — into one identity and one journey, then turns that journey into a decision: it captures the anonymous pre-login signal everyone misses, explains why a customer is at risk, and lets an analyst branch the journey into alternate futures ranked by outcome — honestly labeled as cohort outcome rates, not causal guarantees.

All four seed phases are implemented and the frontend runs off the live backend, with a static offline fallback so the hero demo never breaks.


Run it (two commands · fully offline · no API keys, no external services)

Each script creates its environment on first run (Python venv + pip install, or npm install), then starts the server. Run the backend first, then the frontend in a second terminal.

Windows (PowerShell)

./scripts/run-backend.ps1        # → http://localhost:8000  (docs at /docs)
./scripts/run-frontend.ps1       # → http://localhost:5173   (second terminal)

If PowerShell blocks the scripts, either unblock once with Set-ExecutionPolicy -Scope Process Bypass or run the commands inline:

cd backend; python -m venv .venv; .\.venv\Scripts\python -m pip install -r requirements.txt
.\.venv\Scripts\python -m uvicorn main:app --reload --port 8000
# new terminal:
cd frontend; npm install; npm run dev

macOS / Linux (bash)

./scripts/run-backend.sh         # → http://localhost:8000  (docs at /docs)
./scripts/run-frontend.sh        # → http://localhost:5173   (second terminal)

Sanity-check the whole pipeline with no server: backend/.venv/Scripts/python scripts/seed.py (Windows) or backend/.venv/bin/python scripts/seed.py (macOS/Linux).

The frontend talks to the backend on :8000 (override with VITE_API_BASE; see frontend/.env.example). If the backend is down, Priya's hero flow still renders from the static seed (frontend/src/data/priya.ts).


Build status — all four phases done

Phase Scope Status
1 Frontend hero visual — Identity / Journey / Counterfactual, 3 views end-to-end ✅ done
2 Synthetic generator (real schemas) + DuckDB store + ingestion → canonical events + FastAPI ✅ done
3 Fuzzy identity resolution + churn model + attribution + cohort comparison, wired to the frontend ✅ done
4 Review-queue UI, predicted-vs-observed loop, uncertainty bands, population "fix-the-flow", audit export, optional LLM planner ✅ done

Phase 1 — the hero (frontend)

Three linked views: Identity (channel streams collapse into one golden identity; click the fuzzy SAME_AS edge for the 91% merge explanation + un-merge; a review-queue badge holds low-confidence merges), Journey (cross-channel swim-lane timeline + churn gauge + risk attribution), and Counterfactual (the journey branches into interventions; the gauge drains along the winning branch; every number labeled "outcome rate for N similar journeys"; Approve → mock execute → predicted-vs-observed loop).

Phase 2 — the spine (backend)

  • Real industry schemas (backend/data/): events are generated as Adobe XDM ExperienceEvents, Amazon Connect CTRs, ISO 8583 messages and system-derived SLA breaches, then normalized to one canonical event model (backend/ingestion/). Map real Amex feeds onto these shapes and the pipeline runs unchanged.
  • Embedded, persistent DuckDB store (backend/data/store.py) — no server; a file at backend/timeweave.duckdb that survives restarts (see Data & persistence). ~835 synthetic customers / ~4,200 events across all four channels, plus the authored edge cases.
  • Feature-driven ground truth (backend/data/outcomes.py): each customer's churn/retention outcome is drawn from one generative model over the journey's features, so the churn classifier and the cohort comparison are real properties of the data — not numbers invented at display time.

Phase 3 — resolution + intelligence (wired to the frontend)

  • Explainable identity resolution (backend/resolution/): deterministic pass (exact account/PAN/phone + ECID cookie-continuation) then a fuzzy pass (rapidfuzz, or a stdlib fallback) that scores anonymous sessions on a weighted feature blend → confidence, matched features, plain-English explanation. Thresholds: ≥0.85 auto-merge · 0.55–0.85 review queue · <0.55 held back. Merges are reversible (un-merge) with an append-only audit history.
  • Journey graph (backend/graph/): a NetworkX-style temporal identity + journey graph (Identity / SourceIdentifier / Event / Outcome / Intervention) exposed at /graph/{gid}.
  • Churn model + attribution (backend/intelligence/): a logistic classifier trained on the population; per-driver attribution via leave-one-out marginals. Priya lands ≈68% risk, ordered broken-callback > redemption-failure > cancel-research > … with the fee barely registering.
  • Cohort comparison: observed retention per intervention over the cohort of similar journeys (DuckDB), with a Wilson uncertainty band and mandatory correlational labeling.

Phase 4 — polish + credibility

  • Review-queue approve/reject UI, live un-merge, edge-case toggle in the picker.
  • Predicted-vs-observed loop that closes on the synthetic ground truth.
  • Uncertainty bands on every cohort number; population "fix-the-flow" business case (churn prevented / calls avoided / value preserved); regulator-ready audit export (JSON).
  • Optional LLM planner/explainer (backend/llm/) — auto-detects ANTHROPIC_API_KEY, sends only IDs + aggregates, and degrades to templated text when absent. The app is fully functional without it.

Edge cases behave correctly: false_positive_trap scores ~0.38 and is not auto-merged; shared_household (a shared card token) lands in the review queue at 0.71.

API: /identity/{gid} · /journey/{gid} · /churn/{gid} · /interventions/{gid} · /recommendation/{gid} · /graph/{gid} · /merges · /review-queue · POST /unmerge · POST /remerge · POST /review/{id} · POST /approve · /population/fix-the-flow · /population/stats · /system/status · /customers · /events?golden_id= · /health · /admin/db · POST /admin/customer · POST /admin/regenerate


Data & persistence

Data is synthetic and generated locally — no real data, no external source.

  • Persistent by default. The store is a file-backed DuckDB at backend/timeweave.duckdb (gitignored). On first boot it generates ~835 customers / ~4,200 events and persists them; on every restart it hydrates from the file instead of regenerating, so the data (and anything you add) survives and can be inspected later. Override the location — or go ephemeral — with the TIMEWEAVE_DB env var (TIMEWEAVE_DB=:memory: for an in-memory run).

  • Inspect it: GET /admin/db shows the file path + row counts. You can also open backend/timeweave.duckdb in any DuckDB client and query events, ground_truth, profiles.

  • Add your own customer (writes to the DB, then rebuilds resolution + retrains churn so it shows up live in the UI):

    curl -X POST http://localhost:8000/admin/customer -H "Content-Type: application/json" -d '{
      "profile": {"golden_id":"golden-demo-9","name":"Demo Customer","tier":"Platinum",
                  "tenure_years":7,"renewal_in_days":5,"fee_sensitivity":0,
                  "cohort_key":"redemption_failed+callback_broken@renewal"},
      "raw_events": [
        {"_source":"xdm","_id":"d9-app-1","timestamp":"2026-07-11T10:00:00Z",
         "eventType":"application.redemption",
         "identityMap":{"account":[{"id":"acct-d9","authenticatedState":"authenticated"}]},
         "_attributes":{"points":60000},"_outcome":"redemption_failed"},
        {"_source":"system","_id":"d9-sla","_customer_ref":"acct-d9",
         "timestamp":"2026-07-15T15:00:00Z","eventType":"system.sla_breach",
         "_attributes":{"breached":true},"_outcome":"callback_broken"}
      ]
    }'

    raw_events use the same source schemas the generator emits (XDM / CTR / ISO 8583 / system).

  • Regenerate from scratch: POST /admin/regenerate wipes and rebuilds the population.

DuckDB is single-writer — run one backend instance against a given DB file at a time.


Offline by design (no network required)

The optional accelerators — rapidfuzz, networkx, scikit-learn — are exactly that: optional. Each capability ships with a zero-dependency stdlib fallback and auto-upgrades if the library is importable (/system/status shows which backend is active):

Capability Accelerator Stdlib fallback
Fuzzy string matching rapidfuzz difflib
Identity/journey graph networkx internal graph structure
Churn classifier scikit-learn pure-Python logistic regression (gradient descent)
Intervention planner Anthropic LLM deterministic templates

Install the accelerators for a credibility upgrade (pip install rapidfuzz networkx scikit-learn); nothing changes behaviourally.


Honest-framing note (a deliberate design choice)

TimeWeave does not claim causal uplift. Recommendations are observed retention rates within a cohort of similar journeys — correlational, and labeled as such everywhere they appear (UI text and API alike). Validated causal uplift requires controlled experimentation; that's roadmap, not seed. Every merge and every recommendation carries a plain-English "why," and any (mock) action requires human-in-the-loop approval.

Roadmap (out of scope for the seed)

Real causal inference / experimentation, RL policies, production streaming at scale, real PII, clean rooms / PPRL, multi-node graph DB, real channel integrations, a Splink matcher swap-in, merge-drift monitoring, and an agent copilot at point-of-contact.


Structure

amex-hackathon/
├─ frontend/          React + Vite + TS + Tailwind + react-flow + framer-motion
│  ├─ src/api/        typed client + response types + offline fallback bundle
│  ├─ src/hooks/      useTimeWeave (loads the selected customer's bundle)
│  └─ src/components/ Identity / Journey / Counterfactual / Population + review queue
├─ backend/           FastAPI + DuckDB (embedded, persistent), all offline
│  ├─ timeweave.duckdb  persisted data file (gitignored; created on first run)
│  ├─ schemas/        Pydantic: canonical event, API models
│  ├─ data/           generator · seed_priya · store · features · outcomes
│  ├─ ingestion/      raw XDM/CTR/ISO8583/system → canonical
│  ├─ resolution/     deterministic + fuzzy matcher · review queue · un-merge · audit
│  ├─ graph/          in-memory identity + journey graph (networkx-optional)
│  ├─ intelligence/   logistic churn model · attribution · timeline · cohort comparison
│  └─ llm/            optional planner/explainer (degrades gracefully)
├─ scripts/           run-backend(.ps1/.sh) · run-frontend(.ps1/.sh) · seed.py
└─ seed.md            the full project spec