Skip to content

perf(prompt-filter): 功能关闭时仍重复扫描大请求体,validate 增加 5–8 秒 #417

Description

@OneAHPro

问题概述

prompt_filter_enabled=false,且 review、advanced risk、sidecar、newapi policy 等扩展能力也全部关闭时,/v1/responses 仍会提取并遍历请求正文;HTTP 路径至少重复调用两次 ExtractText

在 12–16 MB 的多模态/长会话请求体上,validate 阶段稳定增加约 5–8 秒,导致下游网关看到的首字时间显著高于 usage_logs.first_token_ms。当前 main6f2e65d1)仍存在该路径。

生产对账证据(已匿名,不含正文、账号、密钥或 IP)

同一会话连续 17 条 gpt-5.5 请求:

  • 请求体:16,118–16,184 KB,中位数 16,157 KB
  • 下游记录首字:13,397–18,100 ms,中位数 15,462 ms
  • codex2api validate:4,928–7,759 ms,中位数 5,759 ms
  • codex2api usage_logs.first_token_ms:3,574–6,657 ms,中位数 4,330 ms
  • 全部 via_websocket=true
  • 全部 attempt_index=0is_retry_attempt=false
  • upstream_error_kind,没有 busy timeout、fallback 或重试

其中 5 条逐条对应如下:

input_tokens body_kb validate_ms first_token_ms downstream_frt_ms
128,186 16,118 5,066 6,657 16,264
129,272 16,128 4,985 3,900 13,698
137,094 16,161 5,486 3,795 14,756
140,310 16,180 6,335 4,357 16,113
140,546 16,184 6,538 3,740 15,677

同一时间窗口的对照请求:

input_tokens body_kb validate_ms first_token_ms downstream_frt_ms
139,981 588 183 802 1,372

两条请求的 Token 数量几乎相同,但大请求体是对照的约 27.5 倍,validate 是约 35.7 倍,下游首字是约 11.4 倍。因此延迟与正文 JSON 字节数/遍历成本高度相关,而不是 Token 数量本身,也不是当时的全局服务器负载。

对应大请求的阶段日志示例:

[TIMING] /v1/responses model=gpt-5.5 mw=195;read=0;validate=6538;prepare=348;schedule=12;body_kb=16184

对照请求:

[TIMING] /v1/responses model=gpt-5.5 mw=8;read=0;validate=183;prepare=37;schedule=7;body_kb=588

当前配置

prompt_filter_enabled=false
prompt_filter_review_enabled=false
advanced.normalization.enabled=false
advanced.risk.enabled=false
advanced.sidecar.enabled=false
advanced.output.enabled=false
advanced.intelligence.enabled=false
advanced.newapi.enabled=false

代码路径

proxy/prompt_filter.go

cfg := h.store.GetPromptFilterConfig()
verdict := promptfilter.Inspect(rawBody, endpoint, cfg) // 第一次 ExtractText
...
verdict = h.applyAdvancedPromptProtection(
    c,
    promptfilter.ExtractText(rawBody, endpoint, cfg.MaxTextLength), // 第二次 ExtractText
    verdict,
    cfg,
)

security/promptfilter/filter.go

func Inspect(body []byte, endpoint string, cfg Config) Verdict {
    text := ExtractText(body, endpoint, NormalizeConfig(cfg).MaxTextLength)
    return InspectText(text, cfg)
}

InspectText 到提取完成后才检查:

if !cfg.Enabled || strings.TrimSpace(text) == "" {
    return verdict
}

因此功能关闭并没有避开正文遍历。inspectPromptFilterOpenAIForWebSocket 也会在关闭状态下调用一次 Inspect

期望行为

  1. 当本地 filter、review 以及所有确实需要正文的 advanced 能力均关闭时,在提取正文前直接返回。
  2. 需要检查时只提取一次文本,在 InspectText、review 和 advanced protection 之间复用。
  3. 同步检查 HTTP /v1/responses、Responses WebSocket、Anthropic/Chat/Images 等共用路径。
  4. 增加关闭配置下的大请求体回归测试/benchmark,确认 ExtractText 不被调用或耗时不再随正文大小线性增长。

这不会改变过滤功能开启时的安全语义;主要是消除完全关闭状态下的无效扫描。若需要,我可以继续提供匿名化分段样本或协助测试修复版本。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions