Independent Project · AI Solutions Engineering
Salesforce Intelligence
Grounded CRM Answers
A retrieval-augmented chat assistant that answers natural-language questions about Salesforce accounts, cases, and opportunities, grounded in the actual data, with sources shown for every answer.
Status: Live
This is an independent project built against a Salesforce developer org, not a real customer environment. It does not represent any employer's product, data, or internal methods.
The problem
Support and sales context lives scattered across Cases, Accounts, and Opportunities. Finding the answer to a simple question, like a customer's open cases or a deal's status, means digging through multiple records by hand. A chat interface only helps if its answers are actually grounded in the CRM data rather than guessed, and if every answer shows its sources so it can be trusted.
How it works
Salesforce data flows through embeddings into Pinecone once; every question then retrieves the most relevant records and hands them to the model as its only source of truth.
- 01Pull Cases, Accounts, and Opportunities from Salesforce via SOQL.
- 02Convert each structured record into a plain-English paragraph, since embedding models work on natural language, not raw JSON, plus a matching metadata dict (type, amount, is_closed) for structured filtering later.
- 03Embed every document and store the vectors, with metadata, in a Pinecone index. The index is cleared first so re-running the pipeline is idempotent instead of duplicating data.
- 04At question time, embed the question, retrieve the nearest document vectors, and hand them to the LLM as context with an explicit instruction not to answer beyond it. Retrieval also accepts an optional metadata filter for exact criteria like "open deals over $100k," since semantic similarity alone only captures topical relevance.
- 05Serve it through a chat UI with clickable sample prompts, showing the retrieved source records alongside every answer.
Architecture
Python, OpenAI for embeddings and chat, LangChain, Pinecone, Streamlit, and the Salesforce API via simple-salesforce.
Why JWT Bearer auth
Salesforce has been retiring credential-based SOAP login org-wide, and new orgs block the OAuth 2.0 username-password flow by default; both are being phased out in favor of provable-identity flows. This app authenticates via the OAuth 2.0 JWT Bearer flow instead: a self-signed certificate proves the app's identity, and no password is ever transmitted. It's also the standard pattern for real server-to-server Salesforce integrations, not just a workaround.
Retrieval also needed to go beyond pure semantic search: a metadata filter handles exact criteria, like deal size or case status, that similarity search alone can only approximate.
What I'd do for production
Say every limitation plainly. This project demonstrates retrieval design and engineering judgment, not a production-hardened deployment.
Incremental sync
Pull only records modified since the last run and upsert into Pinecone, instead of a full re-embed every time.
Retrieval evaluation
A labeled set of question and expected-answer pairs to catch retrieval regressions. Not hypothetical: adding Opportunities to the corpus silently broke a previously reliable question, because deals started crowding out that same account’s support cases in the top-k results. A query classifier that picks a metadata filter based on question intent would fix this generally instead of per-query.
Cost monitoring
Track embedding and completion token usage per query, since k directly trades off recall against noise and cost.
Conversational memory
Multi-turn chat that remembers prior context. Today every question is answered independently.
Broader auth
The JWT Bearer flow here authenticates as a single pre-authorized user; a multi-user production app would need full OAuth authorization-code flow with per-user consent.
The retrieval-evaluation gap isn't hypothetical: adding Opportunities to the corpus silently broke a previously reliable answer, because deals started crowding out that same account's support cases in the top-k results. A production system needs a way to catch that automatically, not by eyeballing answers.