Profiles CVs and letters - nigifabio/swiss-job-agent GitHub Wiki
Everything here is rule-based: no language model, no external service. Documents only ever contain facts from your own profile.
Start values come from .env (every option is documented in .env.example).
The Settings page writes data/settings.json, which overrides the .env values it contains;
both containers reload it.
| Setting | Purpose |
|---|---|
PROVIDERS |
sources, see sources |
SEARCH_TERMS, WHERE
|
queries and regions (;-separated) for the search-based sources |
TITLE_KEYWORDS, TITLE_EXCLUDE
|
titles to keep / drop (word* = prefix) |
LOCATION_KEYWORDS, ALLOW_REMOTE, REMOTE_REGIONS
|
where |
LANGUAGES |
languages of the ads to keep (en,fr,de,it) |
SCORE_KEYWORDS |
your skills: the basis of the 0-100 match score |
ORP_MONTHLY_TARGET |
applications per month your ORP/RAV office asked for (default 10) |
CF_TEAM_DOMAIN, CF_ACCESS_AUD, ALLOWED_EMAILS
|
Cloudflare Access check, see installation |
After changing SCORE_KEYWORDS in .env:
docker compose run --rm scheduler python -m app.fetch --rescore.
Set from job pages (+ I have it, ⛔ Avoid, ✕) and stored in the database: effective skills =
SCORE_KEYWORDS − removed + added; each avoided word costs 25 points. A skill matches in every posting
language: catalog skills have French, German and Italian spellings (sales = vente / Verkauf /
vendita), see SKILL_I18N in app/roles.py. Letters use the posting's own word.
With ONBOARDING=1 (the installer's default) the app opens on a wizard until data/profile.json exists:
- Import a CV (PDF / Word / text), a LinkedIn profile PDF (More → Save to PDF) and/or the LinkedIn data export ZIP (Settings → Data privacy → Get a copy of your data). A partial export (LinkedIn's first quick archive, or only some categories) and single CSV files taken from it work too: what they hold is used and the rest is filled in by hand. Files are parsed locally, in a time- and memory-limited subprocess (8 MB, 12 pages at most).
- Review the draft profile: contact, summary, skills, languages with levels, jobs with bullets, education.
- Target: roles from a catalog of 37 (titles in EN/FR/DE/IT, best matches pre-ticked), extra titles, level, home town and commute radius, remote, posting languages, words to avoid.
- Confirm the generated search (search terms, title words, exclusions by level, towns and cantons within the radius, skills, sources) and get a CV for each target role.
Later: Edit my profile and Re-run the setup wizard on the Settings page.
{
"name": "...", "headline": "...", "summary": "...",
"contact": {"email": "...", "phone": "...", "linkedin": "...", "location": "..."},
"expertise": ["..."],
"experience": [{"title": "...", "org": "...", "loc": "...", "dates": "...", "bullets": ["..."]}],
"education": ["..."], "extras_title": "Languages", "extras": ["..."]
}Full example: examples/profile.example.json. An employer named
"Confidential" is left out of letters. This file stays on your machine (data/ is never in git).
Reviewed translations of the same facts (fr, it, de). One is used only if it has exactly the
same jobs and number of bullets as profile.json, otherwise it is ignored and the CV page says so.
Name and contact always come from profile.json. Nothing is translated automatically: write or
review the translation yourself.
- Language: the posting's language (detected) if you have a profile in it, else your profile's own language. CV and letter always use the same language.
- Tailored CV: summary = your opening sentence + your most relevant other sentence + "Directly relevant to this role: …" (skills the posting asks for, spelled as the posting spells them); bullets in each job ordered by relevance; expertise re-ordered; section headings localised.
- Cover letter: sender, recipient, place and date, subject → "I am applying for the {role} position at {company}" → your current role and the skills the posting asks for → your 3 most relevant achievements → closing; in English, French, Italian or German.
- Keyword CV: keywords your profile supports get a Key skills line and move matching bullets up; unsupported ones are reported, never added.
- Relevance = content words shared with the posting (stop words removed, title words ×2) + 3 per CV skill mentioned.