The problem: rethinking routing in multi-agent systems on Databricks
Databricks native supervisor agent pattern is a solid default for orchestrating multiple Genie spaces and tools but it comes with a routing constraint worth examining. The supervisor relies heavily on user-provided instructions to decide which agent or Genie space a query should go to. That routing logic has to be authored, maintained, and gets re-evaluated on every single call, since the supervisor treats each incoming question independently before deciding where it goes.
This raises a natural question: do we need a fresh routing decision, with full context, on every single query?
The alternative we explored was building a custom LangGraph agent that consolidates all the Genie spaces and the Knowledge Assistant into one unified flow, rather than sitting a supervisor on top of them. The core idea: instead of routing purely on static instructions, generate a lightweight intent summary for the incoming question up front. That intent summary becomes the basis for routing to the correct Genie space or Knowledge Assistant tool for the remainder of that conversation thread, rather than re-deriving routing logic on every hop.
Where this gets more interesting is what the intent summary is routed against. Rather than hard-coding a static mapping of “these keywords go to this Genie space,” routing is grounded in Atlan’s semantic metadata layer the business glossary, asset descriptions, and lineage context Atlan already holds for each Genie space’s underlying tables. So the routing decision isn’t just “does this question sound like it’s about supply chain” it’s checked against the actual, governed context of what each Genie space is scoped to know about. This makes routing more resilient as new Genie spaces get added, since the routing signal comes from Atlan’s catalog rather than a manually maintained instruction set that has to be updated every time.
The hypothesis: an upfront intent summary plus Atlan-grounded routing reduces redundant LLM calls in the routing path, which should show up directly in the per-run cost we track via MLflow later in this post.
To be clear, this isn’t a case against the supervisor agent pattern; it’s a legitimate, well-supported approach for many use cases. This post is about exploring a specific optimization for a specific shape of problem: many Genie spaces plus a Knowledge Assistant, high query volume, cost-per-run as a first-class concern, and Atlan metadata already in place to lean on.
Architecture overview
The stack: a LangGraph agent calling a Databricks-hosted foundation model through Databricks AI Gateway, Atlan connected via API key for both routing context and standalone metadata lookups, a Knowledge Assistant loaded with unstructured docs, one or more Genie spaces, and MLflow tracing wrapping the whole run for token and cost visibility.

At a high level:
- A user query comes in.
- The LangGraph agent (running a Databricks-hosted model via AI Gateway) reasons over the query and builds an intent summary.
- The intent summary is checked against Atlan’s semantic catalog to decide which Genie space or tool the query belongs to.
- The agent calls the right tool a Genie space, the Knowledge Assistant, or Atlan directly for a metadata/lineage question.
- Every LLM call along the way is traced through MLflow, which reports tokens and cost for the run.
- The final answer goes back to the user.
The agent design: a single ReAct node, not a multi-node graph
It would have been easy to over-engineer this as a LangGraph graph with separate nodes for intent extraction, routing, and tool execution. We deliberately kept it simpler: a single ReAct-style node that holds the whole loop reason, decide on a tool call, observe the result, repeat until it has an answer.
The intent summary and the Atlan-grounded routing decision aren’t separate graph nodes with their own state transitions they’re part of the same reasoning step the ReAct agent already does before picking a tool. Practically, that means:
- The agent’s prompt is augmented with Atlan’s semantic metadata (glossary terms, asset descriptions, table-to-domain mapping) so that when it reasons about which tool should handle a question, it’s reasoning with governed context already in front of it, rather than routing rules bolted on as a separate step.
- Genie spaces and the Knowledge Assistant are exposed to the ReAct loop as tools, the same way any LangGraph tool-calling agent would see them. The “routing” is really the model picking the right tool given the metadata it has, rather than a distinct supervisor hop.
- The graph itself stays minimal: one reasoning node, a tool-execution edge, and a loop-back edge until the agent is done. That’s part of what keeps the token and cost profile lean compared to a multi-agent supervisor topology.
Worth naming honestly: a single ReAct node puts more weight on the system prompt and the quality of the metadata fed into it, since there’s no separate routing stage to fall back on if the model reasons poorly. That’s a fair trade given the goal here, but not one to gloss over.

Knowledge Assistant: the “how do I even find this” tool
Genie spaces answer questions grounded in structured, tabular data but a large share of real questions from users aren’t “what’s the number,” they’re “where do I even go to get the number.” That’s the gap the Knowledge Assistant covers in this design.
It’s populated with organizational knowledge that usually lives scattered across wikis, onboarding docs, and tribal memory:
- FAQs and process documentation for Databricks and adjacent systems how workflows are triggered, what a given pipeline does, standard procedure for common requests.
- Access and schema guidance which workspace, catalog, or schema a user should be looking at for a given project, and how to request access to it.
- Project-level pointers where the documentation, design docs, or SharePoint artifacts for a particular initiative actually live.
In the ReAct loop, this is a distinct tool the agent reaches for when the intent summary suggests a “where/how” question rather than a “what’s the value” question “where do I get access to the supply chain schema” routes to the Knowledge Assistant, while “what were last month’s shipment delays” routes to a Genie space. The Atlan-grounded routing context helps make that distinction cleanly, since Atlan’s metadata already documents which catalog or schema owns which domain, reinforcing rather than duplicating what the Knowledge Assistant holds.
Atlan integration: two separate jobs
Once Atlan is wired in via an API key, it does two different things in this architecture, worth keeping distinct rather than treating “Atlan integration” as one monolithic capability.
1. Routing context, via Atlan AI Applications. Atlan’s AI Applications layer holds structured information about each Genie space — what domain it covers, which tables and schemas back it, what kind of questions it’s scoped to answer. This is the metadata the Atlan-grounded router queries against. Instead of the agent working from a static, hand-maintained list of “Genie space X handles supply chain, Genie space Y handles finance,” it asks Atlan which Genie space fits the current intent summary. As more Genie spaces get added, this stays current without touching the agent’s routing logic.
2. Direct answers, for metadata and lineage questions. Separately, Atlan is exposed as its own tool for a different class of question entirely — when a user isn’t trying to get data out of a Genie space, but wants to know about a table: where it lives, what it means, where it comes from, or what depends on it. For these, the agent calls Atlan directly to answer things like what a column means, where a table’s data originates, or what breaks downstream if a table changes — lineage and metadata questions that neither a Genie space nor the Knowledge Assistant is built to answer well.
So in the ReAct loop, Atlan wears two hats: a routing input the agent consults before dispatching to a Genie space, and a standalone tool the agent calls directly when the question itself is about metadata or lineage rather than the underlying data.
Token cost tracking with MLflow: what we can measure, and what we can’t
Databricks-managed MLflow tracing gives per-run visibility into the LLM calls that actually happen inside the agent loop. Specifically, cost and token usage are tracked across three points in a single run:
- The user question as it enters the agent.
- The intent summary generation step.
- The final answer generation step.
Since these are all calls to a model served through the Databricks AI Gateway, MLflow’s tracing captures prompt tokens, completion tokens, and the resulting cost for each of these spans automatically, and rolls them up into a per-run total the user can see.
The gap: Genie cost isn’t visible through MLflow. Genie runs as a managed Databricks service, and from the agent’s point of view it’s effectively free till January 2027 — there’s no LLM call MLflow can trace on that leg, so a Genie-heavy run’s true compute cost doesn’t show up in the MLflow number at all. If a fuller cost picture is the goal, that has to come from a separate source: Databricks’ system tables log Genie’s underlying warehouse and compute consumption, and that data can be joined in afterward alongside the traced MLflow numbers.
It’s worth being upfront about this rather than presenting the MLflow number as the full cost of a run it’s the full cost of the agent’s own reasoning, not of everything the agent triggers downstream.
Closing thoughts
None of this is a rejection of Databricks’ out-of-the-box supervisor pattern it’s a good default for a lot of teams. What we found useful was treating routing as a governance problem rather than a prompt-engineering problem: let Atlan’s already-maintained semantic catalog decide where a question goes, keep the agent itself as simple as a single ReAct loop, and be honest in the cost reporting about what MLflow can and can’t see.
If you’re running a similar setup — many Genie spaces, a Knowledge Assistant, and Atlan already in your stack — this pattern is worth trying before reaching for a full supervisor topology.
Have you tried something similar on your own Databricks + LangGraph stack? Would love to hear what routing approach worked for you.