feat(agent): use native parallel nodes and early reply streaming

This commit is contained in:
Eric Wang
2026-10-27 00:29:14 +08:00
parent e6a96ed135
commit 666dda4dba
14 changed files with 635 additions and 180 deletions
+82
View File
@@ -0,0 +1,82 @@
# 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