Claude Platform Docs
Managed Agents将工作委派给智能体

使用事件增量预览响应

在模型仍在生成响应文本时,将智能体的响应文本渲染为实时预览。

默认情况下,智能体的响应文本以缓冲的 agent.message 事件形式到达会话事件流。每个事件仅在生成它的模型请求完成后才会发出。"Event deltas"(事件增量)让您可以在模型仍在生成文本时,以实时预览的形式增量渲染该文本。

预览是一种尽力而为的显示辅助手段,缓冲的 agent.message 始终是权威记录。忽略预览的客户端仍会收到完整、正确的事件流。

选择启用预览

预览需要按每个流连接选择启用。将 event_deltas[] 查询参数添加到您正在读取的流中,并为每个您希望预览的事件类型重复一次该参数。可接受的值为 agent.message 和 agent.thinking。任何其他值都会返回 400 错误,包含超过 100 个值的请求也是如此。

两个流端点都接受该参数:

  • 会话级流: GET /v1/sessions/{session_id}/events/stream
  • 会话线程流: GET /v1/sessions/{session_id}/threads/{thread_id}/stream

子智能体的预览出现在该子智能体自己的线程流上。

[] 是 shell 通配符模式,因此每当您在 shell 中构建请求时,请为 URL 加上引号。示例将方括号百分号编码为 %5B%5D,这种方式同样有效。

预览事件

当被预览的事件开始时,流会发出一个 event_start,携带即将到来的事件的类型和 id:

{
  "type": "event_start",
  "event": {
    "type": "agent.message",
    "id": "sevt_01abc..."
  }
}

对于 agent.message,开始事件之后是携带增量文本的 event_delta 事件。每个增量在 event_id 中指明它所扩展的事件,并在 delta.index 中指明它所扩展的内容块:

{
  "type": "event_delta",
  "event_id": "sevt_01abc...",
  "delta": {
    "type": "content_delta",
    "index": 0,
    "content": {
      "type": "text",
      "text": "Here is the summary"
    }
  }
}

对于 agent.thinking,仅发出 event_start,作为思考块已开始的信号。之后不会有 event_delta 事件。结束预览的缓冲 agent.thinking 事件是一个进度信号,不携带任何思考内容。

与持久化事件不同,event_start 和 event_delta 没有自己的 id 或 processed_at。它们携带的唯一标识符是其所预览事件的 id。它们的类型字符串也是持久化事件 {domain}.{action} 命名约定的例外。

累加与协调

每个支持事件增量的 SDK 都包含一个累加器辅助工具,可为您处理 index 记账。当您需要自定义记账逻辑时,本节中的手动模式适用于所有语言。请将其应用于生成的事件类型。

在手动模式中,将预览文本保存在以 (event_id, index) 为键的临时映射中,并将缓冲事件视为记录。按每个模型请求协调两者。

一个轮次以单个 session.status_running 事件开始。在正常完成的轮次中,每个模型请求随后按顺序产生以下事件:

  1. span.model_request_start
  2. event_start
  3. event_delta 事件
  4. 缓冲的 agent.message
  5. span.model_request_end(位于"Span 事件"选项卡中)

在传输层面,以下是该序列中被预览的部分,与该连接的其他缓冲事件交错出现:

event_start     {"event": {"type": "agent.message", "id": "sevt_01abc..."}}
event_delta     {"event_id": "sevt_01abc...", "delta": {"type": "content_delta", "index": 0, "content": {"type": "text", "text": "..."}}}
...
agent.message   {"id": "sevt_01abc...", "content": [...]}

event_delta 行对每个文本片段重复一次。在每个事件到达时处理它:

  1. 收到 event_start 时,记下所宣告的 id。这些标识符始终一致:event_start.event.id、每个 event_delta.event_id 以及缓冲 agent.message 的 id 是同一个值。
  2. 收到每个 event_delta 时,将 delta.content.text 追加到 (event_id, delta.index) 处的条目,并渲染当前累积的文本。某个 index 的第一个增量会创建该条目。
  3. 当缓冲的 agent.message 到达时,按 id 进行匹配,丢弃累积的预览,改为渲染该消息的内容。
  4. 收到 span.model_request_end 时,关闭任何尚未被其缓冲事件协调的预览。该预览不会再有更多增量。如果轮次出错或被中断,缓冲事件可能永远不会到达,但 span.model_request_end 仍会到达。

该模式依赖于两项保证:

  • 按到达顺序、以 (event_id, index) 为键拼接预览的增量,得到的是缓冲事件中 content[index].text 的前缀。它不一定是完整文本,因为增量可能会在负载下被丢弃。
  • 一个连接针对每个 event_id 最多发出一个 event_start,并且缓冲事件是该连接针对该 id 传递的最后一项内容。

SDK 累加器辅助工具

每个 SDK 的辅助工具都会处理 index 记账。Go、Java、Ruby 和 C# 的辅助工具还会按事件的 id 为累积中的预览建立键。使用 Python、TypeScript 和 PHP 的辅助工具时,请自行维护该映射,并将每个增量合并到其 id 对应的条目中。

以下示例选择启用 agent.message 预览,并将其与缓冲事件进行协调:

# 预览快照,以事件 id 为键。accumulate_managed_agents_event 将每个
# event_start / event_delta 折叠为一个 agent.message 快照;缓冲的
# agent.message 会将其替换。
previews: dict[str, BetaManagedAgentsAgentMessageEvent] = {}

# 在此连接上启用 agent.message 预览
with client.beta.sessions.events.stream(
    session.id, event_deltas=["agent.message"]
) as stream:
    client.beta.sessions.events.send(
        session.id,
        events=[
            {
                "type": "user.message",
                "content": [{"type": "text", "text": "Describe the repo in one sentence."}],
            },
        ],
    )

    for event in stream:
        match event.type:
            case "event_start":
                snapshot = accumulate_managed_agents_event(None, event)
                if snapshot is not None:
                    previews[event.event.id] = snapshot
                print(f"event_start             {event.event.type} {event.event.id}")
            case "event_delta":
                preview = accumulate_managed_agents_event(previews.get(event.event_id), event)
                if preview is not None:
                    previews[event.event_id] = preview
                    text = "".join(block.text for block in preview.content)
                    print(f"event_delta             preview: {text!r}")
            case "agent.message":
                # 缓冲事件才是正式记录:它会替换并关闭预览
                preview = accumulate_managed_agents_event(previews.pop(event.id, None), event)
                text = "".join(block.text for block in preview.content)
                print(f"agent.message           {event.id} {text!r}")
            case "span.model_request_end":
                # 不会再有增量到来。关闭所有其
                # 缓冲事件从未到达的预览。
                for event_id in previews:
                    print(f"span.model_request_end  closing preview for {event_id}")
                previews.clear()
            case "session.status_idle":
                break

预览会话线程事件

在多智能体会话中,每个会话线程都有自己的事件流。它接受相同的 event_deltas[] 参数和相同的值。

一个连接只预览它正在读取的线程。会话级流预览主线程,子线程的预览永远不会被交叉发布到会话级流。要在模型生成时观察子智能体的文本,请打开该子智能体的线程流。

线程流的路径以 /threads/{thread_id}/stream 结尾。/events/stream 仅存在于会话级别,因此不存在 /threads/{thread_id}/events/stream 端点。

event_start 和 event_delta 在线程流上的结构与在会话级流上相同,累加与协调模式可按原样适用。请为每个流连接运行一个累加器实例。

# 列出会话的线程并选择一个子线程:子线程带有非空的
# parent_thread_id,而主线程的 parent_thread_id 为 null。
child_thread = next(
    thread
    for thread in client.beta.sessions.threads.list(session.id)
    if thread.parent_thread_id is not None
)

# 子线程的流接受与会话流相同的
# event_deltas 参数。
with client.beta.sessions.threads.events.stream(
    child_thread.id,
    session_id=session.id,
    event_deltas=["agent.message"],
) as stream:
    for event in stream:
        match event.type:
            case "event_delta":
                print(event.delta.content.text, end="")
            case "agent.message":
                # 缓冲事件是权威记录;渲染其内容
                print()
                for block in event.content:
                    if block.type == "text":
                        print(block.text, end="")
                print()
            case "session.thread_status_idle":
                break

读取循环在收到 session.thread_status_idle 时退出,该事件在会话线程的轮次完成且线程进入空闲状态时发出。

限制

  • 尽力而为: 在负载下,服务器可能会丢弃某个事件的增量。发生这种情况时,您会收到文本的一个连续前缀,之后该事件不再有任何增量。缓冲的 agent.message 仍会完整到达。切勿将累积的预览视为最终结果。
  • 重新连接时不重放: 增量仅在选择启用的连接处于打开状态时传递给该连接。这同样适用于会话级流和每个会话线程流。在模型请求开始后才打开的连接不会收到该进行中事件的任何增量。无法重新请求错过的增量。
  • 单一线程,仅限文本: 预览涵盖连接正在读取的线程上的助手文本。工具使用、工具结果和 MCP 结果永远不会被预览。
  • 永不持久化: event_start 和 event_delta 仅存在于实时流中。它们不会出现在会话的事件历史记录(GET /v1/sessions/{session_id}/events)或任何会话线程的事件历史记录中。

预览故障排除

您看到的情况含义
流中有缓冲事件,但没有 event_start 或 event_delta您正在读取的连接未选择启用,或者该轮次从未涉及您正在流式传输的线程。event_deltas[] 按连接生效,而不是按会话生效。要查找运行了哪个线程,请列出会话的线程(GET /v1/sessions/{session_id}/threads)。
流在预览期间中断增量不会被重放。请按照重新连接流程操作:重新打开流并列出事件历史记录。历史记录包含您断开连接期间发出的所有缓冲事件,包括您的预览正在等待的 agent.message。
流 URL 返回 404路径或某个 ID 有误,或者请求完全没有携带 managed-agents beta 标头。线程端点受 beta 限制,因此没有该标头时它们不存在。
返回提及 event_deltas 的 400 错误仅接受 agent.message 和 agent.thinking。

后续步骤

发送事件、流式传输响应,并在执行过程中中断或重定向您的会话。

在单个会话中协调多个智能体。

Was this page helpful?