Run Event

Run Event 是 Agent Run 执行过程中产生的各类事件,包括文本增量、工具调用、进度更新、错误和完成事件。它是前端渲染和后端记录的数据来源。

#type / concept #status / evergreen #tech / ai #tech / architecture

[!info] related notes

Run Event

一句话定义

Run Event 是 Agent Run 执行过程中产生的各类事件。它是 Agent Runtime 的输出格式,前端用它来渲染 UI,后端用它来记录 Trace。

核心原理

事件类型

事件触发时机数据
text_deltaLLM 产生 token{delta: "..."}
tool_call_start开始调用工具{tool_name, arguments}
tool_call_result工具执行完成{result, duration_ms}
progress进度更新{message, step, total}
interrupt需要人类审批{tool_call, message}
error发生错误{code, message, recoverable}
doneRun 完成{total_tokens, duration_ms}
heartbeat心跳保活{}

Python 实现

from dataclasses import dataclass
from typing import Any

@dataclass
class RunEvent:
    event: str
    data: Any
    meta: dict = None

    def to_sse(self) -> str:
        return f"event: {self.event}\ndata: {json.dumps(self.data)}\n\n"

class EventEmitter:
    def __init__(self, run_id: str):
        self.run_id = run_id
        self.sequence = 0

    def emit(self, event_type: str, data: Any) -> RunEvent:
        self.sequence += 1
        return RunEvent(
            event=event_type,
            data=data,
            meta={
                "run_id": self.run_id,
                "sequence": self.sequence,
                "timestamp": time.time(),
            },
        )

事件流

async def agent_run(context) -> AsyncIterator[RunEvent]:
    emitter = EventEmitter(context.run_id)

    # 开始
    yield emitter.emit("progress", {"message": "开始执行", "step": 0})

    for step in range(MAX_STEPS):
        response = await llm.chat(messages)

        if response.tool_calls:
            for tc in response.tool_calls:
                yield emitter.emit("tool_call_start", {
                    "tool_call_id": tc.id,
                    "tool_name": tc.name,
                    "arguments": tc.arguments,
                })

                result = await execute_tool(tc)

                yield emitter.emit("tool_call_result", {
                    "tool_call_id": tc.id,
                    "result": result,
                })
        else:
            yield emitter.emit("text_delta", {"delta": response.content})
            break

    # 完成
    yield emitter.emit("done", {
        "total_tokens": total_tokens,
        "duration_ms": duration_ms,
    })

常见坑

  1. 事件类型不够用: 新增功能时没有对应的事件类型
  2. 不带序列号: 前端无法检测丢包和乱序
  3. 数据太大: 工具返回了 1MB 的结果,事件太大

参考资料

创建于 2026/6/30 更新于 2026/7/15