Skip to content
rookie_L
Go back

Agent 流式输出的三种粒度:Token、Step 与全局 State

Edit page

在普通聊天机器人的时代,前端工程师对“流式输出(Streaming)”的理解非常朴素:

“起一个 SSE 或 HTTP 分块连接,服务端吐一个字的 Token,前端就往页面追加一个字。”

但在构建现代 Agent(特别是包含思考规划、并行工具调用、多步回溯的复杂系统)时,只支持 Token 流会立刻让前端陷入混乱:

LangGraph 团队发布的《Streaming in LangGraph》一文,系统性梳理了生产级 Agent 系统的三层流式传输粒度。理解这三种粒度,是前端工程师搭建专业级 Agent 工作台的必修课。


Agent 流式通信的三种粒度

在 LangGraph 架构中,流式输出被清晰解耦为三种不同职责的数据通道:

┌────────────────────────────────────────────────────────┐
│                   Agent 流式通信的三种粒度              │
└────────────────────────────────────────────────────────┘

         1. Token 级流 (LLM Generation Chunk)
            - 关注微观文字呈现(打字机效果)

         2. Step / Node 级流 (Intermediate Steps)
            - 关注行为推进(哪个工具正在运行、耗时、出参入参)

         3. State 级流 (State Values & Updates Snapshot)
            - 关注宏观业务状态(全局进度树、审批状态、数据快照)

粒度一:Token 级流(LLM Generation Token)

这是最基础的流式层级,由大模型底层 API 原生派发(例如 OpenAI 的 delta.content 或 Anthropic 的 content_block_delta)。


粒度二:Step / Node 级流(中间执行步骤)

当 Agent 决定调用工具或图流转到某个特定节点时,框架会派发 Step 级别的事件。

在 LangGraph 中对应 stream_mode="updates"

// 后端发出的 Step 增量事件
{
  "event": "on_tool_start",
  "data": {
    "tool": "github_pull_request_check",
    "input": { "pr_id": 412 },
    "started_at": 1722759300120
  }
}
// 2 秒后工具完成的事件
{
  "event": "on_tool_end",
  "data": {
    "tool": "github_pull_request_check",
    "output": { "status": "approved", "mergeable": true },
    "duration_ms": 2040
  }
}

粒度三:State 级流(全局状态投影快照)

这是最适合复杂前端产品形态的高级流式模式。在 LangGraph 中对应 stream_mode="values"

每当图中的任何一个节点执行完毕并返回 State Update 时,框架会自动将当前合并后的**完整状态树(Full Graph State Snapshot)**推送到客户端:

// 客户端收到的完整 State 快照
export type FullAgentStateSnapshot = {
  activeNode: "code_reviewer";
  phase: "analyzing" | "waiting_approval" | "done";
  progressPercentage: number;
  openFiles: string[];
  findings: Array<{ line: number; issue: string }>;
  messages: Array<{ role: string; content: string }>;
};

为什么前端极度需要 State 级流?

  1. 彻底消除前端状态拼装的脆弱性: 如果只给前端推碎片事件,前端需要写大量极其复杂的 Reducer 试图“脑补”出后端当前的全貌,一旦中间丢失一个包,整个 UI 就会状态错乱。 拥有 State 级流后,前端只需要把接收到的最新对象执行一次 setState(newSnapshot),即可永远与服务端保持绝对一致。
  2. 多终端多组件解耦: 左侧的“文件树组件”、右侧的“审查报告组件”和底部的“进度条组件”,可以直接各自订阅 State 快照的局部字段,不需要彼此通过全局事件总线艰难通信。

前端实战:三合一消费 Hook 设计

在生产级 React 应用中,我们需要在一条 SSE 连接上统一消费这三类混合数据:

import { useEffect, useState } from "react";

export function useAgentMultiStream(runId: string) {
  const [tokens, setTokens] = useState("");
  const [timelineSteps, setTimelineSteps] = useState<any[]>([]);
  const [globalState, setGlobalState] = useState<any>(null);

  useEffect(() => {
    const es = new EventSource(`/api/runs/${runId}/mixed-stream`);

    // 1. 监听微观 Token
    es.addEventListener("token", e => {
      const { text } = JSON.parse(e.data);
      setTokens(prev => prev + text);
    });

    // 2. 监听中间工具步骤
    es.addEventListener("step", e => {
      const step = JSON.parse(e.data);
      setTimelineSteps(prev => [...prev, step]);
    });

    // 3. 监听宏观状态快照
    es.addEventListener("state_snapshot", e => {
      const fullState = JSON.parse(e.data);
      setGlobalState(fullState);
    });

    return () => es.close();
  }, [runId]);

  return { tokens, timelineSteps, globalState };
}

结语:让“思考的新陈代谢”清晰可见

大模型的思考耗时不会在一夜之间缩短至零。在长达数十秒的复杂推理任务中,可见性就是最好的用户体验

通过将流式能力解耦为 微观文字(Token)行为中枢(Step)宏观全局(State) 三个层次,前端工程师得以将复杂、黑盒的 Agent 运算,重构为层次分明、动静皆宜的可视化交互现场。


Edit page

Previous Post
Agent 产品的信任感,来自前端的三个确认点
Next Post
接手一个陌生前端项目,我先让 Claude Code 做什么