Skip to content

Releases: jamwithai/observable-job-agent

Part 4: Speak β€” Jobvis, a voice concierge over the observable agent

Choose a tag to compare

@jamwithai jamwithai released this 13 Aug 10:48

The Observable Job Agent, Part 4: Speak.

Say "find me jobs" and a real search starts. Jobvis tells you it has begun, goes quiet while it runs, then breaks the silence itself to say what it found. Ask it to tailor an application for the second one and a minute later the finished pack is on screen while it reads you the highlights.

The Part 2 grounding contract, carried into a new modality: the browser holds the conversation and forwards every question to Python, so the voice can only say what the LangGraph checkpoint returns. There is nowhere for it to get a fit score it did not measure.

πŸ“– Blog post: https://jamwithai.substack.com/p/build-your-own-voice-agent

What's inside

  • The console (web/): a Next.js static export with a Three.js orb that reacts to the real output spectrum, served by FastAPI. The conversation runs in the browser over WebRTC, which is what buys real barge-in and the browser's own echo cancellation, with no audio library to build
  • api.py: mints short-lived voice tokens, dispatches tool calls, streams events over SSE, and serves the console. Your ElevenLabs key never leaves Python
  • voice/: persona.py declares the agent as code (system prompt, greeting, seven tool schemas), tools.py implements them next to the checkpoint, bridge.py holds the session and run manager, announce.py breaks the silence when a run finishes
  • Seven tools registered under the same names in three places, so a tool added on the agent without a handler fails loudly in one obvious spot
  • make jobvis-agent: idempotent agent creation, so the persona is version-controlled rather than typed into a form
  • Named source failures: a job source that returns nothing now says why (quota exhausted, key rejected, timed out) instead of looking like a quiet day
  • Optional webcam hand control via MediaPipe, off until you set a flag AND click the toggle. No frame leaves the tab

Get started

git clone --branch part4.0 https://github.com/jamwithai/observable-job-agent
cd observable-job-agent
uv sync --all-groups
cp .env.example .env    # add one LLM key (OPENAI_API_KEY, or free via groq:/ollama:)
make test               # 230 tests, no network or keys needed
make app                # http://localhost:7860

The voice console is optional and needs an ElevenLabs key with the Agents Platform (Conversational AI) scopes, plus a voice id you have added under Voices > My Voices:

make jobvis-agent   # prints the agent id, paste it back into .env
make web-build      # npm ci + Next.js static export
make app            # wizard on :7860 AND the console on :8000, one process

Part 3: Self-improve, with receipts β€” the job agent that measures its own fixes

Choose a tag to compare

@jamwithai jamwithai released this 13 Aug 10:47

The Observable Job Agent, Part 3: Self-improve, with receipts.

An AI assistant in the loop made the search fifteen times faster, found a regression we had shipped a week earlier and never noticed, and got one thing confidently wrong. Our own gate looked at all of it and said you have not fixed it, and it was right.

πŸ“– Blog post: https://jamwithai.substack.com/p/build-your-own-job-agent-part-3

What's inside

  • Per-source spans (traced_call): every job source is timed separately, which is what turned "search takes 15s" into "jsearch takes 15s and contributes nothing"
  • A two-phase soft deadline in the source cascade: bound the wait on the primary, fall through to the finished sources, then go back for the primary if the cascade is still short. 15.3s β†’ 1.0s median, and the spread collapses from ~5s to 7ms
  • job-scout-search-suite: a regression gate that grades the cascade rather than the prose, written BEFORE the fix so it could still disagree afterwards. It did: 33% β†’ 33%, because containing a slow source is not the same as making it fast
  • The HRPO-optimized tailor prompt, tuned against our own deterministic validator rather than an LLM judge: fabrication 0.2768 β†’ 0.1288 on fresh live jobs
  • gates/, scripts/bench_search.py, the phase-3 measurement records, and docs/ollie.md: the full assistant transcript with what to check each answer against

Get started

git clone --branch part3.0 https://github.com/jamwithai/observable-job-agent
cd observable-job-agent
uv sync --all-groups
cp .env.example .env    # add one LLM key (OPENAI_API_KEY, or free via groq:/ollama:)
make test               # 129 tests, no network or keys needed
make gates              # the deterministic regression gate, no LLM calls
make app                # http://localhost:7860

Part 2: Extend, then evaluate β€” the checkable job agent

Choose a tag to compare

@jamwithai jamwithai released this 30 Jul 08:36

The Observable Job Agent, Part 2: Extend, then evaluate.

Pick a job from Part 1's ranked list and the agent prepares the application: a personalized cover letter plus a tailored CV for that exact posting, grounded strictly in what your CV actually says. Then it checks its own work, claim by claim, and shows you a warning card for everything it cannot verify.

πŸ“– Blog post: https://jamwithai.substack.com/p/build-your-own-job-agent-part-2

What's inside

  • One graph, two doors: a conditional entry router sends a second invocation on the SAME thread into the tailor node β€” profile, ranked jobs and CV text come back from the checkpoint, nothing re-runs
  • The candidate corpus: your CV (plus an optional official LinkedIn export) becomes typed, addressable items β€” the only permissible source for tailoring (corpus_ref on every bullet)
  • A deterministic fabrication validator: zero LLM calls, env-tunable thresholds (SCOUT_FAB_*), every report records the thresholds it ran with
  • PDF rendering with a degradation contract: Jinja2 β†’ LaTeX β†’ tectonic, falls back to .tex + Overleaf, never fails a run
  • The human decides where to search: a location + remote chooser whose choices persist with your saved profile
  • The evaluation stack in Opik: datasets built from your own traces, deterministic metrics vs G-Eval judges, trajectory metrics, an annotation queue, and judge-vs-human calibration (the same 30 outputs scored 0.44 by one judge and 0.84 by another β€” that split is the lesson)

Get started

git clone --branch part2.0 https://github.com/jamwithai/observable-job-agent
cd observable-job-agent
uv sync --all-groups
cp .env.example .env    # add one LLM key (OPENAI_API_KEY, or free via groq:/ollama:)
make test               # 129 tests, no network or keys needed
make app                # upload a CV, search, pick a job, tailor

Part 1: Build β€” The Observable Job Agent

Choose a tag to compare

@jamwithai jamwithai released this 28 Jul 15:25

The Observable Job Agent, Part 1: Build.

A real, observable AI job-matching agent: CV (PDF) in, real job openings out, ranked 0–100 for fit with honest gap explanations β€” every LLM and tool call traced in Opik from the very first run.

πŸ“– Blog post: https://jamwithai.substack.com/p/build-your-own-job-agent-part-1

What's inside

  • LangGraph agent: fetch_jobs β†’ rank_jobs β†’ bounded reformulation loop (max 2)
  • LLM-driven tool use: the model chooses the search arguments, you watch it choose in the trace
  • Multi-source search cascade (JSearch, Adzuna, Remotive, offline cache) β€” runs with zero API keys
  • Gradio step-wizard UI with streamed progress and per-run cost footer
  • Opik observability: span tree, agent graph, versioned prompts, CV attached to the trace
  • An honest documented baseline (docs/phase1_findings.md, docs/baseline.json)

Get started

git clone --branch part1.0 https://github.com/jamwithai/observable-job-agent
cd observable-job-agent
uv sync --all-groups
cp .env.example .env
make test   # 35 tests, no network needed
make app    # then upload a CV from data/fixture_cvs/