A recruiting assistant agent (job lookup, building candidate profiles, scoring candidates, and sending emails to candidates) with a LangSmith evaluation harness and a GitHub Actions workflow that runs experiments when a PR is labeled.
- Python
>=3.11, managed with uv - LangChain / LangGraph agent (
recruiting_agent/package) - LangSmith for tracing + evals (
eval.py)
Everything below is wired to a specific LangSmith workspace/project/dataset. Swap these for your own before running.
Create a .env file in the project root. It is gitignored and loaded automatically by
recruiting_agent/recruiting_agent.py (via load_dotenv()) and referenced by langgraph.json
("env": ".env").
| Variable | Required | What to change |
|---|---|---|
LANGSMITH_API_KEY |
Yes | Your LangSmith API key. |
LANGSMITH_PROJECT |
Yes | Your project name (where agent traces land). |
DATASET_NAME |
Yes* | Your dataset name. Read by eval.py when --dataset is not passed. |
LANGSMITH_WORKSPACE_ID |
If key spans multiple workspaces | Your workspace ID, so datasets/experiments resolve to the right workspace. |
LANGSMITH_ENDPOINT |
Only if non-default | Set to your region/self-hosted URL (e.g. https://eu.api.smith.langchain.com). Omit for default US. |
LANGSMITH_TRACING |
No | Defaults to true (set by recruiting_agent.py). Set false to disable tracing. |
| Model provider access | Yes | The agent calls a chat model (MODEL_NAME in recruiting_agent/recruiting_agent.py). Configure whatever credentials your model routing (direct provider key or LangSmith gateway) requires. |
* DATASET_NAME is required if you run eval.py without --dataset (see below).
Copy .env.example to .env and fill in your own values (see the table above).
eval.pyresolves the dataset as--datasetif given, otherwise theDATASET_NAMEenvironment variable. If neither is set it hard-fails (there is no computed default).- Set
DATASET_NAMEin your.envfor local runs, or pass--dataset "<name>"per run. - Create the dataset(s) in your own LangSmith workspace — they don't come with the repo.
Dataset examples must have inputs shaped like
eval.py'sevaluation_targetexpects:inputs["messages"][0]["content"], plus optionaluser_id/thread_id.
LANGSMITH_PROJECT(env) — where agent traces land.--experiment-prefix— names the experiment (defaults tobaselinelocally; the CI workflow passespr-<number>).
The workflow .github/workflows/run-evals-on-label.yml reads config from repository Actions
secrets (not your local .env — that never transfers to CI).
Add these under Settings → Secrets and variables → Actions → Secrets → Repository secrets.
Use repository secrets, not environment secrets: the workflow job does not declare an
environment:, so environment-scoped secrets would never resolve.
| Secret | Notes |
|---|---|
LANGSMITH_API_KEY |
Required. |
LANGSMITH_PROJECT |
Required for trace routing / default dataset name. |
DATASET_NAME |
Required. The workflow reads ${{ secrets.DATASET_NAME }} and eval.py hard-fails without a dataset. |
OPENAI_API_KEY |
Required. The agent calls the model at import time; without it the workflow fails with openai.OpenAIError: Missing credentials. |
LANGSMITH_WORKSPACE_ID |
Add if your key spans multiple workspaces. |
LANGSMITH_ENDPOINT |
Only if non-default (EU / self-hosted); the line is commented out in the workflow — uncomment it and add the secret. |
Add via the web UI, or with the gh CLI:
gh secret set LANGSMITH_API_KEY
gh secret set LANGSMITH_PROJECT
gh secret set DATASET_NAME
gh secret set OPENAI_API_KEY
gh secret set LANGSMITH_WORKSPACE_ID # only if key spans multiple workspacesIn .github/workflows/run-evals-on-label.yml:
-
Trigger label: the job runs only when a label named
run_evalsis added to a PR (if: github.event.label.name == 'run_evals'). This label does not exist by default — you must create it in your repo (or change the name here to an existing label). The label name must matchrun_evalsexactly (case-sensitive).Create it via the web UI at Issues → Labels → New label, or with the
ghCLI:gh label create run_evals \ --description "Add to a PR to run the LangSmith eval workflow against its head commit" \ --color 1D76DBSuggested values:
- Label name:
run_evals - Description:
Add to a PR to run the LangSmith eval workflow against its head commit - Color: any (e.g.
#1D76DB) — purely cosmetic, not read by the workflow.
- Label name:
-
Dataset: the workflow does not pass
--dataset. It setsDATASET_NAMEfrom theDATASET_NAMEsecret, andeval.pyreads that env var (--datasetoverrides it if you add the flag). Set theDATASET_NAMEsecret to your dataset name. -
Experiment name: the workflow passes
--experiment-prefix "pr-<number>".
Fork this repo to your own account, then clone your fork:
# Replace <your-username> with your GitHub username
git clone https://github.com/<your-username>/lca-engine.git
cd lca-engineuv sync# Uses the DATASET_NAME env var (from .env)
uv run python eval.py
# Or target a specific dataset / experiment name
uv run python eval.py --dataset "my-dataset" --experiment-prefix "local-test"recruiting_agent/recruiting_agent.py exposes
run_agent(user_message, *, user_id=None, environment="production", thread_id=None)
and a recruiting_agent graph (see langgraph.json).
Two scripts drive the agent over batches of example recruiter requests:
uv run python3 run.py # email-a-candidate requests (recruiter identity via user_id)
uv run python3 run_homework.py # scoring and skill-update requests- Add the Actions secrets (section 4).
- Create the
run_evalslabel in your repo (section 5 — it doesn't exist by default). - Open a PR and add the
run_evalslabel → the workflow checks out the PR head, runsuv sync, and executeseval.pyagainst the configured dataset.
The run_evals label triggers CI, but you can run the exact same experiment locally against an
open PR before merging. This mirrors the workflow: check out the PR head, sync deps, run eval.py.
# 1. Check out the PR branch (gh handles forks; use the PR number)
gh pr checkout <PR-number>
# 2. Sync deps (the PR may have changed them)
uv sync
# 3. Run the experiment (env vars come from your local .env)
uv run python eval.py --experiment-prefix "pr-<number>"Notes:
- CI evaluates the PR head SHA.
gh pr checkoutlands on the branch tip, which matches as long as no new commits are pushed while you run. To pin it exactly:git checkout <head-sha>. --experiment-prefixis just a label — locally you can use e.g.pr-<number>-localto distinguish your run from the CI-generated one. Defaults tobaselineif omitted.- For a before/after comparison, run
eval.pyonmainand on the PR branch with different prefixes, then diff the experiments in the LangSmith UI. - Switch back when done:
git checkout main.
| File | Purpose |
|---|---|
recruiting_agent/recruiting_agent.py |
The agent graph and run_agent entrypoint. |
recruiting_agent/data_service.py |
Data access layer. |
recruiting_agent/recruiting_records.py |
Records / fixtures. |
eval.py |
LangSmith evaluate() harness. |
run.py |
Runs the agent over example email-a-candidate requests. |
run_homework.py |
Runs the agent over example scoring / skill-update requests. |
langgraph.json |
LangGraph config (graph + .env). |
.github/workflows/run-evals-on-label.yml |
Runs evals on the run_evals PR label. |