AI engineer: real-time voice agents and LLM systems that hold up in production.
I build the parts of AI products that break in production: latency, reliability, evals, and the plumbing around them. Right now I work on real-time voice AI at withlayer.ai. Outside work I build JustHireMe.
Open to remote contract or full-time roles with UK/EU/US teams. 4–5 h UK/EU overlap, open to relocating.
Experience
withlayer.ai
AI Engineering Intern
Jun 2026 → Present
I work on real-time voice AI (WebRTC, LiveKit, speech-to-text and text-to-speech, latency, interruptions), LLM agent workflows and their reliability (tool calling, grounding, evals, regression tests), and full-stack features (Python, TypeScript, React, Next.js, PostgreSQL).
Python
TypeScript
React
Next.js
PostgreSQL
WebRTC
LiveKit
Vitest
Playwright
+3LiveKit, Vitest, Playwright
Read about this role +
I work on real-time voice AI (WebRTC, LiveKit, speech-to-text and text-to-speech, latency, interruptions), LLM agent workflows and their reliability (tool calling, grounding, evals, regression tests), and full-stack features (Python, TypeScript, React, Next.js, PostgreSQL).
I work on the technical details that make spoken interaction feel responsive and predictable.
Develop and debug speech-to-text, language-model response, and text-to-speech integrations using Python and TypeScript, with WebRTC and LiveKit among the tools I work with.
Work on turn detection, interruptions, streaming responses, and connection lifecycle handling, including how the system behaves when a conversation pauses or a connection changes.
Investigate transcription quality and spoken-output behavior.
LLM and agent workflows
I contribute to the engineering around model calls: preparing context, handling outputs, and making asynchronous work dependable.
Implement and refine LLM integrations, context preparation, context handling, and structured model outputs.
Work on grounding and evaluation so generated responses can be checked against the information available to the system.
Contribute to asynchronous workflows and background processing, including failure handling and consistency between related operations.
Full-stack and data work
Implement application changes across React and Next.js interfaces, TypeScript APIs, and PostgreSQL-backed data flows.
Work through UI states and backend behavior together, including loading, unavailable data, and error cases.
Check authentication, permissions, and tenant-scoped data access as part of implementation and regression testing.
Performance and reliability
Profile response latency and time to first token to understand where time is spent in streaming and asynchronous processing.
Add diagnostics and investigate timing, cancellation, and state-management issues instead of judging responsiveness only by how a demo feels.
Review edge cases involving partial results, interrupted work, and unexpected inputs; validate fixes with targeted checks.
Testing and delivery
My contribution includes verification and review as well as implementation.
Write and maintain regression tests, integration checks, and browser-level checks with tools including Vitest and Playwright.
Use automated evaluations to examine AI behavior, and add coverage for failures and data-access boundaries.
Work through code-review feedback and document what has been verified, what remains uncertain, and where a change is ready for release.
I rebuilt an AI assistant backend and took it to production. Eval pass rate went from 32/54 to 52/54, time to first results from 72–122 s to under 10 s, and search from 10.6 s to 53 ms. I also built a multi-million-vector retrieval index that answers in ~15–21 ms, and I owned releases end to end.
Python
FastAPI
TypeScript
MongoDB
Docker
AWS
Read about this role +
I rebuilt an AI assistant backend and took it to production. Eval pass rate went from 32/54 to 52/54, time to first results from 72–122 s to under 10 s, and search from 10.6 s to 53 ms. I also built a multi-million-vector retrieval index that answers in ~15–21 ms, and I owned releases end to end.
32/54 → 52/54 · Eval pass rate<10 s · Time to first results (from 72–122 s)53 ms · Search (from 10.6 s)~15–21 ms · Vector retrieval
Applied AI and backend engineering
A long-running contract covering an AI assistant and the retrieval and data systems behind it. The client and product are confidential.
AI assistant backend
Rebuilt an AI assistant backend as a multi-agent service in Python and FastAPI, and took it to production.
Cut time to first results from 72–122 s to under 10 s by profiling each stage and restructuring how work is orchestrated and streamed.
Added guardrails that keep the assistant inside its intended scope.
Retrieval and search
Built a multi-million-vector retrieval index that answers in ~15–21 ms.
Reduced keyword search latency from 10.6 s to 53 ms.
Worked with embeddings, vector retrieval and multimodal inputs.
Data pipelines
Built data-processing and enrichment pipelines with Python, FastAPI and MongoDB, designed for repeatable runs and validation.
Evals, testing and delivery
Built a golden eval suite that gates changes, taking the pass rate from 32/54 to 52/54.
Wrote automated tests and set up CI/CD with Docker and AWS.
Owned releases end to end, including production incidents and fixes.
PythonFastAPIMongoDBvector searchDockerAWS
Freelance · Confidential client
Full-stack Engineer
Mar → Apr 2026
Fintech P&L platform, built solo in 18 days: 5 payment/commerce integrations, ~4,500× faster attribution, 641 orphaned orders recovered.
TypeScript
React
Next.js
PostgreSQL
Read about this role +
Fintech P&L platform, built solo in 18 days: 5 payment/commerce integrations, ~4,500× faster attribution, 641 orphaned orders recovered.
18 days · Solo build5 · Integrations~4,500× · Faster attribution641 · Orphaned orders recovered
Full-stack delivery
A solo engagement building an internal finance and multi-currency P&L platform. The client and product are confidential.
What I built
Built the full platform solo in 18 days with TypeScript, React, Next.js and PostgreSQL: 14 data models, 24 migrations and 40+ indexes.
Integrated 5 payment and commerce systems into one ledger.
Performance
Made ingestion 10× faster and syncs 5–10× faster.
Made the FX backfill ~100× faster and attribution ~4,500× faster.
Cut dashboard load time by more than 95%; filters went from 500 ms to instant.
Data recovery
Recovered 641 orphaned orders and 2,350 mis-attributed transactions.
Delivery
290 commits and 85 tests; delivered with client sign-off.
Job hunting is mostly tabs. I wanted one place that pulls in leads, scores them against my actual profile, tells me why, and writes the first draft, without shipping my career history to someone else’s server.
Changing a prompt and reading three transcripts is not evaluation. Odeon simulates adversarial callers, judges the results per metric, proposes a revision, and keeps the diff so you can see what actually changed.
Most dictation tools are fine until you use them in a terminal, or your first word gets clipped, or a recording vanishes. Svara is my attempt at dictation that survives real use.
Fully on-device ·8.33% WER on CPU ·754 ms to first word
Pulls job leads from several sources, normalizes them, and runs a deterministic quality gate before anything reaches your list.
Ranks each lead against your profile with a graph of your experience plus vector similarity, and shows the reasoning instead of a bare score.
Drafts a tailored resume, cover letter, and outreach message that you review before anything goes out.
How it’s built
A Tauri shell hosts a React workbench and a bundled Python FastAPI sidecar bound to localhost behind a per-run token. Career data lives in a local SQLite CRM; a bundled ONNX embedding model means matching works with no API key. External AI providers are optional and only used when you configure them.
Where it stands
Open source under AGPL, with CI-built installers for all three desktop platforms and a few hundred backend and frontend tests. The launch went further than I expected: 2,249★. A native iPhone version is a separate project below.
System-wide hotkey, a pre-roll buffer so the first syllable is never lost, streaming transcription, and insertion at the active cursor.
Knows what app it is typing into: terminal-safe handling, editor-aware paste, and a clipboard fallback when Windows privilege boundaries block input.
Personal vocabulary, local history, optional LLM cleanup through Ollama, tray controls, and a packaged release with an update flow that waits for you.
Decisions I’d defend
Recognition stays on the device. CPU inference is a first-class path, not a fallback, so it runs on a laptop without a GPU.
Unfinished dictation is protected with temporary recovery audio, and transcript content is redacted from ordinary logs.
A benchmark harness measures latency and accuracy on your hardware rather than quoting numbers from mine.
Where it stands
Shipped and in daily use. Regression tests cover cleanup, insertion, configuration, and log redaction. Performance depends on the model and machine you pick, which is why the benchmark ships with it.
Simulate: generated personas push the agent through difficult, off-script conversations.
Score: an LLM judge grades each run against per-metric thresholds you set.
Revise: the prompt is rewritten toward the failing metrics, then the loop runs again.
How it’s built
Simulation, judging, and revision are separate stages so each can be swapped. Progress streams over WebSockets; run history and word-level prompt diffs are persisted so every optimization cycle can be inspected later.
Where it stands
A working prototype. Simulated callers and judge scores are a fast way to compare prompt versions, not a substitute for listening to real calls, and I say so in the README.
Save an idea, let agents research and argue with it overnight, and wake up to a document you can actually edit.
TypeScript
Node.js
PostgreSQL
React
React Native
Docker
+1Vitest
+1Vitest
Private · Idea workspace
TypeScript
Node.js
PostgreSQL
React
React Native
Docker
Vitest
+1Vitest
Running locally · pre-release
What it does
An idea vault with an API, a web client, and a mobile client sharing one worker system for research, planning, critique, and refinement.
Agents never overwrite. They propose attributed section changes with version history; the owner accepts or rejects.
An opt-in discovery mode investigates an idea, records evidence for and against, and applies explicit acceptance rules.
Decisions I’d defend
PostgreSQL-backed work records with leases, heartbeats, retries, and idempotent scheduling so a multi-hour job can crash and resume.
Provider routing and usage accounting live outside the workflow, so swapping a model does not touch orchestration.
Evidence checks and a publication quarantine: a model saying an idea is good is not treated as proof that it is.
Where it stands
Private and running on my machine. Unit and database integration tests cover proposals, workers, migrations, and recovery. Managed staging and mobile device acceptance are still ahead of a public release.
Source kept private for now.
Ask a question, get a report where every claim points at a quote that really exists in a source.
Python
FastAPI
Pydantic
SQLite
HTTPX
pytest
Private · Research engine
Python
FastAPI
Pydantic
SQLite
HTTPX
pytest
Working prototype
What it does
A planner splits the question into bounded workstreams, searches iteratively, and extracts full pages and PDFs.
A claim ledger stores each claim with its source, quoted passage, document location, and a provenance hash.
Checkpoints in SQLite, cancel and restart, live progress events, a REST API, a dashboard, and a CLI.
Decisions I’d defend
Two gates, not one: the quote must exist verbatim in the retrieved text, and the claim must be supported by that quote in context.
Passages are picked for relevance to the sub-question instead of truncating documents from the top.
When an evidence gate fails the pipeline stops and keeps the rejected material for inspection rather than papering over it.
Where it stands
Private source, working locally. Tests exercise evidence persistence, fabricated-quote rejection, passage selection, and PDF extraction with controlled fixtures. They prove the pipeline behaves; they do not make the internet truthful, so live output still gets read.
Source kept private for now.
Ask questions about a PDF and get answers from its tables and figures, not just its paragraphs.
A vision model transcribes figures into searchable text, with extracted captions as a fallback when quota runs out.
Chunks are embedded with a small sentence-transformer into Qdrant, linked back to page and image metadata.
Retrieval
A LangGraph agent routes between direct answers and retrieval, grades every retrieved chunk for relevance, and rewrites failed queries with a bounded retry.
Visual questions automatically pull in figure transcriptions; Groq serves the final generation for low latency.
A no-context refusal path keeps it from inventing answers the document does not contain.
Where it stands
A single-document prototype with in-memory retrieval, built for an assessment. It shows the ingestion and orchestration; persistence and multi-user isolation are out of scope for this version.
The candidate codes in Monaco. A Python voice agent runs the interview over WebRTC with LiveKit for transport, Deepgram for speech in and out, Silero for voice activity, and Llama 3.3 70B on Groq for reasoning. Next.js in front, Express and MongoDB holding sessions.
Behavior rules
Socratic: hint, never solve.
Speaks only on a bug, an approach change, or a stall.
Cross-checks what the candidate says against what they typed.
After the call
The same agent does a second pass: strict Big-O on time and space, line-by-line fixes, communication analysis quoting the transcript, and a hallucination check comparing claims to code. It renders as a radar chart on the results page.
tdd: no production code without a failing test first, and you watch it fail.
debug: after three failed fixes, question the architecture instead of trying a fourth.
verify: a claim-to-proof table before “done” is allowed. Each skill names the excuse it forbids.
The memory loop
Lifecycle hooks log every edit and command as compact JSONL. A throttled background distiller extracts trigger-to-action lessons through a strict quality gate, and the highest-confidence ones are injected at the next session start. That subsystem later became its own plugin, engram.
The code graph
A dependency graph of the repo with god nodes, modules, blast radius, and cycles, patched incrementally after each change so it never goes stale.
Native subscriptions and credits with StoreKit, server-side purchase records, and recovery of unfinished transactions.
A clearly labelled demo mode with fictional data so the app can be explored without an account or payment.
Decisions I’d defend
The mobile cloud service is separate from the desktop sidecar and scopes everything through authenticated endpoints.
Session credentials sit in the Keychain; account content stays in memory over an ephemeral networking session.
A purchase is not complete until the server says so.
Where it stands
Compiles and passes demo UI tests on a hosted simulator, including onboarding, tracking, dark mode, and larger text. Real auth, payments, device testing, signing, and TestFlight are still ahead.
Source kept private for now.
Earlier tools
Smaller builds from the last couple of years. Still public, still useful for seeing how I think.
5 projects
A phone-call voice agent with live transcripts and two personas that handle a difficult conversation differently.
Connects a FastAPI call API to LiveKit voice sessions and a React monitoring interface.
Explores interruption handling, live transcription, and conversation-event tags. The demo does not establish measured latency or regulatory compliance.
Every message is a node. Forking from any message spawns a branch that inherits history up to that point, so you can chase a tangent without polluting the main thread. Merging is explicit, and the branch stays intact afterwards so you can keep exploring it.
Smart merging
Before a merge, Llama 3.3 on Groq summarises the branch, so the parent absorbs only the new insight, not the whole transcript.
Merge logic filters out shared history and appends only what is new.
Each merge is recorded as a distinct system event in the chat stream.
Tree navigation
A visual Git-style sidebar renders the whole conversation graph. Click any node to jump; each branch keeps its own history up to the fork point, so switching context is unambiguous.
How it’s built
Next.js App Router with Server Actions, the conversation tree in Neon Postgres through Drizzle, inference wired through the Vercel AI SDK, and full Markdown and LaTeX rendering in the chat.
Things I built because I wanted them to exist. Origami, games, wallpapers, a turtle.
10 projects
Private · Origami in 3D
Learn origami from a 3D model you can orbit, scrub, and pause while you fold along with real paper.
TypeScript
React
Three.js
GSAP
Python
TypeScript
React
Three.js
GSAP
Python
Personal learning app
The engine
Rigid polygon clipping and folding with layer selection, mountain and valley motion, unfolding, and turn-over steps.
The moving layer is chosen from the authored crease segment so a fold on one flap does not drag unrelated paper along.
Previews are generated from the same geometry as playback, so the picture and the animation never disagree.
The lessons
Crease guides, moving-flap highlights, alignment targets, and a tutorial theater with camera moves, chapters, captions, narration, speed control, and a fold-along pause mode. Reduced motion and keyboard controls are built in.
Honest limits
It models rigid folds, not paper physics or collisions. Complex workshop diagrams deliberately simplify hidden layers. Tests check paper-area conservation, edge lengths, layer selection, and chapter coverage.
Source kept private for now.
Private · Interactive handbook
An applied-AI handbook where you can poke at tokens, embeddings, retrieval, and prompt injection instead of reading about them.
JavaScript
HTML
CSS
Web Audio
JavaScript
HTML
CSS
Web Audio
Personal project
What’s inside
A standalone docs site with topic navigation, themes, and progress tracking, no build step required.
Playgrounds for token sampling, embedding space, retrieval pipelines, prompt-injection awareness, agent failures, and model-based evaluation.
Quizzes, interview practice, and audio and video lessons with transcripts and saved playback position.
Honest limits
The demos are simplified on purpose to expose cause and effect. They are not production tokenizers or retrieval engines, and I have not evaluated it as a formal course.
Source kept private for now.
Private · Browser game
How big is a blue whale next to a bus? Guess by resizing, measuring, or placing things on a scale, then see how far off you were.
TypeScript
React
Pointer Events
Web Audio
TypeScript
React
Pointer Events
Web Audio
Personal project
Three modes
Relative size: resize an object beside a reference.
Make a measurement: construct a length with your hands.
Place it: drop an object on a logarithmic size line.
Decisions I’d defend
Every measurement is stored in centimetres with explicit conversions and a reference source attached.
Scoring is symmetric: guessing half the size and double the size are equally wrong.
Pinch and drag handle pointer add, release, cancel, and near-coincident fingers without jumps. Keyboard and buttons work too.
Honest limits
Artwork is stylized and screen pixels are not life-size units. Logic tests cover scoring, conversions, log mapping, challenge rotation, and gesture transitions.
Source kept private for now.
Live wallpapers behind your desktop icons: still images with WebGL drift, refraction, and light, picked from a floating scene panel.
Electron
WebGL
Windows
Private · Windows wallpaper studio
Electron
WebGL
Windows
Personal experiment
Inside the project
Attaches wallpaper behind Windows desktop icons and offers a floating scene picker, tray controls, and playback settings.
Applies WebGL drift, refraction, and lighting to still images, with character-protection masks on selected scenes. These are animated stills, not reconstructed character animation.
Source kept private for now.
Your webcam as live ASCII or emoji art, with background segmentation so only you get turned into characters.
Full-color mode samples the original image's pixel colors and applies them per-character, creating the illusion of the original image through typography. Download triggers a dedicated HD render pass at the source image's original resolution — so a 4K input gives you a 4K ASCII PNG, not a screenshot.
Changing a prompt and reading three transcripts is not evaluation. Odeon simulates adversarial callers, judges the results per metric, proposes a revision, and keeps the diff so you can see what actually changed.
PythonFastAPIWebSocketsReactSQLiteGroq
The loop
Simulate: generated personas push the agent through difficult, off-script conversations.
Score: an LLM judge grades each run against per-metric thresholds you set.
Revise: the prompt is rewritten toward the failing metrics, then the loop runs again.
How it’s built
Simulation, judging, and revision are separate stages so each can be swapped. Progress streams over WebSockets; run history and word-level prompt diffs are persisted so every optimization cycle can be inspected later.
Where it stands
A working prototype. Simulated callers and judge scores are a fast way to compare prompt versions, not a substitute for listening to real calls, and I say so in the README.
A phone-call voice agent with live transcripts and two personas that handle a difficult conversation differently.
LiveKitDeepgramGroqFastAPIReact
Inside the project
Connects a FastAPI call API to LiveKit voice sessions and a React monitoring interface.
Explores interruption handling, live transcription, and conversation-event tags. The demo does not establish measured latency or regulatory compliance.
Long chats bloat the context window: every tangent and dead end stays in history, burning tokens and diluting what matters. BranchGPT treats a conversation as a Git-style tree instead of a list.
Next.jsTypeScriptDrizzle ORMNeon PostgresVercel AI SDKGroq
The idea
Every message is a node. Forking from any message spawns a branch that inherits history up to that point, so you can chase a tangent without polluting the main thread. Merging is explicit, and the branch stays intact afterwards so you can keep exploring it.
Smart merging
Before a merge, Llama 3.3 on Groq summarises the branch, so the parent absorbs only the new insight, not the whole transcript.
Merge logic filters out shared history and appends only what is new.
Each merge is recorded as a distinct system event in the chat stream.
Tree navigation
A visual Git-style sidebar renders the whole conversation graph. Click any node to jump; each branch keeps its own history up to the fork point, so switching context is unambiguous.
How it’s built
Next.js App Router with Server Actions, the conversation tree in Neon Postgres through Drizzle, inference wired through the Vercel AI SDK, and full Markdown and LaTeX rendering in the chat.