Building Pulse: A Relationship-Prioritization Engine on Oracle 23ai Vector Search
Oracle 23ai September 01, 2026We're all bad at staying in touch with the people who matter. Not because we don't care, because there's no signal telling you who to reach out to today. Your closest mentor from two years ago quietly falls off your radar while your phone keeps nudging you about people you barely know. I built Pulse to fix that: a personal CRM that ranks your contacts by how urgently they need a touchpoint, using a mix of relationship closeness, time decay, and shared interests, and it's powered end-to-end by Oracle Autonomous Database 23ai.
The Core Idea: Decay, Weighted by Who Actually Matters
Pulse ranks every contact with a single priority score:
Priority Score = Decay Risk × Tier Weight × (1 + 0.5 × Compatibility)
- Decay Risk models relationship fade the way you'd model radioactive decay:
1 - 0.5^(days_since_contact / half_life_days). The longer it's been, the higher the risk climbs. - Tier Weight reflects how close the relationship is, inner circle contacts are weighted at 2.0, mentors at 1.75, friends at 1.5, acquaintances at 1.0, so a fading connection with someone close to you surfaces faster than a fading connection with someone peripheral.
- Compatibility is cosine similarity between a contact's interest embedding and your own goals/interests vector, so the people you'd actually have something to talk about with get bumped up the list.
That's the ranking logic. The interesting engineering decision is where the vector math actually happens.
Why Oracle 23ai, Not a Separate Vector Database
The obvious architecture for "compute similarity between embeddings" is: stand up a vector database (Pinecone, Weaviate, pgvector, whatever) alongside your relational store, and now you're syncing two systems and paying for two pieces of infrastructure.
Oracle Database 23ai's native VECTOR(384, FLOAT32) column type made that unnecessary. Contact interest embeddings, generated locally with sentence-transformers/all-MiniLM-L6-v2, no external embedding API required, live in the same table as the contact's name, tier, and last-contact date. Compatibility scores are computed in Python using cosine similarity on the fetched vectors, but the storage, indexing, and querying all happen in one Autonomous Database instance. One system of record, no synchronization headaches, no extra vendor.
For anyone evaluating whether AI Vector Search actually changes how you'd architect a project (versus being a checkbox feature), this was a real test: it removed an entire category of infrastructure decisions I would otherwise have had to make.
Grounded Nudges, Not Hallucinated Small Talk
A ranked list is useful, but "who to talk to" is only half the problem, the other half is what to say. Pulse's nudge pipeline filters contacts above a priority_score > 0.6 threshold, extracts shared interest terms via semantic similarity, and asks Gemini to generate a conversation starter, explicitly instructed to reference only verified terms already stored in that contact's interest_text, never invented details. It's a small constraint, but it's the difference between a nudge that sounds like you actually know the person and one that's generic AI filler.
In the demo seed, three contacts and a stated goal of "AI agentic workflows, distributed databases, vector search", the top-ranked contact came back with a nudge asking about distributed databases and vector search specifically, because that's what was actually in their stored interest text. No embellishment, no guessing.
What's Next
Pulse is functional end to end, contact CRUD, priority ranking, embedding generation, and the nudge pipeline all run against a live Oracle 23ai instance (Always Free tier works fine for this scale). Next up: expanding the tier/decay model with configurable half-lives per relationship type, and exploring Oracle 23ai's other AI-native features for the enrichment step.
If you're curious about the code or want to run it yourself, it's on GitHub: pulseweight.