feat: improve ai statistic plugin (#2671)

This commit is contained in:
aias00
2025-08-11 13:43:00 +08:00
committed by GitHub
parent f7813df1d7
commit a76808171f
3 changed files with 294 additions and 51 deletions

View File

@@ -5,7 +5,8 @@ description: AI可观测配置参考
---
## 介绍
提供AI可观测基础能力包括 metric, log, trace其后需接ai-proxy插件如果不接ai-proxy插件的话则需要用户进行相应配置才可生效。
提供 AI 可观测基础能力,包括 metric, log, trace其后需接 ai-proxy 插件,如果不接 ai-proxy 插件的话,则需要用户进行相应配置才可生效。
## 运行属性
@@ -13,9 +14,10 @@ description: AI可观测配置参考
插件执行优先级:`200`
## 配置说明
插件默认请求符合openai协议格式并提供了以下基础可观测值用户无需特殊配置
- metric提供了输入token、输出token、首个token的rt流式请求、请求总rt等指标支持在网关、路由、服务、模型四个维度上进行观测
插件默认请求符合 openai 协议格式,并提供了以下基础可观测值,用户无需特殊配置:
- metric提供了输入 token、输出 token、首个 token 的 rt流式请求、请求总 rt 等指标,支持在网关、路由、服务、模型四个维度上进行观测
- log提供了 input_token, output_token, model, llm_service_duration, llm_first_token_duration 等字段
用户还可以通过配置的方式对可观测的值进行扩展:
@@ -25,45 +27,48 @@ description: AI可观测配置参考
| `attributes` | []Attribute | 非必填 | - | 用户希望记录在log/span中的信息 |
| `disable_openai_usage` | bool | 非必填 | false | 非openai兼容协议时model、token的支持非标配置为true时可以避免报错 |
| `value_length_limit` | int | 非必填 | 4000 | 记录的单个value的长度限制 |
| `enable_path_suffixes` | []string | 非必填 | [] | 只对这些特定路径后缀的请求生效,可以配置为 "\*" 以匹配所有路径(通配符检查会优先进行以提高性能)。如果为空数组,则对所有路径生效 |
| `enable_content_types` | []string | 非必填 | [] | 只对这些内容类型的响应进行缓冲处理。如果为空数组,则对所有内容类型生效 |
Attribute 配置说明:
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|----------------|-------|-----|-----|------------------------|
| `key` | string | 必填 | - | attribute 名称 |
| `value_source` | string | 必填 | - | attribute 取值来源,可选值为 `fixed_value`, `request_header`, `request_body`, `response_header`, `response_body`, `response_streaming_body` |
| `value` | string | 必填 | - | attribute 取值 key value/path |
| `default_value` | string | 非必填 | - | attribute 默认值 |
| `rule` | string | 非必填 | - | 从流式响应中提取 attribute 的规则,可选值为 `first`, `replace`, `append`|
| `apply_to_log` | bool | 非必填 | false | 是否将提取的信息记录在日志中 |
| `apply_to_span` | bool | 非必填 | false | 是否将提取的信息记录在链路追踪span |
| `trace_span_key` | string | 非必填 | - | 链路追踪attribute key默认会使用`key`的设置 |
| `as_separate_log_field` | bool | 非必填 | false | 记录日志时是否作为单独的字段,日志字段名使用`key`的设置 |
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------------------- | -------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `key` | string | 必填 | - | attribute 名称 |
| `value_source` | string | 必填 | - | attribute 取值来源,可选值为 `fixed_value`, `request_header`, `request_body`, `response_header`, `response_body`, `response_streaming_body` |
| `value` | string | 必填 | - | attribute 取值 key value/path |
| `default_value` | string | 非必填 | - | attribute 默认值 |
| `rule` | string | 非必填 | - | 从流式响应中提取 attribute 的规则,可选值为 `first`, `replace`, `append` |
| `apply_to_log` | bool | 非必填 | false | 是否将提取的信息记录在日志中 |
| `apply_to_span` | bool | 非必填 | false | 是否将提取的信息记录在链路追踪 span |
| `trace_span_key` | string | 非必填 | - | 链路追踪 attribute key默认会使用`key`的设置 |
| `as_separate_log_field` | bool | 非必填 | false | 记录日志时是否作为单独的字段,日志字段名使用`key`的设置 |
`value_source` 的各种取值含义如下:
- `fixed_value`:固定值
- `request_header` attribute 值通过 http 请求头获取value 配置为 header key
- `request_body` attribute 值通过请求 body 获取value 配置格式为 gjson 的 jsonpath
- `response_header` attribute 值通过 http 响应头获取value 配置为header key
- `response_header` attribute 值通过 http 响应头获取value 配置为 header key
- `response_body` attribute 值通过响应 body 获取value 配置格式为 gjson 的 jsonpath
- `response_streaming_body` attribute 值通过流式响应 body 获取value 配置格式为 gjson 的 jsonpath
`value_source``response_streaming_body` 时,应当配置 `rule`,用于指定如何从流式 body 中获取指定值,取值含义如下:
`value_source``response_streaming_body` 时,应当配置 `rule`用于指定如何从流式body中获取指定值取值含义如下
- `first`多个chunk中取第一个有效chunk的值
- `replace`多个chunk中取最后一个有效chunk的值
- `append`拼接多个有效chunk中的值可用于获取回答内容
- `first`:多个 chunk 中取第一个有效 chunk 的值
- `replace`:多个 chunk 中取最后一个有效 chunk 的值
- `append`:拼接多个有效 chunk 中的值,可用于获取回答内容
## 配置示例
如果希望在网关访问日志中记录ai-statistic相关的统计值需要修改log_format在原log_format基础上添加一个新字段示例如下
如果希望在网关访问日志中记录 ai-statistic 相关的统计值,需要修改 log_format在原 log_format 基础上添加一个新字段,示例如下:
```yaml
'{"ai_log":"%FILTER_STATE(wasm.ai_log:PLAIN)%"}'
```
如果字段设置了 `as_separate_log_field`,例如:
```yaml
attributes:
- key: consumer
@@ -73,12 +78,14 @@ attributes:
as_separate_log_field: true
```
那么要在日志中打印需要额外设置log_format
那么要在日志中打印,需要额外设置 log_format
```
'{"consumer":"%FILTER_STATE(wasm.consumer:PLAIN)%"}'
```
### 空配置
#### 监控
```
@@ -120,17 +127,20 @@ irate(route_upstream_model_consumer_metric_llm_duration_count[2m])
```
#### 日志
```json
{
"ai_log":"{\"model\":\"qwen-turbo\",\"input_token\":\"10\",\"output_token\":\"69\",\"llm_first_token_duration\":\"309\",\"llm_service_duration\":\"1955\"}"
"ai_log": "{\"model\":\"qwen-turbo\",\"input_token\":\"10\",\"output_token\":\"69\",\"llm_first_token_duration\":\"309\",\"llm_service_duration\":\"1955\"}"
}
```
#### 链路追踪
配置为空时不会在span中添加额外的attribute
### 从非openai协议提取token使用信息
在ai-proxy中设置协议为original时以百炼为例可作如下配置指定如何提取model, input_token, output_token
配置为空时,不会在 span 中添加额外的 attribute
### 从非 openai 协议提取 token 使用信息
在 ai-proxy 中设置协议为 original 时,以百炼为例,可作如下配置指定如何提取 model, input_token, output_token
```yaml
attributes:
@@ -150,6 +160,7 @@ attributes:
apply_to_log: true
apply_to_span: false
```
#### 监控
```
@@ -160,7 +171,9 @@ route_upstream_model_consumer_metric_llm_duration_count{ai_route="bailian",ai_cl
```
#### 日志
此配置下日志效果如下:
```json
{
"ai_log": "{\"model\":\"qwen-max\",\"input_token\":\"343\",\"output_token\":\"153\",\"llm_service_duration\":\"19110\"}"
@@ -168,10 +181,13 @@ route_upstream_model_consumer_metric_llm_duration_count{ai_route="bailian",ai_cl
```
#### 链路追踪
链路追踪的 span 中可以看到 model, input_token, output_token 三个额外的 attribute
### 配合认证鉴权记录consumer
### 配合认证鉴权记录 consumer
举例如下:
```yaml
attributes:
- key: consumer # 配合认证鉴权记录consumer
@@ -181,31 +197,33 @@ attributes:
```
### 记录问题与回答
```yaml
attributes:
- key: question # 记录问题
value_source: request_body
value: messages.@reverse.0.content
apply_to_log: true
- key: answer # 在流式响应中提取大模型的回答
- key: answer # 在流式响应中提取大模型的回答
value_source: response_streaming_body
value: choices.0.delta.content
rule: append
apply_to_log: true
- key: answer # 在非流式响应中提取大模型的回答
- key: answer # 在非流式响应中提取大模型的回答
value_source: response_body
value: choices.0.message.content
apply_to_log: true
```
## 进阶
配合阿里云SLS数据加工可以将ai相关的字段进行提取加工例如原始日志为
```
配合阿里云 SLS 数据加工,可以将 ai 相关的字段进行提取加工,例如原始日志为:
````
ai_log:{"question":"用python计算2的3次方","answer":"你可以使用 Python 的乘方运算符 `**` 来计算一个数的次方。计算2的3次方即2乘以自己2次可以用以下代码表示\n\n```python\nresult = 2 ** 3\nprint(result)\n```\n\n运行这段代码你会得到输出结果为8因为2乘以自己两次等于8。","model":"qwen-max","input_token":"16","output_token":"76","llm_service_duration":"5913"}
```
````
使用如下数据加工脚本可以提取出questionanswer
使用如下数据加工脚本,可以提取出 questionanswer
```
e_regex("ai_log", grok("%{EXTRACTJSON}"))
@@ -213,9 +231,9 @@ e_set("question", json_select(v("json"), "question", default="-"))
e_set("answer", json_select(v("json"), "answer", default="-"))
```
提取后SLS中会添加questionanswer两个字段示例如下
提取后SLS 中会添加 questionanswer 两个字段,示例如下:
```
````
ai_log:{"question":"用python计算2的3次方","answer":"你可以使用 Python 的乘方运算符 `**` 来计算一个数的次方。计算2的3次方即2乘以自己2次可以用以下代码表示\n\n```python\nresult = 2 ** 3\nprint(result)\n```\n\n运行这段代码你会得到输出结果为8因为2乘以自己两次等于8。","model":"qwen-max","input_token":"16","output_token":"76","llm_service_duration":"5913"}
question:用python计算2的3次方
@@ -227,4 +245,57 @@ print(result)
运行这段代码你会得到输出结果为8因为2乘以自己两次等于8。
```
````
### 路径和内容类型过滤配置示例
#### 只处理特定 AI 路径
```yaml
enable_path_suffixes:
- "/v1/chat/completions"
- "/v1/embeddings"
- "/generateContent"
```
#### 只处理特定内容类型
```yaml
enable_content_types:
- "text/event-stream"
- "application/json"
```
#### 处理所有路径(通配符)
```yaml
enable_path_suffixes:
- "*"
```
#### 处理所有内容类型(空数组)
```yaml
enable_content_types: []
```
#### 完整配置示例
```yaml
enable_path_suffixes:
- "/v1/chat/completions"
- "/v1/embeddings"
- "/generateContent"
enable_content_types:
- "text/event-stream"
- "application/json"
attributes:
- key: model
value_source: request_body
value: model
apply_to_log: true
- key: consumer
value_source: request_header
value: x-mse-consumer
apply_to_log: true
```