Conversation log schema
This document describes the table layout, columns, event types, user identity fields, and JSON payload structure for conversational analytics conversation logging in BigQuery. For an overview of how conversation logging works, see Conversation logging overview.
Table layout and auto-creation
Create the destination dataset in the same project as the data agent before
enabling logging; the Conversational Analytics API service agent creates the table in
the dataset's location on the first logged turn (or adds any missing schema
columns to an existing compatible table). The auto-created table is partitioned
by day on timestamp (inheriting any default partition expiration on the
dataset) and clustered on event_type, agent, and user_id. Always filter on
timestamp when you query the table.
Columns
The following table describes the columns in the conversation logging destination table.
| Column | Type | Mode | Contents |
|---|---|---|---|
timestamp |
TIMESTAMP |
REQUIRED |
UTC time of the event. Partitioning column. |
event_id |
STRING |
NULLABLE |
Unique ID for the event. It stays the same if a write is retried, so you can use it to remove duplicates. |
event_type |
STRING |
NULLABLE |
Kind of event. See Event types. |
agent |
STRING |
NULLABLE |
The agent type, not your agent's name or ID (for example,
ConversationalAnalyticsAgent). To tell agents apart, use the
data agent ID (see
Fields inside
attributes). |
session_id |
STRING |
NULLABLE |
One turn, not a whole conversation. All events for
one question and its answer share a session_id. To group
turns into conversations, use the conversation ID in
attributes. |
invocation_id |
STRING |
NULLABLE |
One execution within a turn. |
user_id |
STRING |
NULLABLE |
The caller who asked the question. See User identity. |
trace_id |
STRING |
NULLABLE |
OpenTelemetry trace ID (32 hexadecimal characters). |
span_id |
STRING |
NULLABLE |
ID of this operation in the turn's execution tree. |
parent_span_id |
STRING |
NULLABLE |
ID of the parent operation. |
content |
JSON |
NULLABLE |
Event payload. Its shape depends on event_type. |
content_parts |
RECORD |
REPEATED |
Multi-part content, with one element per part (for example,
mime_type, uri, object_ref,
text, part_index, part_attributes,
and storage_mode). |
attributes |
JSON |
NULLABLE |
Metadata: data agent ID, conversation ID, internal user key, model version, and detailed token usage. |
latency_ms |
JSON |
NULLABLE |
Latency measurements, such as total_ms and
time_to_first_token_ms. |
status |
STRING |
NULLABLE |
OK or ERROR. |
error_message |
STRING |
NULLABLE |
Error details. It can also be set on OK
LLM_RESPONSE rows when the model stops for a reason other
than a normal stop (for example, when it reaches the maximum token
limit). |
is_truncated |
BOOLEAN |
NULLABLE |
true if any text value in the event exceeded 512,000
characters (500 * 1,024) and was cut. Cut values end with
...[TRUNCATED]. The removed part can't be recovered. |
Event types
The following table describes the values that can appear in the event_type
column and the columns or JSON fields that store the data for each event.
event_type value |
Description | Populated columns and JSON fields |
|---|---|---|
INVOCATION_STARTING |
Start of a turn. | None |
USER_MESSAGE_RECEIVED |
The user's question. | content.text_summary |
AGENT_RESPONSE |
The agent's answer, as a structured object. | content.text_summary (plain text) and
content.response (see
Structure of
AGENT_RESPONSE) |
LLM_RESPONSE |
One row for each model call, including calls that requested a tool
(not written when a turn is answered directly from a verified query, where
attributes.fast_path is true). The model's
output isn't stored. |
Tokens: content.usage.prompt,
content.usage.completion, content.usage.total.
Model: attributes.model_version. Finish reason:
attributes.finish_reason. Detailed usage:
attributes.usage_metadata (for example,
cached_content_token_count and
thoughts_token_count). |
INVOCATION_COMPLETED |
End of a turn, with per-turn latency. content is
empty. |
latency_ms.total_ms |
EMBEDDING_SUGGESTION |
Suggestions for the agent owner about columns that could benefit from embeddings. It appears only for some agents. | content.reason,
content.suggested_columns |
INVOCATION_ERROR, AGENT_ERROR,
LLM_ERROR |
Failed turn. status is ERROR and
content is NULL. |
error_message (one-line summary with SQL statements and
table names redacted) |
User identity
The user_id column contains the caller's email address: the user's email
address, a service account's email address when an API caller authenticates with
a service account, or NULL if the caller has no email address (such as some
Workforce Identity Federation identities). An internal user key is also recorded
in attributes.session_metadata.user_id on every row, so you can count distinct
users with internal_user_key (see Create the cross-agent events
view).
To attribute turns to your own end users when you call the API through a service account, record the user mapping in your application. The API doesn't accept an end-user identifier for logging.
Fields inside attributes
The following table shows the GoogleSQL expressions used to extract
metadata fields from the attributes JSON column.
| What you need | GoogleSQL expression |
|---|---|
| Data agent ID | JSON_VALUE(attributes, '$.session_metadata.state."data-agent-id"') |
| Data agent full resource name | JSON_VALUE(attributes, '$.adk.app_name') |
| Conversation ID | JSON_VALUE(attributes, '$.session_metadata.state."conversation-id"') |
| Internal user key | JSON_VALUE(attributes, '$.session_metadata.user_id') |
Model version (on LLM_RESPONSE rows) |
JSON_VALUE(attributes, '$.model_version') |
Structure of AGENT_RESPONSE
To read the agent's answer as plain text, use
JSON_VALUE(content, '$.text_summary'). It combines the answer's Markdown,
assumption, and insight parts, and any clarifying questions. In AGENT_RESPONSE
rows, content.response is a JSON object, not a string, so
JSON_VALUE(content, '$.response') returns NULL.
The following table describes the paths under content.response.
Path under content.response |
Type | Contents |
|---|---|---|
parts |
ARRAY<JSON> |
The answer, in parts. |
parts[].markdown |
STRING |
Answer text (Markdown). |
parts[].insight |
STRING |
An insight drawn from the data. |
parts[].assumption |
STRING |
An assumption that the agent made. |
parts[].data_result_artifact_name |
STRING |
Name of a data result. |
parts[].chart_artifact_name |
STRING |
Name of a chart. |
followup_questions |
ARRAY<STRING> |
Suggested follow-up questions. |
clarifying_question |
STRING |
Set when the agent asked the user a clarifying question instead of answering. |
citation_sources |
JSON |
Sources cited in the answer. |