This is a submission for the Sanity Challenge, Path One: Ship an Agent That Queries Real Content
What I Built
Ask Richter turns a static software-engineering resume into an interactive, multilingual AI Career Agent.
Instead of treating my career history as a large block of text, I modeled it as structured, interconnected content covering:
- Professional experiences
- Projects
- Skills
- Education
- Achievements
- Architectural decisions
The application allows recruiters, engineering leaders, and developers to ask natural-language questions about my professional background in English, Portuguese, and Spanish.
The core design principle is:
Retrieve evidence first. Generate second.
The agent is designed to answer from retrieved evidence. When the required information is not present or cannot be verified, it abstains instead of inventing a fact.
For example, asking:
“Which projects use React?”
is handled differently from:
“What were the architectural challenges in building Ask Richter's RAG architecture?”
The first is a precise structured retrieval problem. The second is a semantic retrieval problem.
Relational questions can also use bounded knowledge-graph traversal across references between entities.
The result is a career agent where the underlying content model is part of the reasoning system, rather than simply a CMS sitting behind a chatbot.
Demo
Live application:
https://ask-richter-frontend.vercel.app/
The deployed application provides the conversational interface for exploring the structured career knowledge base.
Example queries:
Structured retrieval
Which projects use React?
The system identifies the technology relationship and retrieves the matching projects using deterministic structured retrieval.
Semantic retrieval
What were the architectural challenges in building Ask Richter's RAG architecture?
The system performs semantic retrieval over the architectural knowledge and generates an answer from the retrieved evidence.
Relational retrieval
Which technologies connect the Ask Richter project to Richter's professional experiences?
The system routes the query to the agentic retrieval path and attempts bounded traversal across the relationships modeled in Sanity.
Factual abstention
What was Richter's experience developing production systems in Rust?
When the required evidence is not available in the knowledge base, the system abstains rather than fabricating an experience.
Code
GitHub repository:
https://github.com/lfrichter/ask-richter
The repository contains the complete Turborepo monorepo, including:
- Next.js application
- Sanity Studio
- Sanity schemas and migrations
- Retrieval and orchestration code
- Knowledge graph traversal
- AI provider integration
- Automated tests
- Architecture Decision Records
- Submission and regression evidence
How I Used Sanity
Sanity is the canonical knowledge layer for Ask Richter.
Rather than storing a traditional resume as one document, I modeled the career domain as interconnected typed entities in the Sanity Content Lake.
The current model contains six main schemas:
skill · experience · project · education · achievement · architectureDecision
The captured dataset contains 101 documents and 121 references at the time of the submission snapshot.
These references are important because the agent can reason about relationships between entities instead of relying exclusively on text similarity.
For example:
Project
├── uses → Skills
├── belongs to → Experience
├── documents → Architecture Decisions
└── demonstrates → Achievements
Sanity MCP in the AI Engineering Workflow
The project also uses the official Sanity MCP server as part of the AI Engineering Agent (AGY) environment.
The MCP configuration points directly to the production Sanity project:
{
"mcpServers": {
"sanity": {
"command": "npx",
"args": [
"-y",
"@sanity/mcp-server@latest"
],
"env": {
"SANITY_PROJECT_ID": "q19ey6yx",
"SANITY_DATASET": "production"
}
}
}
}
This gives the engineering agent direct, schema-aware access to the same Sanity Content Lake used by the application.
During the final verification, the AGY used Sanity MCP tools including:
query_documentslist_workspace_schemasget_schemasemantic_search
For example, a live MCP query:
count(*[_type == "project"])
returned 18 projects from the production dataset.
The MCP schema inspection also identified the deployed Ask Richter workspace.
This was particularly useful during development because the agent could inspect the actual content model and production data instead of relying on assumptions about the CMS.
The Sanity MCP server is designed specifically to let AI assistants interact with Sanity projects, including schema exploration, GROQ queries, and semantic search.
Structured Retrieval — M2
For deterministic questions, Ask Richter uses GROQ-based structured retrieval.
For example:
Which projects use React?
does not require vector similarity search.
The system can query the structured relationship between projects and technologies directly.
This provides deterministic retrieval for questions involving:
- Technology filters
- Project catalogs
- Entity lists
- Cardinality
- Chronological information
- Explicit relationships
Semantic Retrieval — M3
Open-ended questions require a different strategy.
Ask Richter uses Sanity Dataset Embeddings with text::semanticSimilarity() to retrieve semantically relevant content.
This is useful for questions where the user does not necessarily use the exact terminology present in the source material.
For example:
What were the architectural challenges in building Ask Richter's RAG architecture?
requires finding related architectural evidence rather than simply matching a specific project or technology field.
The retrieved evidence is then passed to the language model for grounded response generation.
Agentic Knowledge Graph Retrieval — M4
Some questions are relational rather than purely semantic.
For those cases, Ask Richter uses a bounded agentic retrieval layer built around the references in the structured content model.
The retrieval process has explicit execution limits:
- Maximum 2 hops
- Maximum 4 tool calls
- 4-second global timeout
- Cycle detection
- Read-only retrieval
- No content mutation
The purpose of these limits is to prevent an open-ended agent loop while keeping relational questions useful.
The current implementation verifies first-order traversal scenarios such as project → skill relationships. More complex reverse multi-hop traversal remains intentionally bounded by the current execution budget.
Resilient Fallback
The application also maintains a pure TypeScript TSVectorStore as a deterministic fallback.
This replaced an earlier native FAISS dependency and helped move the application toward a fully serverless architecture.
The fallback uses in-memory vector calculations over the same 768-dimensional embedding space and is designed to preserve safe abstention when the primary retrieval path is unavailable.
Grounding and Abstention
Retrieval is separated from generation.
The application first determines the appropriate retrieval strategy, obtains evidence, and only then asks the language model to formulate the response.
If the retrieval layer cannot provide sufficient evidence, the agent does not attempt to fill the gap with general model knowledge.
This distinction is particularly important for a career agent.
A fabricated professional experience may sound plausible while still being completely false.
For Ask Richter, “I don't have enough evidence to answer that” is a valid system outcome.
Sanity Project Details
Sanity Project ID: q19ey6yx
Dataset: production
The Sanity Studio lives in:
apps/studio
The content model currently contains:
skill
experience
project
education
achievement
architectureDecision
The Sanity Content Lake is the canonical source for the career knowledge represented by the application.
The project uses structured references to preserve relationships between the entities rather than flattening everything into independent text documents.
The production snapshot used for this submission contains:
- 101 documents
- 121 references
- 18 projects
These values were verified against the production dataset during final validation.
Agent Session
The Ask Richter engineering workflow was developed and verified with an AI Engineering Agent (AGY) connected to Sanity through the official MCP server.
The session demonstrates how the agent can:
- Inspect the Sanity schema
- Query real production content
- Inspect document relationships
- Validate the structured content model
- Verify retrieval behavior
- Run regression checks against the resulting application
The MCP connection used the Sanity project:
q19ey6yx
and dataset:
production
A curated public Agent Session can be embedded here after publication through DEV's Agent Sessions interface.
Before publication, the transcript should be reviewed to ensure that no API keys, tokens, credentials, local paths containing sensitive information, or other private data are exposed.
Technical Architecture
At a high level, the system looks like this:
┌──────────────────────┐
│ User │
│ EN / PT / ES │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Next.js 15 App │
│ /api/chat │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Intent Classifier │
│ & Router │
└───────┬──┬──┬───────┘
│ │ │
┌───────────┘ │ └────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌──────────────┐
│ M2 │ │ M4 │ │ M3 │
│ Structured │ │ Agentic │ │ Semantic │
│ GROQ │ │ KG │ │ Embeddings │
└─────┬──────┘ └─────┬──────┘ └──────┬───────┘
│ │ │
└──────────────┼───────────────┘
▼
┌──────────────────────┐
│ Sanity Content Lake │
│ Structured Knowledge │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Grounded Context │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Gemini 3.6 Flash │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Factual Answer or │
│ Safe Abstention │
└──────────────────────┘
There is also a separate engineering-time MCP path:
AI Engineering Agent (AGY)
│
▼
Sanity MCP Server
@sanity/mcp-server
│
▼
Sanity Content Lake
q19ey6yx / production
This separation is intentional.
The MCP integration gives the engineering agent direct access to the real Sanity environment for schema inspection, querying, and validation.
The production application independently uses Sanity's APIs and retrieval capabilities to serve end-user questions.
Tech Stack
Knowledge Layer
- Sanity Studio v3.78
- Sanity Content Lake
- GROQ
- Sanity Dataset Embeddings
- Structured document references
- Official Sanity MCP server
Application
- Next.js 15
- React 19
- TypeScript
- Turborepo
- Vercel Serverless
AI
- Google GenAI API
gemini-3.6-flashgemini-embedding-2- 768-dimensional embeddings
Supporting Infrastructure
- Supabase
- Server-side resilience mechanisms
- TypeScript in-memory vector fallback
- GitHub Actions
- AI Engineering Agent (AGY)
Engineering Quality
- TDD-oriented development
- Intent-routing regression tests
- Jidoka validation pipeline
- Automated integration tests
- End-to-end HTTP regression scenarios
- Architecture Decision Records
Verification
The final release candidate was validated through multiple independent gates.
Automated Tests
170 / 170 tests passing
- 151 frontend tests
- 19 backend tests
HTTP Regression
17 / 17 end-to-end HTTP scenarios passing
All verified scenarios returned successful HTTP responses.
Jidoka Validation
The full Jidoka pipeline completed successfully across the monorepo:
- TypeScript type checking
- Linting
- Production builds
- Test execution
Repository Integrity
git diff --check completed with zero errors.
The release candidate was committed and deployed to Vercel.
What I Learned
The most important lesson was that a structured content model changes what an AI agent can reliably do.
A conventional RAG pipeline often starts with:
“How do I retrieve the most similar text?”
This project led me toward a different question:
“What kind of information is the user actually asking for?”
Sometimes the answer is a structured query.
Sometimes it is semantic retrieval.
Sometimes it is a relationship traversal.
Trying to force all three problems through a single retrieval mechanism creates unnecessary ambiguity.
Sanity made it possible to represent the career domain as structured content first, and then build retrieval strategies around that model.
That changed the role of the CMS.
It became part of the reasoning architecture.
Final Thought
Ask Richter started as an experiment to make my resume conversational.
It evolved into a structured knowledge system where an AI agent has to remain accountable to the underlying evidence.
The goal was not to make an LLM sound confident about my career.
The goal was to make the agent answer from what the knowledge base can actually support.
Retrieve evidence first. Generate second. Abstain when the evidence is not there.
Top comments (0)