Inspiration

I hate CVs.

Not because they are useless, but because they pretend that a person can be compressed into two pages.

A CV remembers where you worked and which technologies you used. It does not remember the difficult decision you made, the feature that failed, the problem you solved under pressure, or what you learned when something went completely wrong.

Then every job application asks you to reconstruct those moments from memory.

I felt this problem personally. My experience can support applications for crypto investigation, Solidity development, product ownership, and now AI roles. The facts about my work do not change, but the experience that matters most—and how it should be explained—changes for every role.

The same blockchain project might demonstrate architecture for an engineering role, prioritization for a product role, or evidence analysis for an investigation role.

The facts do not change. The useful story does.

I believe hiring in the AI era will become much deeper, and probably a little Black Mirror. Instead of presenting only a static document, candidates may carry an evolving digital memory of how they work, think, decide, fail, learn, and improve.

DeepCandidate is my first experiment in that direction.

Not another generic CV generator. A memory of your professional life that retrieves the right truth at the right moment.

Same experience. Different story. Right context.

What it does

DeepCandidate is a local-first, open-source AI career memory and job application agent that becomes more useful with every application.

A candidate can upload PDF or DOCX CVs, paste project notes, add career stories, or answer focused questions about their experience. DeepCandidate uses GPT-5.6 to turn this material into structured, source-grounded proposed evidence, including:

  • Projects and responsibilities
  • Challenges and constraints
  • Decisions and actions
  • Results and measurable outcomes
  • Skills and technologies
  • Lessons learned
  • Audits, hackathons, public work, and proof links

Every extracted fact keeps the candidate's original source quote. Nothing becomes approved career memory silently.

When preparing an application, the candidate adds the job description as role context and enters one or more application questions separately. DeepCandidate uses semantic retrieval to find the experiences that best demonstrate what that role needs.

If relevant evidence is still proposed, the application pauses and asks the candidate to approve or reject only those specific facts. Generation then uses approved evidence only.

DeepCandidate can:

  • Prepare grounded, role-specific application answers
  • Select the strongest story for each role and question
  • Generate an editable, ATS-readable CV tailored to the role
  • Show the evidence supporting each response and CV section
  • Preserve links to audits, repositories, projects, and other public proof
  • Detect conflicting information instead of silently choosing one version
  • Refuse requests that would require fabricated experience
  • Ask one focused follow-up question when an important detail is missing

The follow-up flow is important. When career memory is not deep enough, DeepCandidate does not invent a better candidate. It asks what really happened. The candidate's answer becomes proposed evidence, is reviewed in context, and can then improve future applications.

This makes every application useful twice: it creates an output for the current role and can leave the candidate's career memory richer for the next one.

How I built it

DeepCandidate is a local-first Next.js, React, and TypeScript application styled with Tailwind CSS. Career sources, candidate-approved evidence, profile details, application sessions, and application-answer drafts are stored in a local SQLite database using Drizzle ORM and @libsql/client.

The AI workflow has two separate responsibilities.

First, GPT-5.6 uses the OpenAI Responses API to extract strict structured evidence from candidate-provided material. It also generates grounded application answers and structured CV content. Every model response is validated through strict Zod schemas before the application can use it. Unknown fields and invalid outputs fail closed rather than being silently accepted.

Second, text-embedding-3-small creates a semantic search index from the structured evidence. The original structured evidence remains the permanent memory; embeddings are only a derived retrieval index. When a candidate adds a role and question, DeepCandidate embeds that context and computes cosine similarity locally against compatible evidence vectors. The MVP does not require a vector database.

The generation model receives only the most relevant approved evidence. It must return the exact evidence IDs supporting its answer. Application code also checks for unsupported metrics, incompatible evidence, sensitive facts, and missing attribution.

All OpenAI calls happen server-side. API credentials never enter the browser, generation requests disable model-response storage, and only the context needed for the current action is sent to the provider. Uploaded document binaries are parsed in memory and discarded; confirmed career data remains in local SQLite.

PDF and DOCX ingestion use unpdf and Mammoth. The parsing flow preserves document hyperlinks so links to audits, projects, repositories, and other proof are not lost during extraction.

Prompts are versioned in the repository. Generation and embedding sit behind small provider interfaces, so a local provider such as Ollama can be added later without rewriting the domain workflows. I deliberately avoided LangChain, multi-agent frameworks, authentication, and a vector database to keep the product understandable and focused on one complete loop.

I built the AI evaluation system alongside the product. Automated tests use fake providers and never spend API credits. The current suite contains 114 passing tests, including a 14-case offline AI-quality evaluation covering retrieval, grounding, attribution, clarification, conflicts, refusals, answer limits, extraction, and forbidden claims. A separate bounded live OpenAI evaluation passed 13/13 synthetic cases.

The repository also contains a fictional multi-path candidate demo. It asks the same behavioral question for a crypto investigator, Solidity developer, and product owner role. The expected result is a different truthful story for each role while the underlying evidence remains unchanged.

Challenges I ran into

The hardest challenge was designing memory that remains useful without allowing the model to silently rewrite the candidate's history.

My first instinct was to make candidates review every fact immediately after importing a CV. That was safe, but it created an exhausting onboarding queue with no context. I changed the flow so normal proposed evidence is embedded for discovery immediately, but the candidate reviews it only when it becomes relevant to an application—or earlier if they choose to visit the Evidence page.

Retrieval was another major challenge. The same candidate can have several stories about leadership, problem-solving, or difficult decisions, but the strongest story depends on the role. I created a reproducible synthetic profile and role-sensitive evaluations to verify that changing only the job context changes the retrieved story.

Strict grounding also created difficult edge cases. I had to distinguish between an ordinary employer question for which evidence is missing and a request that explicitly asks the model to fabricate ownership or results. The first should produce a focused clarification; the second should produce a refusal. A live evaluation exposed an error at exactly this boundary, and I updated the prompt and regression suite to fix it.

I also handled duplicate claims across several CVs, conflicting dates, sensitive evidence, unsupported metrics, stale embeddings after edits, and model or provider failures that initially appeared to users only as generic errors.

Document ingestion had its own surprises. Normal text extraction can silently lose hyperlink destinations, which is especially damaging when a CV links to audits, repositories, hackathons, or shipped work. I updated the parsers and extraction rules to preserve those links and treat them as first-class proof.

Finally, reducing user friction required almost as much work as the AI itself. I moved from large review queues and dense cards to searchable, filtered, paginated source and evidence libraries. I separated job context from application questions, added batch actions, reused confirmed candidate details across CVs, and made the application workspace focus on one active question at a time.

Accomplishments that I'm proud of

I am proud that DeepCandidate is now a coherent product loop rather than a disconnected collection of AI demos.

A candidate can import real career material, extract structured proposed evidence, retrieve the right experience for a role, approve only the relevant facts, generate a grounded answer, inspect its supporting evidence, and create a tailored CV from the same memory.

I am especially proud of the parts that make the system trustworthy:

  • Every extracted fact remains traceable to the candidate's wording
  • Proposed evidence cannot support generation until the candidate approves it
  • Every generated response identifies the evidence it used
  • Missing facts produce clarification instead of invention
  • Unsupported metrics and ownership claims are rejected
  • Candidate edits to an application draft do not silently become permanent memory
  • Local career data can be inspected, edited, deleted individually, deleted in batches, or cleared entirely

The role-sensitive synthetic demo captures the core idea clearly: the same candidate answers the same question for three different roles, and semantic retrieval selects three different truthful experiences.

I am also proud that I treated evaluation as part of the product. The automated quality suite measures observable behavior rather than subjective confidence: retrieval rank, expected outcome, schema validity, evidence attribution, clarification quality, answer limits, and forbidden claims. It also gave me a concrete regression from a live model run and proved that the evaluation process could improve the system.

Finally, DeepCandidate is designed to remain open and replaceable. It currently uses OpenAI for the hackathon runtime, but the domain layer does not depend directly on the OpenAI SDK. Career memory remains usable even when the generation or embedding provider changes.

What I learned

The biggest lesson was that an AI memory product should not treat chat history as memory.

Useful memory needs structure, source attribution, lifecycle state, deletion, and explicit rules about when information becomes reusable. The model can propose an interpretation, but the candidate must control what becomes permanent evidence.

I also learned that retrieval quality can matter more than generation quality. Even a strong model cannot create a genuinely personal answer if it receives the wrong experience. Selecting one coherent, role-relevant story is often more valuable than passing the model a large collection of loosely related facts.

Another lesson was that approval should happen when it is meaningful. Asking candidates to review dozens of facts during onboarding creates work without context. Reviewing only the evidence that is about to support a real application makes the decision faster and easier to understand.

I learned that “the AI learns about you” should not mean hidden model training. In DeepCandidate, learning means building transparent structured memory that the candidate can inspect, approve, edit, and delete.

I also learned that reliable AI products require much more than a prompt. Schemas, deterministic guards, retrieval rules, provider boundaries, observability, cost controls, failure recovery, and evaluations are all part of the AI system.

Most importantly, AI should not create a fictional ideal candidate. Its value is helping a real person remember and present the strongest truthful evidence they already have.

What's next for DeepCandidate

The immediate next step is refinement rather than reinvention.

For candidates, I want to continue improving the UI and UX, make the first-use flow even faster, add an application-history workspace where previous roles, questions, answers, and CV drafts can be reopened, and improve export options. I also want to implement the existing provider boundary with Ollama so people can run the complete AI workflow locally, privately, and without a paid API key.

These are important improvements, but they are extensions of the product that already exists.

The larger next step is the employer side of DeepCandidate.

Today, candidates are asked to compress themselves into CVs, while employers often compress a real team problem into a generic job description. Both sides lose context.

An employer-side workspace could help a hiring team describe the real role more deeply: the problems the person will solve, the decisions they will own, the environment they will work in, the trade-offs they will face, and the evidence that would demonstrate readiness.

AI could then reason transparently across two richer forms of context: the employer's structured role needs and the candidate's approved career evidence. Instead of matching only titles and keywords, it could explain where the evidence aligns, where important information is missing, and which focused questions would help both sides understand the fit.

This should remain decision support, not an opaque automatic hiring score. Candidates should control their evidence, employers should understand why a match is suggested, and humans should make the final decision.

That is the bigger direction: moving from AI-assisted applications toward a more evidence-based conversation between candidates and hiring teams.

DeepCandidate starts with helping one person tell the right true story. The long-term opportunity is helping both sides understand whether that story belongs in the role they are trying to build.

Built With

  • drizzle-orm
  • embeddings
  • gpt-5.6
  • libsql
  • mammoth.js
  • next.js
  • node.js
  • openai-api
  • openai-responses-api
  • react
  • semantic-search
  • sqlite
  • structured-outputs
  • tailwind-css
  • text-embedding-3-small
  • typescript
  • unpdf
  • vitest
  • zod
Share this project:

Updates