sangkyoonnam commented on code in PR #1262:
URL: https://github.com/apache/flink-agents/pull/1262#discussion_r4311620646


##########
docs/content/docs/operations/monitoring.md:
##########
@@ -22,107 +22,124 @@ specific language governing permissions and limitations
 under the License.
 -->
 
-## Metric
 
-### Built-in Metrics
+## Overview
 
-We offer data monitoring for built-in metrics, including input runs, events, 
actions, execution health, and token usage.
+A running agent produces three kinds of observability data, and each answers a 
different question.
 
-#### Event and Action Metrics
+- **Metrics** show how much and how fast: Event and Action throughput, 
input-run outcomes and latency, LLM, Tool and sub-agent calls, and token usage. 
They go through [Flink's metric 
system](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/ops/metrics/),
 so they reach the same reporters and dashboards as the rest of the job.
+- **Logs** carry what your Action code and the framework write through Flink's 
logging framework, such as warnings and stack traces.
+- **The [Trace Log](#trace-log)** records Events, Action executions and the 
calls made within Actions as structured JSON, so you can follow what one input 
did. By default it records only Events; configure `trace-log.targets` to 
include Actions and component calls.
 
-| Scope       | Metrics                                          | Description 
                                                                     | Type  |
-|-------------|--------------------------------------------------|----------------------------------------------------------------------------------|-------|
-| **Agent**   | numOfEventProcessed                              | The total 
number of Events this operator has processed.                          | Count |
-| **Agent** | numOfEventProcessedPerSec                        | The number of 
Events this operator has processed per second.                     | Meter |
-| **Agent** | numOfActionsExecuted                             | The total 
number of actions this operator has executed.                          | Count |
-| **Agent** | numOfActionsExecutedPerSec                       | The number of 
actions this operator has executed per second.                     | Meter |
-| **Agent** | numOfInputRunsSucceeded                          | The number of 
input runs that reached the run-completion boundary.                | Count |
-| **Agent** | numOfInputRunsFailed                             | The number of 
input runs terminated by an unhandled exception.                    | Count |
-| **Agent** | inputRunLatencyMs                                | End-to-end 
input-run latency from entering the agent operator to completion or failure, 
including time queued behind another input with the same key. | Histogram |
-| **Agent** | inputRunQueueLatencyMs                           | Time from 
entering the agent operator until the input run starts processing. | Histogram |
-| **Agent** | inputRunProcessingLatencyMs                      | Time from the 
input-run start boundary until completion or failure. | Histogram |
-| **Agent** | numOfPendingInputEvents                          | Current 
number of input Events buffered behind an active run with the same key. | Gauge 
|
-| **Agent** | numOfActiveInputRuns                             | Current 
number of logical input runs that are processing or waiting for asynchronous 
work. | Gauge |
-| **Action**  | action.\<action_name\>.numOfActionsExecuted | The total number 
of actions this operator has executed for a specific action name. | Count |
-| **Action**  | action.\<action_name\>.numOfActionsExecutedPerSec | The number 
of actions this operator has executed per second for a specific action name. | 
Meter |
-| **Action** | action.\<action_name\>.actionSchedulingLatencyMs | Time from 
enqueuing the initial Action task until it is selected for execution. | 
Histogram |
-| **Action** | action.\<action_name\>.actionExecutionLatencyMs | End-to-end 
latency of one logical Action execution, including asynchronous waits and 
continuations. | Histogram |
-| **Action** | action.\<action_name\>.numOfPendingActionTasks | Current number 
of physical Action task segments waiting to run, including continuations. | 
Gauge |
-| **Action** | action.\<action_name\>.numOfActiveActionExecutions | Current 
number of logical Action executions that have started but have not reached a 
terminal state. | Gauge |
-| **Action**  | action.\<action_name\>.routingDecisionLatencyMs | Wall-clock 
time, in milliseconds, spent resolving a model-routing decision when a 
`ChatRequestEvent` names a `MODEL_ROUTER`. Recorded once per routing decision, 
including replayed ones; see [Model Routing]({{< ref 
"docs/development/calling-llms/model_routing#observability" >}}). | Histogram |
-| **Agent**   | traceLogTruncatedRecords                          | Number of 
Trace Log records whose attributes were truncated at `STANDARD` level. 
Increments once per record, regardless of how many fields were truncated. Use 
this to adjust truncation limits or select entities at `VERBOSE`. | Count |
-| **Agent**   | traceLogWriteFailures                           | Number of 
Trace Log write attempts for which `append`, `flush`, or both failed. Trace Log 
writes are best-effort and do not fail the job. | Count |
-
-For a locally observed input run, `inputRunLatencyMs` is split into queueing 
and processing time at the input-run start boundary. `numOfPendingInputEvents` 
counts buffered inputs, while `numOfActiveInputRuns` counts logical runs; an 
asynchronous run remains active while it is waiting for its continuation.
-
-An Action execution can be active while one of its continuation tasks is 
pending, so `numOfActiveActionExecutions` and `numOfPendingActionTasks` are 
independent. Action scheduling latency is recorded only for the initial task; 
continuation queueing does not create another scheduling sample.
-
-Input-run outcomes and all latency samples are process-local. Runs or Action 
executions already in flight when a task is restored do not produce latency 
samples because their original timestamps are unavailable. An input Event 
restored from the pending queue can still produce an outcome and 
processing-latency sample after it starts in the new task attempt, but it does 
not produce queue or end-to-end latency. Current-count gauges are rebuilt from 
Flink state after restore.
+A typical investigation starts from a metric that looks wrong, such as a 
rising `numOfInputRunsFailed`, moves to the logs of the affected task for the 
error, and then to the Trace Log to see which Events and Actions led there.
 
-#### Execution Metrics
+## Metrics
 
-LLM, Tool, and sub-agent metrics describe calls observed by the framework 
within an Action. The framework reports each call's start and outcome directly 
to the metric collectors. These reports do not create Events for Action 
routing. Metrics are collected independently of Trace Log output: changing 
`trace-log.targets` or `level` does not disable them.
+Flink Agents registers its metrics with the job's Flink metric system. For the 
metric types used below (Counter, Meter, Histogram and Gauge), see [Metric 
types](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/ops/metrics/#metric-types).
 Latency metrics are in milliseconds, as their `Ms` suffix shows; 
`retryWaitSec` is in seconds.
 
-The `model_resource`, `tool`, `skill`, `mcp_server`, and `subagent` scopes are 
independent key-value scopes directly under an Action. The existing `model` 
scope remains dedicated to model usage metrics.
+### Accessing metrics
 
-| Scope | Metrics | Description | Type |
-|-------|---------|-------------|------|
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.numOfLlmCallsSucceeded 
| The number of framework-observed model invocations that returned 
successfully. | Count |
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.numOfLlmCallsFailed | 
The number of framework-observed model invocations that failed. | Count |
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.llmCallLatencyMs | 
Latency of each framework-observed model invocation, excluding 
structured-output parsing and retry wait time. | Histogram |
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.retryCount | The number 
of additional model invocations initiated when retries are enabled 
(`max-retries > 0`). Only recorded when at least one retry occurs. See 
[retry-wait-interval]({{< ref "docs/operations/configuration#core-options" 
>}}). | Count |
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.retryWaitSec | The 
total backoff time, in seconds, accumulated when retries are enabled 
(`max-retries > 0`). Only recorded when at least one retry occurs. | Count |
-| **Tool** | action.\<action_name\>.tool.\<tool_name\>.numOfToolCallsSucceeded 
| The number of successful calls to the Tool. | Count |
-| **Tool** | action.\<action_name\>.tool.\<tool_name\>.numOfToolCallsFailed | 
The number of failed calls to the Tool. | Count |
-| **Tool** | action.\<action_name\>.tool.\<tool_name\>.toolCallLatencyMs | 
Time spent invoking the individual Tool, excluding time waiting for other calls 
in the same parallel batch. | Histogram |
-| **Skill** | action.\<action_name\>.skill.\<skill_name\>.numOfSkillLoads | 
The number of terminal explicit `load_skill` calls attributed to the Skill, 
regardless of outcome. | Count |
-| **Skill** | action.\<action_name\>.skill.\<skill_name\>.skillLoadLatencyMs | 
Time spent invoking an explicit `load_skill` call. | Histogram |
-| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.numOfMcpToolCallsSucceeded | 
The number of successful Tool calls served by the MCP Server. | Count |
-| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.numOfMcpToolCallsFailed | The 
number of failed Tool calls served by the MCP Server. | Count |
-| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.mcpToolCallLatencyMs | 
Individual Tool invocation latency aggregated across the MCP Server. | 
Histogram |
-| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.numOfSubagentCallsSucceeded | 
The number of sub-agent delegations that returned a successful result. | Count |
-| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.numOfSubagentCallsFailed | The 
number of sub-agent delegations that failed, either while submitting or 
awaiting the delegation or because the sub-agent returned a failed result. | 
Count |
-| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.subagentCallLatencyMs | Time the 
Action observed for one sub-agent delegation, from handing the prompt to the 
sub-agent until its result is observed. | Histogram |
+Metrics are available wherever the job's other metrics are: through a 
configured [metric 
reporter](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/deployment/metric_reporters/),
 such as Prometheus, or in the Flink Web UI (see [Dashboard 
integration](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/ops/metrics/#dashboard-integration)).
 
-An LLM metric represents one framework invocation of `ChatModel`. A framework 
retry that calls the model again produces another LLM outcome and latency 
sample; retries hidden inside a provider or connection are not observed. Every 
named Tool execution emits Tool metrics. Skill metrics are emitted only for 
explicit `load_skill` calls; subsequent Tool calls are not inferred to belong 
to a Skill. MCP metrics aggregate only Tool executions carrying an explicit MCP 
Server resource name. A `load_skill` or MCP Tool execution therefore 
contributes to both its Tool scope and the corresponding Skill or MCP Server 
scope. A sub-agent delegation the model requests through the reserved 
`_subagent_` callable name emits Subagent metrics keyed by the registered 
sub-agent name, and is reported only under the Subagent scope rather than also 
under a Tool scope. `subagentCallLatencyMs` spans the Action-observed 
delegation from hand-off to result observation; for delegations dispatched 
concurrently in on
 e batch, this window includes their overlap.
+With Flink's default operator scope, metric identifiers start with 
`<host>.taskmanager.<tm_id>.<job_name>.<operator_name>.<subtask_index>`; 
reporters may format them differently. For an agent operator, `<operator_name>` 
is the agent name, or `action-execute-operator` when the agent has no name. In 
the Flink Web UI, the agent operator's metrics appear under the prefix 
`<subtask_index>.<operator_name>`.
 
-During fine-grained recovery, a cached durable child result contributes 
another call outcome to these metrics; cache reuse is not separately 
identified. A cached LLM result is reported as successful and may produce a 
near-zero latency sample. A cached Tool result retains its normalized outcome: 
`ToolResponse.error(...)` counts as failed, while a successful `ToolResponse` 
or an ordinary return value that can be converted to Tool content counts as 
successful. A cached Tool result produces no latency sample because its 
callable was not invoked.
+{{< img src="/fig/operations/metricwebui.png" alt="Metric Web UI" >}}
 
-Tool names that are not registered runtime resources are aggregated under the 
fixed `tool=unknown` scope. Requested Skill names that do not resolve in the 
runtime registry are similarly aggregated under `skill=unknown`. A `_subagent_` 
callable name that does not resolve to a registered sub-agent is reported as a 
Tool and therefore aggregated under `tool=unknown`; the Subagent scope is keyed 
only by registered sub-agent names, so its cardinality is bounded by the Agent 
plan. When selected for Trace Log output, the call observations retain the 
original requested names; metric scope cardinality remains bounded.
+### Scopes
 
-Tool outcomes follow the same two-layer contract in Java and Python. Resource 
preparation and invocation exceptions are failures. A returned 
`ToolResponse.error(...)` is also a failed Tool execution even though the 
durable invocation returned normally. Tools can return an explicit 
`ToolResponse` or an ordinary value that is converted to text: strings are used 
directly, and other values use JSON when possible, falling back to their string 
representation. A return value that still cannot be converted to text is 
reported as a failed Tool execution. A durable-persistence exception is 
reflected as a Tool failure only when the existing durable execution path 
exposes it to `ToolCallAction`.
+The **Scope** column in the tables below identifies the metric group. The 
example names omit the operator prefix shown above; reporters may represent the 
groups as labels instead.
 
-`numOfSkillLoads` counts terminal calls rather than successful loads. In both 
runtimes, `load_skill` reports an unavailable manager, missing Skill, or 
missing resource through `ToolResponse.error(...)`; the Tool outcome is 
therefore failed while the Skill load counter still increments. MCP 
protocol-level errors follow the same failed Tool outcome contract.
+| Scope | Metric group | Example name |
+|-------|--------------|--------------|
+| **Agent** | the agent operator itself | `numOfEventProcessed` |
+| **Action** | `action.<action_name>` | 
`action.chat_model_action.numOfActionsExecuted` |
+| **Model** | `action.<action_name>.model.<model_name>` | 
`action.chat_model_action.model.qwen3:1.7b.promptTokens` |
+| **Model Resource**, **Tool**, **Skill**, **MCP Server**, **Subagent** | 
`action.<action_name>.<kind>.<name>`, where `<kind>` is `model_resource`, 
`tool`, `skill`, `mcp_server` or `subagent` | 
`action.tool_call_action.tool.get_weather.numOfToolCallsSucceeded` |
 
-Execution latency is measured from matching start and outcome observations of 
the same call in the same task attempt. Assigning a call identity before 
preparation or submission does not start the timer. Invoked Tools retain their 
own start and completion timestamps, even when the reports arrive after a 
durable call or parallel batch returns. Queueing and time spent waiting for 
other calls in the batch are excluded. Outcome counters still update when no 
matching start is available.
+Each group below the Agent scope is a key-value group: `<action_name>` is the 
Action's name, `<model_name>` the model a chat model reports usage for, and 
`<name>` the resource, Tool, Skill, MCP Server or sub-agent name. Reporters 
that support key-value groups, such as Prometheus, expose them as labels.

Review Comment:
   Updated in bb48488e: the paragraph after the scope table now identifies 
`chat_model_action` and `tool_call_action` as [built-in 
Actions](https://nightlies.apache.org/flink/flink-agents-docs-main/docs/development/create-agent/events/#built-in-events-and-actions).
   



##########
docs/content/docs/operations/monitoring.md:
##########
@@ -22,107 +22,124 @@ specific language governing permissions and limitations
 under the License.
 -->
 
-## Metric
 
-### Built-in Metrics
+## Overview
 
-We offer data monitoring for built-in metrics, including input runs, events, 
actions, execution health, and token usage.
+A running agent produces three kinds of observability data, and each answers a 
different question.
 
-#### Event and Action Metrics
+- **Metrics** show how much and how fast: Event and Action throughput, 
input-run outcomes and latency, LLM, Tool and sub-agent calls, and token usage. 
They go through [Flink's metric 
system](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/ops/metrics/),
 so they reach the same reporters and dashboards as the rest of the job.
+- **Logs** carry what your Action code and the framework write through Flink's 
logging framework, such as warnings and stack traces.
+- **The [Trace Log](#trace-log)** records Events, Action executions and the 
calls made within Actions as structured JSON, so you can follow what one input 
did. By default it records only Events; configure `trace-log.targets` to 
include Actions and component calls.
 
-| Scope       | Metrics                                          | Description 
                                                                     | Type  |
-|-------------|--------------------------------------------------|----------------------------------------------------------------------------------|-------|
-| **Agent**   | numOfEventProcessed                              | The total 
number of Events this operator has processed.                          | Count |
-| **Agent** | numOfEventProcessedPerSec                        | The number of 
Events this operator has processed per second.                     | Meter |
-| **Agent** | numOfActionsExecuted                             | The total 
number of actions this operator has executed.                          | Count |
-| **Agent** | numOfActionsExecutedPerSec                       | The number of 
actions this operator has executed per second.                     | Meter |
-| **Agent** | numOfInputRunsSucceeded                          | The number of 
input runs that reached the run-completion boundary.                | Count |
-| **Agent** | numOfInputRunsFailed                             | The number of 
input runs terminated by an unhandled exception.                    | Count |
-| **Agent** | inputRunLatencyMs                                | End-to-end 
input-run latency from entering the agent operator to completion or failure, 
including time queued behind another input with the same key. | Histogram |
-| **Agent** | inputRunQueueLatencyMs                           | Time from 
entering the agent operator until the input run starts processing. | Histogram |
-| **Agent** | inputRunProcessingLatencyMs                      | Time from the 
input-run start boundary until completion or failure. | Histogram |
-| **Agent** | numOfPendingInputEvents                          | Current 
number of input Events buffered behind an active run with the same key. | Gauge 
|
-| **Agent** | numOfActiveInputRuns                             | Current 
number of logical input runs that are processing or waiting for asynchronous 
work. | Gauge |
-| **Action**  | action.\<action_name\>.numOfActionsExecuted | The total number 
of actions this operator has executed for a specific action name. | Count |
-| **Action**  | action.\<action_name\>.numOfActionsExecutedPerSec | The number 
of actions this operator has executed per second for a specific action name. | 
Meter |
-| **Action** | action.\<action_name\>.actionSchedulingLatencyMs | Time from 
enqueuing the initial Action task until it is selected for execution. | 
Histogram |
-| **Action** | action.\<action_name\>.actionExecutionLatencyMs | End-to-end 
latency of one logical Action execution, including asynchronous waits and 
continuations. | Histogram |
-| **Action** | action.\<action_name\>.numOfPendingActionTasks | Current number 
of physical Action task segments waiting to run, including continuations. | 
Gauge |
-| **Action** | action.\<action_name\>.numOfActiveActionExecutions | Current 
number of logical Action executions that have started but have not reached a 
terminal state. | Gauge |
-| **Action**  | action.\<action_name\>.routingDecisionLatencyMs | Wall-clock 
time, in milliseconds, spent resolving a model-routing decision when a 
`ChatRequestEvent` names a `MODEL_ROUTER`. Recorded once per routing decision, 
including replayed ones; see [Model Routing]({{< ref 
"docs/development/calling-llms/model_routing#observability" >}}). | Histogram |
-| **Agent**   | traceLogTruncatedRecords                          | Number of 
Trace Log records whose attributes were truncated at `STANDARD` level. 
Increments once per record, regardless of how many fields were truncated. Use 
this to adjust truncation limits or select entities at `VERBOSE`. | Count |
-| **Agent**   | traceLogWriteFailures                           | Number of 
Trace Log write attempts for which `append`, `flush`, or both failed. Trace Log 
writes are best-effort and do not fail the job. | Count |
-
-For a locally observed input run, `inputRunLatencyMs` is split into queueing 
and processing time at the input-run start boundary. `numOfPendingInputEvents` 
counts buffered inputs, while `numOfActiveInputRuns` counts logical runs; an 
asynchronous run remains active while it is waiting for its continuation.
-
-An Action execution can be active while one of its continuation tasks is 
pending, so `numOfActiveActionExecutions` and `numOfPendingActionTasks` are 
independent. Action scheduling latency is recorded only for the initial task; 
continuation queueing does not create another scheduling sample.
-
-Input-run outcomes and all latency samples are process-local. Runs or Action 
executions already in flight when a task is restored do not produce latency 
samples because their original timestamps are unavailable. An input Event 
restored from the pending queue can still produce an outcome and 
processing-latency sample after it starts in the new task attempt, but it does 
not produce queue or end-to-end latency. Current-count gauges are rebuilt from 
Flink state after restore.
+A typical investigation starts from a metric that looks wrong, such as a 
rising `numOfInputRunsFailed`, moves to the logs of the affected task for the 
error, and then to the Trace Log to see which Events and Actions led there.
 
-#### Execution Metrics
+## Metrics
 
-LLM, Tool, and sub-agent metrics describe calls observed by the framework 
within an Action. The framework reports each call's start and outcome directly 
to the metric collectors. These reports do not create Events for Action 
routing. Metrics are collected independently of Trace Log output: changing 
`trace-log.targets` or `level` does not disable them.
+Flink Agents registers its metrics with the job's Flink metric system. For the 
metric types used below (Counter, Meter, Histogram and Gauge), see [Metric 
types](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/ops/metrics/#metric-types).
 Latency metrics are in milliseconds, as their `Ms` suffix shows; 
`retryWaitSec` is in seconds.
 
-The `model_resource`, `tool`, `skill`, `mcp_server`, and `subagent` scopes are 
independent key-value scopes directly under an Action. The existing `model` 
scope remains dedicated to model usage metrics.
+### Accessing metrics
 
-| Scope | Metrics | Description | Type |
-|-------|---------|-------------|------|
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.numOfLlmCallsSucceeded 
| The number of framework-observed model invocations that returned 
successfully. | Count |
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.numOfLlmCallsFailed | 
The number of framework-observed model invocations that failed. | Count |
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.llmCallLatencyMs | 
Latency of each framework-observed model invocation, excluding 
structured-output parsing and retry wait time. | Histogram |
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.retryCount | The number 
of additional model invocations initiated when retries are enabled 
(`max-retries > 0`). Only recorded when at least one retry occurs. See 
[retry-wait-interval]({{< ref "docs/operations/configuration#core-options" 
>}}). | Count |
-| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.retryWaitSec | The 
total backoff time, in seconds, accumulated when retries are enabled 
(`max-retries > 0`). Only recorded when at least one retry occurs. | Count |
-| **Tool** | action.\<action_name\>.tool.\<tool_name\>.numOfToolCallsSucceeded 
| The number of successful calls to the Tool. | Count |
-| **Tool** | action.\<action_name\>.tool.\<tool_name\>.numOfToolCallsFailed | 
The number of failed calls to the Tool. | Count |
-| **Tool** | action.\<action_name\>.tool.\<tool_name\>.toolCallLatencyMs | 
Time spent invoking the individual Tool, excluding time waiting for other calls 
in the same parallel batch. | Histogram |
-| **Skill** | action.\<action_name\>.skill.\<skill_name\>.numOfSkillLoads | 
The number of terminal explicit `load_skill` calls attributed to the Skill, 
regardless of outcome. | Count |
-| **Skill** | action.\<action_name\>.skill.\<skill_name\>.skillLoadLatencyMs | 
Time spent invoking an explicit `load_skill` call. | Histogram |
-| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.numOfMcpToolCallsSucceeded | 
The number of successful Tool calls served by the MCP Server. | Count |
-| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.numOfMcpToolCallsFailed | The 
number of failed Tool calls served by the MCP Server. | Count |
-| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.mcpToolCallLatencyMs | 
Individual Tool invocation latency aggregated across the MCP Server. | 
Histogram |
-| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.numOfSubagentCallsSucceeded | 
The number of sub-agent delegations that returned a successful result. | Count |
-| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.numOfSubagentCallsFailed | The 
number of sub-agent delegations that failed, either while submitting or 
awaiting the delegation or because the sub-agent returned a failed result. | 
Count |
-| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.subagentCallLatencyMs | Time the 
Action observed for one sub-agent delegation, from handing the prompt to the 
sub-agent until its result is observed. | Histogram |
+Metrics are available wherever the job's other metrics are: through a 
configured [metric 
reporter](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/deployment/metric_reporters/),
 such as Prometheus, or in the Flink Web UI (see [Dashboard 
integration](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/ops/metrics/#dashboard-integration)).
 
-An LLM metric represents one framework invocation of `ChatModel`. A framework 
retry that calls the model again produces another LLM outcome and latency 
sample; retries hidden inside a provider or connection are not observed. Every 
named Tool execution emits Tool metrics. Skill metrics are emitted only for 
explicit `load_skill` calls; subsequent Tool calls are not inferred to belong 
to a Skill. MCP metrics aggregate only Tool executions carrying an explicit MCP 
Server resource name. A `load_skill` or MCP Tool execution therefore 
contributes to both its Tool scope and the corresponding Skill or MCP Server 
scope. A sub-agent delegation the model requests through the reserved 
`_subagent_` callable name emits Subagent metrics keyed by the registered 
sub-agent name, and is reported only under the Subagent scope rather than also 
under a Tool scope. `subagentCallLatencyMs` spans the Action-observed 
delegation from hand-off to result observation; for delegations dispatched 
concurrently in on
 e batch, this window includes their overlap.
+With Flink's default operator scope, metric identifiers start with 
`<host>.taskmanager.<tm_id>.<job_name>.<operator_name>.<subtask_index>`; 
reporters may format them differently. For an agent operator, `<operator_name>` 
is the agent name, or `action-execute-operator` when the agent has no name. In 
the Flink Web UI, the agent operator's metrics appear under the prefix 
`<subtask_index>.<operator_name>`.
 
-During fine-grained recovery, a cached durable child result contributes 
another call outcome to these metrics; cache reuse is not separately 
identified. A cached LLM result is reported as successful and may produce a 
near-zero latency sample. A cached Tool result retains its normalized outcome: 
`ToolResponse.error(...)` counts as failed, while a successful `ToolResponse` 
or an ordinary return value that can be converted to Tool content counts as 
successful. A cached Tool result produces no latency sample because its 
callable was not invoked.
+{{< img src="/fig/operations/metricwebui.png" alt="Metric Web UI" >}}
 
-Tool names that are not registered runtime resources are aggregated under the 
fixed `tool=unknown` scope. Requested Skill names that do not resolve in the 
runtime registry are similarly aggregated under `skill=unknown`. A `_subagent_` 
callable name that does not resolve to a registered sub-agent is reported as a 
Tool and therefore aggregated under `tool=unknown`; the Subagent scope is keyed 
only by registered sub-agent names, so its cardinality is bounded by the Agent 
plan. When selected for Trace Log output, the call observations retain the 
original requested names; metric scope cardinality remains bounded.
+### Scopes
 
-Tool outcomes follow the same two-layer contract in Java and Python. Resource 
preparation and invocation exceptions are failures. A returned 
`ToolResponse.error(...)` is also a failed Tool execution even though the 
durable invocation returned normally. Tools can return an explicit 
`ToolResponse` or an ordinary value that is converted to text: strings are used 
directly, and other values use JSON when possible, falling back to their string 
representation. A return value that still cannot be converted to text is 
reported as a failed Tool execution. A durable-persistence exception is 
reflected as a Tool failure only when the existing durable execution path 
exposes it to `ToolCallAction`.
+The **Scope** column in the tables below identifies the metric group. The 
example names omit the operator prefix shown above; reporters may represent the 
groups as labels instead.
 
-`numOfSkillLoads` counts terminal calls rather than successful loads. In both 
runtimes, `load_skill` reports an unavailable manager, missing Skill, or 
missing resource through `ToolResponse.error(...)`; the Tool outcome is 
therefore failed while the Skill load counter still increments. MCP 
protocol-level errors follow the same failed Tool outcome contract.
+| Scope | Metric group | Example name |
+|-------|--------------|--------------|
+| **Agent** | the agent operator itself | `numOfEventProcessed` |
+| **Action** | `action.<action_name>` | 
`action.chat_model_action.numOfActionsExecuted` |
+| **Model** | `action.<action_name>.model.<model_name>` | 
`action.chat_model_action.model.qwen3:1.7b.promptTokens` |
+| **Model Resource**, **Tool**, **Skill**, **MCP Server**, **Subagent** | 
`action.<action_name>.<kind>.<name>`, where `<kind>` is `model_resource`, 
`tool`, `skill`, `mcp_server` or `subagent` | 
`action.tool_call_action.tool.get_weather.numOfToolCallsSucceeded` |
 
-Execution latency is measured from matching start and outcome observations of 
the same call in the same task attempt. Assigning a call identity before 
preparation or submission does not start the timer. Invoked Tools retain their 
own start and completion timestamps, even when the reports arrive after a 
durable call or parallel batch returns. Queueing and time spent waiting for 
other calls in the batch are excluded. Outcome counters still update when no 
matching start is available.
+Each group below the Agent scope is a key-value group: `<action_name>` is the 
Action's name, `<model_name>` the model a chat model reports usage for, and 
`<name>` the resource, Tool, Skill, MCP Server or sub-agent name. Reporters 
that support key-value groups, such as Prometheus, expose them as labels.
 
-A timeout does not necessarily stop a running Tool. If the callable has not 
finished when the Action observes its durable result or failure, that 
observation time fixes the call's terminal timestamp. A later completion cannot 
extend the latency or add another outcome. This boundary excludes subsequent 
response processing and reporting delays, but may include a delay before the 
durable result reaches the Action. A callable that starts after this boundary 
has no reported start or latency sample, even if it later completes in the 
background. Cached results and failures before invocation also have no Tool 
latency sample.
+### Built-in Metrics
 
-Available per-call outcomes are reported independently of response 
aggregation, so a later response-processing failure does not repeat or discard 
them. If a batch raises an ordinary Exception without returning per-call 
outcomes, known starts may still be observed, but no outcomes are inferred from 
timestamps or the batch exception. If a Java batch propagates an `Error`, 
directly or through a `CompletionException`, unresolved prepared calls are 
reported as failed and the original `Error` is rethrown. Those failures 
indicate a fatal batch abort; they do not imply that every Tool body ran. These 
rules describe the framework's observations and do not change timeout handling, 
durable persistence, or the business `ToolResponseEvent` error contract.
+Flink Agents provides built-in metrics for input runs, Events, Actions, 
execution health, and token usage. Numbers in brackets refer to the notes below 
each table.
 
-In previous releases, `retryCount` and `retryWaitSec` used the 
`model.<connection_name>` scope. They now use `model_resource.<resource_name>` 
so retries are attributed to the configured ChatModel resource. Existing 
queries and dashboards for these two metrics must use the new scope.
+#### Event and Action Metrics
 
-#### Token Usage Metrics
+| Scope | Metrics | Description | Type |
+|-------|---------|-------------|------|
+| **Agent** | numOfEventProcessed | Events processed by this operator. | 
Counter |
+| **Agent** | numOfEventProcessedPerSec | Events processed per second. | Meter 
|
+| **Agent** | numOfActionsExecuted | Actions executed by this operator. | 
Counter |
+| **Agent** | numOfActionsExecutedPerSec | Actions executed per second. | 
Meter |
+| **Agent** | numOfInputRunsSucceeded | Input runs that completed. [1] | 
Counter |
+| **Agent** | numOfInputRunsFailed | Input runs terminated by an unhandled 
exception. [1] | Counter |
+| **Agent** | inputRunLatencyMs | Time from an input entering the operator 
until its run completes or fails, including time queued behind an earlier input 
with the same key. [1] | Histogram |
+| **Agent** | inputRunQueueLatencyMs | Time from an input entering the 
operator until its run starts. [1] | Histogram |
+| **Agent** | inputRunProcessingLatencyMs | Time from the start of an input 
run until it completes or fails. [1] | Histogram |
+| **Agent** | numOfPendingInputEvents | Inputs currently waiting behind an 
active run with the same key. [2] | Gauge |
+| **Agent** | numOfActiveInputRuns | Input runs currently processing or 
waiting for asynchronous work. [2] | Gauge |
+| **Action** | action.\<action_name\>.numOfActionsExecuted | Executions of 
this Action. | Counter |
+| **Action** | action.\<action_name\>.numOfActionsExecutedPerSec | Executions 
of this Action per second. | Meter |
+| **Action** | action.\<action_name\>.actionSchedulingLatencyMs | Time from 
scheduling an Action execution until it starts running. [3] | Histogram |
+| **Action** | action.\<action_name\>.actionExecutionLatencyMs | Time for one 
Action execution, including asynchronous waits. [1] | Histogram |
+| **Action** | action.\<action_name\>.numOfPendingActionTasks | Action tasks 
waiting to run, including continuations of asynchronous Actions. [3] | Gauge |
+| **Action** | action.\<action_name\>.numOfActiveActionExecutions | Action 
executions that have started and not yet finished. [3] | Gauge |
+| **Action** | action.\<action_name\>.routingDecisionLatencyMs | Time to 
resolve a routing decision when a `ChatRequestEvent` names a `MODEL_ROUTER`, 
including replayed decisions; see [Model Routing]({{< ref 
"docs/development/calling-llms/model_routing#observability" >}}). | Histogram |
+| **Agent** | traceLogTruncatedRecords | Trace Log records whose attributes 
were truncated at `STANDARD` level, counted once per record. | Counter |
+| **Agent** | traceLogWriteFailures | Failed Trace Log writes. Writes are best 
effort and do not fail the job. | Counter |
+
+1. Input-run outcomes and latency samples are local to a task attempt. Input 
runs already active when a task is restored produce neither outcome counts nor 
latency samples, and Action executions already active when the task is restored 
produce no latency sample. An input restored from the pending queue records its 
outcome and processing latency once it starts in the new attempt, but not its 
queue or end-to-end latency.
+2. `numOfPendingInputEvents` counts buffered inputs and `numOfActiveInputRuns` 
counts runs; a run waiting for an asynchronous continuation stays active. Both 
gauges are rebuilt from Flink state after a restore.
+3. An Action execution stays active while one of its continuation tasks is 
pending, so the two Action gauges move independently. Scheduling latency is 
recorded only for the first task of an execution.
 
-Token usage metrics are automatically recorded when chat models are invoked 
through `ChatModelConnection`. These metrics help track LLM API usage and costs.
+#### Execution Metrics
 
-| Scope     | Metrics                                                      | 
Description                                                                    
| Type  |
-|-----------|--------------------------------------------------------------|--------------------------------------------------------------------------------|-------|
-| **Model** | action.\<action_name\>.model.\<model_name\>.promptTokens     | 
The total number of prompt tokens consumed by the model within an action.      
| Count |
-| **Model** | action.\<action_name\>.model.\<model_name\>.completionTokens | 
The total number of completion tokens generated by the model within an action. 
| Count |
+These metrics describe LLM, Tool, Skill, MCP Server and sub-agent calls that 
the framework observes inside an Action. They are collected whatever 
`trace-log.targets` selects.
 
-### How to add custom metrics
+| Scope | Metrics | Description | Type |
+|-------|---------|-------------|------|
+| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.numOfLlmCallsSucceeded 
| Model invocations that returned successfully. [1] [6] | Counter |
+| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.numOfLlmCallsFailed | 
Model invocations that failed. [1] [6] | Counter |
+| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.llmCallLatencyMs | Time 
for one model invocation, excluding structured-output parsing and retry waits. 
[1] | Histogram |
+| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.retryCount | Additional 
invocations made by framework retries (`max-retries > 0`); recorded only when a 
retry occurs. See [retry-wait-interval]({{< ref 
"docs/operations/configuration#core-options" >}}). | Counter |
+| **Model Resource** | 
action.\<action_name\>.model_resource.\<resource_name\>.retryWaitSec | Backoff 
time accumulated by framework retries; recorded only when a retry occurs. | 
Counter |
+| **Tool** | action.\<action_name\>.tool.\<tool_name\>.numOfToolCallsSucceeded 
| Successful calls to the Tool. [2] [6] | Counter |
+| **Tool** | action.\<action_name\>.tool.\<tool_name\>.numOfToolCallsFailed | 
Failed calls to the Tool. [2] [6] | Counter |
+| **Tool** | action.\<action_name\>.tool.\<tool_name\>.toolCallLatencyMs | 
Time spent in one Tool call, excluding waits for other calls in the same 
parallel batch. [5] | Histogram |
+| **Skill** | action.\<action_name\>.skill.\<skill_name\>.numOfSkillLoads | 
Explicit `load_skill` calls for the Skill, whatever their outcome. [3] | 
Counter |
+| **Skill** | action.\<action_name\>.skill.\<skill_name\>.skillLoadLatencyMs | 
Time for one explicit `load_skill` call. | Histogram |
+| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.numOfMcpToolCallsSucceeded | 
Successful Tool calls served by the MCP Server. [3] | Counter |
+| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.numOfMcpToolCallsFailed | 
Failed Tool calls served by the MCP Server. [3] | Counter |
+| **MCP Server** | 
action.\<action_name\>.mcp_server.\<server_name\>.mcpToolCallLatencyMs | Time 
for one Tool call served by the MCP Server. | Histogram |
+| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.numOfSubagentCallsSucceeded | 
Sub-agent delegations that returned a successful result. [4] | Counter |
+| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.numOfSubagentCallsFailed | 
Sub-agent delegations that failed while submitting or awaiting, or returned a 
failed result. [4] | Counter |
+| **Subagent** | 
action.\<action_name\>.subagent.\<agent_name\>.subagentCallLatencyMs | Time 
from handing the prompt to the sub-agent until its result is observed. [4] | 
Histogram |
+
+1. One sample is one framework invocation of `ChatModel`. A framework retry 
produces another outcome and latency sample; retries inside a provider client 
are not observed. A response that is later rejected for its finish reason or 
structured-output parsing still counts as a successful call.
+2. A Tool call fails when preparing or invoking it raises an exception, when 
it returns `ToolResponse.error(...)`, or when its return value cannot be 
converted to text. Tool names that are not registered resources are counted 
under `tool=unknown`.
+3. A `load_skill` call or an MCP Tool call is also counted in its Tool scope. 
`numOfSkillLoads` increments even when the load fails, for example for a 
missing Skill. Skill names that do not resolve are counted under 
`skill=unknown`.
+4. Only delegations the model requests through the reserved `_subagent_` name 
are reported, under the Subagent scope keyed by the registered sub-agent name 
and not also under a Tool scope. A `_subagent_` name that does not resolve is 
counted under `tool=unknown`. For delegations dispatched together in one batch, 
the latency includes their overlap.
+5. A Tool call has a latency sample only when its callable started in the 
current task attempt.
+6. During recovery, a result replayed from durable state counts as another 
outcome, without implying another provider request or Tool invocation. A 
replayed LLM response keeps its outcome and may produce a near-zero latency 
sample; a replayed Tool result keeps its outcome and has no latency sample.
 
-In Flink Agents, users implement their logic by defining custom Actions that 
respond to various Events throughout the Agent lifecycle. To support 
user-defined metrics, we introduce two new properties: `agent_metric_group` and 
`action_metric_group` in the RunnerContext. These properties allow users to 
create or update global metrics and independent metrics for actions. For an 
introduction to metric types, please refer to the [Metric types 
documentation](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/ops/metrics/#metric-types).
+#### Token Usage Metrics
 
-Metric names listed in the built-in tables above are reserved in their 
corresponding scopes. Custom metrics must use different names within the same 
scope.
+Token usage metrics are recorded when chat models are invoked through 
`ChatModelConnection`, which helps track LLM API usage and cost.

Review Comment:
   Clarified in bb48488e: the built-in chat flow records token usage metrics 
under `action.chat_model_action.model.<model_name>`, not under the Action that 
sends `ChatRequestEvent`.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to