Inspiration

Real disputes don't respect category boundaries, but every tool built to handle them does.

A flight gets cancelled two hours before departure — that's Airlines. The traveler had bought travel insurance, and the insurer is now denying the claim because "the airline already refunded the ticket" — that's Health Insurance. A builder delays possession of a flat by 18 months — Housing — while the buyer's home loan EMI, tied to a possession-linked schedule, keeps getting charged anyway — Banking. A telecom SIM gets deactivated over a KYC mismatch, blocking the OTP needed to freeze a bank account during a fraud dispute — Telecom and Banking, in the same hour.

Every existing tool — a general chatbot, a single-vertical legal-tech SaaS, a domain-specific support bot — forces you to pick one box. Say "insurance" and you get insurance advice that never mentions the airline's obligations. Say "airline" and you lose the insurance angle entirely. The moment your real problem crosses a boundary, every existing system quietly drops half of it.

The deeper insight came from data, not intuition: once real cases started accumulating, the same institutions kept showing up across multiple domains — not coincidentally, but because a single company genuinely operates across categories from a citizen's point of view (an airline that also sells travel insurance; a bank that also runs a telecom-linked wallet). No single-domain system could ever surface that, because its own data model doesn't span domains either. We wanted to build the thing that does — one system, 8 domains, that treats "which category is this" as a routing decision made fresh for every query, not a wall baked into the product.

What it does

You describe your dispute in one sentence, in any language, optionally attaching evidence. Walk through a real composite case end to end:

"My flight was cancelled and my travel insurance rejected the claim."

  1. The Domain Router scores all 8 domains simultaneously — not sequentially, not by asking you to pick. Airlines scores highest; Health Insurance clears the relative-confidence bar too (see the math below) because "insurance" and "claim rejected" are unambiguous domain-specific signals, not generic overlap.
  2. Two full agent pipelines launch concurrently — one instance of the entire 12-node LangGraph state machine per matched domain, running in parallel via asyncio.gather, not queued one after another.
  3. Both pipelines share the same uploaded evidence. If you attach the insurer's rejection letter, it's fed into both the Airlines Evidence Agent and the Health Insurance Evidence Agent — one upload, evaluated against two different regulatory lenses at once.
  4. Each domain researches, drafts, and self-corrects independently — Airlines cites DGCA's cancellation-compensation rule; Health Insurance cites IRDAI's claim-rejection disclosure requirement; each has its own Review-agent gate and can retry on its own, without one domain's hallucination blocking the other's clean answer.
  5. The results merge into one report — a single combined_summary, a single deduplicated combined_citations list tagged by which domain each came from — so you get one coherent answer to a problem that was never actually one-dimensional.
  6. The whole thing replies in whatever language you asked in, and every regulation it cites has already been checked against the real retrieved source text before you see it.

Why Combining 8 Domains Is the Actual Innovation

This is the part that's hardest to copy, because it's not a prompt trick — it's an architectural choice made at the very bottom of the stack.

Most "multi-domain" AI products are 8 separate apps wearing one skin. Different prompts, different data models, different teams, stitched together behind a single login. PROXY is the opposite: one LangGraph pipeline, one set of agent roles (Planner, Retrieval, Knowledge Graph, Web Search, Specialists, Negotiator, Evidence, Strategy, Negotiation, Review, Response), parameterized by domain — the same 12 nodes run for a banking dispute as for a housing dispute; only the keyword sets, regulator profile, and specialist prompts change. Adding a 9th domain means adding a keyword set and a regulator profile, not building a 9th application.

The fan-out is real concurrency, not a fake "multi-agent" label on a for-loop:

per_domain_results = await asyncio.gather(
    *(run_for_candidate(c) for c in candidates)
)

Every matched domain gets its own full state — its own case_id suffix, its own copy of the shared evidence_bundle — and they run at the same time. Total latency is bounded by the slowest single domain, not the sum of all matched domains. A 3-domain query costs roughly what a 1-domain query costs in wall-clock time.

The merge is deterministic, not another LLM call papering over the seams:

for entry in per_domain_results:
    state = entry["state"]
    for citation in state.get("structured_citations", []) or []:
        combined_citations.append({**citation, "domain": entry["domain"]})
    final = state.get("final_report") or state.get("final_answer")
    if final:
        combined_summaries.append(f"[{entry['domain']}] {final}")

Nothing is silently dropped, nothing is re-summarized and re-hallucinated in the merge step — every domain's real, individually-reviewed output survives into the final response, tagged by origin.

And it doesn't stop at one query — it compounds across an entire citizen base. The same architectural choice (domain as data, not as boundary) is what makes the Institution Accountability Radar possible: a single graph traversal aggregates dispute volume per institution, across every domain at once. This is live in production right now:

Institution Total disputes Domains
IndiGo 11 Airlines (10) + Health Insurance (1)

The exact same airline that shows up 10 times for cancellation disputes also shows up once inside a travel-insurance claim rejection — a real cross-domain accountability signal that is architecturally impossible for any single-domain tool to produce, because its data was never in the same graph to begin with.

The same principle powers My Knowledge Footprint, one Cypher query spanning every domain for a single citizen:

MATCH (citizen:Citizen {id: $user_id})-[:FILED]->(c:Case)-[:IN_DOMAIN]->(d:Domain)
OPTIONAL MATCH (c)-[:AGAINST_INSTITUTION]->(inst:Institution)
RETURN d.name AS domain, c.id AS case_id, c.title AS title,
       collect(DISTINCT inst.name) AS institutions

One traversal, every domain the citizen has ever touched, in one answer — because the graph was built domain-agnostic from day one.

Novelty at a glance:

Capability General chatbot Single-domain SaaS PROXY
Handles a dispute crossing 2+ domains in one query ❌ (picks one, drops the rest) ❌ (not its category) ✅ concurrent fan-out, merged report
Same institution's behavior visible across domains ❌ no memory ❌ siloed data model ✅ Institution Radar
One evidence upload reused across every matched domain ✅ shared evidence_bundle
Self-corrects hallucinations autonomously, per domain ❌ rare ✅ independent Review gate per domain
Adding a new category means a new app ✅ (that's the problem) ❌ — new keyword set + regulator profile only

How we built it

  • Frontend: Next.js 15 (App Router) + React 19 + TypeScript, with a fully custom 3D visualization layer (@react-three/fiber + drei) powering three real, data-driven graph modes.
  • Backend: FastAPI orchestrating the domain-parameterized LangGraph state machine described above — a directed graph of 12 nodes with a conditional edge that can route backward from review to strategy, run once per matched domain, concurrently.
  • Data layer: Qdrant (vector search, one collection per domain), Neo4j Aura (one knowledge graph spanning all domains and all citizens), Upstash Redis (cache), NVIDIA NIM (LLM + embeddings) — all real managed services.

The Algorithms Under the Hood

1. Domain Router — deterministic multi-label classification, zero LLM cost, decides WHEN to fan out.

$$score(d) = \min\Big(1,\ 0.2 + 0.12|E_d| + 0.06|F_d| + 0.15|S_d|\Big)$$

where $E_d$ = exact keyword hits, $F_d$ = fuzzy (typo-tolerant) hits, $S_d$ = unambiguous strong-signal terms for domain $d$. A second domain is only accepted — triggering the concurrent fan-out — if:

$$score(d) \ge 0.55 \times score(d^{*}) \quad \text{and} \quad S_d \neq \emptyset$$

The strong-signal requirement is what stops false fan-out: "refund," "complaint," and "charged" are generic words that appear in banking, telecom, and e-commerce alike, so a plain banking query never spuriously triggers a duplicate Telecom analysis just because of shared vocabulary. Only a genuinely cross-domain query — carrying unambiguous signal terms for two domains — clears this gate. For non-English queries where every domain scores zero, a fallback layer translates once and re-scores — verified live across Tamil, Hindi, Japanese, Chinese, and Portuguese.

2. Multi-Domain Fan-Out & Merge — concurrency with a deterministic join.

$$\text{results} = \text{gather}\Big(\text{Pipeline}(d, q, \text{evidence}) \ \forall\, d \in \text{Candidates}(q)\Big)$$

$$\text{combined_citations} = \bigcup_{d\,\in\,\text{Candidates}} {c \cup {\text{domain}: d} : c \in \text{citations}_d}$$

Wall-clock cost is $O(\max_d \text{latency}_d)$, not $O(\sum_d \text{latency}_d)$ — the same property that makes the fan-out practical instead of merely possible.

3. Evidence Scoring Engine — ranking by more than cosine similarity, shared across every domain's retrieval.

$$\text{authority_composite} = 0.60A + 0.25L + 0.15F$$ $$\text{overall_evidence_score} = S \times (0.6 + 0.4 \times \text{authority_composite})$$

$S$ = vector similarity, $A$ = source authority tier (1.0 for a primary regulator like RBI/DGCA/IRDAI, down to 0.5 for unclassified), $L$ = legal weight, $F$ = freshness. Similarity gates relevance; authority can only modify it by ±40%.

4. Citation Verification — a deterministic ground-truth check, zero extra LLM calls, applied per domain.

$$\text{verified}(c) = \begin{cases} \text{true}, & c \subseteq \text{source (near-verbatim)} \ \text{true}, & \dfrac{|{w \in c : w \in \text{source}}|}{|c|} \ge 0.7 \ \text{false}, & \text{otherwise} \end{cases}$$

Anything under the 70% overlap threshold is flagged inline instead of silently presented as fact — independently, in every domain that matched.

5. The Self-Correction Loop — an independent closed-loop controller per matched domain.

review_should_retry = (hallucination_found OR bad_citation_found)
                       AND NOT approval_ready
                       AND retry_count < 1

Because each domain in the fan-out runs its own copy of this gate, one domain's hallucination retry never blocks or delays another domain's already-clean answer — the self-correction is per-pipeline, not global.

6. Knowledge Graph Layout — golden-angle phyllotaxis, positioning every entity from every domain in one scene.

$$\theta = \pi(3-\sqrt5) \approx 137.5^{\circ}, \qquad r_i = 2.1 + 1.5\sqrt{i}$$ $$(x_i, y_i, z_i) = \big(r_i\cos(i\theta),\ 0.7\sin(0.75i),\ r_i\sin(i\theta)\big)$$

The same angle that packs sunflower seeds without overlap positions every entity — across however many domains a case actually touched — so nothing visually collides regardless of count.

Challenges we ran into

  • Domain classification silently failed on non-English text — a Tamil flight-cancellation query was misclassified as a health-insurance case at 0% confidence, because the router only matched ASCII keywords. Fixing it for non-Latin scripts alone wasn't enough — Portuguese/Spanish/French, written in the same Latin alphabet as English, needed a separate outcome-based fallback, not a script-detection heuristic.
  • False-positive fan-out was a real early bug: a plain single-domain banking query ("my bank charged me twice") was triggering a full duplicate Telecom analysis purely because both domains share generic billing vocabulary — fixed by requiring at least one unambiguous strong-signal term per additional domain, not just relative score.
  • The review-agent self-correction gate was only wired into one of our two execution graphs — the more heavily used planner-driven path (the one the multi-domain fan-out actually runs) shipped rejected strategies unchanged until we found and closed the gap.
  • 3 of our 8 domains (government, housing, healthcare) had no keyword set in the document-relevance filter — every document uploaded under them was permanently rejected as "not relevant," silently excluded from both the vault UI and every domain specialist's evidence, invisible until we tested each of the 8 domains explicitly rather than trusting the aggregate.

Accomplishments that we're proud of

  • A single query genuinely fanning out into two independent, concurrently-running, self-correcting agent pipelines and merging into one coherent report — not a demo script, running in production.
  • The Institution Radar surfacing a real cross-domain signal (IndiGo: 10 Airlines disputes + 1 Health Insurance dispute) that no single-domain data model could have produced.
  • Watching the self-correction loop catch a real hallucinated compensation figure and autonomously re-run the strategy step, live, in production.
  • Verified multilingual replies — same case, five languages, equivalent substance, zero mixed-language drift.
  • A strict no-mock-data discipline: every number in this project, including every one in this document, traces back to a real Qdrant/Neo4j/Redis query.

What we learned

Agentic AI isn't one clever prompt — it's a state machine with the discipline to route backward when its own output fails a check, and the discipline to run sideways — concurrently, independently — when a real problem doesn't fit in one box. Combining 8 domains isn't a UI feature; it has to be true at the data-model layer (one graph, not eight) before it can ever be true at the product layer. Verification has to be deterministic wherever possible — asking an LLM to grade its own homework a second time is not the same as a citation actually appearing in the source text, and a fan-out threshold has to be strict enough to resist shared vocabulary, or "combining domains" becomes "duplicating noise."

What's next for PROXY

More domains, more languages, direct integration with regulator e-filing APIs where they exist, and expanding the Institution Accountability Radar's cross-domain view into a public, citable dataset — the same architecture that let IndiGo's cross-domain pattern surface today should scale to surfacing it for every institution operating across category lines.

Built With

Share this project:

Updates