Grounding Humans and AI in Complex Data
You do not have to outrun the bear. You have to harness it.
AI assistants become smarter every quarter, and many humans and many businesses are quietly asking the same question: how do we operate in this new, scary world? I think the honest starting point is the old camping joke. Two friends meet a bear in the woods. One of them starts lacing up his running shoes. “Are you crazy? You cannot outrun a bear!” “I do not have to outrun the bear. I only have to outrun you.”
That is where we actually are. Humans and businesses do not have to outrun the AI. That race was lost before it started, and it was never the race. They have to outrun other humans and other businesses. And the way to do that is not to run faster. It is to harness the bear: to ground it in your particular knowledge, your terms, your systems, your rules, so that humans and AI speak the same language and can work together instead of past each other. The one who harnesses the bear does not have to run at all.
This story is about what the harness is made of, and how I am braiding one in the open.
The language nobody wrote down
Almost a quarter century ago Eric Evans wrote down what a shared language buys a team. He called it the Ubiquitous Language, and its documented form, the one other teams can translate into and out of, the Published Language. Domain-Driven Design became one of the most cited books in software engineering, and the language part remained the least practiced part of it. Not because anyone disagreed. Because it was hard: the vocabulary was tribal knowledge, and tribal knowledge tolerates not being written down. Patient humans absorbed it at the coffee machine and re-derived what the documents never said.
It is still hard, but two things have changed. It is easier, because elicitation can now be AI-assisted: an agent drafts, a human corrects, and correction is far cheaper than authoring. And it is far more important, because a new kind of coworker has arrived that cannot absorb tribal knowledge at the coffee machine. An AI assistant sees only the shadows of things on the cave wall: meeting notes, wiki pages, a deck from 2021. Plato’s prisoners at least knew they were looking at shadows. An assistant confidently reasons from them as if they were the things themselves.
And here is the uncomfortable part: often there is nothing behind the shadows to go look at. Humans frequently cannot formulate their vocabulary even for themselves. When they can, they do not agree. A corporate party trick: if you want to watch several architects debate for an hour, ask them what a “channel” is.
The pattern I encoundered more than once is the controlled vocabulary in an agent’s system prompt. Measures, dimensions, permitted values, indented into groups. A flat list, usually written by whoever was closest to the prompt file, who is usually the first to admit the terminology is not theirs. That list is a type system with the types erased: the categories are classes, the terms are instances, the dimension values are enumerations, and all of that structure is expressed as indentation and reconstructed by a language model on every single call. It asserts that a term exists and cannot say what it means. Try it at home: pick the third measure on such a list and ask three people what it means. In my experience the first question produces three answers, and the follow-up question, who owns the definition, produces silence, then a suggestion that some other team may know. Even if that team knows, what they know is not going to find its way into a string literal.
Give the vocabulary a model instead, and one source serves both audiences: a generated glossary with definitions, owners and history for humans, and a lookup tool for the agent, so the prompt says how to find a term rather than enumerating everything that exists. The gain that matters most is not tokens. It is that a model gives disagreement somewhere to live: a term with two competing owned definitions is a visible problem, while the same term in a flat list is a word everybody assumes they share. And the by-product is the thing Evans asked for in 2003. You set out to ground the AI, and the humans end up with a shared language too. The bear turns out to be the reason the vocabulary finally gets written down.
Moving up a level, again
To harness the AI, humans have to start thinking differently, and we have been here before. Punch cards gave way to terminals. Assembly, with its jumps and labels, gave way to third-generation languages, and those gave way to objects. At every step the machines got better at the low-level details, and humans had to move one level of abstraction up. Not everyone made each jump. I have read accounts of procedural developers who never became comfortable with objects, and I once worked with a solid engineer who never understood double pointers. No shame in either: every abstraction leaves somebody behind, which is exactly why it is worth saying out loud that another jump is upon us.
This time the machines are taking over the middle: syntax, boilerplate, translation between formats, first drafts of everything. What is left for humans is the top: deciding what things are, what words mean, who owns which fact, and what good looks like. That is not prompt engineering. That is knowledge engineering, and the organizations that treat it as a first-class discipline will be the ones holding the reins while everyone else is still running.
The same data, many interaction surfaces
So much for the why. Now the what: humans and agents operating on the same data, each through the door that fits them.
For humans: generated sites today, a reflective viewer next. Today every model in the Nasdanika model tower generates its own documentation site: one page per element, with diagrams. The next step is a reflective React viewer operating on strongly typed objects with type information available at runtime, generated from the type system model. No hand-written UI per model: the viewer reads the types and renders accordingly. Modules can be bundled, or published to npm for releases and cross-referencing, the way Java artifacts are published to Maven Central.
Query languages, for humans and agents alike. XPath and Cypher. I ran XPath over Ecore models a decade ago, so the pattern is proven. For humans, a query application will be part of the reflective viewer, and contextual: stand on an element and ask from there. Results are rendered through a “view” relationship, resolved reflectively: show as Markdown, as a table, as a UI panel, depending on what the result is and who is looking.
The engines run on a reactive graph provider, and the graph may be live. A query does not require a database, and it does not even require a model file. A provider can produce the graph as it is traversed: a live query over GitHub or GitLab, over a running JVM, even over a fleet of JVMs. The graph does not exist until somebody asks, and then only the part they asked about.
Agents get semantic contexts, not the whole world. An agent should not pay for the entire estate in its context window, and it should not see what it has no business seeing. So an agent gets a bounded view, at the data level and at the type level. Types are flattened: inherited features are inlined and the supertype chain is cut at a named class, so nothing has to walk a hierarchy inside a context window. And the boundary is honest: everything beyond it is gathered into a single point at infinity, the way the extended complex plane closes the ordinary one, so “outside the scope” is a place the agent can name rather than a ragged edge it hallucinates past.
Concepts generalize, and models bridge. Worked examples began inside the tool model and became their own model when they outgrew the host: the same example is a few-shot prompt for an agent, a conformance test for a tool implementation, a mock for running an agentic loop offline in CI, and a drift detector when a schema changes underneath it. Tools themselves are modeled independently of who calls them and how, because an MCP server, a CLI and an agent were about to describe the same concept three times. And models bridge: the type system bridges to Ecore so that every existing modeling tool keeps working, and the CLI bridges to tools, so a command line becomes a callable surface an agent can be handed, with bindings that fix what the agent must not control.
The first wave
The models to be available first, and what each brings to the table:
| Model | What it brings |
|---|---|
| nxcore | The root: identity, markers for provenance, documentation as structure, evaluators for behavior in the model, occupancy, stakeholder and concern, kind and kinded |
| meta | A language-neutral type system: about twenty classifiers, dynamic objects that can be classified later, harvesting from SQL, loading from Excel, interoperability with Ecore and TypeScript |
| example | Worked examples: few-shot prompts, conformance suites, offline mocks, and loud breakage when schemas change |
| tool | What can be called, independent of who calls it and how: a conceptual tool with a few-shot implementation drawing on examples |
| inference | Providers, models, options and cost as a catalog, and sessions saved as models for inspection in the reflective UI |
| cli | The resolved command tree of a running CLI, documented and bridged to tools |
| expression | A closed pushdown algebra: deliberately a model with no language of its own |
| xpath | The default evaluator language, replacing SpEL: simplicity and stability |
| cypher | The query language for graphs: borrow the language, not the database |
| typescript | Generation of TypeScript sources from the type system and other models |
| react | JSX and components as a transformation target, on the way to the reflective viewer |
| jvm | Loading from bytecode and from a live JVM |
| maven | Build coordinates joining models to the artifacts they describe |
Then the tower as needed, pulled by use rather than pushed by ambition. The first floors above this wave are role, because elicited knowledge needs ownership, and lifecycle, because it needs stages. An elicited statement without an owner, a date and a lifecycle stage is a wiki page with better syntax.
Embeddings without the vector database
Because “find by meaning” is part of grounding, embeddings are part of the plan, and the arithmetic is worth stating before the industry reflex kicks in. How many vectors does an estate actually have? Thousands. Low millions at the very best. A 1536-dimensional embedding at float32 is about 6 KiB, so a gibibyte of memory holds on the order of 170 thousand raw vectors. That is two to three orders of magnitude below the point where a vector database begins to earn its keep, and reaching for one is the same category error as standing up a cluster to run one process.
So, no vector database:
- Embeddings computed with an inexpensive model such as text-embedding-3-small, and the model identity recorded, because a similarity result is only citable if the measurement names its instrument.
- Vectors keyed by the SHA-512 digest of the normalized text plus the model profile, its name and configuration: the same text embedded with the same instrument is the same vector, and nothing is recomputed without a reason.
- Published as resource jars to Maven and packages to npm, alongside the models they index.
- Incremental and federated: one package may reference other packages, for example the previous version of itself, so an index grows by diffs rather than by rebuilds.
- Served by an in-memory index such as hnswlib or multi-vector-hnsw, or by brute-force search, which below a few tens of thousands of vectors might yield better results than an index.
What this buys is the retrieval that neither pure vectors nor pure structure can deliver alone: find by meaning, then walk the structure, filter by type, and arrive at an answer that says who owns it.
The road
Is this ambitious for one person? Yes. It is also more achievable than it looks, for a reason that is itself the point of this story: the work is grounded. AI assistants working on Nasdanika are grounded in the Nasdanika body of knowledge, the stories, the design notes, the model documentation, and code generators do the mechanical part. The initial value arrives early: a model pays for itself the day its documentation site is published, pays again when the first query runs, and pays a third time when an agent grounds in it. Viam supervadet vadens - the path will be overcome by the person walking it.
Federation is the load-bearing part of the how. Sources live in Git, on GitHub and GitLab. Artifacts live in Maven repositories and npm registries. URI handlers load federated models transparently, so a model references another model the way a class references a class in another jar, and the holistic picture is assembled rather than centralized. Models are published alongside the artifacts they describe, so the description and the described cannot drift apart. And the release model, together with the JVM and Maven models and CLI tools built on them, manages the releases of the very repositories all of this lives in.
This story is part of the harness
One more thing, and it is the reason this story exists at all. This text is itself grounding. It is a digital twin of intent: for my future self, who will need to remember why the pieces have the shapes they have; for machines, the AI assistants that will help build the next module and the search engines that will route the next reader here; and for other humans, who may recognize their own organization somewhere above. The stories and the model documentation work the same way: much of it is written before the models are usable in code, and it has a purpose even if no single human ever reads it, because it is the vocabulary the next conversation starts from, whether that conversation is with a person or with an agent.
I am not trying to outrun anybody. I am busy braiding the harness.
Nasdanika