Resume Agent
A local, evidence-backed agent that learns a canonical version of your career history once, then tailors your resume to a job URL or pasted job post without inventing facts.
How it works
- Profile: reads your master resume and optional career notes, then extracts a structured profile. Every usable claim receives an evidence ID and source excerpt.
- Tailor: analyzes the job, selects and rewrites only supported facts, and produces an ATS-friendly Markdown resume.
- Audit: makes a separate LLM pass and runs deterministic checks to reject unknown evidence IDs, unsupported claims, and changed contact information.
The profile is your source of truth. Tailoring may reorder, shorten, or rephrase facts; it must not create experience you do not have.
Setup
You need Python 3.11+ and access to either OpenAI or an OpenAI-compatible API.
uv sync --extra dev
cp .env.example .env
The app loads .env automatically. The default OpenAI model is gpt-5.6-terra;
override it with RESUME_AGENT_MODEL or --model.
Configure the single-user login before starting the web app:
RESUME_AGENT_USERNAME=your-username
RESUME_AGENT_PASSWORD=replace-with-a-long-random-password
You can generate a strong password with:
uv run python -c "import secrets; print(secrets.token_urlsafe(32))"
Authentication covers the Signal interface, APIs, downloads, static files, and the bundled Oh My CV editor. The server fails closed with HTTP 503 when either credential is missing or still uses the placeholder value. The login page creates a signed, HttpOnly, SameSite session cookie that expires after 12 hours, and both frontends provide a sign-out button. Oh My CV offline caching is disabled so its app shell cannot bypass the server authentication gate.
Binding to localhost is suitable for personal use. If the app is reachable from another
machine, place it behind an HTTPS reverse proxy, set
RESUME_AGENT_SECURE_COOKIES=true, and do not expose the plain HTTP port.
For an OpenAI-compatible provider, configure the endpoint without putting credentials in the repository:
RESUME_AGENT_API_KEY=your-rotated-token
RESUME_AGENT_BASE_URL=https://service.nokod.ir/v1
RESUME_AGENT_API_STYLE=chat
RESUME_AGENT_LLM_TIMEOUT_SECONDS=1800
Discover the provider's exact model IDs, then add your choice to .env:
uv run resume-agent models
RESUME_AGENT_MODEL=the-model-id-you-selected
Custom endpoints default to the Chat Completions API because it is more widely
implemented by OpenAI-compatible services. Set RESUME_AGENT_API_STYLE=responses only
when the provider supports the Responses API and structured outputs.
Model calls are logged to stdout with a query ID, endpoint, model, API style, schema, timing, prompt, input, parsed response, and full exception traceback. API keys are never logged. Because prompts contain resume data, disable payload and response bodies while keeping request metadata and errors with:
RESUME_AGENT_LOG_MODEL_PAYLOADS=false
RESUME_AGENT_LLM_TIMEOUT_SECONDS controls the provider read/write timeout. It defaults
to 1800 seconds (30 minutes), while connection establishment is capped at 30 seconds.
An upstream gateway may still enforce its own shorter timeout.
Usage
Web interface
Launch the local frontend:
uv run resume-agent editor-build
uv run resume-agent serve
Then open http://127.0.0.1:8000. Open /cv/ (or click Open CV editor) for the
bundled Oh My CV Markdown editor. Its AI Tailor with Signal tool can learn the
current editor buffer, tailor it to a job URL or pasted description, apply the audited
Markdown directly, and restore the pre-AI version with one click.
Docker Compose
After configuring .env, build and start the complete app with:
docker compose up --build
Open http://127.0.0.1:8900. The image builds Oh My CV and serves it from /cv/ through
the same FastAPI process. Career-profile state and generated resumes persist in the
host's .resume-agent/ and output/ directories.
To use another host port, set RESUME_AGENT_PORT when starting Compose:
RESUME_AGENT_PORT=8080 docker compose up --build
Stop the service with docker compose down. This leaves the persisted profile and
outputs intact.
Both frontends include a Tailoring strength slider. At 0, the agent stays close
to the source wording and organization. At 100, it uses the fullest supported detail
and strongest truthful job alignment. The slider never relaxes evidence validation;
unsupported requirements remain gaps or follow-up questions rather than resume claims.
Below the slider, Tailoring configuration can switch between:
- Strict source evidence: every candidate-facing claim must map to one or more evidence IDs extracted from the source resume.
- Flexible profile-based rewrite: the complete profile and original resume remain the factual source, but claim-level evidence IDs are optional. This mode still tells the model not to invent employers, dates, skills, metrics, or achievements.
Use the Evidence guard control in Signal's header or the Oh My CV AI dialog to
disable claim-level evidence enforcement across the entire app. The setting is persisted
in .resume-agent/settings.json and applies to tailoring and revision chat. Turning it
off forces flexible profile mode everywhere; the supplied-profile truth and no-invention
rules remain active.
After tailoring in the main Signal interface, use Open in Oh My CV to create a new local Oh My CV resume from the optimized Markdown and open it directly in the editor.
The tailored-results section also includes a revision chatbot. It can change tone, emphasis, detail, ordering, and length, then refreshes the preview, match assessment, honest gaps, downloads, and Oh My CV handoff. Every revision runs through the same profile-evidence validation and factuality audit as the initial tailoring pass.
Long-running frontend actions use background tasks rather than holding one HTTP request
open. Profile extraction, tailoring, Oh My CV tailoring, and revision chat return a task
ID immediately; both frontends poll /api/tasks/{task_id} until the model work succeeds
or fails. Temporary polling connection errors are retried without cancelling model work.
The main browser interface supports resume upload, career-profile review, job URLs or pasted descriptions, evidence-linked resume preview, match analysis, and Markdown/JSON downloads. API credentials stay in the server environment and are never returned to the browser.
Oh My CV is vendored from
Renovamen/oh-my-cv at the commit recorded in
vendor/README.md. It is GPL-3.0 licensed; preserve its license and source availability
if you distribute the combined application.
Command line
Build your reusable profile:
uv run resume-agent profile build ./my-master-resume.pdf
Add factual context not captured in the resume:
uv run resume-agent profile build ./resume.docx \
--about-file ./career-notes.md
Inspect what the agent believes about you:
uv run resume-agent profile show
Tailor from a URL:
uv run resume-agent tailor "https://company.example/jobs/123"
Or from a saved/pasted job post:
uv run resume-agent tailor ./job-post.txt
uv run resume-agent tailor "Paste the complete job description here..."
Outputs are written to output/:
resume.md: clean tailored resumeresume-audited.md: internal audit copy with evidence IDs in invisible HTML commentsreport.md: matches, real gaps, changes, and questionstailoring.json: complete structured result
Candidate-facing Markdown and Oh My CV PDF output never display evidence references such
as [F013]; those IDs remain available only in internal audit data.
Privacy and safety
- Resume and profile files remain local, but their text is sent to the configured
OpenAI API when you run
profile buildortailor. - Job URLs are restricted to public HTTP(S) addresses and response size is capped.
- Web-page instructions are treated as untrusted text, not agent instructions.
- Keep
.resume-agent/,.env, and generated outputs out of version control.
Development
uv run --extra dev pytest
uv run --extra dev ruff check .