Skip to content

Agent 事件

语言:中文 | English | 日本語 | 四川话

面向需要自定义 UI / 嵌入的开发者。入门请看 快速开始;库结构见 开发指南

结构化事件用于观察 Agent 循环,而不绑定某一种 UI(终端、Web、IDE)。思路类似 Kimi Code 的 Soul / Wire 分离:循环发出事实,消费者订阅并渲染。

现状: Agent.arun_events() 会发出这些事件。Agent.arun() 为兼容层,只透出 TextDelta.text

原生 Event 与 Wire(JSON-RPC)

同一时间线,两种形态 — 不是两套不同的事件系统。

层次得到什么典型场景
原生 EventPython 冻结 dataclass(TextDeltaRunEnd 等)Python 服务、CLI、测试、Notebook — match / isinstance,IDE 类型提示
Wire(JSON-RPC)每行一条 NDJSON:{"jsonrpc":"2.0","method":"TextDelta","params":{...}}浏览器/移动端、其他语言、SSE/WebSocket、落盘 wire.jsonl
API返回
agent.run()仅最终 RunEnd(非流式)
agent.arun()仅答案文本片段 str
agent.arun_events()Event 对象(完整流)
agent.arun_wire()str NDJSON 行(同一流,已序列化)

Wire 是对 arun_events() 的薄序列化层,见 Wire 协议。在 Python 里也可对单个事件 encode_event_line(event) 再发到 socket。

用原生:消费端是 Python,需要类型与模式匹配。

用 Wire:消费端不是 Python,或需要 HTTP/SSE/WebSocket 上的稳定 JSON 格式。

导入

python
from pagent import (
    Event,
    RunBegin,
    TurnBegin,
    TextDelta,
    ReasoningDelta,
    StepEnd,
    ToolCallBegin,
    ToolResult,
    TurnEnd,
    RunEnd,
)

Event 是所有具体事件类的联合类型,便于 match / isinstance

事件说明

生命周期

事件字段时机(设计意图)
RunBeginuser_input: str用户消息已写入 session,开始本轮
TurnBeginturn: intmax_turns 内开始一次模型调用(从 0 计)
TurnEndturn: int, stopped: bool助手消息已写入;stopped=True 表示本次 run 不再调模型
RunEndcontent, tool_calls, reasoning_content, usage整次 run / arun 结束(与 LLM.invoke 返回值同类型)

流式片段

事件字段时机(设计意图)
TextDeltatext: strinvoke_stream 返回的 assistant content 片段
ReasoningDeltatext: strreasoning_content 片段(视 Provider 而定)

单步边界

事件字段时机(设计意图)
StepEndcontent, tool_calls, reasoning_content, usage一次 LLM 调用结束(流式拼完或单次 invoke

字段与 RunEnd 相同。tool_calls 为 OpenAI 形状:[{ "id", "type", "function": { "name", "arguments" } }, ...]

工具

事件字段时机(设计意图)
ToolCallBegintool_call_id, name, arguments即将执行某个工具
ToolResulttool_call_id, name, content工具结果以 role: tool 写入 session

典型顺序

单轮,仅文本

text
RunBegin
  TurnBegin(0)
    TextDelta …
    StepEnd(content=…, tool_calls=[])
  TurnEnd(0, stopped=True)
RunEnd

先调工具,再给出最终回复

text
RunBegin
  TurnBegin(0)
    TextDelta …
    StepEnd(…, tool_calls=[…])
    ToolCallBegin(…)
    ToolResult(…)
  TurnEnd(0, stopped=False)
  TurnBegin(1)
    TextDelta …
    StepEnd(…, tool_calls=[])
  TurnEnd(1, stopped=True)
RunEnd

达到 max_turns 时仍有未处理工具

最后一次 TurnEnd 可能 stopped=False;最后的 RunEnd 事件即最后一条助手消息(可能仍带 tool_calls)。

消费者示例

python
async for event in agent.arun_events("你好"):
    match event:
        case TextDelta(text=t):
            print(t, end="", flush=True)
        case ToolCallBegin(name=n):
            print(f"\n[工具 {n}]")
        case RunEnd(content=c):
            print(f"\n[结束: {c!r}]")

仅需文本的调用方可继续用 arun()

python
async for chunk in agent.arun("你好"):
    print(chunk, end="")  # str,不是 Event

设计说明

  • 不可变 dataclass — 事件是快照,可入队或写日志。
  • Wire 协议 — JSON-RPC 2.0 通知 + NDJSON:wire.md。可用 Agent.arun_wire()encode_event_line()
  • 进站控制(审批、外部工具、中途 steer)— 未建模;将来应是单独的 request 类型,不属于 Event
  • Session 与事件session.messages 仍是给模型的 API 历史;事件是平行的 UI 时间线(类比 Kimi 的 context.jsonlwire.jsonl)。

源码

定义见 src/pagent/events.py

reasoning_content 用法示例(非流式 / 流式、--zh 鸡兔同笼):思考过程

Released under the MIT License.