❓ Stop Guessing the Questions
📅 Monday, Aug 31, 2026
⏰ 10:00 AM
In my last post, I argued that data is king . If that is true, why are we still building analytics as if we have to predict every question a customer will ever ask? For decades that was the job: gather requirements, decide what “usage” meant, pick the charts, ship the report, then open another ticket when someone asked a question we had not guessed. That was not bad engineering. We simply had to know the question before we could build software to answer it. That constraint is starting to disappear.
We Don’t Need to Predict Every Question Anymore
Imagine a SaaS product with years of usage, billing, customer, feature, session and performance data. Instead of building every possible report, a customer could ask which business units increased AI-assisted development usage over the last six months without a corresponding increase in pull requests reaching production. Nobody had to anticipate that exact question.
The more interesting cases are questions nobody would ever put on the roadmap.
Consider a building management system with years of elevator trips, stair-door events, badge swipes, occupancy data, access control and sensor readings. Then someone asks:
“We ran a Use the Stairs campaign. Are employees actually taking the stairs more often, and does it vary by building?”
Nobody built that product to answer a wellness campaign. The evidence may already exist. The hard part is knowing what it means. Does a stair-door opening represent an employee taking the stairs? Do cleaners count? Visitors? Freight elevators? Can badge events distinguish employees from contractors? Are the sensors equally reliable across buildings?
The application no longer has to encode every possible question. It has to encode the meaning of the data and the operations that are safe to perform against it. That is a different architecture.
DuckDB Is the Engine, Not the Meaning
This is one reason I keep coming back to DuckDB. I want an analytical engine that can sit next to the data, query formats such as Parquet directly, and push projections and filters into those reads . That is the compute. It is not the business. DuckDB knows SQL, columns, types, and the relationships you give it. It does not know what an active customer is, whether ARR includes trials, or whether a stair-door event counts as an employee choosing the stairs. That meaning still has to come from humans.
This is where a semantic layer, or more broadly a context layer, belongs. It is the place you define entities, metrics, dimensions, relationships, filters and rules. Wren is the open-source version of that idea I would start with: a Modeling Definition Language for business meaning, plus a Rust semantic engine that plans SQL through those definitions. I would not let an agent improvise against raw tables and call that governance.
Human expertise belongs there: not predicting every future question, but defining what the business actually means.
Don’t Just Hand the LLM a Database
The obvious shortcut is to give an LLM your schema and let it generate SQL. That makes a great demo. It is a much weaker production boundary. The model can choose the wrong join, misunderstand the grain, apply the wrong definition, or generate perfectly valid SQL that answers the wrong business question. There is also a security issue. DuckDB warns that untrusted SQL should be treated like untrusted Bash or Python , because it can reach files, extensions, networking and other system resources depending on configuration.
A Design Proposal
The architecture I want is a runtime harness, not a chat window bolted onto a warehouse.
User
↓
Runtime harness + agent
↓
Governed MCP tools
↓
Semantic / context layer (Wren MDL, Rust engine)
↓
DuckDB
↓
Data
↓
Answer + receipt
I have not had the chance yet to put this combination into practice. DuckDB, Wren, and MCP are real. The runtime harness, the governed tools, and the receipt are the architecture I am proposing. From building MCP servers, and from measuring what happens when business rules live in the model instead of in code , this is the path I would take.
DuckDB is the engine I would put under it. Wren is where I would put the meaning: an Apache-2.0 Modeling Definition Language and a Rust semantic engine that plans SQL through those definitions. MCP is the tool boundary. In this design, the agent never gets the database. It gets a small set of governed operations: list metrics, describe one, query it, compare periods, and so on. MCP does not make those tools safe by itself. Authorization, tenant isolation, validation and auditing still belong to the software that implements them.
Let the model decide which governed operation it needs. Let software enforce what that operation is allowed to do.
Natural language is the right interface for the first ask. It is the wrong interface for the tenth. Once the facilities lead has asked whether employees are taking the stairs more than the elevators, they will want the trend next month. That should not cost another agent loop, another pile of tokens, and another chance for the model to assemble the question slightly differently.
So the runtime produces a receipt.
Not a transcript. An executable artifact: Python plus the data it is allowed to see plus the MCP tools it is allowed to call. That receipt is the proof of work for the current answer, and it is all that is needed to ask the same question again.
"""
Receipt: stairs_vs_elevator_campaign
Question: Are employees taking the stairs more than the elevators
since the Use the Stairs campaign launched?
Re-run without an LLM: python stairs_vs_elevator_campaign.py
"""
from analytics_runtime import tools
CAMPAIGN_START = "2026-01-15"
EMPLOYEE_BADGE_TYPES = ["employee", "contractor"]
EXCLUDE_ELEVATORS = ["freight", "service"]
def stair_vs_elevator(as_of: str) -> dict:
buildings = tools.list_buildings(campaign="use_the_stairs")
stairs = tools.query_stair_entries(
buildings=buildings,
since=CAMPAIGN_START,
until=as_of,
badge_types=EMPLOYEE_BADGE_TYPES,
exclude_after_hours_service=True,
)
elevators = tools.query_elevator_trips(
buildings=buildings,
since=CAMPAIGN_START,
until=as_of,
badge_types=EMPLOYEE_BADGE_TYPES,
exclude=EXCLUDE_ELEVATORS,
)
return tools.explain_result(
question="employee stairs vs elevators by building",
stairs=stairs,
elevators=elevators,
baseline=tools.compare_periods(
metric="vertical_circulation",
current=(CAMPAIGN_START, as_of),
baseline="prior_equal_window",
dimensions=["building_id"],
),
)
if __name__ == "__main__":
print(stair_vs_elevator(as_of="today"))
The first run still uses the agent. It has to interpret the question, pick the tools, and apply the semantic definitions. What comes back is the answer and this receipt. A human can see which buildings were included, which sensors counted, which badge types counted as employees, and which elevators were excluded.
The output is all you keep:
Question → business interpretation → governed tools → verified result
↓
receipt
↓
run again, no LLM
Next month the facilities lead does not spend tokens to find out whether the campaign is still working. They run the receipt against newer data. The trend is software, not a second interpretation by a model. If “taking the stairs” needs to change, a human changes the receipt or the metric underneath it. The model does not quietly drift.
Once we know how to answer a question correctly, we should not require an LLM to invent the answer path again.
I Have Not Wired This Together Yet
I am not going to pretend MotherDuck or Wren already is this architecture. I have not wired the combination end to end. What I have seen is close enough that, from my experience, this is the right path.
MotherDuck
already exposes DuckDB-based analytics to agents through MCP: inspect catalogs, run SQL, isolated compute, and the SQL itself available for inspection. That is useful. It is not my receipt, and it is not a set of governed metric tools. The agent still gets SQL. What I would steal from it is the permission split: a read-oriented query tool is a different boundary than query_rw. That is the lesson. The protocol should make the dangerous operation a different tool, not a different prompt.
Wren is the closest open-source version of the semantic-plus-memory side I would use. The engine is Apache-2.0, written in Rust, sits on Apache DataFusion, and compiles modeled metrics and relationships into SQL. Successful questions can be kept as versioned examples and retrieved when a similar ask shows up later. That is still an agent pulling an example so it can try again. My proposal is stricter. Once we know the path, we should not need the model to invent it a second time. Discover a question with AI, then preserve what worked as software.
The Questions Become Data Too
Once customers ask in natural language, the questions themselves become data. If hundreds of customers start asking which teams have high AI usage but low acceptance of generated changes, you did not need six months of interviews to learn that the concept mattered. The same thing happens with the stairs example. If facilities teams keep asking about stair usage versus elevators, perhaps vertical circulation mix deserves to become a governed metric.
Wren’s MDL and memory layer are built for a version of that loop: modeled meaning in version-controlled definitions, plus stored examples the agent can retrieve. I would still want a human to decide which of those patterns become part of the governed understanding of the business. AI can surface how people are actually trying to use the data. It should not silently rewrite what the business means.