Home Projects Blog Resume Contact 中文
Back to list
2026年7月5日 1,824 words 5 min read

07:执行过程可解释:Events、Trace、Metrics 和 Manifest

拆解 Events、Trace、Metrics 与 Manifest 如何共享统一事件时间线,让一次 Agent Run 的执行过程可以排查、聚合、回放并安全展示。

#可观测性#RunLens#Trace#Metrics#Agent Runtime

07:执行过程可解释:Events、Trace、Metrics 和 Manifest

上一篇介绍了 RunLens 的结果验证。模型结束后,Stop Hook 会继续检查产物结构和 Evidence 来源,最终决定 Run 进入 completed 还是 failed。

有了明确结果,还需要回答另一个问题:这次 Run 为什么走到了这个结果?

Agent 运行包含模型、工具、权限、Hook、恢复和产物。只保留最终状态,很难定位问题发生在哪一层。

RunLens 使用统一事件时间线记录运行事实,再从事件生成 Metrics、Trace 和兼容投影。

业务产物回答“任务交付了什么”,运行事件回答“任务怎样走到这里”。

Agent 失败时只返回错误还不够

假设一个 Run 最终返回:

{
  "status": "failed",
  "error_message": "missing required files: artifacts/insight_report.md"
}

这条信息说明报告缺失,还缺少很多上下文:

  • 模型是否成功读取 CSV。
  • 两个 JSON 是否已经创建。
  • Provider 调用了几次。
  • 是否发生过参数纠正。
  • Permission 对工具给出了什么裁决。
  • Plan 停在第几步。
  • 失败发生在模型循环还是 Stop Validation。

这些问题需要一条按时间排序的运行记录。

RunLens 将 runtime/events.jsonl 作为当前可观察性的权威来源。

一条统一事件记录了什么

每个 ObservabilityEvent 包含:

字段作用
event_id唯一事件 ID
run_id所属 Run
sequenceRun 内连续序号
timestampUTC 时间
event_type事件类型
source产生事件的组件
data事件业务数据
duration_ms可选耗时
parent_event_id可选父事件

事件 Schema 会校验 ID、sequence、timestamp、duration 和 data 必需字段。

例如 tool.failed 要求 data 中至少存在:

tool_name
call_id
status
error_code
retryable

这样前端和 Metrics 可以直接依赖稳定字段,错误展示也有统一结构。

sequence 表达确定顺序

每个 Run 拥有独立 LocalEventStore

事件从 sequence 1 开始连续递增:

1  run.created
2  run.state_changed
3  run.started
4  skill.match_evaluated
5  skill.selected
...

EventStore 构造时会读取已有文件并恢复最后序号。读取过程中还会检查:

  • run_id 是否一致。
  • sequence 是否连续。
  • event_id 是否重复。
  • JSON 和 Event Contract 是否有效。

事件文件损坏时,Events API 会返回明确错误,避免把不完整时间线当成正常记录。

parent_event_id 表达因果关系

sequence 告诉我们先后顺序,parent event 表达一次动作内部的关联。

例如 ToolExecutor 收到工具请求后,会先发布:

tool.requested

后续事件可以指向这个 parent:

permission.evaluated
permission.allowed
tool.started
tool.completed

这让前端可以把多个事件折叠成一次工具调用,也让排查者知道某次权限拒绝属于哪个 ToolCall。

LLM 请求和响应同样会建立父子关系,并记录 Provider、模型、response type、usage 和 duration。

RunLens 记录哪些运行事件

事件可以按领域归类。

Run 生命周期

run.created
run.started
run.state_changed
run.completed
run.failed

Skill 和 Prompt

skill.match_evaluated
skill.selected
skill.loaded
skill.activated
skill.validation_failed
prompt.assembled

LLM

llm.requested
llm.responded
llm.failed

Tool 和 Permission

tool.requested
tool.started
tool.completed
tool.failed
permission.evaluated
permission.allowed
permission.denied

Hook、Recovery 和 Artifact

hook.started
hook.completed
hook.blocked
hook.failed
recovery.started
recovery.completed
recovery.failed
artifact.created
artifact.validated
artifact.rejected

这些事件覆盖了一次 Run 的主要交接点。

EventBus 先持久化,再通知观察者

EventBus 的 publish 流程是:

构造并校验 Event
→ 写入 LocalEventStore
→ 通知内存 Observer

先持久化可以确保 Observer 收到的事件已经落盘。

某个 Observer 抛出异常时,EventBus 会把错误记录到 observer_errors,其余 Observer 和主 Run 继续执行。

当前 Runtime 主要直接使用 EventStore,Observer 机制为后续实时推送、告警或外部观测后端留下入口。

Metrics 怎样从事件聚合

Run 最终化时,aggregate_metrics() 读取完整事件列表并计算:

  • 总事件数。
  • LLM 调用次数。
  • Token Usage。
  • 工具请求、成功和失败数量。
  • Permission Denials。
  • Hook Blocks。
  • Recovery Attempts。
  • Artifact 创建、验证和拒绝数量。
  • 各事件类型累计 duration。

结果写入:

runtime/metrics.json

Metrics 直接由事件生成,避免 Event 和统计维护两套事实。

例如工具调用成功时,系统记录 tool.completed。Metrics 统计这一类事件数量即可得到 succeeded tool calls。

Trace API 怎样兼容新旧 Run

RunLens 提供:

GET /api/runs/{run_id}/trace

新 Run 存在 runtime/events.jsonl 时,Trace API 直接返回统一事件,并标记:

source = events_projection

老 Run 缺少统一事件文件时,API 回退读取旧 agent_trace.jsonl

这种兼容让前端可以继续使用 Trace 入口,内部权威来源已经切换到 Events。

Events API 支持增量查看

Events API 提供三个常用参数:

  • after_sequence:只读取某个序号之后的事件。
  • event_type:按类型过滤。
  • limit:限制单次返回数量。

返回中的 next_sequence 可以用于下一次轮询。

前端可以先加载已有事件,再周期性请求新增部分,完整时间线只需读取一次。

当前服务采用同步 Run,增量接口仍然适合历史查看和后续异步改造。

一个例子:从事件定位缺失报告

模型成功写入两个 JSON,随后返回 final answer,报告文件缺失。

关键事件可能是:

llm.responded: response_type=content
run.state_changed: model_thinking → validating
hook.started: RequiredOutputValidationHook
hook.blocked: missing insight_report.md
artifact.validated: feedback_analysis.json
artifact.validated: product_backlog.json
artifact.rejected: insight_report.md
run.state_changed: validating → failed
run.failed

从这条时间线可以得到明确结论:

  • Provider 正常返回。
  • AgentLoop 已经收束。
  • 问题发生在最终交付检查。
  • 两个 JSON 已经存在。
  • Markdown 报告缺失。

相比“模型失败了”,事件能把问题定位到具体阶段和产物。

日志脱敏和路径可见性

事件写入前会经过 sanitize_event_data()

它会处理:

  • api_key、authorization、password、secret、token 等敏感字段。
  • 超长字符串。
  • 当前 Run 内绝对路径。
  • Run 外部绝对路径。
  • .env 路径。

当前 Run 内路径会转成相对路径,外部绝对路径会显示为:

[EXTERNAL_PATH]

敏感字段会显示为:

[REDACTED]

这让事件适合前端展示和本地排查,同时减少密钥和主机路径泄露。

Manifest 怎样控制公开文档

Run 完成后,Plugin Runner 会扫描工作区并生成 manifest.json

Manifest 包含:

  • Run ID 和状态。
  • Plugin 名称和版本。
  • Schema Version。
  • 公开文件路径。
  • Audience。
  • Description。
  • Sensitivity。
  • Kind。

Documents API 读取 Manifest,把其中声明的文件作为 allowlist。

六类私有 Runtime 文件会被排除:

message_history.jsonl
llm_events.jsonl
tool_calls.jsonl
hook_events.jsonl
permission_events.jsonl
recovery_events.jsonl

这些文件可能包含更细的模型和工具信息,适合留在本地调试。

Legacy Projection 的作用

统一事件引入前,Runtime 已经拥有多个独立 JSONL:LLM、Tool、Permission、Hook、Recovery 和 Trace。

当前版本在最终化阶段根据 Events 重新生成这些兼容文件。

它们的定位是:

  • 兼容旧接口和调试习惯。
  • 提供按领域拆分的本地视图。
  • 保持确定性输出。

排查最终 Run 时,events.jsonl 仍然是首选来源。

当前可观察性边界

RunLens 当前使用单 Run、单 JSONL 的本地事件存储。

它适合:

  • 本地开发。
  • 单进程同步执行。
  • Demo 和架构验证。
  • 运行复盘。

后续面向并发和生产部署时,可以扩展:

  • 数据库 Event Store。
  • WebSocket 或 SSE 实时事件流。
  • OpenTelemetry Exporter。
  • Run 级日志查询。
  • Event Retention 和归档。
  • 告警规则。

Manifest 当前会公开 request、Prompt、resolved config 和 Evidence Index 等非私有文件。外部部署前适合将公开规则进一步收窄为显式业务文档集合。

小结

RunLens 使用统一 Event Schema 记录一次 Run 的顺序和因果关系,再从事件生成 Metrics、Trace 和兼容投影。

Events 解释任务怎样执行,Metrics 提供聚合视图,Manifest 控制运行文件如何对外展示。模型、工具、权限、恢复和产物验证都进入同一条时间线后,Run 的成功和失败就有了可追踪证据。

下一篇从另一个方向看 Runtime 的价值:Feedback Analysis 和 File Summary 业务差异很大,它们怎样通过 Plugin Contract 共享同一套执行底座。