AI Engineer World's Fair 2026

Beyond RAG: Build a Relational Context Engine from Scratch — Peter Werry, Unblocked

Read the talk

Beyond RAG: Build a Relational Context Engine from Scratch

Peter Werry builds a natural-language query engine over GitHub pull requests, showing how schema discovery, identity resolution, procedural validation and retries turn plausible queries into useful retrieval—and where semantic search still belongs.

From a talk by Peter Werry

At a glance

Ideas worth remembering

  • Semantic relevance does not enforce authorship, state or time. Let the model write a structured query when those conditions determine which records answer the question.

  • Query planning needs the database schema, resolved identities and a current time reference; natural language alone leaves crucial values unspecified.

  • Validate generated queries procedurally with permitted operators. Keep internal tenant constraints under application control rather than accepting model-supplied metadata.

  • Return validation and execution errors for bounded retries, but distinguish a bad plan from a missing capability: the authentication example needed full-text search, not merely another exact-match query.

  • Dynamic schema sampling supports exploration, but 300 documents is an empirical choice that can miss rare structures. Controlled production schemas can instead be maintained with code and CI.

A relevant pull request can still be three years too old

Engineering context lives across pull requests, review comments, tickets, wikis and chat. Those records contain meaning, but they also contain authors, dates, states, labels and relationships. Peter Werry, who builds context engines at Unblocked, uses that distinction to frame the workshop: retrieve the discussion that sounds relevant, and you may still miss the records the question actually asks for.

Here, “RAG” has a deliberately narrow meaning: semantic similarity search or vector retrieval. Embed the question, find documents with similar meaning, rerank them and put the selected content into a model’s prompt. That works well as a starting point for finding code, a PR that changed it or a Slack thread that debated it. An agent can then inspect files and follow leads through GitHub.

“PRs I merged last week” exposes the missing mechanism. Nearest-vector search can find text about merging or about last week without enforcing who performed the action, whether the PR was merged or when that happened. A semantically convincing result could have been merged three years ago. Reranking for relevance does not by itself enforce those structural and temporal conditions.

The other half of retrieval is a database query that expresses those conditions. The model’s job changes: it writes the query that accesses the records. The database supplies the matching data. This separation is the foundation for the six stacked pull requests in the workshop, which progressively replace a stubbed engine with a working one.

0:123:02
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

0:12 · section reference included

The query needs more than natural language

A request such as “Show me Peter’s open PRs” looks simple until it reaches the database. Peter may not be the value stored in the author field. A generated query may also use an unsafe operator, or fail even when its intent is sensible. Four supporting mechanisms make query generation useful:

  • Schema awareness. Give the model the fields and types it can query.
  • Identity resolution. Map a person’s name to the identifier stored in the records.
  • Validation. Treat the generated query as untrusted output and restrict what it can execute.
  • Retries. Return validation and execution errors to the model so it can revise its plan.

The starting application already has a query playground and a chat tab, but the backing query engine is a stub. Ingestion loads repository content first; the workshop then builds around the existing entry point. Keeping the interface in place makes the changes observable: the playground begins unable to answer anything and becomes usable when query planning is connected in step three.

6:457:15
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

6:45 · section reference included

Reconstruct a schema from 300 documents

The ingestion script knows enough about its records to download and store them. A separate query service may not have that knowledge. Unlike reading table definitions from a relational database, this MongoDB example reconstructs a description from the documents themselves.

Schema discovery samples 300 documents and merges their nodes into one tree. Null values are skipped because they do not constrain the type of a field. Procedural rules infer types and recognize enumeration patterns. An LLM could perform extraction, but its variable output would still need structural checks; this implementation puts the extraction itself in ordinary code.

What information will the planner actually receive? The discovery run produces a Markdown description of the GitHub pull-request schema and also creates some indexes. Markdown is an intentional output format: the resulting field description becomes part of the prompt used to synthesize queries. Discovery therefore turns sampled document structure into an input the model can consult, rather than asking it to guess the database’s shape.

The slide pairs an enum inference rule with discovered pull-request fields, showing how values such as state and review status become explicit schema information for the planner.Open full source frame
The slide pairs an enum inference rule with discovered pull-request fields, showing how values such as state and review status become explicit schema information for the planner.
state: closed|open · draft: boolean · reviews: [{state: APPROVED|COMMENTED}]
indexes: (user.login) (state) (created_at) (text)
9:5810:27
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

9:58 · section reference included

Peter is a name; pwerry is the stored identity

Knowing that an author field exists does not tell the model which value belongs there. A literal match for Peter can return nothing when GitHub stores a login instead. Identity resolution supplies that missing connection between the person named in the request and the identifier used by the data.

The workshop uses simple fuzzy matching; Werry names Levenshtein distance as a suitable technique. This approach couples the resolver to some knowledge of the schema. Dynamically discovering the mappings with an LLM is another possibility, but the example chooses a smaller procedural solution. The resolver alone does not yet make the playground work: it must be connected to the planning step that follows.

12:4013:11
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

12:40 · section reference included

Give the planner a schema, a resolver and a clock

Query planning is the LLM portion of the engine. The prompt receives the schema, and a required tool call performs name extraction as part of producing the plan. Temporal requests need another input: the current date and time. Without that reference, a phrase such as “last week” lacks the anchor needed to generate a useful date condition.

How does a human request become a database condition? With “Open PRs by Peter,” the connected engine generates a match clause and resolves Peter to pwerry, Werry’s GitHub login. The important change is the value used for matching: the request can use a familiar name while the query uses the identity present in the records.

The generated pipeline matches `user.login` to `pwerry` and filters for open pull requests; the result pane shows the returned PR.Open full source frame
The generated pipeline matches `user.login` to `pwerry` and filters for open pull requests; the result pane shows the returned PR.

json

[
  {
    "$match": {
      "user.login": "pwerry",
      "state": "open"
    }
  },
  {
    "$sort": {
      "created_at": -1
    }
  },
  {
    "$limit": 50
  }
]
14:3615:06
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

14:36 · section reference included

A generated query is still untrusted code

The first working query is a milestone, not permission to execute whatever the model produces. MongoDB’s where or function clauses can run JavaScript on the server. Passing generated queries through unchecked would make the model’s output an execution path for unwanted behavior. Internal ID or metadata fields create a separate risk: the query can expose information the model should not control.

Validation uses “deny by default.” An allowlist defines the permitted operators, so an unfamiliar operator does not become executable merely because nobody remembered to prohibit it. Fields can also be allowlisted, although this dynamically discovered schema example does not implement a general field allowlist. Known internal metadata still needs explicit handling.

Tenant isolation makes the distinction concrete. Werry recommends separate collections for separate tenants. If a collection does contain multiple tenants, hiding its tenant-ID metadata from the model is only part of the job. Application code must also remove any model-supplied version of that metadata and inject the application’s own tenant constraint. The trusted tenant selection comes from code outside the model, and this sanitization should be procedural.

The next demonstration searches for authentication in a PR title. Regular expressions are disallowed, so the validator rejects the generated query. At this stage the error is simply returned. The engine has successfully enforced a restriction, but it still needs a way to recover from the rejection.

16:2216:52
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

16:22 · section reference included

The chat agent reasons over the same retrieval engine

The final application step moves to the chat tab and asks, “Did we work on anything authentication related last week?” The agent generates a query, retrieves the data and reasons over it. The query playground and chat agent share the same backing engine; the chat interface adds reasoning over the retrieved records.

The resulting engine supports filtering, aggregations, time-scoped queries and full-text search, with validation and error-driven retries. Werry singles out the error loop as especially important, but also recommends strengthening the validation harness before internal deployment. The workshop establishes an end-to-end implementation; it does not make the sample validator a complete deployment safeguard.

22:3623:10
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

22:36 · section reference included

Scale schema selection separately from ingestion

The first audience question widens the example from one pull-request collection to many collections and tables. Putting every schema into every prompt would enlarge the planner’s context. The proposed extra LLM step works with small collection summaries and descriptions, selects the relevant collection, and loads the corresponding schema for the query.

Keeping the records current is a different problem. Unblocked continuously ingests changes: a PR event triggers a webhook, the content is ingested and re-embedded, and the result is stored in the database. Schema selection reduces what the planner must read; the ingestion pipeline keeps its searchable data current. The answer describes that architecture without quantifying processing latency or cost.

Choosing sources depends on the questions people want answered. Two sources carry especially useful decision history:

  • Slack conversations. Werry prioritizes Slack for “why” questions. A recollection such as a conversation with a particular person roughly three weeks ago already contains participant and time constraints that structured retrieval can use.
  • PR comments. Review discussions hold organizational history that is otherwise easy to lose. Unblocked processes past PR comments into “best practice memories,” distilling useful lessons before serving them as context.
25:4926:08
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

25:44 · section reference included

The ending sharpens the limits: sampling, tool calls and schema change

Why sample 300 documents? The number came from empirical trial and error, including customer reports that retrieval had missed something they expected to find. It is not a completeness guarantee. Document variety affects the sample size a use case needs, and rare shapes can still be missed. Unblocked uses many static schemas to prioritize correctness; dynamic discovery is useful for exploring unfamiliar data and connecting arbitrary databases.

The discussion also clarifies what the required tool call does: schema discovery happens at startup; the enforced call is for name extraction. Two enforcement techniques are described:

  • Validate the query tool’s username argument. Make the username an input to the query tool and reject an incorrect or malformed value there.
  • Monitor the extraction call. Watch the agent’s actions. If it skips the required extraction tool, insert a message telling it to make that call before continuing.

Finally, schema change requires an explicit maintenance path. An engine discovering an unknown schema can rerun its sampling process periodically as documents arrive. Unblocked’s controlled, static schemas instead change with the codebase: upstream changes are adopted in code and deployed through CI. These choices leave a practical decision for an implementation: use discovery to learn an unfamiliar database, or maintain a schema deliberately when the application controls its shape.

33:4433:48
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

33:44 · section reference included

Transcript download
Read the transcript and explore slides

Loading transcript and slides…