Under the hood

How this site works

A plain-English look at how the assistant finds the right context, chooses an answer, checks its work, and handles failures.

Responds in real time

Answers appear as they are written, and the status reflects what the assistant is actually doing — not a fake typing animation.

Finds the right context

Searches for both exact words and related ideas, then combines the strongest matches before answering.

Human judgment first

I review test answers first, then compare the AI judges against those human decisions.

Built to fail safely

If context search is unavailable, the assistant uses the full resume instead, so visitors can still get an answer.

Architecture

One FastAPI service serves the static frontend and the API. Guardrails (rate limits, quotas, validation) run before any model call; retrieval and model routing run concurrently; the answer streams back over SSE.

Architecture diagram The browser connects to the application, which searches for exact words and related ideas while choosing the right Claude model, then streams the answer back. Browser vanilla JS · no framework FastAPI gateway guardrails · quotas · model router Jev (TypeSafe) picks the model Context search exact words + meaning combines best matches resume + project notes Claude Opus — complex Sonnet — simple fallback on failure Answer assembly grounded reply · sources · follow-ups SSE stream back tokens render live

Streaming & model routing

Every chat is a real server-sent-event stream: the browser shows retrieval status, then tokens as Claude produces them. What you see in the status steps is what actually happened — a cached answer says "answered from cache," it doesn't fake a search.

Routing is a typed decision, not a generated one. Each question goes to Jev, TypeSafe's System One model, with one multiple-choice question: can a capable general model answer this, or does it need to weigh several roles or projects against each other? Jev returns a probability for each answer in a single sub-second pass, with no text to parse. Sonnet is the default; Opus is used only when Jev is clearly convinced the question is complex, and a failed classification fails safe to the most capable model. Routing runs concurrently with retrieval, so it adds no time to first token.

event: status   {"stage": "rag_search", "state": "done", "sources": [...]}
event: status   {"stage": "routing", "state": "done", "model": "Sonnet", "router": "Jev"}
event: delta    {"text": "Dakota is PCAP-certified and uses Python in"}
event: done     {"reply": "...", "sources": [...], "followups": [...]}

Requirement map: judgments as data

A pasted job description gets two answers at once. Claude writes the fit analysis in prose. In parallel, Jev receives the résumé and every candidate line of the posting in one request and answers two typed questions per line: is this a requirement at all, and how well does the résumé document it, on four ordered levels from "not documented" to "strong".

Jev returns a probability for each level rather than a sentence, so the page can do the rest without another model call: mark your must-haves and the map regroups; sort by evidence and it re-orders. Re-weighting is arithmetic on judgments that already exist. "Not documented" means the résumé is silent, which is a question for Dakota, not a verdict.

{"type": "score", "score": 2.71, "confidence": 0.82,
 "probabilities": {"0": 0.02, "1": 0.05, "2": 0.13, "3": 0.80},
 "legend": {"0": "Not documented", "1": "Adjacent", "2": "Partial", "3": "Strong"}}

How it finds the right context

When someone asks a question, the assistant searches my resume and project notes in two ways: one looks for the exact words they used, and the other looks for ideas with a similar meaning. It combines the best matches and gives Claude the most relevant context before it answers.

If search is temporarily unavailable, the site uses the full resume instead, so visitors can still get an answer.

Human-judged LLM

I reviewed a 100-question test set by hand, using pass/fail labels and written critiques to identify recurring problems. I then built three focused LLM graders and compare their verdicts with the human labels. The graders are still being tuned, so human judgment remains the standard. This workflow is inspired by Hamel Husain's human-first evaluation approach.

Connect your own AI

The MCP endpoint exposes my resume as structured, AI-readable data, and it lets your agent put questions to my assistant directly. The flow is deliberately simple:

  1. Your MCP client connects to the remote server at /mcp (streamable HTTP, no authentication).
  2. It discovers three read-only tools: get_resume, ask, and analyze_fit.
  3. get_resume returns structured career data — experience, projects, skills, education, and certifications. Private contact details like my phone number are excluded.
  4. ask and analyze_fit run the same guarded pipeline as this page: the same rate limits, free-turn quota, and daily budgets, keyed to your connection's address because MCP clients hold no cookies. Pass the returned session_id back to continue a conversation.
  5. Your AI does the reasoning on top: fit comparisons across candidates, gap analysis, interview prep — with its own model, on your side of the connection.

get_resume keeps the data-only path, so any compatible MCP client that supports remote servers gets the same structured access and what its AI concludes is up to it. The resume is also machine-readable without MCP: /llms.txt is rendered live from the same source of truth.

To connect a compatible client, add a remote MCP server with this URL (streamable-HTTP transport, no authentication):

https://www.dakotaradigan.io/mcp

Claude (claude.ai or desktop): Settings → Connectors → Add custom connector, then paste the URL above.

Claude Code (terminal):

claude mcp add --transport http resume https://www.dakotaradigan.io/mcp

Then ask your assistant something like "Using the resume tool, is Dakota a fit for this role?" Other MCP-compatible clients that support remote servers work the same way.

Ask the assistant about any of this