
有些自定义数据和状态流转,不能走messages通道,要走custom通道
一、整体流程图
ini
Python 节点(search_node)
│ get_stream_writer()({ type, ... }) ← 写入
▼
LangGraph Runtime (stream_mode="custom")
│ 产生 protocol event,method = "custom"
▼
CustomTransformer(required_stream_modes=("custom",))
│ 把 custom 事件原样保留 / 转发
▼
langgraph dev → 协议 v2 SSE
│ event: custom
│ data: { name?, payload }
▼
前端 useStream / useChannel(['custom'])
│ event.params.data
▼
渲染 / 总结
二、四个关键点
1. 必须注册 CustomTransformer,否则事件不会产生
v2 事件流只在 graph 声明了stream_transformers 时,才会请求custom 流。没有它,get_stream_writer() 写了也白写。
python
# streaming.py
from langgraph.stream import CustomTransformer
def stream_transformers():
return [CustomTransformer]
python
# graph.py --- 必须 import 到图模块里,框架用 getattr 反射找这个名字
from agent.streaming import stream_transformers # noqa: F401
关键点:函数 不能只写在别的文件里 ,必须作为属性出现在langgraph.json 指向的那个图模块的命名空间。
2. 用get_stream_writer() 写数据(后端)
arduino
from langgraph.config import get_stream_writer
def search_node(state):
writer = get_stream_writer()
writer({"type": "searching", "query": "..."})
...
writer({"type": "search_done", "count": 3})
- 写进去的是 任意可序列化对象 (dict/list/基本类型);
- 没有
type=参数,传一个位置参数即可; - 只有在 v2 流的 run 里 调用才有效,本地
graph.invoke()里写不会推到前端。
3. 前端用useChannel 订阅(官方)
php
const events = useChannel(stream, ['custom'], undefined, { replay: true, bufferSize: 64 })
参数含义:
取数据:
scss
const payload = computed(() => events.value.at(-1)?.params?.data)
// payload 就是后端 writer 传的原样对象:{ type, query, ... }
4. (补充)replay 和bufferSize 的取舍
replay: true:后订阅也能拿到 之前已发生 的事件,适合进度条、状态展示;replay: false:只收 订阅之后 的事件,适合一次性通知(如"搜索完成"提示,避免重复弹);bufferSize控制回放窗口,太大占内存,太小可能漏;进度展示建议 32~128。
三、数据在各层的形状(对照排查)

四、一句话记忆
scss
后端写 :`get_stream_writer()(payload)`
后端注册 :`stream_transformers() -> [CustomTransformer]` (放在图模块)
前端收 :`useChannel(stream, ['custom'], undefined, {replay:true, bufferSize:64})` ,读`.params.data`