Detailed procedures for querying Session Trace Data Model (STDM) via the `AgentforceOptimizeService` Apex helper class.
---
## Deploy Helper Class (Once Per Org)
`AgentforceOptimizeService` is a bundled Apex class that queries STDM DMOs and returns clean JSON. Deploy it once; subsequent runs reuse the deployed class.
Confirm the deploy succeeds before proceeding. If it fails with a compile error, check that the org has Data Cloud enabled (the `ConnectApi.CdpQuery` namespace requires Data Cloud).
**Skip this step if `AgentforceOptimizeService` is already deployed** -- check with:
```bash
sf data query --json \
--query "SELECT Id, Name FROM ApexClass WHERE Name = 'AgentforceOptimizeService'" \
-o <org>
```
---
## Find Sessions
If the user provided session IDs, skip to conversation details. Otherwise, write `/tmp/stdm_find.apex` and run it (substitute actual ISO 8601 UTC timestamps, DATA_SPACE, and AGENT_API_NAME):
```apex
String result = AgentforceOptimizeService.findSessions(
'DATA_SPACE',
'START_ISO',
'END_ISO',
20,
'AGENT_MASTER_LABEL'
);
System.debug('STDM_RESULT:' + result);
```
```bash
sf apex run --json --file /tmp/stdm_find.apex -o <org>
```
Parse: search for `DEBUG|STDM_RESULT:` (not `STDM_RESULT:` -- the first occurrence of that string is in the source echo, not the debug output) and extract the JSON that follows on that line:
-`end_time` and `duration_ms` may be `null` when the session has no recorded end event -- this is a normal STDM data quality gap, not an error.
-`end_type` values: `USER_ENDED`, `AGENT_ENDED`, or `null` (in-progress or not recorded). A `null``end_type` may indicate an abandoned session.
**Session quality filter:** `findSessions` automatically filters to sessions with actual conversation turns by querying `AiAgentInteraction` first. Sessions from `sf agent preview`, `sf agent test`, and Agent Builder that created `AiAgentSession` records but no `AiAgentInteraction` (TURN) records are excluded. If `findSessions` returns an empty list, there are no sessions with actionable data — go directly to Phase 1-ALT (local traces).
**How agent filtering works** -- `findSessions` tries two strategies in order:
1.**Direct** (preferred): `ssot__AiAgentApiName__c = agentApiName` on `ssot__AiAgentSessionParticipant__dlm` -- no SOQL needed, uses a dedicated DMO field. Resolves in a single Data Cloud query.
2.**Planner fallback**: If strategy 1 returns no rows, SOQL: `SELECT Id FROM GenAiPlannerDefinition WHERE MasterLabel = :agentApiName` -> `ssot__ParticipantId__c IN (...)`. Both 15-char and 18-char ID formats are included (the DMO stores them inconsistently). If both strategies return empty, the query falls back to all sessions in the date range.
**If the debug log shows `Agent not found: <name>`**, no `GenAiPlannerDefinition` matched -- verify the agent name with:
```bash
sf data query --json --query "SELECT Id, MasterLabel, DeveloperName FROM GenAiPlannerDefinition" -o <org>
```
Use the exact `MasterLabel` value (not `DeveloperName`). `MasterLabel` matches the agent's display name; `DeveloperName` has a version suffix (e.g. `TeslaSupportAgent_v1`).
**If the debug log shows a warning about no sessions for the agent**, both strategies returned empty -- the agent may have no sessions in this date range, or Data Cloud ingestion may be delayed. The query falls back to all sessions in the date range.
---
## Get Conversation Details
For up to 5 sessions (most recent first), write `/tmp/stdm_details.apex` and run it (substitute session IDs and DATA_SPACE):
```apex
String result = AgentforceOptimizeService.getMultipleConversationDetails(
'DATA_SPACE',
new List<String>{ 'SESSION_ID_1', 'SESSION_ID_2' }
);
System.debug('STDM_RESULT:' + result);
```
```bash
sf apex run --json --file /tmp/stdm_details.apex -o <org>
```
Parse using the same `DEBUG|STDM_RESULT:` pattern. Each element is a `ConversationData` object:
-`end_type` -- how the session ended (`USER_ENDED`, `AGENT_ENDED`, or null)
-`session_variables` -- final variable snapshot for the session (null when absent)
-`telemetry_trace_id` -- distributed tracing ID for this turn (null when absent)
-`generation_id` / `gateway_request_id` on `LLM_STEP` -- pass these step IDs to `getLlmStepDetails()` to retrieve the actual LLM prompt and response (useful for diagnosing LOW instruction adherence)
Treat any `null` field as absent/unknown. The `"NOT_SET"` sentinel is stripped by the service class before returning.
---
## Get LLM Prompt/Response (Optional, for LOW Adherence)
When a session shows `TRUST_GUARDRAILS_STEP` with `'value': 'LOW'`, use `getLlmStepDetails()` to retrieve the actual LLM prompt and response for the associated `LLM_STEP` records. Pass the `step_id` values from steps where `step_type == "LLM_STEP"` and `generation_id != null`.
```apex
String result = AgentforceOptimizeService.getLlmStepDetails(
'DATA_SPACE',
new List<String>{ 'STEP_ID_1', 'STEP_ID_2' }
);
System.debug('STDM_RESULT:' + result);
```
```bash
sf apex run --json --file /tmp/stdm_llm.apex -o <org>
```
Returns a JSON array of `LlmStepDetail` objects:
```json
[
{
"step_id": "...",
"interaction_id": "...",
"step_name": "...",
"prompt": "System: You are a Tesla support agent...\nUser: I want to schedule a test drive",
"llm_response": "I'd be happy to help you schedule a test drive...",
"generation_id": "...",
"gateway_request_id": "..."
}
]
```
-`prompt` -- full prompt from `GenAIGatewayRequest__dlm.prompt__c` (null if Einstein Audit DMO not enabled)
-`llm_response` -- model response from `GenAIGeneration__dlm.responseText__c` (null if not available)
Use these to confirm whether the agent's instructions were included in the prompt and whether the response deviated from them.
---
## Get Aggregated Metrics (Recommended First Step)
Before drilling into individual sessions, get a high-level health dashboard with `getAggregatedMetrics()`. This gives session rates, top intents, and RAG quality averages across the date range.
```apex
String result = AgentforceOptimizeService.getAggregatedMetrics(
-`top_intents` shows what users ask about most -- verify the agent has topics/actions for each
-`unavailable_dmos` lists any DMOs that couldn't be queried (graceful degradation)
---
## Get Moment Insights (Per-Session Detail)
For deeper analysis of specific sessions, use `getMomentInsights()` to get intent summaries, moment durations, and retriever quality metrics per session.
```apex
String result = AgentforceOptimizeService.getMomentInsights(
'DATA_SPACE',
new List<String>{ 'SESSION_ID_1', 'SESSION_ID_2' }
);
System.debug('STDM_RESULT:' + result);
```
Returns a JSON array of `SessionInsights` objects:
"request_summary": "I want help finding homes in San Jose.",
"response_summary": "The agent provided details on three homes...",
"agent_api_name": "MyServiceAgent",
"agent_version": null,
"quality_score": 5,
"quality_reasoning": "The agent provided a detailed and helpful response..."
}
],
"retriever_metrics": [],
"debug_message": null
}
]
```
Key fields:
-`quality_score` (1-5) -- per-moment quality score from `AiAgentTagAssociation -> AiAgentTag.Value`. Maps to UI labels: 5=High, 3-4=Medium, 2=Low, 1=Very Low
-`quality_reasoning` -- LLM-generated explanation for the score (from `AssociationReasonText`)
-`avg_quality_score` -- session-level average across all scored moments
-`request_summary` / `response_summary` -- LLM-generated intent and response summaries per moment
-`moment_count` vs `turn_count` -- if `turn_count` >> `moment_count`, the agent needed many turns per intent (inefficient)
-`retriever_metrics` -- RAG quality scores per retrieval (empty if agent doesn't use knowledge retrieval)
-`debug_message` -- non-null if a DMO was unavailable (e.g. "AiAgentMoment DMO not available in this org")
---
## Run Observability Queries (RAG Deep-Dive)
For targeted RAG/retriever quality analysis, use the `@InvocableMethod` entry point `runObservabilityQuery()`. This can be called from anonymous Apex, Flows, or Agentforce actions. It queries Data Lake objects (`*__dll`) directly without a Data Space parameter.
sf apex run --json --file /tmp/observability_query.apex -o <org>
```
**When to use observability queries vs `getAggregatedMetrics()`:**
- Use `getAggregatedMetrics()` for a broad health dashboard (session rates, top intents, overall RAG averages)
- Use `runObservabilityQuery()` for targeted RAG deep-dives when knowledge gaps or hallucination issues are detected -- it provides per-subagent and per-retriever breakdowns
---
## Reconstruct Conversations
For each session, render the turn-by-turn timeline from the `ConversationData` JSON:
```
Session <session_id> [<channel>] <duration_ms>ms total <turn_count> turns