乐于分享
好东西不私藏

通过higress AI统计插件学gjson表达式的分享

通过higress AI统计插件学gjson表达式的分享

前言

用过higress的都知道higress是一个功能强大的AI网关,而且支持wasm插件,其中有个插件是AI Statistics(AI统计),可以用来进行大模型调用相关的可观测,可以统计token使用量,可以提取请求内容写入日志。比如记录用户的输入。问题的由来就在这里了。接下来我们就要说下AI统计插件存在的问题、以及解决问题的主角gson。

从一个插件问题说起

针对记录用户的输入,AI统计插件默认的配置如下:

attributes:-apply_to_log:truekey:"question"value:"messages.@reverse.0.content"value_source:"request_body"

我们知道大模型调用,用户消息都在messages,它是一个数组,包括用户消息+AI返回,正常来说发送给大模型的messages最新的message是用户消息(role=user)。

但是对于toolcall调用的情况,最新消息就是tool消息(工具调用结果),而且在不规范调用的情况下,也可能最新就是AI消息(role=assistant,实际测试这种情况大模型也是可以正常调用的)。

在上面的情况下,官方默认的配置messages.@reverse.0.content就存在问题了,message先反转再取第0个元素,可能取到的就是AI消息或tool执行结果,而不是用户消息。

看了下插件代码

case RequestBody:    value = gjson.GetBytes(body, attribute.Value).Value()

可见是通过gjson方式以jsonpath实现的,那么我们就要gjson看是不是支持jsonpath带筛选条件(针对messages数组做role=user的筛选)。

现在都很少去百度搜索、csdn的,多数直接找个AI大模型去问了,可惜啊AI是给出了答案,但是各种试就是不行。

比如messages.@reverse.#(role==”user”)#.0.content,结果返回了[],看着是进行了筛选然后取第一个元素,但实际不行

又attributeValue = messages.@reverse.#(role=="user")#.content,结果返回[“今天天气怎么样?”],变成了数组

这里测试,都得在higress控制台修改配置,然后调用路由api,再去日志查看,比较费劲麻烦,于是写了个测试代码,方便调试。

package mainimport ("fmt""github.com/tidwall/gjson")funcmain() {// 测试场景1: 只有一条 user 消息    body := []byte(`{        "messages": [            {                "role": "user",                "content": "今天天气怎么样?"            }        ],        "model": "qwen3-30b-a3b-instruct-2507",        "stream": false,        "enable_thinking": false    }`)    fmt.Println("=== 测试结果 ===")    attributeValue := `messages.@reverse.#(role=="user")#.0.content`    attributeValue = `messages.@reverse.#(role=="user")#.0`    attributeValue = `messages.@reverse.#(role=="user")#.content`    attributeValue = `messages.@reverse.0.content`    value := gjson.GetBytes(body, attributeValue).Value()    fmt.Printf("result: %s\n", value)}

然后开始启用Claude Code,还是这哥们给力,给出了两个可用的答案,同时还写了测试代码进行验证。

答案1:messages.@reverse.#(role==”user”).content

答案2:messages.#(role==”user”)#|@reverse|0.content

发现这两个答案筛选部分,一个带1个#一个带了2个#,于是追问,答案逐渐清晰

特性
表达式 1
表达式 2
使用符号
单 #
双 ##
返回类型
直接返回对象
返回数组
是否需要索引
❌ 不需要
✅ 需要 0
何时取值
查询时就取第一个
过滤后需要手动取

这也解释了为啥答案1不需要带索引0

对于答案2,个人感觉思路更清晰,通过|管道模式,一步步解析得到最终结果。

至于为啥messages.@reverse.#(role=”user”)#.0.content不行,而且返回是[]?

首先,messages.@reverse.0.content是可以返回结果的(不过可能不是role=user的结果),但是在增加#(role=”user”)#筛选后,无法再应用索引,.0直接识别为了0属性,那么肯定就不符合预期了。

然后,我又想到一个答案messages.#(role==”user”)#|@reverse|0.content,就是先筛选再反转,取第0个元素(这里问了AI,也做了代码测试,元素顺序不会出问题)

然后又想到2个#的问题在于无法直接索引0那么只需要通过|进行索引0就好了,于是又有一个新答案:messages.#(role==”user”)#.@reverse|0.content

至此,这篇关于gjson的学习分享就结束了。


欢迎关注、交流!

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 通过higress AI统计插件学gjson表达式的分享

评论 抢沙发

5 + 9 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮