Custom Agent Protected Content
Query
// Legacy protected-only source, retained for reference; the current pack deploys CustomAgentInlineContent instead.
let Spans=CustomAgentSpans(lookback)
| where Classification !in ('Excluded managed platform', 'Unattributed GenAI') and HasLineage;
let Protected=AppGenAIContent
| where TimeGenerated > ago(lookback)
| extend _ResourceId=tolower(tostring(_ResourceId)), OperationId=tolower(tostring(TraceId)),
SpanId=tolower(tostring(SpanId)), ContentTime=TimeGenerated
| where isnotempty(_ResourceId) and isnotempty(OperationId) and isnotempty(SpanId)
| summarize arg_max(ContentTime, *) by _ResourceId, OperationId, SpanId
| project _ResourceId, OperationId, SpanId, ContentTime,
Messages=todynamic(InputMessages), OutputText=tostring(OutputMessages),
ToolResult=tostring(column_ifexists('ToolCallResult', ''));
Spans
| join kind=inner Protected on _ResourceId, OperationId, SpanId
| extend Messages=iff(gettype(Messages) == 'array' and array_length(Messages) > 0, Messages, dynamic([{}]))
| mv-apply Message=Messages on (
extend Role=tolower(tostring(Message.role)), Parts=coalesce(Message.parts, Message.content)
| extend Parts=case(gettype(Parts) == 'string', pack_array(bag_pack('content', tostring(Parts))),
gettype(Parts) == 'array' and array_length(Parts) > 0, Parts, dynamic([{}]))
| mv-apply Part=Parts on (
extend PartText=coalesce(tostring(Part.content), tostring(Part.text),
iff(gettype(Part) == 'string', tostring(Part), ''))
| summarize MessageText=strcat_array(make_list(PartText, 100), '\n') by Role
)
| summarize UserText=strcat_array(make_list_if(MessageText, Role == 'user', 100), '\n'),
SystemText=strcat_array(make_list_if(MessageText, Role in ('system', 'developer'), 100), '\n')
)
| project-away MessagesExplanation
This KQL (Kusto Query Language) query is designed to process and analyze data related to application-generated AI content. Here's a simplified breakdown of what it does:
-
Define Spans:
- It starts by filtering data from
CustomAgentSpansto exclude certain classifications and only include entries with lineage information.
- It starts by filtering data from
-
Define Protected Content:
- It retrieves data from
AppGenAIContentthat has been generated within a specified time frame (lookback). - It ensures that certain fields (
_ResourceId,OperationId,SpanId) are not empty and converts them to lowercase. - It summarizes the data to get the most recent entry for each combination of
_ResourceId,OperationId, andSpanId. - It extracts and processes message content and tool results.
- It retrieves data from
-
Join and Process Data:
- It performs an inner join between the
SpansandProtecteddatasets based on_ResourceId,OperationId, andSpanId. - It processes the
Messagesfield to ensure it is an array and applies transformations to extract and concatenate message parts. - It separates the message content into
UserTextandSystemTextbased on the role of the message (e.g., user, system, developer).
- It performs an inner join between the
-
Output:
- The final output excludes the original
Messagesfield and provides a structured view of user and system messages related to AI content interactions.
- The final output excludes the original
In essence, this query is used to analyze and summarize interactions with AI content, focusing on the roles of different message parts and organizing them into user and system text for further analysis.