226 lines
8.2 KiB
Markdown
226 lines
8.2 KiB
Markdown
# 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
|
|
|
|
1. **Profile:** reads your master resume and optional career notes, then extracts a
|
|
structured profile. Every usable claim receives an evidence ID and source excerpt.
|
|
2. **Tailor:** analyzes the job, selects and rewrites only supported facts, and produces
|
|
an ATS-friendly Markdown resume.
|
|
3. **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.
|
|
|
|
```bash
|
|
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:
|
|
|
|
```dotenv
|
|
RESUME_AGENT_USERNAME=your-username
|
|
RESUME_AGENT_PASSWORD=replace-with-a-long-random-password
|
|
```
|
|
|
|
You can generate a strong password with:
|
|
|
|
```bash
|
|
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. Your browser will display its standard
|
|
username/password prompt. Oh My CV offline caching is disabled so its app shell cannot
|
|
bypass the server authentication gate.
|
|
|
|
HTTP Basic authentication protects credentials only when the connection uses HTTPS.
|
|
Binding to localhost is suitable for personal use; if the app is reachable from another
|
|
machine, place it behind an HTTPS reverse proxy and do not expose the plain HTTP port.
|
|
|
|
For an OpenAI-compatible provider, configure the endpoint without putting credentials
|
|
in the repository:
|
|
|
|
```dotenv
|
|
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`:
|
|
|
|
```bash
|
|
uv run resume-agent models
|
|
```
|
|
|
|
```dotenv
|
|
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:
|
|
|
|
```dotenv
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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`](https://github.com/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:
|
|
|
|
```bash
|
|
uv run resume-agent profile build ./my-master-resume.pdf
|
|
```
|
|
|
|
Add factual context not captured in the resume:
|
|
|
|
```bash
|
|
uv run resume-agent profile build ./resume.docx \
|
|
--about-file ./career-notes.md
|
|
```
|
|
|
|
Inspect what the agent believes about you:
|
|
|
|
```bash
|
|
uv run resume-agent profile show
|
|
```
|
|
|
|
Tailor from a URL:
|
|
|
|
```bash
|
|
uv run resume-agent tailor "https://company.example/jobs/123"
|
|
```
|
|
|
|
Or from a saved/pasted job post:
|
|
|
|
```bash
|
|
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 resume
|
|
- `resume-audited.md`: internal audit copy with evidence IDs in invisible HTML comments
|
|
- `report.md`: matches, real gaps, changes, and questions
|
|
- `tailoring.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 build` or `tailor`.
|
|
- 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
|
|
|
|
```bash
|
|
uv run --extra dev pytest
|
|
uv run --extra dev ruff check .
|
|
```
|