Kara: AI Meeting Agent
A real-time meeting-capture product across web, iOS and Android that acts on meetings end to end, from live capture to automated follow-up.
3
enterprise customers
4
connected systems
2,785
automated tests
95%
of the agent service's commits
An agent that works inside meetings
Kara records a meeting, then does the work that follows: searches past meetings and uploaded documents, reads the note on screen, keeps a contact book and to-dos, looks up Google Calendar and Drive, and queries a real-estate CRM. It is now used by three enterprise customers: two banks and a real-estate group.
I led the agent workstream and wrote 95% of the agent service's commits. My slice: the agent service with its tool and guard layer, memory, the knowledge-graph service behind it, the document search service, the Google Calendar connector, the real-estate platform integration, and the quality loop that keeps it honest. The service has 2,785 automated tests.
Three nodes, one tool loop
The assistant is a small graph, not a single prompt: fetch context, then run a tool-calling loop, then end. Tools are served over MCP and every call is routed through the interceptor rather than straight to a backend, so reads and writes follow the same rules.
- Three-node graphLangGraph runs context, then a ReAct tool loop. Streamed to clients over FastAPI SSE.
- Postgres checkpointsEvery turn is checkpointed, so a blocked or unsafe turn can be rewound.
- RedisCaches and the opaque-ID alias ledger.
- Mem0 on QdrantLong-term memory, admitted through a judge.
- Document searchDense and sparse retrieval fused with reciprocal rank fusion on Qdrant, in a service I built.
- LLM gateway and LangfuseAll model calls go through a gateway. Every request is traced, and prompts are managed live.
The model never holds the keys
The interceptor treats the model as an untrusted caller. A prompt, or a hallucination, must never be able to read or write another user's data, so the controls live in server code. I treat hallucinated identifiers as a grounding problem in the architecture, not a thinking problem, so the ID-alias layer removes every identifier the model could invent.
- Scope injectionThe interceptor sets user, workspace and note scope on the tools that need it, and discards any identifier the model supplies.
- Hidden parametersInjected parameters are removed from the schema the model sees, so there is nothing for it to fill in.
- Opaque aliasesInternal IDs never reach the model. It sees short aliases from a per-session ledger, and an unknown ID is rejected.
- Egress filterID-shaped tokens in the final answer that are not in the ledger are redacted before the user sees them.
Writes need a human yes
The agent proposes and the user decides. Each kind of write has its own confirmation pattern, and connector tokens are never stored in prompts, memory or logs.
- ContactsSaving a contact returns a confirmation request until the user explicitly agrees. Duplicates are caught first.
- To-dosThe agent previews the list, the user confirms, and only then are items created. Stale previews are rejected.
- Screen editsEdits to the note on screen are proposal cards. The agent has no write path; the user taps Apply.
- Write-state ledgerEach write is marked committed, not sent or unknown, so a write that may have landed is never re-sent.
Memory that forgets on purpose
Conversations are checkpointed in Postgres, and a rolling summary trims long threads. Long-term memory is gated: a judge admits only durable facts the user stated, and each one must be traceable to the user's own words. When an input guardrail blocks a turn, the thread rewinds to the checkpoint before it, leaving no trace. When a streamed answer turns out unsafe, the stored answer is overwritten.
Quality from production
Two loops, both built around Langfuse traces. AI Agent Evaluations has an agent role-play users against the dev environment, judges the resulting traces and compares them with a baseline. A hill-climbing technique reads production traces daily, read-only, screens them cheaply, has an LLM triage the flagged ones, and proposes fixes in tiers. Every proposal ends at a draft merge request that a human reviews.
Four connected systems
The connector layer is stateless: the user's token arrives with each call and is discarded after it. It exposes both REST and MCP entry points.
- Google CalendarCalendar access through a connector module I built.
- Google DriveFile lookup through the same connector layer.
- Real-estate CRM platformSearch and write-back through an MCP integration I led.
- Partner bank's Salesforce CRMRetrieval and write-back for a banking customer.
Also in my slice
A per-user knowledge graph that links meetings and folds to-dos together, contact insight generated from meeting history, screen-context editing, LLM-gateway routing, live prompt management in Langfuse, and prompts ordered to keep the model's prefix cache warm.
Tech stack
- Python
- FastAPI
- LangGraph
- MCP
- FastMCP
- Postgres
- Redis
- Qdrant
- Mem0
- FalkorDB
- Gemini
- Langfuse
- SSE
- pytest
- Docker
- Kubernetes
- GitOps