Read the story

Architecture

Grades new patient calls with Gemini.Every five minutes, 6 AM to 6 PM.

Every five minutes from 6 AM to 6 PM, a Cloud Run Job pulls new recordings from the phone system's SFTP drop and keeps only real new patient calls on an NPC line. Gemini transcribes and judges each one with a quote per criterion, plain Python adds up the score, and the report lands in the NPC's inbox and on a dashboard.

14 min

median from hang up to a graded call, in October

1 poll

at a time, held by a Firestore lease since reports went out twice

690

calls graded out of 28,745 recordings read since June 17

16 days

from the first commit on Jun 2 to grading live calls on Jun 18

New York --:-- simulated

Next poll, 6 AM to 6 PM
Hybridge NPC Coach Every five minutes from 6 AM to 6 PM, a Cloud Run Job pulls new recordings from the phone system's SFTP drop and keeps only real new patient calls on an NPC line. Gemini transcribes and judges each one with a quote per criterion, plain Python adds up the score, and the report lands in the NPC's inbox and on a dashboard. Cloud Scheduler every 5 min, 6 AM to 6 PM OAuth Vertex Gemini Flash, Flash Lite, Pro VarConnect SFTP call recordings, WAV SFTP poller new files only, 1 lease Grader model judges, code adds Gmail per call + 5 PM email NPCs and leaders 7 NPC lines Cloud Storage transcripts, reports, MP3 BigQuery calls and score rows Daily summary Cloud Run Job, 5 PM npc-webapp React, Google sign in new WAVs run job audio transcribe, judge report inbox transcripts, reports score rows today 5 PM summary calls, trends sign in Cloud Run Job npc-poller, us-central1

Hover a part to trace what it touches. Click it for its notes.

  • Built by me
  • Managed by Google
  • Outside system
  • Main data path
  • On demand
  • Credentials cross

When it breaks

What each failure does, and what it does not do.

  1. Two runs overlap

    A run that outlasts five minutes holds a Firestore lease, so the next one exits at once. A stale lease expires after 70 minutes and the next run takes it over.

    src/processor/poll_lock.py:1

  2. Gemini hits a rate limit

    429 and 503 errors retry up to five times with backoff inside the adapter. A call that still fails is logged and picked up by the next run.

    src/common/transient_retry.py:1

  3. The model breaks the schema

    The validation error goes back to the model for a repair try. If that fails too, the call is saved as not gradeable with the field that broke.

    src/score/scorer.py:122

What I tuned

Small choices that keep it fast and cheap.

  1. Names before network calls

    A file name already in the table is skipped before any SFTP stat. Before this, a run made about 7,700 stat calls and could outlast the five minute schedule.

    src/processor/poller.py:101

  2. Filter before transcribing

    Lines off the allow list, call park files and calls under 60 seconds never reach a model. Of 28,745 recordings, 3,334 were ever transcribed.

    src/processor/poller.py:127

  3. Math outside the model

    Gemini returns six integers and three gate answers. A Python function does the 40/40/20 sum, so the same verdict always gives the same score.

    src/score/formula.py:1

Guards

Rules the code enforces, not rules people remember.

  1. NPC lines only

    An allow list of NPC extensions decides what gets graded. Every other line is tagged and left alone, never transcribed.

    src/processor/poller.py:93

  2. Pinned SFTP host key

    The poller checks the server's key fingerprint before it logs in and refuses a mismatch.

    src/ingest_sftp/sftp_client.py:24

  3. Signed remove links

    The remove button in each email is a signed link for that one call. Opening it changes nothing; only the page's own POST removes the call, so mail scanners are harmless.

    src/delivery/remove_link.py:1