AI 模型的输出通常是逐字生成的流式数据,尤其是在实时对话交互的场景中,用户希望能即时看到 AI 输出内容的过程。而另一方面,我们在很多场景下需要 AI 模型输出结构化的内容,方便我们后续处理和展示。我们可以通过 markdown 的标题格式来组织 AI 的输出,但是 markdown 的无法做到强约束。如果需要保证强结构化,那么我们一般会采用 JSON 格式,对此很多模型都有强制约束输出 Json 的参数。 这其中有两大问题:
因此,我们需要一种能够边接收数据边解析的方案,确保用户实时看到 AI 的输出,同时保证解析的健壮性。
例如我们在理解用户问题这个场景时,既想要结构化的数据,又想要实时的将结果反馈到前端

为了解决上述问题,我们设计了一款流式 JSON 解析器,目标包括:
$.nodes[*].title),减少无效数据处理。我们的解析器基于手写的有限状态机( FSM ),逐字符处理流式数据。以下是实现的关键组件和流程:
["nodes", 0, "title"]),用于路径匹配和回调触发。状态机的核心逻辑如下:

$.nodes[*].title)解析为数组形式(如 ["nodes", "*", "title"])。* 通配符,用于匹配数组中的任意索引。解析器支持两种模式:
以下是增量解析的示例:
matcher := utils.NewSimplePathMatcher()
matcher.On("$.choices[0].delta", func(value interface{}, path []interface{}) {
fmt.Printf("path=%v, value=%v\n", path, value)
})
parser := utils.NewStreamingJsonParser(matcher, true, true)
_ = parser.Write("{\"choices\":[{\"delta\":\"")
_ = parser.Write("Hel") // 增量触发回调:"Hel"
_ = parser.Write("lo\"}]}\n") // 增量触发回调:"lo",结束后不再发送整串
_ = parser.End()
\n, \t, \r, \\, \", \/, \b, \f;当前未支持 \uXXXX Unicode 转义序列,可按需扩展。代码使用 Golang 实现,如果需要使用其他语言,可以让 AI 翻译一下即可。
通过自研流式 JSON 解析器,成功解决了 AI 应用中实时性和结构化输出的难题。希望这次分享能为有类似需求的开发者提供参考。
1
freeman12 2 天前
试过在前端使用 untruncate-json 流式补全 json
|
2
lrwlf 2 天前
有个也能实现流式 json 的项目: https://github.com/josdejong/jsonrepair
|
3
aloxaf 1 天前
不错不错,有的项目在这种场景只是简单地循环调用支持 partial parse 的 json 库
数据量小的时候还好,数据量一高就能明显感到卡顿 |
4
tairan2006 1 天前
不如发明一种更好的数据格式
|
5
wantDoraemon OP @lrwlf 这个看起来是修复 json 。我的那个场景就是 LLM 在输出 JSON 的时候就开始解析,即使 value 还不完整,也需要把内容推给前端
|
6
wantDoraemon OP @tairan2006 还有更好的数据格式吗?大模型支持的强数据结构貌似只有 json😂
|
7
siweipancc 1 天前 via iPhone
当前未支持 \uXXXX Unicode 转义序列
那寄了,面向老外的产品? |
8
wantDoraemon OP @siweipancc 中文是支持的呢 如果有其他字符不支持的,那就得加强一下了,不过我暂时还没遇到这种情况😂
|
9
imqiyue 20 小时 30 分钟前 via iPhone
Yaml 呢?感觉很简洁,很适合
|