Series: Building Quorentra CRM from Zero — A Modular, ChatGPT-Native AI CRM

1. Introduction
Part 35 introduced the AI Agent Execution Layer.
Quorentra can now receive a business goal such as:
Investigate the ACME opportunity and determine whether we need to intervene.
An agent can then:
Gather Context ↓Retrieve CRM Data ↓Search Knowledge ↓Evaluate Evidence ↓Reason ↓Recommend ↓Propose Actions
That is a major capability.
But there is an architectural problem hiding underneath it.
Suppose ten different agents need to determine whether an opportunity is becoming inactive.
Each agent could independently retrieve:
ActivitiesMeetingsEmailsTasksOpportunity Changes
and reason about whether engagement has declined.
That works.
But it is inefficient and inconsistent.
One agent might decide:
Engagement = Healthy
while another concludes:
Engagement = Declining
from the same underlying data.
The problem becomes larger as Quorentra introduces questions such as:
Which opportunities have the strongest momentum?
Which deals are becoming stale?
Which accounts have weak stakeholder coverage?
Which opportunities are progressing unusually slowly?
Which opportunities require immediate attention?
Which salesperson has the healthiest pipeline?
These questions depend on reusable business intelligence.
Instead of asking AI agents to reconstruct the same signals repeatedly, Quorentra needs a dedicated intelligence layer.
That is what we will build in Part 36.
2. From Raw CRM Data to CRM Intelligence
A traditional CRM primarily stores facts.
For example:
Opportunity StageOpportunity ValueClose DateActivitiesTasksMeetingsContactsDocuments
These are important.
But they do not directly answer:
Is this opportunity healthy?Is engagement improving?Is the opportunity becoming stale?Is there enough stakeholder involvement?Is the deal progressing normally?Should someone intervene?
We need derived information.
The architecture becomes:
Raw CRM Data ↓Features ↓Signals ↓Scores ↓Intelligence ↓ChatGPT / Agents / Workflows
3. What Is a CRM Signal?
A signal is a derived observation about CRM state.
Examples:
No customer activity for 18 daysThree customer meetings in the last 30 daysTwo overdue follow-up tasksProposal deadline is six days awayOpportunity has only one customer stakeholderCustomer response frequency decreased by 40%Opportunity remained in Proposal for 35 days
These are not yet overall judgments.
They are structured observations.
4. What Is a Score?
A score combines one or more signals into a normalized business measure.
For example:
Engagement Score:72 / 100
or:
Opportunity Health Score:58 / 100
A score is useful because it allows:
RankingFilteringTrend AnalysisThresholdsAutomationDashboardsAgent Reasoning
But scores must remain explainable.
5. The Most Important Rule
The core principle for Part 36 is:
A score without evidence is not intelligence.
If Quorentra says:
Opportunity Health = 42
the user should be able to ask:
Why?
And Quorentra should answer with evidence such as:
- No customer activity for 18 days.- Two follow-up tasks are overdue.- The proposal deadline is six days away.- Stakeholder coverage is limited to one active contact.- The opportunity has remained in Proposal 12 days longer than the tenant baseline.
That is much more useful than an unexplained number.
6. Deterministic Intelligence First
Part 35 introduced AI reasoning.
It might therefore be tempting to ask an LLM:
Give this opportunity a health score between 0 and 100.
That should not be our core scoring architecture.
Why?
Because the same opportunity might receive:
61
today and:
67
tomorrow without any CRM data changing.
That makes:
Trend AnalysisAutomation ThresholdsRegression TestingAuditingHistorical Comparison
much harder.
Therefore:
Core CRM scores should initially be deterministic.
AI can explain and interpret them.
It should not secretly define them.
7. Intelligence Architecture
The architecture becomes:
CRM Domain Data ↓Feature Extraction ↓Signal Calculation ↓Score Calculation ↓Intelligence Store ↓ ┌─────────────┬──────────────┬──────────────┐ ▼ ▼ ▼ ▼ChatGPT Agents Workflows Analytics
This creates a reusable intelligence substrate.
8. IntelligenceSignal
Introduce:
class IntelligenceSignal(BaseModel): signal_type: str entity_type: str entity_id: UUID value: object confidence: float | None = None calculated_at: datetime evidence_ids: list[str] = []
A signal represents a specific derived fact.
9. Example Signal
For example:
{ "signal_type": "days_since_customer_activity", "entity_type": "opportunity", "entity_id": "...", "value": 18, "calculated_at": "2026-08-03T08:00:00Z", "evidence_ids": [ "activity:12345" ]}
This signal is deterministic.
Given the same CRM state and calculation time, Quorentra should produce the same result.
10. Signal Types
Create a controlled registry of signal types.
Examples:
days_since_customer_activitycustomer_activity_count_7dcustomer_activity_count_30dmeeting_count_30demail_count_30dopen_task_countoverdue_task_countdays_in_current_stagedays_until_closestakeholder_countactive_stakeholder_countdecision_maker_identifiedproposal_document_presentproposal_deadline_dayscustomer_response_latencyactivity_momentumstage_velocityengagement_trend
Do not use arbitrary string signals throughout the codebase without central definitions.
11. SignalDefinition
Introduce:
class SignalDefinition(BaseModel): key: str name: str description: str entity_type: str value_type: str unit: str | None = None
Example:
Key:days_since_customer_activityName:Days Since Customer ActivityEntity:OpportunityValue Type:integerUnit:days
12. Signal Evidence
Every meaningful signal should point back to its source.
For example:
Signal:days_since_customer_activity = 18
Evidence:
Last customer activity:2026-07-16Calculation time:2026-08-03
This creates traceability.
13. SignalEvidence
Introduce:
class SignalEvidence(BaseModel): source_type: str source_id: UUID field: str | None = None observed_value: object | None = None
A signal may have multiple evidence items.
14. Evidence Is More Than Citations
Evidence allows Quorentra to answer:
Where did this signal come from?Which record caused it?Which timestamp was used?Which meeting contributed?Which task was overdue?
This becomes essential for user trust and debugging.
15. Features Versus Signals
It is useful to distinguish features from business signals.
A feature is usually a lower-level calculated value.
For example:
activity_count_30d = 8
A signal may interpret that feature:
recent_activity_level = moderate
And a score may combine several signals:
engagement_score = 71
The hierarchy becomes:
CRM Data ↓Features ↓Signals ↓Scores
16. FeatureDefinition
Introduce:
class FeatureDefinition(BaseModel): key: str entity_type: str value_type: str description: str calculation_version: str
Examples:
customer_activity_count_7dcustomer_activity_count_30dcustomer_activity_count_90dmeeting_count_30ddays_since_last_activitydays_in_stageopen_task_countoverdue_task_count
17. FeatureValue
Persist reusable feature values:
class FeatureValue(Base): __tablename__ = "feature_values" id: Mapped[UUID] organization_id: Mapped[UUID] entity_type: Mapped[str] entity_id: Mapped[UUID] feature_key: Mapped[str] value_json: Mapped[dict] calculation_version: Mapped[str] calculated_at: Mapped[datetime]
Tenant isolation remains mandatory.
18. Why Persist Features?
We could calculate everything on demand.
But persistence gives us:
Faster ReadsHistorical AnalysisTrend DetectionAgent EfficiencyDashboard PerformanceReproducibility
It also reduces repeated calculations across ChatGPT, workflows, agents, and frontend dashboards.
19. Feature Store
We can introduce a lightweight feature store abstraction.
Do not interpret this as requiring a dedicated machine-learning feature platform.
For the MVP:
PostgreSQL
is sufficient.
The logical abstraction is more important than the infrastructure.
20. FeatureStore
Introduce:
class FeatureStore: async def get_feature( self, *, organization_id: UUID, entity_type: str, entity_id: UUID, feature_key: str, ): ... async def save_feature(...): ...
This gives the intelligence layer a consistent interface.
21. Signal Freshness
Signals become stale.
For example:
days_since_customer_activity = 18
will be wrong tomorrow even if no CRM event occurs.
Therefore every signal needs freshness semantics.
22. SignalFreshness
Define concepts such as:
calculated_atvalid_untilfreshness_status
Possible statuses:
freshstaleexpired
A user should not receive an old score as if it were current.
23. Event-Driven Recalculation
Many signals can update when CRM events occur.
For example:
activity.created ↓Recalculate Engagement Features
or:
opportunity.stage_changed ↓Recalculate Stage Features
The Part 34 event architecture becomes useful again.
24. Scheduled Recalculation
Some features depend on the passage of time.
Examples:
days_since_last_activitydays_in_stagedays_until_closetask_overdue_days
These require scheduled recalculation.
Architecture:
Schedule ↓Workflow / Intelligence Job ↓Find Relevant Entities ↓Recalculate Time-Based Features
25. Hybrid Recalculation
The best approach is usually:
Event Driven+Scheduled
Events provide fast updates.
Schedules ensure time-based features remain accurate.
26. FeatureCalculator
Introduce:
class FeatureCalculator: async def calculate( self, *, organization_id: UUID, entity_type: str, entity_id: UUID, feature_key: str, as_of: datetime, ): ...
Each feature calculation should be deterministic and versioned.
27. Calculation Versioning
Suppose we initially define:
activity_count_30d
as all activities.
Later we decide internal notes should not count as customer engagement.
The meaning has changed.
Therefore calculations need versions.
For example:
activity_count_30d:v1activity_count_30d:v2
Historical values should retain the version that produced them.
28. SignalCalculator
Introduce:
class SignalCalculator: async def calculate( self, *, entity_id: UUID, signal_type: str, as_of: datetime, ) -> IntelligenceSignal: ...
The calculator consumes features and CRM evidence.
29. Engagement Signals
One of the first intelligence families should be engagement.
Possible features:
customer_activity_count_7dcustomer_activity_count_30dmeeting_count_30demail_count_30ddays_since_customer_activityaverage_response_latency
Derived signals:
engagement_levelengagement_trendengagement_declineengagement_consistency
30. Engagement Score
Normalize engagement into:
0–100
where:
0=No meaningful engagement
and:
100=Very strong recent engagement
Do not interpret 100 as a guarantee that the opportunity will close.
It only represents the defined engagement dimension.
31. Example Engagement Components
An MVP formula might include:
Recency of Customer Activity 30%Customer Activity Frequency 25%Meeting Frequency 20%Customer Response Behavior 15%Engagement Trend 10%
These weights are explicit.
They are not hidden inside an LLM prompt.
32. Score Component
Introduce:
class ScoreComponent(BaseModel): key: str weight: float raw_value: float normalized_value: float contribution: float
This makes every score decomposable.
33. Score Calculation
Conceptually:
Score=Σ(normalized_component × weight)
For example:
Recency 80 × 0.30 = 24.0Frequency 70 × 0.25 = 17.5Meetings 60 × 0.20 = 12.0Response 50 × 0.15 = 7.5Trend 40 × 0.10 = 4.0---------------------------------Engagement Score = 65
Now the score can be explained precisely.
34. ScoreDefinition
Introduce:
class ScoreDefinition(BaseModel): key: str name: str description: str entity_type: str minimum: float maximum: float version: str components: list[ScoreComponentDefinition]
Definitions should be immutable once used historically.
35. ScoreValue
Persist:
class ScoreValue(Base): __tablename__ = "score_values" id: Mapped[UUID] organization_id: Mapped[UUID] entity_type: Mapped[str] entity_id: Mapped[UUID] score_key: Mapped[str] score_version: Mapped[str] value: Mapped[float] calculated_at: Mapped[datetime] explanation_json: Mapped[dict]
36. Historical Scores
Do not overwrite every previous score.
Suppose:
July 1:Engagement = 84July 15:Engagement = 76August 1:Engagement = 59
The trend itself contains valuable intelligence.
If we retain only:
59
we lose that information.
37. Score History
The intelligence store should therefore support:
Current ScoreHistorical ScoresScore Trend
This enables questions such as:
Which opportunities experienced the largest engagement decline during the last 30 days?
38. Opportunity Health Score
Now we can build a higher-level score.
Potential components:
EngagementActivity MomentumStage ProgressTask ExecutionStakeholder CoverageClose-Date RiskDeal VelocityKnown Risk Signals
This produces:
Opportunity Health Score
39. Health Is Multi-Dimensional
Do not reduce everything to one opaque score.
For example:
Opportunity Health:58
should also expose:
Engagement:42Momentum:51Stakeholder Coverage:80Task Execution:65Deal Velocity:44
The aggregate score provides ranking.
The dimensions provide understanding.
40. Opportunity Health Example
For ACME:
Engagement 42Activity Momentum 48Stakeholder Coverage 75Task Execution 61Deal Velocity 44Close-Date Readiness 35
Weighted result:
Opportunity Health = 49
That tells us more than the number 49 alone.
41. Score Bands
Numbers are useful for machines.
Humans often benefit from bands.
For example:
80–100Healthy60–79Watch40–59At Risk0–39Critical
These thresholds should also be versioned configuration.
42. Avoid False Precision
A score of:
61.384729
suggests a degree of accuracy that probably does not exist.
For CRM users, expose:
61
or:
61 / 100
Internally, calculations may retain greater precision.
43. Staleness Score
A staleness score estimates how inactive an opportunity has become.
Possible components:
Days Since Customer ActivityDays Since Opportunity UpdateDays Since MeetingDays Since Task CompletionDays in Current Stage
Higher values could indicate greater staleness.
44. Activity Momentum
Momentum asks:
Is activity increasing or decreasing?
Compare windows such as:
Last 14 Days
versus:
Previous 14 Days
Example:
Previous period:12 customer activitiesCurrent period:5 customer activities
This produces:
Activity Momentum:Declining
45. Momentum Calculation
A simple deterministic formula might be:
(current_period - previous_period)/max(previous_period, 1)
For example:
(5 - 12) / 12=-0.583
or approximately:
-58%
This becomes a strong negative engagement signal.
46. Deal Velocity
Deal velocity measures how quickly an opportunity progresses.
Useful features include:
Days in Current StageAverage Days per StageStage Transition FrequencyDays Since Last Stage ChangeTotal Opportunity Age
But velocity should be compared with context.
A €20,000 deal and a €5 million enterprise transformation opportunity naturally progress differently.
47. Tenant Baselines
This introduces an important concept:
Tenant Baseline
For example:
Average Proposal Stage Duration:Tenant = 23 daysACME = 37 days
Therefore:
ACME is 14 days slower than tenant baseline.
That is more informative than simply saying:
ACME has been in Proposal for 37 days.
48. BaselineDefinition
Introduce:
class BaselineDefinition(BaseModel): key: str entity_type: str metric: str segment_dimensions: list[str]
Potential segmentation:
Opportunity Value BandPipelineRegionProductIndustry
But keep MVP segmentation limited.
49. Minimum Sample Size
Do not calculate misleading baselines from tiny datasets.
For example:
Average proposal duration based on 2 opportunities.
That may not be meaningful.
Introduce:
BASELINE_MIN_SAMPLE_SIZE
If insufficient data exists, mark:
baseline_unavailable
instead of pretending statistical confidence.
50. Stakeholder Coverage
CRM opportunities often fail because engagement is concentrated in one person.
Useful features:
total_stakeholdersactive_stakeholdersdecision_maker_presentexecutive_sponsor_presenttechnical_contact_presentbusiness_contact_present
A simple stakeholder coverage signal can indicate:
strongmoderateweak
51. Stakeholder Risk Example
Suppose ACME has:
Contacts:5Active Opportunity Stakeholders:1Decision Maker:Unknown
Quorentra can derive:
Stakeholder Coverage:Weak
with evidence pointing to the associated contacts and opportunity relationships.
52. Task Execution Signals
Tasks provide operational signals.
Examples:
open_task_countoverdue_task_counthigh_priority_overdue_counttask_completion_rate_30daverage_task_delayfollow_up_task_coverage
These can contribute to opportunity health.
53. Meeting Signals
Meeting-derived features:
meeting_count_30ddays_since_last_meetingmeeting_frequencycustomer_participant_countinternal_participant_countmeeting_commitment_count
Later, Quorentra can add more sophisticated meeting intelligence.
For the MVP, keep core calculations deterministic.
54. Communication Signals
If communication data is available:
email_count_30dcustomer_reply_count_30daverage_customer_reply_timeunanswered_outbound_countdays_since_customer_reply
These can significantly improve engagement intelligence.
55. Positive Buying Signals
Some signals indicate positive momentum.
Examples:
New stakeholder addedDecision maker engagedProposal requestedTechnical workshop scheduledSecurity review startedProcurement contact introducedContract document requested
Initially, only use positive signals that can be derived reliably from structured CRM events.
56. Negative Risk Signals
Examples:
Engagement declineNo recent customer activityRepeatedly postponed meetingsOverdue customer commitmentOverdue internal follow-upClose date repeatedly movedOpportunity stuck in stageDecision maker absentProposal revision unresolved
Again, distinguish deterministic signals from AI-inferred signals.
57. Deterministic Versus AI-Derived Signals
Eventually we may support:
Signal Source:deterministic
and:
Signal Source:ai_inferred
For example:
days_since_customer_activity
is deterministic.
But:
customer_sentiment_declining
may require AI interpretation of meeting transcripts and communications.
These should never be presented as equivalent types of evidence.
58. SignalSource
Define:
deterministicai_inferreduser_enteredexternal
Every signal should declare its source class.
This is critical for explainability.
59. AI-Inferred Signal Confidence
AI-derived signals should include:
confidencemodel_versionevidencegenerated_at
For example:
Signal:customer_concern_about_timelineSource:ai_inferredConfidence:0.86Evidence:Meeting transcript lines...
This gives agents useful intelligence without disguising inference as fact.
60. Do Not Let AI Overwrite Facts
Suppose CRM says:
Opportunity Stage:Proposal
An AI model should not change the stored fact to:
Negotiation
because a meeting transcript “sounds like negotiation.”
Instead create:
Signal:possible_negotiation_activity
The distinction between:
CRM Fact
and:
AI Inference
must remain explicit.
61. Intelligence Registry
Introduce a registry for:
FeaturesSignalsScoresBaselines
For example:
class IntelligenceRegistry: def get_feature_definition(...): ... def get_signal_definition(...): ... def get_score_definition(...): ...
This prevents intelligence logic from becoming scattered across the application.
62. ScoreCalculator
Introduce:
class ScoreCalculator: async def calculate( self, *, organization_id: UUID, entity_type: str, entity_id: UUID, score_key: str, as_of: datetime, ) -> ScoreValue: ...
The calculator loads required features and signals and applies the versioned definition.
63. IntelligenceService
Introduce:
class IntelligenceService: async def recalculate_entity(...): ... async def get_signals(...): ... async def get_scores(...): ... async def get_score_history(...): ... async def explain_score(...): ...
This becomes the primary application service for derived CRM intelligence.
64. Score Explanation
A score should have a machine-readable explanation.
For example:
{ "score": 49, "band": "at_risk", "components": [ { "name": "engagement", "score": 42, "weight": 0.30, "contribution": 12.6 }, { "name": "stakeholder_coverage", "score": 75, "weight": 0.15, "contribution": 11.25 } ]}
ChatGPT can then explain it naturally.
65. ChatGPT Should Explain, Not Recalculate
If the user asks:
Why is ACME at risk?
ChatGPT should retrieve:
Opportunity Health ScoreScore ComponentsSignalsEvidence
and explain them.
It should not independently invent a second health score.
66. ChatGPT Intelligence Tools
The Apps SDK layer can expose business-level tools such as:
get_opportunity_healthget_opportunity_signalsget_engagement_scoreget_score_historyexplain_opportunity_scorelist_at_risk_opportunitieslist_declining_opportunities
These are much more useful than requiring ChatGPT to reconstruct analytics from raw records every time.
67. Example ChatGPT Interaction
User:
Why is ACME considered at risk?
ChatGPT calls:
get_opportunity_health
Quorentra returns:
Health:49 / 100Band:At RiskEngagement:42Momentum:48Stakeholder Coverage:75Task Execution:61Deal Velocity:44Close-Date Readiness:35
with evidence.
68. Natural-Language Explanation
ChatGPT can then say:
ACME is currently rated 49/100, placing it in the At Risk band. The strongest negative factors are low recent engagement, declining activity momentum, slow progress through the Proposal stage, and the approaching proposal deadline. Stakeholder coverage remains comparatively healthy, so the primary issue is not account access but lack of recent progress.
That is where ChatGPT adds value.
The score itself remains deterministic.
69. Agent Access to Intelligence
Part 35 agents can now use:
get_opportunity_healthget_opportunity_signalsget_score_history
instead of reconstructing everything from scratch.
An agent may begin:
Goal:Investigate ACME risk.
Then retrieve:
Health Score:49Engagement:42Momentum:48
and drill into the evidence only where needed.
70. Intelligence Makes Agents More Efficient
Without Part 36:
Agent ↓Retrieve 50 Activities ↓Retrieve 10 Meetings ↓Retrieve 20 Tasks ↓Reason
With Part 36:
Agent ↓Retrieve Health Intelligence ↓Identify Weak Dimension ↓Retrieve Supporting Evidence ↓Reason
This reduces:
Tool CallsTokensLatencyCost
while improving consistency.
71. Workflows Can Use Scores
Part 34 workflows can now use intelligence.
For example:
WHENOpportunity Health Score ChangesIFHealth Score < 40THENCreate Review Task
or:
Every MorningFind OpportunitiesWHEREEngagement Score < 50ANDValue > €100,000
This creates powerful deterministic automation.
72. Score Change Events
Publish events such as:
intelligence.score_changedopportunity.health_changedopportunity.engagement_changed
But avoid publishing meaningless tiny changes.
For example:
Health:62 → 61
may not matter.
73. Significant Change Thresholds
Introduce:
SCORE_CHANGE_EVENT_THRESHOLD
For example:
5 points
Or publish when a score crosses a band:
Watch→At Risk
Band changes are often more meaningful than small numerical movements.
74. Intelligence Event Example
{ "event_type": "opportunity.health_band_changed", "entity_id": "...", "payload": { "previous_score": 63, "new_score": 57, "previous_band": "watch", "new_band": "at_risk" }}
Part 34 workflows can consume this event.
75. Intelligence Feedback Loop
We now have:
CRM Event ↓Feature Recalculation ↓Signal Recalculation ↓Score Recalculation ↓Score Change Event ↓Workflow ↓Agent ↓Recommendation / Action
This is a major architectural milestone.
76. Prevent Intelligence Loops
Suppose an agent creates a task.
That causes:
task.created
which recalculates opportunity health.
That may trigger another workflow.
The correlation and causation mechanisms from Part 34 must continue to apply.
Intelligence does not bypass loop protection.
77. Historical Intelligence
Store historical score snapshots.
This enables:
Trend ChartsBefore/After AnalysisPipeline MovementAccount DeteriorationRecovery DetectionAgent Evaluation
Historical intelligence is often more useful than a current score alone.
78. ScoreTrend
Introduce:
class ScoreTrend(BaseModel): current_value: float previous_value: float absolute_change: float percentage_change: float | None direction: str period: str
Directions:
improvingstabledeclining
79. Trend Thresholds
Do not label:
70 → 69
as meaningful decline.
Use thresholds.
For example:
absolute change < 3=stable
These thresholds should be part of versioned score configuration.
80. Intelligence Snapshots
For reporting, we may also create periodic snapshots.
For example:
DailyWeeklyMonthly
A daily snapshot allows questions such as:
Show me how opportunity health changed over the last 90 days.
81. Pipeline Intelligence
Once opportunity scores exist, Quorentra can derive pipeline-level metrics.
Examples:
Average Opportunity HealthWeighted Pipeline HealthAt-Risk Pipeline ValueCritical Pipeline ValueHealthy Pipeline ValueDeclining Opportunity CountImproving Opportunity Count
82. Weighted Pipeline Health
A simple metric could weight health by opportunity value.
Conceptually:
Σ(opportunity_value × health_score)/Σ(opportunity_value)
This prevents a €5,000 opportunity and a €5 million opportunity from contributing equally to pipeline health.
83. Do Not Confuse Health With Forecast Probability
Opportunity health and win probability are different concepts.
An opportunity can be:
Healthy
but still early-stage.
Likewise, a late-stage deal may have:
High Win Probability
while showing deteriorating engagement.
Keep separate dimensions:
HealthProbabilityValueStageForecast
84. Data Quality Signals
The intelligence layer should also detect missing CRM information.
Examples:
missing_close_datemissing_opportunity_valuemissing_primary_contactmissing_ownermissing_next_stepmissing_decision_maker
These are valuable signals themselves.
85. Data Quality Score
Eventually we can calculate:
Opportunity Data Quality Score
This helps distinguish:
Low Health
from:
Insufficient Data
An opportunity should not receive a confident risk classification if critical information is missing.
86. Insufficient Data
Scores should support states such as:
availablepartialinsufficient_data
Do not force every entity into a 0–100 score.
Sometimes the correct result is:
There is not enough information to calculate a reliable engagement score.
87. ScoreQuality
Introduce:
class ScoreQuality(BaseModel): status: str completeness: float missing_features: list[str] warnings: list[str]
This allows ChatGPT and agents to understand the limitations of the score.
88. Confidence Versus Completeness
For deterministic scores, the term:
confidence
may not always be appropriate.
Instead, use:
completeness
or:
data_quality
For AI-inferred signals, confidence remains relevant.
Keep these concepts separate.
89. Tenant-Specific Score Configuration
Eventually tenants may want different definitions.
For example:
Company A:No activity for 14 days = risk
while:
Company B:No activity for 30 days = normal
The architecture should eventually support tenant configuration.
But the MVP should begin with system defaults.
90. Configuration Hierarchy
Later:
System Default ↓Tenant Override ↓Pipeline Override
Avoid introducing all levels immediately.
Start with:
System Default
plus limited tenant thresholds if required.
91. Version Tenant Overrides
If a tenant changes:
Engagement Risk Threshold:14 days → 21 days
future scores may change.
Historical calculations should remain associated with the configuration version used at that time.
92. Intelligence Module Structure
Create:
backend/app/intelligence/
Suggested structure:
backend/app/intelligence/├── models.py├── schemas.py├── registry.py├── features.py├── feature_store.py├── signals.py├── scores.py├── baselines.py├── trends.py├── freshness.py├── recalculation.py├── events.py├── explanations.py├── metrics.py├── service.py└── exceptions.py
93. Opportunity Intelligence Submodule
As complexity grows, we can introduce:
backend/app/intelligence/opportunities/
with:
engagement.pymomentum.pyvelocity.pystaleness.pystakeholders.pytasks.pymeetings.pyhealth.py
But do not fragment the module prematurely.
94. Background Intelligence Worker
Feature and score recalculation can run asynchronously.
Architecture:
CRM Mutation ↓Domain Event ↓Outbox ↓Event Worker ↓Intelligence Recalculation Job ↓Feature Store ↓Signal Store ↓Score Store
The user-facing CRM mutation does not need to wait for every derived score to finish.
95. Eventual Consistency
This means intelligence may be:
slightly behind CRM state
for a short period.
That is acceptable if clearly designed.
Store:
calculated_at
and expose it when relevant.
96. Freshness SLA
Define expectations such as:
Event-driven scores:updated within 60 secondsTime-based scores:updated daily
The exact values can evolve.
The important point is to make freshness measurable.
97. Recalculation Idempotency
The same event may be processed more than once.
Recalculation must therefore be idempotent.
For example:
entity_idfeature_keycalculation_versionas_of_bucket
can help define unique calculations.
98. Recalculation Fan-Out
One event may affect several features.
Example:
activity.created
may affect:
days_since_customer_activityactivity_count_7dactivity_count_30dactivity_momentumengagement_scoreopportunity_health_score
We need dependency tracking.
99. Intelligence Dependency Graph
Conceptually:
activity.created ↓activity_count_7dactivity_count_30ddays_since_activity ↓engagement_signals ↓engagement_score ↓opportunity_health_score
The system should understand which derived values need recalculation.
100. DependencyRegistry
Introduce:
class DependencyRegistry: def affected_features( self, event_type: str, ) -> list[str]: ... def dependent_scores( self, feature_key: str, ) -> list[str]: ...
This prevents recalculating every score after every event.
101. Intelligence Query API
Potential endpoints:
GET /api/v1/opportunities/{id}/intelligenceGET /api/v1/opportunities/{id}/signalsGET /api/v1/opportunities/{id}/scoresGET /api/v1/opportunities/{id}/scores/historyGET /api/v1/opportunities/{id}/healthGET /api/v1/opportunities/{id}/engagementGET /api/v1/pipeline/intelligence
102. Internal Tool APIs
For ChatGPT and agents, prefer business-level tools:
get_opportunity_healthget_opportunity_engagementget_opportunity_risk_signalsget_opportunity_score_historylist_at_risk_opportunitieslist_declining_opportunitiesget_pipeline_health
This keeps the Apps SDK surface understandable.
103. Example Intelligence Response
{ "opportunity_id": "...", "health": { "score": 49, "band": "at_risk", "calculated_at": "2026-08-03T08:00:00Z" }, "dimensions": { "engagement": 42, "momentum": 48, "stakeholder_coverage": 75, "task_execution": 61, "deal_velocity": 44, "close_date_readiness": 35 }, "top_negative_signals": [ "no_customer_activity_18_days", "proposal_revision_outstanding", "proposal_deadline_6_days" ]}
This is highly useful to both ChatGPT and agents.
104. Explainability Endpoint
Provide something like:
GET /api/v1/opportunities/{id}/health/explanation
It should return:
ScoreBandComponentsWeightsSignalsEvidenceCalculation VersionCalculation TimeData Quality
105. ChatGPT-Native Explanation
The user should never need to inspect score JSON.
They can ask:
Why did ACME’s health score drop this week?
ChatGPT retrieves the score history and component changes.
It may answer:
ACME dropped from 63 to 49 this week. The main change was engagement: no customer interaction has been recorded for 18 days, activity momentum fell sharply compared with the previous two-week period, and the proposal revision requested by the customer is still outstanding. Stakeholder coverage remained stable, so the decline is primarily related to engagement and deal progress.
That is exactly the kind of interaction a ChatGPT-native CRM should support.
106. Score Comparison
Users may also ask:
Compare ACME and Contoso.
Quorentra can return:
ACME Contoso
Health 49 78
Engagement 42 81
Momentum 48 74
Stakeholders 75 69
Task Execution 61 82
Deal Velocity 44 77
ChatGPT can explain the differences.
107. Intelligence Ranking
Users can ask:
Which five opportunities need the most attention?
Quorentra should not send the entire opportunity database to ChatGPT.
Instead:
Intelligence Query ↓Rank Opportunities ↓Top 5 ↓ChatGPT
This is faster, cheaper, and safer.
108. Intelligence and RAG
The intelligence layer complements RAG.
RAG answers:
What does our CRM knowledge say?
Intelligence answers:
What structured business signals can we derive?
Agents can use both:
Structured Intelligence +Retrieved Knowledge ↓Grounded Reasoning
This combination is much stronger than either alone.
109. Example Agent Flow After Part 36
Opportunity Risk Agent:
Goal ↓Get Opportunity Health ↓Health = 49 ↓Get Weak Dimensions ↓Engagement = 42Velocity = 44 ↓Get Supporting Signals ↓Retrieve Evidence ↓Reason ↓Recommend
The agent becomes more efficient and more consistent.
110. Intelligence and Workflow Automation
A workflow can now be:
WHENhealth_band changes to criticalTHENrun Opportunity Risk Agent
The architecture becomes:
CRM Change ↓Intelligence Recalculation ↓Health Band Changes ↓Domain Event ↓Workflow ↓Agent ↓Recommendation
This is the beginning of proactive CRM intelligence.
111. Metrics
Add:
intelligence_feature_calculations_totalintelligence_signal_calculations_totalintelligence_score_calculations_totalintelligence_recalculation_failures_totalintelligence_stale_values_total
112. Score Metrics
Add:
opportunity_health_score_averageopportunity_engagement_score_averageopportunities_at_risk_totalopportunities_critical_totalopportunities_declining_totalopportunities_improving_total
Be careful with metric cardinality.
Do not create a Prometheus metric label for every opportunity ID.
113. Processing Metrics
Track:
intelligence_recalculation_duration_secondsintelligence_recalculation_queue_depthintelligence_event_to_score_latency_seconds
These help enforce freshness targets.
114. Data Quality Metrics
Track:
intelligence_insufficient_data_totalopportunities_missing_close_date_totalopportunities_missing_value_totalopportunities_missing_primary_contact_totalopportunities_missing_next_step_total
This can also reveal CRM adoption problems.
115. Testing Strategy
Part 36 requires tests for:
Feature CalculationSignal CalculationScore CalculationScore WeightingScore BandsHistorical ScoresTrendsFreshnessRecalculationVersioningTenant IsolationBaselinesMinimum Sample SizesData QualityEvidenceWorkflow IntegrationAgent IntegrationChatGPT Tools
116. Determinism Test
Given identical:
CRM StateCalculation TimeCalculation Version
expected:
Identical Feature ValuesIdentical SignalsIdentical Scores
This is one of the most important tests.
117. Tenant Isolation Test
Create:
Tenant A OpportunityTenant B Activities
Calculate Tenant A engagement.
Expected:
Tenant B activities contribute exactly 0.
118. Evidence Test
Calculate:
days_since_customer_activity
Expected:
signal contains evidence reference to actual last customer activity
119. Historical Score Test
Calculate:
Health = 72
Change CRM data.
Recalculate:
Health = 58
Expected:
Current = 58History contains 72 and 58
120. Version Test
Calculate using:
health:v1
Deploy:
health:v2
Expected:
Historical v1 values remain identifiable.New values use v2.
121. Missing Data Test
Remove critical opportunity fields.
Expected:
Score Quality:partial
or:
insufficient_data
rather than a falsely confident score.
122. Baseline Sample Test
Only two comparable opportunities exist.
Expected:
baseline_unavailable
if minimum sample size is not met.
123. Event Recalculation Test
Create a new customer activity.
Expected:
activity.created ↓engagement features recalculated ↓engagement score recalculated ↓health recalculated if necessary
124. Significant Change Event Test
Health changes:
61 → 60
Expected:
No health-band event
Health changes:
61 → 58
crossing:
Watch → At Risk
Expected:
opportunity.health_band_changed
125. Agent Integration Test
Risk Agent requests opportunity health.
Expected:
structured intelligence returnedevidence availableno cross-tenant informationscore version included
126. ChatGPT Explanation Test
User asks:
Why is this opportunity at risk?
Expected response is based on:
Stored ScoreComponentsSignalsEvidence
not a newly invented score.
127. Security Considerations
The intelligence layer must enforce:
Tenant IsolationAuthorizationEvidence ScopeSafe AggregationControlled ConfigurationImmutable Historical Versions
Aggregated intelligence can still leak information if tenant boundaries are not applied correctly.
128. Intelligence Is Not Authorization
Suppose:
Health Score = 15
That does not automatically authorize:
Delete OpportunityChange OwnerSend EmailCreate Task
Scores can trigger workflows.
Actions still pass through the existing authorization and action architecture.
129. AI Signals Are Untrusted Inputs
If an AI-derived signal says:
customer likely to churn
treat it as intelligence evidence.
Do not treat it as authority.
It may inform:
Agent ReasoningWorkflow ConditionsRecommendations
but sensitive actions should remain governed by policy.
130. Updated Quorentra Architecture
After Part 36:
Quorentra│├── CRM Core│ ├── Companies│ ├── Contacts│ ├── Opportunities│ ├── Activities│ ├── Tasks│ ├── Meetings│ └── Documents│├── Knowledge Layer│ ├── Extraction│ ├── Chunking│ ├── Embeddings│ ├── Retrieval│ └── Grounding│├── Intelligence Layer│ ├── Features│ ├── Feature Store│ ├── Signals│ ├── Evidence│ ├── Scores│ ├── Baselines│ ├── Trends│ ├── Data Quality│ ├── Freshness│ └── Recalculation│├── Action Layer│ ├── ActionIntent│ ├── Validation│ ├── Authorization│ ├── Confirmation│ ├── Idempotency│ └── Audit│├── Workflow Layer│ ├── Triggers│ ├── Conditions│ ├── Actions│ ├── Scheduler│ └── Execution│├── Agent Layer│ ├── Goals│ ├── Context│ ├── Planning│ ├── Tools│ ├── Policies│ ├── Budgets│ ├── Approvals│ └── Execution│└── ChatGPT Layer ├── Apps SDK ├── CRM Tools ├── Knowledge Tools ├── Intelligence Tools ├── Workflow Tools └── Agent Tools
The intelligence layer now sits between raw CRM state and higher-level reasoning.
131. The New Information Flow
The architecture can now support:
CRM DATA
│
▼
FEATURES
│
▼
SIGNALS
│
▼
SCORES
│
┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
ChatGPT Agents Workflows
│ │ │
└─────────┼─────────┘
▼
ACTION LAYER
│
▼
CRM DATA
This creates a closed intelligence loop.
132. Why This Matters for a ChatGPT-Native CRM
Without an intelligence layer, ChatGPT repeatedly needs to interpret raw CRM records.
With Part 36, a user can ask:
How is my pipeline looking?
Quorentra can provide:
Pipeline HealthAt-Risk ValueCritical OpportunitiesEngagement TrendsMomentum TrendsData Quality
ChatGPT can then turn those structured results into a useful executive explanation.
133. The Division of Responsibility
The architecture now becomes even clearer.
Quorentra calculates:
FactsFeaturesSignalsScoresBaselinesTrendsEvidence
ChatGPT interprets:
What It MeansWhy It MattersWhat the User Should InvestigateWhich Questions to Ask NextHow to Explain It Naturally
Agents pursue goals:
InvestigateGather EvidenceCompareReasonRecommendPropose Actions
Workflows automate:
WhenIfThen
Action services execute:
Authorized CRM Mutations
Each layer has a clear responsibility.
134. MVP Intelligence Scope
Do not attempt to build every possible CRM score immediately.
For the MVP, implement:
1. Engagement Score2. Activity Momentum3. Staleness Score4. Stakeholder Coverage Score5. Task Execution Score6. Deal Velocity Score7. Opportunity Health Score8. Data Quality Score
That is already enough to make Quorentra substantially more intelligent.
135. MVP Feature Set
Start with features such as:
days_since_customer_activitycustomer_activity_count_7dcustomer_activity_count_30dcustomer_activity_count_previous_30dmeeting_count_30ddays_since_last_meetingopen_task_countoverdue_task_counttask_completion_rate_30ddays_in_current_stagedays_until_closestakeholder_countactive_stakeholder_countdecision_maker_presentopportunity_age_days
These provide a strong foundation.
136. MVP Score Dependencies
For example:
Engagement Score├── days_since_customer_activity├── customer_activity_count_30d├── meeting_count_30d└── activity_momentum
Task Execution Score├── overdue_task_count└── task_completion_rate_30d
Opportunity Health├── Engagement├── Momentum├── Task Execution├── Stakeholder Coverage├── Deal Velocity└── Close-Date Readiness
Keep dependencies explicit.
137. Acceptance Criteria
Part 36 is complete when:
✓ Intelligence module exists✓ FeatureDefinition exists✓ FeatureValue exists✓ FeatureStore exists✓ FeatureCalculator exists✓ SignalDefinition exists✓ IntelligenceSignal exists✓ SignalEvidence exists✓ SignalCalculator exists✓ SignalSource is explicit✓ deterministic signals are distinguishable✓ AI-inferred signals are distinguishable✓ user-entered signals are distinguishable✓ external signals are distinguishable✓ ScoreDefinition exists✓ ScoreValue exists✓ ScoreComponent exists✓ ScoreCalculator exists✓ ScoreQuality exists✓ scores are normalized✓ score weights are explicit✓ score components are explainable✓ score bands are defined✓ score bands are versioned✓ historical scores are retained✓ score trends can be calculated✓ insignificant changes can be treated as stable✓ feature calculations are versioned✓ signal calculations are versioned✓ score calculations are versioned✓ evidence references are preserved✓ score explanations expose evidence✓ historical calculations remain reproducible✓ freshness is tracked✓ stale intelligence is detectable✓ event-driven recalculation works✓ scheduled recalculation works✓ recalculation is idempotent✓ dependency tracking exists✓ only affected intelligence is recalculated✓ tenant isolation is enforced✓ cross-tenant features are impossible✓ cross-tenant baselines are impossible✓ cross-tenant score aggregation is impossible✓ tenant baselines are supported✓ minimum sample size is enforced✓ unreliable baselines are marked unavailable✓ Engagement Score exists✓ Activity Momentum exists✓ Staleness Score exists✓ Stakeholder Coverage Score exists✓ Task Execution Score exists✓ Deal Velocity Score exists✓ Opportunity Health Score exists✓ Data Quality Score exists✓ insufficient data is represented explicitly✓ missing data does not silently become zero✓ score completeness is available✓ score change events exist✓ health-band change events exist✓ insignificant changes do not create event noise✓ workflows can consume intelligence✓ workflows cannot bypass action authorization✓ agents can retrieve intelligence✓ agents can retrieve score evidence✓ agents do not need to reconstruct every signal from raw data✓ ChatGPT can retrieve opportunity health✓ ChatGPT can retrieve engagement scores✓ ChatGPT can retrieve score history✓ ChatGPT can explain scores using stored components✓ ChatGPT does not invent replacement scores✓ intelligence metrics exist✓ freshness metrics exist✓ recalculation metrics exist✓ data quality metrics exist✓ deterministic calculation tests pass✓ tenant isolation tests pass✓ evidence tests pass✓ historical score tests pass✓ versioning tests pass✓ missing-data tests pass✓ baseline tests pass✓ workflow integration tests pass✓ agent integration tests pass✓ ChatGPT explanation tests pass
Most importantly:
Quorentra now has a reusable, deterministic and evidence-backed intelligence layer that converts raw CRM activity into structured business signals that ChatGPT, agents, workflows, dashboards, and users can consistently understand.
138. What We Have Achieved
The Quorentra architecture has now evolved through:
CRM Records ↓Knowledge ↓Grounded Retrieval ↓ChatGPT ↓Actions ↓Workflows ↓Agents ↓Intelligence
But architecturally, the runtime relationship is even more interesting:
CRM ↓Intelligence ↓ChatGPT / Agents / Workflows ↓Actions ↓CRM ↓Updated Intelligence
Quorentra is becoming a continuous intelligence system rather than merely a database with an AI chat interface.
139. A Practical Example
Imagine the following sequence.
Monday morning:
ACME Health:68
Tuesday:
Customer meeting postponed.
Wednesday:
No response to proposal email.
Thursday:
Proposal revision task becomes overdue.
The intelligence layer recalculates:
Engagement:72 → 55Momentum:68 → 43Task Execution:80 → 61Health:68 → 54
The health band changes:
Watch→At Risk
Quorentra emits:
opportunity.health_band_changed
A workflow sees the event.
The workflow launches:
Opportunity Risk Agent
The agent investigates the evidence.
It determines:
Primary Risk:Unresolved proposal revision combined with declining customer engagement.
ChatGPT can then proactively surface:
ACME moved into the At Risk category today. Engagement has declined significantly, the proposal revision task is overdue, and the customer has not responded to the latest communication. The Opportunity Risk Agent recommends reviewing the proposal revision and contacting the customer before the end of the day.
That entire chain is now supported by the architecture we have built.
140. What Comes Next?
Quorentra now knows:
What happenedWhat changedWhat signals existHow scores are movingWhich opportunities are healthyWhich opportunities are deteriorating
But users will soon want something more powerful:
What should I do today?
Which five actions will have the greatest impact?
What should the salesperson do next on this opportunity?
Should we schedule a meeting, send a proposal revision, involve an executive sponsor, or wait?
This moves us from:
Intelligence
to:
Decision Intelligence
The next layer should convert signals, scores, CRM context, knowledge, and agent reasoning into structured Next-Best-Action recommendations.
Next: Part 37
Building the Next-Best-Action Recommendation Engine
Part 37 will introduce:
RecommendationRecommendationTypeRecommendationCandidateRecommendationContextRecommendationEvidenceRecommendationReasonRecommendationScoreRecommendationPriorityRecommendationConfidenceRecommendationUrgencyRecommendationImpactRecommendationEffortRecommendationRiskRecommendationFreshnessRecommendationStatusCandidate GenerationRule-Based CandidatesSignal-Based CandidatesAgent-Generated CandidatesRecommendation RankingRecommendation DeduplicationRecommendation SuppressionRecommendation ExpirationRecommendation ConflictsNext-Best-Action SelectionTop-N RecommendationsUser FeedbackAccepted RecommendationsRejected RecommendationsDismissed RecommendationsCompleted RecommendationsRecommendation OutcomesRecommendation Learning SignalsWorkflow IntegrationAgent IntegrationChatGPT IntegrationApproval-Gated ExecutionMetricsTestingEvaluation
The architecture will evolve from:
CRM Data ↓Signals ↓Scores ↓Understanding
to:
CRM Data ↓Signals ↓Scores ↓Recommendation Candidates ↓Ranking ↓Next Best Action ↓ChatGPT ↓User Decision ↓Safe Execution
This will make Quorentra considerably more useful in day-to-day CRM work.
Instead of merely telling a salesperson:
ACME has a health score of 49.
Quorentra will be able to say:
ACME needs attention. The highest-priority next action is to finalize the requested migration timeline and contact the customer within 24 hours. The recommendation is based on declining engagement, an unresolved proposal requirement, and the approaching proposal deadline.
That is the next step toward turning Quorentra into a genuinely proactive, ChatGPT-native CRM intelligence platform.