Skip to main content
Glama
maraMoreir

career-agent

by maraMoreir
README.md
# Career Agent

Agente de carreira integrado ao Claude Desktop via MCP. Encontra vagas,
calcula compatibilidade com o seu perfil, personaliza seu curriculo de forma
legitima, gera mensagens e respostas, e mantem o historico de candidaturas.

**A acao externa final e sempre sua.** O agente prepara; voce clica.

---

## Novidades da v1.1

| Recurso | Como usar |
|---|---|
| **Catálogo persistente de vagas** | `run_job_search` coleta e salva; `list_matching_jobs` consulta |
| **5 provedores de ATS** | Greenhouse, Lever, Ashby, Workable, SmartRecruiters |
| **Adzuna** (índice nacional BR) | preencha `ADZUNA_APP_ID`/`ADZUNA_APP_KEY` no `.env` |
| **Pesos configuráveis** | edite `data/config/scoring.json` |
| **11 dimensões de score** | inclui .NET, SAP, fiscal, arquitetura e foco backend |
| **Busca agendada** | `.\scripts\schedule.ps1 -IntervalHours 2` |
| **Dashboard local** | `.\scripts\start-dashboard.ps1` |
| **Retry com backoff** | automático em todas as fontes HTTP |

Detalhes de cada fonte, com o que foi medido: [docs/FONTES.md](docs/FONTES.md).

---

## Indice

1. [Arquitetura](#1-arquitetura)
2. [Pre-requisitos](#2-pre-requisitos)
3. [Instalacao](#3-instalacao)
4. [Configuracao](#4-configuracao)
5. [Configuracao do Claude Desktop](#5-configuracao-do-claude-desktop)
6. [Como iniciar](#6-como-iniciar)
7. [Como testar](#7-como-testar)
8. [Como adicionar uma nova fonte de vagas](#8-como-adicionar-uma-nova-fonte-de-vagas)
9. [Como adicionar um novo curriculo](#9-como-adicionar-um-novo-curriculo)
10. [Como registrar uma candidatura](#10-como-registrar-uma-candidatura)
11. [Exemplos de comandos no Claude Desktop](#11-exemplos-de-comandos-no-claude-desktop)
12. [Limitacoes atuais](#12-limitacoes-atuais)
13. [Proximos passos](#13-proximos-passos)

---

## 1. Arquitetura

### Visao geral

```
                        Claude Desktop
                              |
              +---------------+---------------+
              |               |               |
        career-agent     job-search     career-files
         (MCP stdio)     (MCP stdio)     (MCP stdio)
              |               |               |
              +---------------+---------------+
                              |
                        career_core
              (dominio puro - nao conhece MCP)
                              |
         +--------+-----------+-----------+--------+
         |        |           |           |        |
      profile  scoring   applications  resume  job_sources
       (.md)   (7 dim.)  (SQLite+JSON) (tailor) (IJobSource)
```

### Decisoes arquiteturais

**Dominio separado dos adapters.** Toda a regra de negocio vive em
`src/career_core/`, que nao importa nada de MCP. Os tres `server.py` sao
adapters finos: traduzem argumentos, chamam o dominio, formatam a resposta.
Isso permite testar 100% da logica sem subir servidor nenhum.

**SQLite como fonte de verdade, JSON como espelho.** SQLite da escrita
transacional (o historico nao corrompe se o processo morrer no meio) e
consultas de duplicidade baratas, com zero configuracao — ao contrario do
PostgreSQL, que exigiria servidor e credenciais sem ganho nenhum na escala de
uma pessoa. O `applications.json` continua existindo, reescrito de forma
atomica a cada mudanca, para inspecao a olho nu e versionamento no Git. Ele e
**somente escrita**: nunca e lido de volta, entao nao existe risco de duas
fontes divergirem.

**Score como dimensoes plugaveis.** Cada uma das 7 dimensoes e uma classe que
implementa `IScoreDimension` e sabe pontuar *e explicar* um unico aspecto. O
`JobScorer` so soma e classifica. Adicionar uma dimensao nova nao altera o
somador (Open/Closed).

**Fontes de vagas atras de uma interface.** `IJobSource` tem quatro
implementacoes: `MockJobSource` (offline), `RemotiveJobSource` e
`ArbeitnowJobSource` (APIs publicas reais, sem autenticacao) e
`UnavailableJobSource` (LinkedIn/Indeed/Gupy — declaradas, porem em modo
manual). Adicionar uma fonte e escrever uma classe e registra-la; nada mais
muda.

**Composition root unico.** `CareerServices` monta o grafo de objetos. Os
servidores nao instanciam dependencias a mao, e os testes injetam dublês.

### Estrutura de diretorios

```
career-agent/
├── pyproject.toml            # deps + config do pytest (fonte unica)
├── .env.example              # modelo de configuracao (versionado)
├── .env                      # sua configuracao real (NAO versionado)
│
├── src/career_core/          # DOMINIO - nao conhece MCP
│   ├── config.py             # Settings por ambiente
│   ├── models.py             # Job, CandidateProfile, Application, JobScore
│   ├── text.py               # normalizacao (aliases de stack, URL, empresa)
│   ├── security.py           # politica + maquina de estados (ApprovalGate)
│   ├── paths.py              # SandboxedFileSystem (jail em data/)
│   ├── errors.py             # hierarquia de erros de dominio
│   ├── logging_setup.py      # logging para stderr + arquivo
│   ├── services.py           # composition root
│   ├── job_input.py          # vaga colada -> Job normalizado
│   ├── profile/repository.py # perfil .md -> CandidateProfile
│   ├── scoring/              # dimensions.py (7 dimensoes) + scorer.py
│   ├── applications/         # repository.py, dedupe.py, builder.py
│   ├── resume/tailor.py      # personalizacao + FactGuard
│   └── job_sources/          # base.py, mock.py, http_sources.py,
│                             # unavailable.py, registry.py
│
├── mcp-career/               # MCP 1 - logica de carreira
├── mcp-job-search/           # MCP 2 - obtencao de vagas
├── mcp-career-files/         # MCP 3 - leitura de arquivos (sandbox)
│
├── data/                     # UNICO diretorio visivel ao career-files
│   ├── profile/              # profile.md, skills.md, preferences.md
│   ├── resumes/              # curriculo-principal.md (+ variantes)
│   └── applications/         # applications.db (verdade) + .json (espelho)
│
├── agent/career-agent.md     # instrucoes de comportamento do agente
├── scripts/                  # install.ps1, start.ps1, test.ps1, configure-*
├── tests/                    # pytest
└── docs/                     # SECURITY.md, SCORING.md, ARCHITECTURE.md
```

---

## 2. Pre-requisitos

| Requisito | Versao | Observacao |
|---|---|---|
| Windows | 10/11 | testado no Windows 11 |
| Python | >= 3.11 | `python --version` |
| uv | qualquer | o `install.ps1` instala se faltar |
| Claude Desktop | atual | necessario para usar os MCPs |
| Git | opcional | para versionar o projeto |

---

## 3. Instalacao

```powershell
cd C:\career-agent
powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1
```

O script verifica Python, instala `uv` se faltar, cria o `.venv`, instala as
dependencias, cria a arvore de `data/`, gera o `.env` a partir do
`.env.example` e valida que os tres MCPs sobem.

Para tambem gravar a configuracao do Claude Desktop no mesmo passo:

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1 -ConfigureClaude
```

---

## 4. Configuracao

### 4.1 Preencha seu perfil

Estes arquivos sao a **fonte de verdade**. O agente nunca afirma nada que nao
esteja neles.

| Arquivo | O que colocar |
|---|---|
| `data/profile/profile.md` | nome, contatos, resumo, formacao, empresas bloqueadas |
| `data/profile/skills.md` | tecnologias, arquitetura, dominios |
| `data/profile/preferences.md` | cargos-alvo, senioridade, modalidade, cidades, salario |
| `data/resumes/curriculo-principal.md` | seu curriculo completo |

Procure por `[PREENCHER]` — sao os campos que o agente nao pode inventar.

Dois deles mudam o score na hora:

- **`Anos de experiencia`** em `profile.md`: enquanto estiver
  `nao informado`, a parte de "anos" da dimensao Experiencia fica neutra. O
  agente **nao** deduz esse numero.
- **`Minimo` / `Alvo`** em `preferences.md`: enquanto estiverem
  `[PREENCHER]`, a dimensao Salario fica neutra para vagas com faixa
  divulgada.

### 4.2 Ajuste o `.env`

```ini
CAREER_DATA_ROOT=C:\career-agent\data
CAREER_MIN_SCORE=70

JOB_SEARCH_ENABLE_NETWORK=true
JOB_SEARCH_SOURCES=ats
JOB_SEARCH_ATS_COMPANIES=greenhouse:stone,ashby:nubank,greenhouse:vtex,...
JOB_SEARCH_USER_AGENT=career-agent/1.0 (personal job search; contact: SEU-EMAIL)
```

Ponha seu e-mail no User-Agent — identificar-se e a forma educada de consumir
uma API publica.

### Adicionar empresas a busca

A fonte `ats` so encontra vagas das empresas que voce listar. Para adicionar
uma, abra a pagina de carreiras dela e olhe a URL:

| URL da pagina de carreiras | Adicione |
|---|---|
| `job-boards.greenhouse.io/SLUG` | `greenhouse:SLUG` |
| `jobs.lever.co/SLUG` | `lever:SLUG` |
| `jobs.ashbyhq.com/SLUG` | `ashby:SLUG` |

Empresas cuja pagina de carreiras esta na Gupy nao podem ser adicionadas — a
Gupy nao expoe busca publica. Para essas, use o modo manual.

Nao existe variavel de credencial do LinkedIn neste projeto. Isso e
deliberado.

---

## 5. Configuracao do Claude Desktop

### Automatico (recomendado)

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\configure-claude-desktop.ps1
```

O script faz backup do arquivo existente (`.backup-AAAAMMDD-HHMMSS`), preserva
todas as suas configuracoes e MCPs atuais, e **so** adiciona/atualiza as tres
entradas do Career Agent.

### Manual

Arquivo: `%APPDATA%\Claude\claude_desktop_config.json`
(no seu caso: `C:\Users\Roger\AppData\Roaming\Claude\claude_desktop_config.json`)

```json
{
  "mcpServers": {
    "career-agent": {
      "command": "C:\\career-agent\\.venv\\Scripts\\python.exe",
      "args": ["C:\\career-agent\\mcp-career\\server.py"]
    },
    "job-search": {
      "command": "C:\\career-agent\\.venv\\Scripts\\python.exe",
      "args": ["C:\\career-agent\\mcp-job-search\\server.py"]
    },
    "career-files": {
      "command": "C:\\career-agent\\.venv\\Scripts\\python.exe",
      "args": ["C:\\career-agent\\mcp-career-files\\server.py"]
    }
  }
}
```

> **Caminhos absolutos.** Se voce instalou o projeto em outro lugar, troque
> `C:\\career-agent` pelo seu caminho real, em todas as ocorrencias. As barras
> invertidas precisam ser duplicadas — e JSON.

> **Por que o python do `.venv` e nao o `uv`?** O Claude Desktop inicia os
> servidores sem carregar seu PATH de usuario. Apontar direto para o
> interpretador do ambiente virtual elimina a dependencia de PATH e torna a
> inicializacao mais rapida e previsivel. O `uv` continua sendo a ferramenta
> de instalacao e de execucao dos testes.

Depois de salvar: **feche o Claude Desktop completamente** (inclusive o icone
na bandeja do sistema, ao lado do relogio — fechar a janela nao encerra o
processo) e abra de novo.

Para confirmar, pergunte no chat: *"Quais ferramentas de career voce tem?"*

---

## 6. Como iniciar

Os servidores sao iniciados pelo proprio Claude Desktop — voce nao precisa
deixar nada rodando.

Para verificar manualmente que os tres sobem:

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\start.ps1
```

Logs: `C:\career-agent\logs\` (`mcp-career.log`, `mcp-job-search.log`,
`mcp-career-files.log`).

---

## 7. Como testar

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\test.ps1
```

O script roda a suite pytest e, em seguida, uma validacao ponta a ponta:
importacao dos modulos, inicializacao dos tres MCPs, leitura do perfil,
calculo de score, registro de candidatura, consulta de historico e deteccao de
duplicidade.

Apenas os testes unitarios:

```powershell
C:\career-agent\.venv\Scripts\python.exe -m pytest tests -v
```

---

## 8. Como adicionar uma nova fonte de vagas

**Antes de tudo:** verifique se a fonte tem API publica documentada. Se exigir
login, cookie ou scraping, ela nao entra — use `UnavailableJobSource` e o modo
manual.

1. Crie a classe em `src/career_core/job_sources/`:

```python
from .base import IJobSource, JobQuery, SourceResult, detect_seniority

class MinhaFonteJobSource(IJobSource):
    name = "minhafonte"
    provenance = "API JSON publica de X, sem autenticacao."
    usable = True

    def search(self, query: JobQuery) -> SourceResult:
        # ... chamar a API e converter cada item em `Job`
        return SourceResult(source=self.name, jobs=jobs, ok=True, message="...")
```

2. Registre em `src/career_core/job_sources/registry.py`:

```python
_FACTORIES = {
    ...,
    "minhafonte": (lambda s: MinhaFonteJobSource(...), True),  # True = precisa de rede
}
```

3. Ative no `.env`: `JOB_SEARCH_SOURCES=mock,minhafonte`

4. Adicione um teste em `tests/test_job_sources.py`.

Nenhum outro arquivo do sistema muda. Score, deduplicacao e candidatura
funcionam automaticamente porque a fonte devolve `Job` normalizado.

---

## 9. Como adicionar um novo curriculo

Coloque um `.md` em `C:\career-agent\data\resumes\`. O nome do arquivo importa:
o agente escolhe automaticamente o curriculo cujo nome tem mais palavras em
comum com a vaga.

```
data/resumes/
├── curriculo-principal.md      # padrao / fallback
├── curriculo-backend-dotnet.md # vence em vagas .NET/backend
├── curriculo-fullstack.md      # vence em vagas fullstack/React
└── curriculo-sap.md            # vence em vagas SAP
```

Para forcar um especifico: *"Prepare a candidatura usando curriculo-sap.md"*.

---

## 10. Como registrar uma candidatura

Ciclo de vida:

```
   generate_application          register_application
   (mostra o pacote)      -->    (grava o historico)
                                        |
                                        v
                                pending_approval
                                        |
                          voce aprova   |
                                        v
                                    approved
                                        |
                    VOCE se candidata no site
                                        v
                                     applied
                                        |
              +-------------+-----------+-----------+
              v             v           v           v
          interview  technical_test   offer     rejected
```

`rejected` e `withdrawn` sao estados finais.

**Nao existe caminho de `pending_approval` direto para `applied`.** A tentativa
e recusada pela maquina de estados. Essa e a garantia, em codigo, de que nada
avanca sem voce ter visto.

---

## 11. Exemplos de comandos no Claude Desktop

**Buscar**
```
Procure vagas Backend .NET compativeis com meu perfil.
Priorize remoto e hibrido em Goiania.
Mostre somente vagas com score >= 80.
```

**Analisar uma vaga colada**
```
Analise esta vaga:
[cole aqui a URL e a descricao completa]
```

**Preparar candidatura**
```
Prepare minha candidatura para a vaga da Nexatech.
```

**Acompanhar**
```
Mostre minhas candidaturas pendentes.
Quais candidaturas estao aguardando minha aprovacao?
Atualize a candidatura app-xxxx para entrevista.
```

**Aprovar**
```
Aprovo a candidatura app-xxxx.
```

**Diagnostico**
```
Esta tudo configurado no Career Agent?
De onde vem as vagas que voce busca?
Voce consegue se candidatar por mim no LinkedIn?
```

---

## 12. Limitacoes atuais

- **LinkedIn, Indeed e Gupy funcionam em modo manual.** Nenhum deles oferece
  API publica de busca para candidatos. Voce copia a vaga; o agente faz o
  resto. Isso e uma escolha de seguranca, nao uma pendencia.
- **A cobertura automatica depende de quais empresas voce configura.** A fonte
  `ats` varre os quadros publicos das empresas em `JOB_SEARCH_ATS_COMPANIES`.
  A lista padrao tem 10 empresas verificadas (~1.160 vagas), mas o mercado
  brasileiro tem muito mais — adicione as empresas que te interessam.
- **Nem todo ATS e coberto.** Greenhouse, Lever e Ashby tem endpoint publico.
  Gupy, Solides e Kenoby nao expoem busca publica para candidatos.
- **Remotive e Arbeitnow servem para pouca coisa** (medido em agosto/2026):
  a Remotive devolve um feed de **amostra de 14 vagas** que **ignora o
  parametro `search`**; o Arbeitnow tem 175 vagas quase todas europeias e
  presenciais, **zero** com .NET/C#. Ficam disponiveis, mas fora do padrao.
- **LinkedIn, Indeed e Gupy continuam em modo manual** — nao tem API publica
  de busca para candidatos, e este projeto nao automatiza login nem scraping.
- **A extracao de requisitos e heuristica.** Funciona bem com descricoes em
  bullets; com texto corrido, os requisitos saem menos estruturados.
- **A deteccao de senioridade e por palavra-chave** no titulo e na descricao.
  Titulos ambiguos podem sair como `nao_informado` — informe manualmente
  quando importar.
- **Salario so e comparado quando a vaga divulga a faixa.** A maioria das
  vagas brasileiras nao divulga; nesse caso a dimensao fica neutra.
- **O curriculo personalizado sai em Markdown.** Nao ha exportacao para PDF
  ou DOCX na V1.
- **Instalacao mono-usuario, local.** Sem multi-perfil, sem sincronizacao.

---

## 13. Proximos passos

Ordenados por relacao valor/esforco:

1. **Exportar curriculo para PDF/DOCX** — hoje o material sai em Markdown e
   voce converte a mao.
2. **Ler descricao de vaga a partir de uma URL publica** (paginas de carreira
   abertas, sem login), reduzindo o copiar-e-colar.
3. **Fontes brasileiras** — mapear ATSs que expoem endpoint publico de vagas
   por empresa e implementar como `IJobSource`.
4. **Lembretes de follow-up** — sinalizar candidaturas paradas em `applied` ha
   mais de N dias.
5. **Metricas do funil** — taxa de resposta por score, por stack e por
   modalidade, para calibrar os pesos com dados reais.
6. **Calibracao dos pesos** — hoje sao os pesos definidos na especificacao;
   com historico suficiente, ajustar com base no que realmente converte.
7. **Deteccao de duplicidade semantica** — hoje e por similaridade textual;
   embeddings pegariam "Dev Backend .NET" vs "Engenheiro de Software C#".

---

## Seguranca

Resumo do que este projeto **nao faz**, por design:

| Nao faz | Por que |
|---|---|
| Login automatico no LinkedIn | viola os ToS; risco de bloqueio da conta |
| Guardar senha/cookie/token | superficie de ataque desnecessaria |
| Automatizar cliques | viola os ToS |
| Enviar candidatura sozinho | a decisao final e sua |
| Enviar mensagem sozinho | a decisao final e sua |
| Burlar anti-bot / CAPTCHA | ilegitimo |
| Scraping agressivo | ilegitimo e desrespeitoso |
| Inventar experiencia | mentira em curriculo prejudica voce |

Detalhes em [docs/SECURITY.md](docs/SECURITY.md).

O acesso a arquivos do Claude fica restrito a `C:\career-agent\data`. Ele nao
enxerga `C:\`, nem sua pasta de usuario, nem o codigo do proprio projeto.

Maintenance

ActivityMaintained
ResponsivenessNo issues