Back to Picoclaw

Runtime Events 与事件日志

docs/architecture/runtime-events.zh.md

0.2.911.8 KB
Original Source

Runtime Events 与事件日志

PicoClaw 的 runtime event 是运行时观察面,用来描述 agent、channel、gateway、message bus、MCP 等组件发生了什么。事件发布和日志打印是两件事:

  • 事件发布:组件把 pkg/events.Event 发布到 runtime event bus,供 hook、测试、调试工具或后续 UI 消费。
  • 事件日志:内置 runtime event logger 订阅同一个 bus,并按配置把匹配的事件打印到日志。

这样可以让业务流程继续只负责发布事件,日志策略统一收口到一个地方。

默认行为

默认配置只打印 agent.* 事件:

json
{
  "events": {
    "logging": {
      "enabled": true,
      "include": ["agent.*"],
      "min_severity": "info",
      "include_payload": false
    }
  }
}

这个默认值保持了旧行为:agent turn、LLM、tool、steering、subturn、error 等事件会出现在日志中;channel、gateway、bus、MCP 事件仍会发布到 runtime event bus,但默认不打印,避免网关启动和消息投递日志过于嘈杂。

配置项

配置位于 config.jsonevents.logging

字段类型默认值说明
enabledbooltrue是否启用内置事件日志订阅器
includestring[]["agent.*"]允许打印的事件 kind,支持精确匹配、*agent.* 这类 glob/prefix
excludestring[][]在 include 命中后排除的事件 kind,匹配规则同 include
min_severitystringinfo最低打印级别:debuginfowarnerror
include_payloadboolfalse是否把原始 payload 放进日志字段

include_payload 默认关闭。agent 事件日志会输出安全摘要字段,例如 user_lenargs_countcontent_len,不会默认输出完整用户消息或工具参数。只有在排查问题、并且确认日志存储环境可信时,才建议临时打开 include_payload

匹配规则

includeexclude 都匹配 Event.Kind 字符串:

json
{
  "events": {
    "logging": {
      "include": ["gateway.*", "channel.lifecycle.*", "agent.error"],
      "exclude": ["gateway.ready"],
      "min_severity": "info"
    }
  }
}

常用写法:

  • ["agent.*"]:只打印 agent 事件。
  • ["*"]:打印所有 runtime events。
  • ["gateway.*", "channel.*"]:只打印 gateway 和 channel 事件。
  • exclude: ["agent.llm.delta"]:排除高频流式 delta 事件。
  • min_severity: "warn":只打印 warn/error 事件。

环境变量

同一组配置也可以通过环境变量覆盖,适合临时调试:

bash
PICOCLAW_EVENTS_LOGGING_ENABLED=true
PICOCLAW_EVENTS_LOGGING_INCLUDE="gateway.*,channel.lifecycle.*"
PICOCLAW_EVENTS_LOGGING_EXCLUDE="gateway.ready"
PICOCLAW_EVENTS_LOGGING_MIN_SEVERITY=info
PICOCLAW_EVENTS_LOGGING_INCLUDE_PAYLOAD=false

includeexclude 的环境变量使用逗号分隔。

事件名称与触发时机

下面列出当前 runtime event kind、触发时机和主要事件详情。SourceScopeCorrelation 是所有事件都可能携带的 envelope 字段;表里的“主要详情”指 payload 或日志摘要中最有用的字段。

Agent

事件名触发时机主要详情
agent.turn.startagent 开始处理一次用户输入或系统输入,turn scope 已创建时user_len, media_count; scope 通常包含 agent_id, session_key, turn_id, channel, chat_id, message_id
agent.turn.end一次 turn 退出时,无论完成、报错还是 hard abortstatus (completed/error/aborted), iterations_total, duration_ms, final_len
agent.llm.request每次调用 LLM provider 前model, messages, tools, max_tokens
agent.llm.delta预留给流式 LLM delta;当前实现已定义但没有自然发送点content_delta_len, reasoning_delta_len
agent.llm.responseLLM provider 返回完整响应后content_len, tool_calls, has_reasoning
agent.llm.retryLLM 请求因上下文、限流、临时错误等原因准备重试前attempt, max_retries, reason, error, backoff_ms
agent.context.compress上下文历史被压缩时,例如主动预算检查或 LLM retry 处理reason, dropped_messages, remaining_messages
agent.session.summarize会话历史异步摘要完成时summarized_messages, kept_messages, summary_len, omitted_oversized
agent.tool.exec_startagent 准备执行一个工具调用前tool, args_count; 默认不打印完整参数
agent.tool.exec_end工具调用完成后,包括成功、工具错误和 async 结果tool, duration_ms, for_llm_len, for_user_len, is_error, async
agent.tool.exec_skipped工具调用被跳过时,例如工具不可用、参数无效或 turn 控制逻辑要求跳过tool, reason
agent.steering.injectedqueued steering message 被注入下一轮 LLM 上下文时count, total_content_len
agent.follow_up.queuedasync 工具结果被重新排入 inbound/follow-up 流程时source_tool, content_len
agent.interrupt.receivedturn 接受 steering、graceful interrupt 或 hard abort 指令时interrupt_kind, role, content_len, queue_depth, hint_len
agent.subturn.spawn父 turn 创建子 turn/subagent 时child_agent_id, label, parent_turn_id
agent.subturn.end子 turn 结束时child_agent_id, status
agent.subturn.result_delivered子 turn 结果成功投递到目标 channel/chat 时target_channel, target_chat_id, content_len
agent.subturn.orphan子 turn 结果无法投递或无法关联回父 turn 时parent_turn_id, child_turn_id, reason
agent.erroragent 执行流程报告错误时stage, error

Channel

事件名触发时机主要详情
channel.lifecycle.initializedchannel manager 根据配置创建并注册 channel 实例后type; scope 包含 channel
channel.lifecycle.startedchannel Start() 成功,worker 已启动时;热重载新增 channel 也会触发type
channel.lifecycle.start_failedchannel Start() 失败时type, error; severity 为 error
channel.lifecycle.stoppedchannel Stop() 成功后type
channel.webhook.registeredchannel 的 webhook handler 被注册到共享 HTTP mux 时type; scope 包含 channel
channel.webhook.unregisteredchannel 的 webhook handler 从共享 HTTP mux 移除时type; scope 包含 channel
channel.message.outbound_queuedoutbound 文本或媒体消息被放入对应 channel worker 队列时media, content_len, reply_to_message_id; scope 来自原 inbound context
channel.message.outbound_sentoutbound 文本或媒体消息成功发送,或 placeholder edit 已处理响应时media, content_len, message_ids, reply_to_message_id
channel.message.outbound_failedoutbound 文本或媒体消息重试耗尽或遇到永久失败时media, content_len, retries, error, reply_to_message_id; severity 为 error
channel.rate_limitedchannel worker 等待 rate limiter token 时被 context 取消,导致本次发送被限流/中断media, content_len, error, reply_to_message_id; severity 为 warn

Message Bus

事件名触发时机主要详情
bus.publish.failedinbound、outbound、media、audio 或 voice control 发布失败,或缺少必要 context 时stream, error; scope 尽量来自消息 context
bus.close.startedmessage bus 开始关闭时drained 通常为 0
bus.close.drainedclose 期间等待队列 drain,并且 drain 到至少一条 buffered message 时drained
bus.close.completedmessage bus 完成关闭时drained

Gateway

事件名触发时机主要详情
gateway.startgateway 完成 agent/runtime event bus/bootstrap 绑定后duration_ms
gateway.readygateway 服务、channel manager、HTTP 等关键服务启动完成后duration_ms
gateway.shutdowngateway 开始关闭流程时无固定 payload,可能只有 envelope 字段
gateway.reload.started热重载开始执行时duration_ms
gateway.reload.completed热重载成功完成时duration_ms
gateway.reload.failed热重载失败时duration_ms, error; severity 为 error

MCP

事件名触发时机主要详情
mcp.server.connectingMCP manager 准备连接某个 server 前server, type, url, command
mcp.server.connectedMCP server 连接成功并完成工具列表初始化后server, type, url, command, tool_count
mcp.server.failedMCP server 连接失败,或 manager 已关闭导致无法连接时server, type, url, command, error; severity 为 error
mcp.tool.discoveredMCP server 的某个工具被发现并注册时server, type, url, command, tool
mcp.tool.call.startMCP tool wrapper 开始执行一次远端工具调用前server, tool; 如果在 agent turn 内触发,scope 会带上对应 turn/chat 信息
mcp.tool.call.endMCP tool wrapper 完成一次远端工具调用后,包括失败结果server, tool, duration_ms, is_error, error

日志字段

所有事件日志都会尽量包含稳定 envelope 字段:

  • event_id
  • event_kind
  • severity
  • event_time
  • source_component
  • source_name
  • agent_id
  • session_key
  • turn_id
  • channel
  • account
  • chat_id
  • topic_id
  • space_id
  • space_type
  • chat_type
  • sender_id
  • message_id
  • trace_id
  • parent_turn_id
  • request_id
  • reply_to_id

agent 事件还会追加 payload 摘要字段:

事件摘要字段
agent.turn.startuser_len, media_count
agent.turn.endstatus, iterations_total, duration_ms, final_len
agent.llm.requestmodel, messages, tools, max_tokens
agent.llm.deltacontent_delta_len, reasoning_delta_len
agent.llm.responsecontent_len, tool_calls, has_reasoning
agent.llm.retryattempt, max_retries, reason, error, backoff_ms
agent.context.compressreason, dropped_messages, remaining_messages
agent.session.summarizesummarized_messages, kept_messages, summary_len, omitted_oversized
agent.tool.exec_starttool, args_count
agent.tool.exec_endtool, duration_ms, for_llm_len, for_user_len, is_error, async
agent.tool.exec_skippedtool, reason
agent.steering.injectedcount, total_content_len
agent.follow_up.queuedsource_tool, content_len
agent.interrupt.receivedinterrupt_kind, role, content_len, queue_depth, hint_len
agent.subturn.spawnchild_agent_id, label
agent.subturn.endchild_agent_id, status
agent.subturn.result_deliveredtarget_channel, target_chat_id, content_len
agent.subturn.orphanparent_turn_id, child_turn_id, reason
agent.errorstage, error

可打印的事件域

当前 runtime event kind 定义在 pkg/events/kind.go。事件日志配置可以选择这些域:

  • agent.*:agent turn、LLM、tool、context、steering、interrupt、subturn、error。
  • channel.*:channel lifecycle、webhook 注册、outbound queued/sent/failed、rate limited。
  • bus.*:publish failed、close started/drained/completed。
  • gateway.*:start、ready、shutdown、reload started/completed/failed。
  • mcp.*:server connecting/connected/failed、tool discovered、tool call start/end。

默认事件日志示例见 ../../config/config.example.json