Replies: 2 comments 4 replies
|
Thanks for writing this up. the model reads well. One thing that might be worth mentioning explicitly is that the fields line up pretty closely with the OpenTelemetry GenAI semantic conventions. Since OTel export is listed as a non-goal here, I read that as a possible follow-up rather than a “never,” so calling out the alignment could make that future path clearer. Concretely:
Two things might be worth deciding while the contracts are still open:
Happy to take on the OTel export follow-up once this lands, if helpful. |
|
Thanks for the detailed write-up. The split between Business Events and Execution Events reads right to me. A few questions on the recording contract before implementation, mostly around run identity, execution lifecycle, and replay boundaries. Run identity. The proposal says the runtime creates or restores a run context, but doesn't define how Execution counting. §4.2 and §6 seem to point in slightly different directions: §4.2 says invoking the same entity twice creates two distinct executions, while §6 says retry attempts don't count as separate LLM executions. In the structured-output path, the model call and parse may be retried together, so a call that succeeds after parse retries could read as one LLM execution but several Parser executions under the same Action. Would it be worth stating the retry-collapsing rule once at the execution level, so Parser and LLM counting are consistent, for example one execution per logical call carrying an attempt count? Boundary safety. Since Execution Events reuse the existing Operational cost. Explicit Action/LLM/Parser/Tool lifecycle records can multiply Event Log volume noticeably for tool-heavy agents. Would the first implementation include a recording switch or sampling option, or at least clear guidance on when to expect log growth and whether recording is on by default? Durable replay. For |
Uh oh!
There was an error while loading. Please reload this page.
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 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. The built-in lifecycle reporting path sends it directly to observability consumers without automatically submitting it to
EventRouter; it therefore does not trigger Actions, notifyEventListener, or contribute to business Event metrics. This integration boundary does not prohibit a future explicit routing API.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 serialized field is named
inputRunIdto make this scope explicit.An execution is one concrete framework-observed invocation of an Action, LLM, Parser, or Tool. Invoking the same entity twice within one run creates two distinct executions. A framework retry that invokes an entity again therefore creates another execution; retries performed internally by a provider remain outside this model.
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:
executionId, linking the lineage to a concrete execution.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.timestampEventContexteventId,eventType,eventAttributesEventor Execution EventinputRunId,businessKey,agentNameExecutionTraceContextexecutionId,parentExecutionIdExecutionTraceContextentityType,entityName,entityMetadataExecutionTraceContextstatus,problemCategoryEventContextmirrorsEvent.getType()as part of its occurrence contract, and the serializer writes that value once aseventType. For Execution Events,statusandproblemCategoryare promoted from Event attributes to top-level fields during serialization.entityTypeandentityNameidentify the primary execution node, such as an Action, LLM, Parser, or Tool invocation.entityMetadatacontains supplemental attributes for that node; it neither introduces another execution node nor defines a parent-child relationship.parentExecutionIdrepresents execution containment. Business Event lineage remains responsible for cross-Action causality, while a business Event emitted by an Action carries the sameexecutionIdas that Action to connect the two models.For example, a structured-output parsing failure may be written as:
{ "timestamp": "2026-07-02T01:23:45.678Z", "inputRunId": "run-1", "businessKey": "order-1", "agentName": "order-agent", "executionId": "parser-execution-1", "parentExecutionId": "action-execution-1", "entityType": "parser", "entityName": "structured_output", "eventId": "event-17", "eventType": "_execution_failed_event", "status": "failed", "problemCategory": "model_output_parse_error", "eventAttributes": { "errorType": "JsonProcessingException", "errorMessage": "Unable to parse the model response" } }eventIdidentifies a logical Event occurrence; it does not imply that the corresponding physical log record is written exactly once.6. Recording Boundaries
Run Context
When the runtime starts processing a new input, it creates a run context containing:
inputRunIdbusinessKeyagentNameThe same context is propagated to business Events and executions created during the run. If a checkpointed
ActionTaskis restored, its existing run and execution context is restored with it rather than recreated.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 framework invocation of
ChatModel. If framework retry logic calls the model again, the retry creates another LLM execution. Retries hidden inside the provider or connection implementation are not separately observed.model_call_failed, even when the framework subsequently retries it.Parser
Each structured-output parsing attempt 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,
entityTypeandentityNameidentify the Tool execution as the primary entity. Its Tool kind and MCP Server provenance are stored inentityMetadata, not represented as child execution nodes:{ "entityType": "tool", "entityName": "search", "entityMetadata": { "toolType": "mcp", "mcpServer": "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. A framework retry appears as another LLM or Parser execution under the same Action. Provider-internal retries are not visible. Runtime replay may instead reproduce Event Log records as described in the recovery contract below.
7. Runtime Integration and Compatibility
Business Events and built-in execution lifecycle reporting 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"] --> EM["Execution metrics"] EE -. "trace enabled" .-> ES["ExecutionEventSink"] ES --> W W --> LOG["Event Log"]This separation preserves existing externally observable behavior:
EventRouterbehavior.EventRouter, so they do not trigger Actions or notify userEventListenerimplementations.EventLoggerimplementations can continue to handleEventandEventContextwithout consuming trace context.Trace persistence is controlled by
event-log.trace.enabled, which defaults tofalse:ExecutionTraceContext, while Execution Events are not persisted.New records use a normalized, flat JSON format with camelCase field names. Built-in Event type values retain their existing snake_case names, such as
_execution_started_event. The deserializer continues to support the previous nested format. Legacy records have no run or execution context and therefore cannot form a complete trace.When Trace persistence is enabled, 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. Run identity follows Flink's restore-versus-replay boundary:
ActionTaskcarries itsExecutionTraceContextin Flink state. Restoring that task preserves its existing run and execution identities.ActionTask, the runtime processes it as a new run with new run and execution identities.ActionStateStoreadds a separate reuse path:reusedinstead ofstartedandfinished.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.
All reactions