Note that once you set it up, the default dashboard will do no good for you. An agent doesn't use the same fields as the default dashboard is set up to see. The agent's content is all in "customEvents" table. I set up a custom dashboard using a bunch of different queries and this is the result!
customEvents |where customDimensions !contains "User Inactivity" and customDimensions !contains "PowerVirtualAgentRoot"
above ^^^ shows all interaction events (BotMessageSend, BotMessageReceived, etc.) while excluding my on-a-timer "User Inactivity" chat reset topic, as well as the AgentRoot topic which seems to be system logging activity of some type.
above ^^^ shows top interactive users
customEvents
| where timestamp > ago(14d)
| extend PromptRaw = tostring(customDimensions["text"])
| extend Prompt = trim(" ", tolower(PromptRaw))
| where isnotempty(Prompt)
| summarize PromptCount=count() by Prompt
| top 50 by PromptCount desc
above ^^^ shows top interactive messages (so you'll see a lot of static topic replies, which is helpful to see what topics are triggering most)
and my favorite, below: shows messages, with text, with and without replies. this is what the above graphs are based off of, so if you play with it, you can get those too!
let lookback = 14d;
let replyWindow = 5m;
let In =
customEvents
| where timestamp > ago(lookback)
| where name == "BotMessageReceived"
| extend
InTime = timestamp,
ConversationId = tostring(customDimensions["conversationId"]),
UserId = tostring(customDimensions["fromId"]),
UserName = tostring(customDimensions["fromName"]),
BotName = tostring(customDimensions["recipientName"]),
InText = tostring(customDimensions["text"])
| where isnotempty(ConversationId) and isnotempty(UserId) and isnotempty(UserName) and isnotempty(InText)
| extend MsgKey = strcat(ConversationId, "|", UserId, "|", tostring(InTime))
| project MsgKey, ConversationId, InTime, UserId, UserName, BotName, InText;
let Out =
customEvents
| where timestamp > ago(lookback)
| where name == "BotMessageSend"
| extend
OutTime = timestamp,
ConversationId = tostring(customDimensions["conversationId"]),
ToUserId = tostring(customDimensions["recipientId"]),
ToUserName = tostring(customDimensions["recipientName"]),
OutText = tostring(customDimensions["text"])
| where isnotempty(ConversationId) and isnotempty(ToUserId) and isnotempty(OutText)
| project ConversationId, OutTime, ToUserId, ToUserName, OutText;
let Matched =
In
| join kind=inner (Out) on ConversationId
| where ToUserId == UserId
| where ToUserName == UserName
| where OutTime between (InTime .. InTime + replyWindow)
| summarize arg_min(OutTime, OutText) by MsgKey;
In
| join kind=leftouter (Matched) on MsgKey
| extend HasReply = isnotnull(OutTime)
| extend ReplyLatencyMs = iif(HasReply, datetime_diff("millisecond", OutTime, InTime), long(null))
| project UserName, HasReply, ReplyLatencyMs, InText, ReplyText=OutText, InTime, ReplyTime=OutTime, ConversationId, BotName
| order by InTime desc