83 lines
3.9 KiB
Markdown
83 lines
3.9 KiB
Markdown
# LangGraph 原生并行与流式输出
|
|||
|
|
|
||
|
|
文本模型使用 ChatOpenAI,业务图嵌入 FastAPI。对外仍使用原来的
|
||
|
|
stage_code / text_delta / formUpdate / done / error SSE 契约。
|
||
|
|
|
||
|
|
## 图内并行
|
||
|
|
|
||
|
|
事故采集阶段:
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
flowchart LR
|
||
|
|
P[normalize_input] --> R[collect:生成并校验回复]
|
||
|
|
P --> X[extract_accident_fields:提取候选字段]
|
||
|
|
R --> M[merge_collect]
|
||
|
|
X --> M
|
||
|
|
M --> C[应用服务事务提交]
|
||
|
|
```
|
||
|
|
|
||
|
|
条件边只在采集阶段选中两个分支;welcome、拍照、人员核实走自己的分支。
|
||
|
|
通过 `add_edge(["collect", "extract_accident_fields"], "merge_collect")`
|
||
|
|
保证两个分支都完成后,汇合节点只运行一次。
|
||
|
|
|
||
|
|
`normalize_input` 固定服务器参考时间并清空本轮临时通道。回复节点只更新
|
||
|
|
回复、阶段和对话簿记;提取节点只写 `extracted_accident_fields`;汇合节点
|
||
|
|
更新 accident_fields / turn_patch,再清空候选通道。两条分支都读取本轮开始时
|
||
|
|
的快照,不需要为并发写同一字段增加合并 reducer。
|
||
|
|
|
||
|
|
人员核实的提取提示词当前依赖本轮模型回复,并负责声明人员切换。
|
||
|
|
本次保留它的串行依赖与提示词,仍能在抽取完成前发送完整回复。
|
||
|
|
|
||
|
|
## 原生流与校验
|
||
|
|
|
||
|
|
后端使用 LangChain `astream_events(version="v2")`,其生命周期管理负责
|
||
|
|
消费期间的任务调度,以及流关闭后的取消与等待。
|
||
|
|
|
||
|
|
| 原生事件 | 条件 | 应用服务适配 |
|
||
|
|
|---|---|---|
|
||
|
|
| on_chain_stream | name=validated_reply_tokens | TextDelta |
|
||
|
|
| on_chain_end | name=validated_reply | ReplyFinished,刷新分句器 |
|
||
|
|
| on_chain_end | 根 accident_turn 正常结束 | 已提交结果,可发送 FormUpdate |
|
||
|
|
|
||
|
|
`validated_reply_tokens` 是原生 RunnableGenerator,封装现有模型文本迭代器,
|
||
|
|
等待完整合法的 state 前缀后才产出第一段文本。原始 ChatOpenAI 的 model 事件
|
||
|
|
仍可被 callbacks 追踪,但公开文本只消费受控回复 Runnable 的事件。
|
||
|
|
原生 messages 流包含校验前 token,单纯按模型或节点过滤不能替代前缀校验。
|
||
|
|
|
||
|
|
`validated_reply` 是原生 RunnableLambda。完整回复通过格式、阶段转换和可选
|
||
|
|
时间依据校验后,其正常 on_chain_end 才表示回复结束。模型结束事件本身
|
||
|
|
不能证明这些业务校验通过,因此不作为刷新最后一句的依据。
|
||
|
|
|
||
|
|
字段提取保留 complete / ainvoke,显式 stream=False,避免事件追踪 callback
|
||
|
|
把该调用隐式切换为流式。辅助模型输出不会映射为 TextDelta。
|
||
|
|
|
||
|
|
## 回复结束与事务完成
|
||
|
|
|
||
|
|
ReplyFinished 仅是后端中立适配层的内部标记,不新增公开 SSE 事件。
|
||
|
|
它立即刷新 useTextChunk 的剩余完整回复,覆盖有/无字段差量和
|
||
|
|
needFormUpdate=false 的情况。
|
||
|
|
|
||
|
|
文本可以早于提取和业务事务完成到达客户端;FormUpdate 与 done 仍在事务
|
||
|
|
成功后出现。完整回复失败、图失败、业务提交失败时,发送 error 而不发送 done。
|
||
|
|
已经输出的文字不可撤回,客户端仍以 done 判断本轮成功。
|
||
|
|
|
||
|
|
取消流时必须等图分支与模型关闭后才释放会话锁。持久化模式继续从业务提交
|
||
|
|
快照恢复;并行分支产生的未提交 checkpoint 不成为业务成功状态。
|
||
|
|
|
||
|
|
## 验证
|
||
|
|
|
||
|
|
`test/backends/test_native_parallel_streaming.py` 使用事件屏障验证:
|
||
|
|
|
||
|
|
- 回复和抽取同时运行,汇合与提交等待两者完成。
|
||
|
|
- 抽取阻塞时已经收到完整正文和 ReplyFinished。
|
||
|
|
- 分句器最后一句及时刷新;公开 SSE 类型、文本顺序和终止事件保持兼容。
|
||
|
|
- 抽取 JSON 不泄漏,原生节点和汇合在事件流中可见。
|
||
|
|
- 断开取消剩余抽取,SQLite 未提交状态可恢复并重试。
|
||
|
|
- 完整回复校验失败和业务提交失败均不产生虚假成功。
|
||
|
|
- ChatOpenAI 的真实 HTTP mock 路径保留普通提取与流式回复的请求参数。
|
||
|
|
|
||
|
|
官方接口参考:
|
||
|
|
|
||
|
|
- https://docs.langchain.com/oss/python/langgraph/streaming
|
||
|
|
- https://docs.langchain.com/oss/python/langgraph/use-graph-api
|