You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This proposal is the first in a two-part Flink Agents observability series:
Recording Agent Traces in the Event Log (this proposal)
Built-in Operational Metrics for Flink Agents (follow-up)
The two proposals serve complementary purposes. Agent Trace reconstructs what happened during a single run, while operational metrics describe the aggregate health and behavior of many runs.
1. Context and Existing Work
Flink Agents currently provides three main observability surfaces: metrics, Event Log, and EventListener. They provide aggregate signals, persisted business Event records, and callback hooks, but do not capture enough context to reconstruct an agent run as a causal trace.
This proposal builds on the gap identified in Discussion #710 and focuses on recording. Related community work includes:
Proposes adding run identity, the emitting Action, and source Event information to existing Event Log records.
Overlaps with this proposal on business Event lineage. This proposal also introduces Execution Events for Action, LLM, Parser, and Tool execution boundaries.
The Event Log may contain the original input, a chat request, a chat response, and an Action failure. Reconstructing the complete run requires both lineage and execution information.
Run identity and business Event lineage answer:
Which records belong to the same run?
Which Event triggered an Action, and which Action emitted a later Event?
Execution information answers:
Was the failure caused by the model call or by parsing its response?
Which concrete invocation of chat_model_action contained those operations?
How long did the LLM call and Parser execution take independently?
Event lineage alone cannot represent the LLM and Parser boundaries because those operations are not business Events. A complete trace therefore needs both forms of context.
3. Goals and Non-goals
Goals
Associate Event Log records with one run, business key, and Agent.
Reconstruct the actual Event-to-Action causal relationship at runtime.
Represent Action, LLM, Parser, and Tool execution boundaries.
Reconstruct parent-child relationships between nested executions.
Record execution outcomes and stable failure categories.
Preserve the original business Event payload.
Keep Java and Python Event Log records semantically aligned.
Extend the existing Event Log instead of introducing an independent tracing backend.
Non-goals
Trace query APIs or storage indexing.
Trace reconstruction or visualization.
OpenTelemetry export.
Evaluation datasets or experiment tracking.
Dashboards, alerts, or other product-level capabilities.
Provider-attempt-level tracing for retries.
Session-level tracing across multiple inputs.
4. Proposed Trace Model
The existing Event Log model records a business Event together with its occurrence context:
Existing EventLogRecord
= Event
+ EventContext
This model records which business Event occurred and when it was processed. It does not identify the run or execution to which the record belongs, and it cannot represent runtime work that is not a business Event.
The proposal leaves the responsibilities of Event and EventContext unchanged. It adds Execution Events for runtime lifecycle boundaries and ExecutionTraceContext for run identity, Event-to-Action causality, and execution hierarchy. Together, business Events describe workflow causality, while Execution Events describe the runtime work performed at each Action node.
Abstraction
Responsibility
Event
Describes what happened through an Event id, type, and attributes.
Execution Event
Describes the lifecycle and result of one execution.
EventContext
Carries occurrence metadata, currently the Event type and timestamp.
ExecutionTraceContext
Associates the record with a run, Action scope, execution hierarchy, and execution entity.
EventLogRecord
Combines an Event, its occurrence context, and its trace context into one persistent record.
4.1 Business Event and Execution Event
Business Events and Execution Events have distinct semantics:
A business Event represents a workflow occurrence and retains its original payload. It continues to participate in Event routing, EventListener callbacks, and existing business Event metrics.
An Execution Event represents the lifecycle of one execution. It records started, finished, failed, or reused and is used only for runtime observation.
An Execution Event is a runtime recording abstraction, not a new public Event base class. It reuses the existing Event data structure, but does not enter EventRouter, notify EventListener, trigger Actions, or contribute to business Event metrics.
Trace information remains outside user-owned Event attributes. A typed business Event, including a Memory Event or AgentRunBeginEvent, can receive the same run and execution context when written without changing its payload schema.
4.2 Trace Relationships
In this proposal, a run is one processing instance initiated by an input for one business key. It is not a user session and does not span multiple inputs. The examples use input_run_id to make this scope explicit; the final name can be aligned with the existing Agent Run terminology.
An execution is one concrete invocation of an Action, LLM, Parser, or Tool. Invoking the same entity twice within one run creates two distinct executions.
The trace combines two relationships:
Business Events connect Action executions into a causal graph.
Parent execution identifiers connect operations performed inside an Action into an execution tree.
An Action execution records the business Event occurrence that triggered it.
Action executions connected by business Events are siblings rather than parent and child executions.
Static Action trigger rules describe possible routes, but cannot identify the Event occurrence that triggered a concrete Action execution under concurrency, retry, or replay.
For execution hierarchy:
The run groups all trace records and serves as the trace root; it is not itself an execution.
LLM, Parser, and Tool executions are children of the Action execution in which they run.
Together, business Event records reconstruct the workflow graph, while Execution Events and their parent identifiers reconstruct the work performed within each Action execution.
5. Event Log Record
The serialized representation remains a flat JSONL record so that commonly queried fields can be indexed and aggregated without parsing nested envelopes. The in-memory model preserves the separation between Event, EventContext, and ExecutionTraceContext.
Field group
Fields
Source
Purpose
Event occurrence context
timestamp
EventContext
Records when the Event occurrence was processed.
Event
event_id, event_type, event_attributes
Event or Execution Event
Preserve the Event identity, semantics, and payload.
Reconstruct nested executions and Event-to-Action causality.
Execution entity
entity_type, entity_name, entity_metadata
ExecutionTraceContext
Identify the primary execution entity and carry its supplemental metadata.
Execution outcome
status, problem_category
Execution Event attributes
Describe the lifecycle result and stable failure category of an Execution Event.
EventContext mirrors Event.getType() as part of its occurrence contract, and the serializer writes that value once as event_type. For Execution Events, status and problem_category are promoted from Event attributes to top-level fields during serialization.
entity_type and entity_name identify the primary execution node, such as an Action, LLM, Parser, or Tool invocation. entity_metadata contains supplemental attributes for that node; it neither introduces another execution node nor defines a parent-child relationship.
parent_execution_id and action_trigger_event_id capture different relationships:
action_trigger_event_id identifies the business Event occurrence that triggered the current Action scope.
Child LLM, Parser, and Tool records may copy the Action's trigger Event id so that the Action scope can be queried directly. This does not imply that the business Event directly invoked the child execution.
For example, a structured-output parsing failure may be written as:
event_id identifies a logical Event occurrence; it does not imply that the corresponding physical log record is written exactly once.
6. Recording Boundaries
Run Context
When an input enters the Agent runtime, the framework creates or restores a run context containing:
input_run_id
business_key
agent_name
The same context is propagated to business Events and executions created during the run.
Action
The runtime records one Action execution for each concrete invocation:
started before invoking the Action.
finished after successful completion.
failed when an unhandled Action exception terminates the invocation.
reused when ActionStateStore returns an already completed Action result without re-executing the Action.
Action-level failures use action_execution_failed; the runtime does not infer a more specific category from a child failure. LLM, Parser, and Tool executions record their own precise failure categories.
LLM
An LLM execution represents one logical ChatModel call rather than an individual provider attempt. In the initial scope, intermediate retry attempts are not separate executions.
A final model response records success.
A final model-call failure records model_call_failed.
Parser
Structured-output parsing has its own Parser execution so that consumers can distinguish between:
A model call that failed.
A model call that succeeded but returned an unparseable response.
Parser failures use model_output_parse_error. Recording the Parser boundary must not change the existing retry or ignore behavior of structured-output processing.
Tool
A Tool execution represents one concrete tool invocation. If a ToolRequestEvent requests multiple calls, each invocation produces a separate Tool execution.
A successful Tool response records success.
A Tool error response keeps the original ToolResponseEvent payload but records the Tool execution as failed.
A thrown Tool exception records tool_call_failed and preserves the underlying error type and message.
Tool calls, MCP Servers, and Skills have different roles in the trace model:
Concept
Execution node
Trace representation
Tool call
Yes
Each invocation is one Tool execution.
MCP Tool call
Yes, as a Tool execution
The MCP Server is the resource exposing the Tool, not another execution level.
MCP Server
No
Recorded as resource provenance on its MCP Tool executions.
Skill
No
Represents instructions and resources; only an explicit load_skill invocation is an execution.
For example, an MCP Tool call has one execution node:
In the serialized record, entity_type and entity_name identify the Tool execution as the primary entity. Its Tool kind and MCP Server provenance are stored in entity_metadata, not represented as child execution nodes:
The MCP Server therefore has no separate execution id or lifecycle Events. Its metadata can still be used to query or aggregate related MCP Tool executions.
load_skill is a built-in Tool that reads the requested Skill's SKILL.md content or a specific resource and returns it to the model. Its invocation is represented as a Tool execution, with the Skill name and optional resource path stored as metadata. The runtime does not propagate a Skill execution context to subsequent Tool calls, so the trace does not attribute those calls to the loaded Skill.
Retry and Replay
Retry and replay do not have dedicated Event types. Depending on the runtime path, a repeated operation appears as a new execution or another physical Event Log record.
7. Runtime Integration and Compatibility
Business Events and Execution Events follow separate semantic paths but share the same Event Log writer:
flowchart TD
BE["Business Event"] --> ER["EventRouter"]
ER --> EL["EventListener"]
ER --> BM["Business Event metrics"]
ER --> AR["Action routing"]
ER --> W["Event Log writer"]
EE["Execution Event"] --> ES["ExecutionEventSink"]
ES --> W
W --> LOG["Event Log"]
Loading
This separation preserves existing externally observable behavior:
Business Events retain their existing EventRouter behavior.
Execution Events do not trigger Actions or notify user EventListener implementations.
Both paths use the same writer lifecycle and output format.
Event Log writes remain best-effort and do not affect the outcome of an Action.
Existing EventLogger implementations can continue to handle Event and EventContext without consuming trace context.
New records use a normalized, flat JSON format with snake_case field names. The deserializer continues to support the previous nested format. Legacy records have no run or execution context and therefore cannot form a complete trace.
Explicit execution boundaries add lifecycle records, so log volume grows with the number of recorded Action, LLM, Parser, and Tool executions. The proposal uses the existing Event Log path instead of introducing a separate tracing exporter or recording pipeline.
8. Recovery and Replay Semantics
The Event Log is best-effort and does not provide exactly-once delivery.
Without ActionStateStore:
Source replay may process the same logical business input again.
The replayed input currently receives new run and execution identities.
Each resulting run still has an internally consistent execution hierarchy.
With ActionStateStore:
A completed Action can be reused without re-executing user code.
The current run records a reused Execution Event for that Action.
Persisted output Events continue through the current run.
The original ExecutionTraceContext is not stored with the completed Action, so the Event Log cannot link a replayed output Event to the execution that originally produced it.
Consumers must not infer missing identities from timestamps or payload content.
9. Prototype Status
The model has been prototyped across the Java and Python runtime paths. The prototype validates:
A shared Event Log schema for business Events and Execution Events.
Action, LLM, Parser, and Tool lifecycle records, including success and failure paths.
Event-to-Action causality, nested executions, and multiple Tool calls from one request.
MCP Server and explicit Skill metadata.
Java and Python semantic alignment through conformance tests and end-to-end probes.
The purpose of this discussion is to review the model and contracts before submitting the implementation upstream.
10. Summary
Business Event lineage is necessary for Agent Trace, but it is not sufficient. A complete trace must also represent runtime executions that are not business Events.
This proposal extends the existing Event Log with:
Run identity and business key association.
Explicit Event-to-Action causality.
Action, LLM, Parser, and Tool execution lifecycle records.
Parent-child execution relationships.
Stable execution outcomes and failure categories.
A shared recording format for Java and Python.
The result is a framework-native recording model that allows consumers to reconstruct a run without changing business Event payloads or introducing a separate tracing system.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
This proposal is the first in a two-part Flink Agents observability series:
The two proposals serve complementary purposes. Agent Trace reconstructs what happened during a single run, while operational metrics describe the aggregate health and behavior of many runs.
1. Context and Existing Work
Flink Agents currently provides three main observability surfaces: metrics, Event Log, and
EventListener. They provide aggregate signals, persisted business Event records, and callback hooks, but do not capture enough context to reconstruct an agent run as a causal trace.This proposal builds on the gap identified in Discussion #710 and focuses on recording. Related community work includes:
AgentRunBeginEventcarrying a short-term-memory snapshot.To close the remaining recording gap, this proposal combines two complementary forms of context:
2. Motivating Example
Consider an Action that calls an LLM and parses the response into a structured output:
The Event Log may contain the original input, a chat request, a chat response, and an Action failure. Reconstructing the complete run requires both lineage and execution information.
Run identity and business Event lineage answer:
Execution information answers:
chat_model_actioncontained those operations?Event lineage alone cannot represent the LLM and Parser boundaries because those operations are not business Events. A complete trace therefore needs both forms of context.
3. Goals and Non-goals
Goals
Non-goals
4. Proposed Trace Model
The existing Event Log model records a business Event together with its occurrence context:
This model records which business Event occurred and when it was processed. It does not identify the run or execution to which the record belongs, and it cannot represent runtime work that is not a business Event.
The proposed model extends the record as follows:
The proposal leaves the responsibilities of
EventandEventContextunchanged. It adds Execution Events for runtime lifecycle boundaries andExecutionTraceContextfor run identity, Event-to-Action causality, and execution hierarchy. Together, business Events describe workflow causality, while Execution Events describe the runtime work performed at each Action node.EventEventContextExecutionTraceContextEventLogRecord4.1 Business Event and Execution Event
Business Events and Execution Events have distinct semantics:
EventListenercallbacks, and existing business Event metrics.started,finished,failed, orreusedand is used only for runtime observation.An Execution Event is a runtime recording abstraction, not a new public Event base class. It reuses the existing Event data structure, but does not enter
EventRouter, notifyEventListener, trigger Actions, or contribute to business Event metrics.Trace information remains outside user-owned Event attributes. A typed business Event, including a Memory Event or
AgentRunBeginEvent, can receive the same run and execution context when written without changing its payload schema.4.2 Trace Relationships
In this proposal, a run is one processing instance initiated by an input for one business key. It is not a user session and does not span multiple inputs. The examples use
input_run_idto make this scope explicit; the final name can be aligned with the existing Agent Run terminology.An execution is one concrete invocation of an Action, LLM, Parser, or Tool. Invoking the same entity twice within one run creates two distinct executions.
The trace combines two relationships:
flowchart TB IN["Input Event"] -->|"triggers"| A1["Action execution A"] A1 -->|"contains"| L1["LLM execution"] A1 -->|"contains"| P1["Parser execution"] A1 -->|"emits"| E1["Business Event E"] E1 -->|"triggers"| A2["Action execution B"] A2 -->|"contains"| T1["Tool execution 1"] A2 -->|"contains"| T2["Tool execution 2"]For business Event causality:
For execution hierarchy:
Together, business Event records reconstruct the workflow graph, while Execution Events and their parent identifiers reconstruct the work performed within each Action execution.
5. Event Log Record
The serialized representation remains a flat JSONL record so that commonly queried fields can be indexed and aggregated without parsing nested envelopes. The in-memory model preserves the separation between
Event,EventContext, andExecutionTraceContext.timestampEventContextevent_id,event_type,event_attributesEventor Execution Eventinput_run_id,business_key,agent_nameExecutionTraceContextexecution_id,parent_execution_id,action_trigger_event_idExecutionTraceContextentity_type,entity_name,entity_metadataExecutionTraceContextstatus,problem_categoryEventContextmirrorsEvent.getType()as part of its occurrence contract, and the serializer writes that value once asevent_type. For Execution Events,statusandproblem_categoryare promoted from Event attributes to top-level fields during serialization.entity_typeandentity_nameidentify the primary execution node, such as an Action, LLM, Parser, or Tool invocation.entity_metadatacontains supplemental attributes for that node; it neither introduces another execution node nor defines a parent-child relationship.parent_execution_idandaction_trigger_event_idcapture different relationships:parent_execution_idrepresents execution containment.action_trigger_event_ididentifies the business Event occurrence that triggered the current Action scope.Child LLM, Parser, and Tool records may copy the Action's trigger Event id so that the Action scope can be queried directly. This does not imply that the business Event directly invoked the child execution.
For example, a structured-output parsing failure may be written as:
{ "timestamp": "2026-07-02T01:23:45.678Z", "input_run_id": "run-1", "business_key": "order-1", "agent_name": "order-agent", "execution_id": "parser-execution-1", "parent_execution_id": "action-execution-1", "action_trigger_event_id": "input-event-1", "entity_type": "parser", "entity_name": "structured_output", "event_id": "event-17", "event_type": "_execution_failed_event", "status": "failed", "problem_category": "model_output_parse_error", "event_attributes": { "error_type": "JsonProcessingException", "error_message": "Unable to parse the model response" } }event_ididentifies a logical Event occurrence; it does not imply that the corresponding physical log record is written exactly once.6. Recording Boundaries
Run Context
When an input enters the Agent runtime, the framework creates or restores a run context containing:
input_run_idbusiness_keyagent_nameThe same context is propagated to business Events and executions created during the run.
Action
The runtime records one Action execution for each concrete invocation:
startedbefore invoking the Action.finishedafter successful completion.failedwhen an unhandled Action exception terminates the invocation.reusedwhenActionStateStorereturns an already completed Action result without re-executing the Action.Action-level failures use
action_execution_failed; the runtime does not infer a more specific category from a child failure. LLM, Parser, and Tool executions record their own precise failure categories.LLM
An LLM execution represents one logical
ChatModelcall rather than an individual provider attempt. In the initial scope, intermediate retry attempts are not separate executions.model_call_failed.Parser
Structured-output parsing has its own Parser execution so that consumers can distinguish between:
Parser failures use
model_output_parse_error. Recording the Parser boundary must not change the existing retry or ignore behavior of structured-output processing.Tool
A Tool execution represents one concrete tool invocation. If a
ToolRequestEventrequests multiple calls, each invocation produces a separate Tool execution.ToolResponseEventpayload but records the Tool execution as failed.tool_call_failedand preserves the underlying error type and message.Tool calls, MCP Servers, and Skills have different roles in the trace model:
load_skillinvocation is an execution.For example, an MCP Tool call has one execution node:
In the serialized record,
entity_typeandentity_nameidentify the Tool execution as the primary entity. Its Tool kind and MCP Server provenance are stored inentity_metadata, not represented as child execution nodes:{ "entity_type": "tool", "entity_name": "search", "entity_metadata": { "tool_type": "mcp", "mcp_server": "search_server" } }The MCP Server therefore has no separate execution id or lifecycle Events. Its metadata can still be used to query or aggregate related MCP Tool executions.
load_skillis a built-in Tool that reads the requested Skill'sSKILL.mdcontent or a specific resource and returns it to the model. Its invocation is represented as a Tool execution, with the Skill name and optional resource path stored as metadata. The runtime does not propagate a Skill execution context to subsequent Tool calls, so the trace does not attribute those calls to the loaded Skill.Retry and Replay
Retry and replay do not have dedicated Event types. Depending on the runtime path, a repeated operation appears as a new execution or another physical Event Log record.
7. Runtime Integration and Compatibility
Business Events and Execution Events follow separate semantic paths but share the same Event Log writer:
flowchart TD BE["Business Event"] --> ER["EventRouter"] ER --> EL["EventListener"] ER --> BM["Business Event metrics"] ER --> AR["Action routing"] ER --> W["Event Log writer"] EE["Execution Event"] --> ES["ExecutionEventSink"] ES --> W W --> LOG["Event Log"]This separation preserves existing externally observable behavior:
EventRouterbehavior.EventListenerimplementations.EventLoggerimplementations can continue to handleEventandEventContextwithout consuming trace context.New records use a normalized, flat JSON format with snake_case field names. The deserializer continues to support the previous nested format. Legacy records have no run or execution context and therefore cannot form a complete trace.
Explicit execution boundaries add lifecycle records, so log volume grows with the number of recorded Action, LLM, Parser, and Tool executions. The proposal uses the existing Event Log path instead of introducing a separate tracing exporter or recording pipeline.
8. Recovery and Replay Semantics
The Event Log is best-effort and does not provide exactly-once delivery.
Without
ActionStateStore:With
ActionStateStore:reusedExecution Event for that Action.ExecutionTraceContextis not stored with the completed Action, so the Event Log cannot link a replayed output Event to the execution that originally produced it.Consumers must not infer missing identities from timestamps or payload content.
9. Prototype Status
The model has been prototyped across the Java and Python runtime paths. The prototype validates:
The purpose of this discussion is to review the model and contracts before submitting the implementation upstream.
10. Summary
Business Event lineage is necessary for Agent Trace, but it is not sufficient. A complete trace must also represent runtime executions that are not business Events.
This proposal extends the existing Event Log with:
The result is a framework-native recording model that allows consumers to reconstruct a run without changing business Event payloads or introducing a separate tracing system.
Beta Was this translation helpful? Give feedback.
All reactions