Inspiration
Call centres process thousands of sensitive conversations. A single unconstrained LLM endpoint hides: which agent ran, which model/memory/policy revision was used, what happens when reviewers disagree, whether a failed job can resume, and whether the model can directly mutate state. Tele-Governed makes those boundaries explicit and verifiable — not with a dashboard, but with durable jobs, policy gates, and hash-bound records.
Original seam: on Vertex AI Agent Engine + Memory Bank, a byte-identical quarantine request returned invoice-classifier → billing-reconciler after one memory correction (evidence/h4b-*.sse). The trace kept text, not memories/{id}/revisions/{rev}. We record the retrieval boundary so the move is explainable.
What it does
Live: https://tele-governed-597227190850.us-central1.run.app — console + API one origin. Dedicated Tele view: /tele
Approved GCS audio (TELE_APPROVED_AUDIO_URIS allowlist)
→ POST /api/tele/batches → Firestore tele_jobs (queued first)
→ Pub/Sub telephony-batches → private Cloud Run worker (/api/tele/pubsub, IAM-protected, TELE_ENABLE_PUBSUB_PUSH)
→ tele-ingestor (ASR)
→ tele-qa-analyst (Gemini 3.5 Flash, 4 claimant roles: compliance / customer-harm / evidence-skeptic / operations)
→ commit → reveal → code verifies spans, detects incompatible classification/remedy, deterministic adjudication
→ awaiting_human for critical/quarantine/delete (exact remediation envelope bound to SHA-256)
→ tele-report-remediator → Firestore report + decision ledger + correction ledger
- Resumable:
BatchWorkerpersists each stage;retryableon transient ASR/Gemini failure resumes after last durable stage, not re-transcribing. Proven:evidence/video-rehearsal.jsonjobd348f36b…attempts 6:queued → published → transcribing → failed (bucket allowlist) → transcribing → analyzing → disputing → governing → awaiting_human - Dispute: typed
EvidenceSpan(wav,start_ms,end_ms,text,sha256)ops/disputes.py,commit = sha256(reveal),verify_rulingchecks hashes, pureadjudicate()withROLE_PRIORITY evidence-skeptic > customer-harm > compliance > operationsandINCOMPATIBLE_REMEDIES. - ASR routing: deterministic
inspect_wav+fingerprint(customer, codec, noise band, accent, language_mix) +choosemeasuredWER + 0.15*calibrationwithmin_samplesfloor and shadow scoring on correctionops/asr_routing.py. - Remediation:
make_envelopebinds job, target revision/state, action, payload, evidence hashes, policy revision, retrieval hash, ruling hash, expiry 15m;APPROVAL_EXPIREDif target changed, edit createsSUPERSEDEDnew envelopeops/remediation.py. - Verification:
GET /api/tele/batches/{job}/dispute/verify+/api/decisions/{hash}/verifycapabilityBOUND_UNCORROBORATED / BOUND / UNBOUND / NOT_CERTIFIEDops/verifier.py—BOUNDonly with exported Cloud Trace bundle (scripts/export_bundle.py).
How we built it
- Agent Engine: Gemini 3.5 Flash via
google-cloud-aiplatform[agent_engines,adk],deploy/agent_engine.py(now with--gatewayAgent Identity +AGENT_IDENTITY). - Memory Bank:
VertexMemoryBankfor retrieval manifestops/retrieval.py— fail-closed unlessGAO_ENGINE_IDset,TELE_ALLOW_DEMO=1labeledmode=demo. - API/Worker:
FastAPI+uvicornops/service.pyone image, two deployments (public vsTELE_ENABLE_PUBSUB_PUSHworker with Cloud Run IAM as per-route guard). - State:
Firestore(fleet, jobs, ledger, corrections, routing measurements),Pub/Subpublisherops/jobs.py,CloudRunASRTranscriberops/jobs.py:152. - Hardening: Model Armor
TELE_MODEL_ARMOR_TEMPLATE, Agent Registry/Gateway/Identity + IAP egress grants (deny canary 403), OpenTelemetryops/telemetry.py— all reported viaGET /api/tele/evidenceonly whenTELE_*_EVIDENCEfile present (content-bound, not config text). - Tests:
PYTHONPATH=. pytest -q237 passed+ frozentele-ci/router-ci4-arm benchmarks.
Challenges we ran into
- Memory scope must be
{app_name,user_id}exactly or it’s invisible to the agent — debugged viascripts/memories.sh:3. - Global
GEMINI_LOCATION=global429 whileeurope-west2200 — made model location a deploy var. Pub/Subpush IAM applies to whole service, not one route — split public vs private worker.verifyhonesty: manifest built by executor’s call, not observed agent retrieval →BOUND_UNCORROBORATEDuntil trace exported.
Accomplishments that we’re proud of
- Real async pipeline that survives failure and proves it (not a demo queue).
- Code, not prose, settles disputes and binds approvals.
- No claim without artifact:
GET /api/tele/evidencerefuses unverified controls;wer-evidence.jsongate keeps accuracy claimfalseuntil benchmark passes.
What we learned
Audit boundary is revision + population, not just text. Falsifiability matters: ablation identities stripped → UNBOUND, population stripped → UNBOUND, manifest removed → NOT_CERTIFIED proves the binding is load-bearing.
What's next
- Replace seeded readiness evidence with deployment-sourced checks (still per-exact-revision, never inherited).
- Promote router policy via frozen benchmark gate only.
- Expand approved audio set beyond demo smoke object with owner permission.
Built With
- agent-gateway
- agent-identity
- agent-registry
- fastapi
- gemini-3.5-flash
- google-adk
- google-cloud
- google-cloud-firestore
- google-cloud-pubsub
- google-cloud-run
- google-cloud-trace
- iap
- model-armor
- opentelemetry
- python-3.12
- vertex-ai-agent-engine
- vertex-ai-memory-bank
Log in or sign up for Devpost to join the conversation.