☰
流式响应、并发性能、统计边界:caveman proxy 实测中容易忽略的三个隐患
2026/10/10 13:59:13 网站建设 项目流程

流式响应、并发性能、统计边界:caveman proxy 实测中容易忽略的三个隐患

【免费下载链接】caveman🪨 why use many token when few token do trick. Viral skill + proxy for coding agents that cuts 65% of tokens by talking like a caveman.项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman

把 AI 编码代理的请求从本地转发到云端模型时,proxy 层的价值常常被一句话概括:"改请求、省 token、看账单"。但从社区实测反馈看,真正让开发者踩坑的地方恰恰藏在三个角落:流式响应下的 token 统计是否失真、并发场景下前缀缓存与计量是否稳定、统计边界到底把什么算进去、什么不算。这三个问题如果没想清楚,proxy 省下来的 token 数字就会变成一份"看似精确、实则无法对账"的报表。

caveman proxy(proxy/ 目录,网关核心在 proxy/internal/gateway/)在这三个维度上给出了相当硬核的工程答案。本文结合其源码,逐一拆解这三处隐患及其在实现层面的对策——这不是教程,而是把 proxy 当作计量仪器的边界条件测试。

隐患一:流式响应下的 token 统计失真

流式请求为什么默认被排除在压缩路径之外

先看一个容易忽略的事实:流式请求根本不参与压缩改造。proxy/internal/gateway/proxy.go 的compress分支里有这样一行判断:

serverRetrieveAllowed := authMode == AuthModePAYG && !s.recoveryViaMCP && !meta.Stream && !hasRetrieveTool(body) && serverRetrieveSupported(meta.Provider, meta.Endpoint)

!meta.Stream意味着一旦请求声明了流式,服务端注入 retrieve 工具、走 CCR 恢复路径的资格立即被取消。为什么?因为压缩请求的原始内容存放在本地 CCR(缓存恢复)存储里,压缩后的请求通过<<ccr:handle>>标记携带句柄;恢复路径要么靠服务端注入 retrieve 工具在一次流式会话中多轮调用,要么靠 MCP 工具由代理侧自行取回。流式请求让这两种恢复方式都变得不可靠——你无法在一条不断吐字的事件流里优雅地插入一次"先取回原始内容再继续"的往返。caveman 的取舍很干脆:恢复不可达的改造一律不做。

这引出了第一个实测教训:如果你在 compress 模式下对 SDK 默认开启stream: true的调用做压测,会发现压缩率数字远低于文档宣称的 65%。那不是 bug,而是流式请求被有意挡在压缩门外,属于"安全优先于省钱"的策略。

响应流的计量:tee 扫描与 TTFB 的真实测量

流式响应的计量则是另一套机制。代理把上游响应体通过io.TeeReader同时喂给客户端拷贝和UsageScanner(proxy/internal/gateway/proxy.go 的streamResponse),边转发边解析 SSE 事件流里的 usage 字段,不缓冲客户端拷贝。

这里有两个值得注意的边界:

  • 扫描缓冲上限:UsageScanner默认只缓冲 2 MiB(CAVE_USAGE_SCAN_MAX_BYTES,见 proxy/providers/adapter.go)。一旦响应超过上限,它放弃全量缓冲,只保留一个有限尾部,且仅在编码为 identity 时才尝试从尾部恢复完整的 usage 事件——否则直接标记response_scan_limit_exceeded,整行计费信息降级为 unknown。换句话说,超大流式响应可能"丢统计"。
  • usage 事件的合并语义:流式场景下 provider 会把 usage 拆在多个事件里(如 Anthropic 的message_start带 input/cache 字段,message_delta只带 output)。proxy/providers/adapter.go 的UsageObservation注释明确指出这是merge 而非"取最后一个 chunk",且同一字段取较大值、空值不覆盖非空值——否则 Anthropic 的缓存增量数据会从账单里无声消失。

TTFB 的测量同样只在流式路径上成立:countingWriter记录首个字节写入时刻(proxy/internal/gateway/proxy.go 的firstByteAt),配合streamResponse在首读前强制Flush(),让客户端尽早看到 SSE 头部。proxy/internal/gateway/server_test.go 的TestRoundTripStreamTTFB专门断言"TTFB 为真实正值且低于总延迟"。这个测试的存在说明:非流式路径的 TTFB 没有意义——整段响应被缓冲完才提交头,所谓首字节延迟就是总延迟。

流式中断的记账规则

最后一个容易被忽视的点:流式中断时,usage 已部分入账怎么办?caveman 的规则是流式中断不再重放(never replay a partial response),因为 provider 可能已经完成了推理并计费;同时,已解析的完整 usage 仍可保留在行内用于支出统计,但失败的流绝不产生反事实节省——record()里压缩节省的归属条件是status 2xx && errorCode == "" && !usage.ProviderError && !retrieved。一个中途断掉的流,无论压缩器产出了多少,都不会被计入"省了多少钱"。

隐患二:并发场景的性能与计量偏差

并发前缀稳定:一个真实的回归与修复

代理对每个请求做字节级改造(压缩、工具 schema 裁剪、缓存断点注入),这要求同一会话的同一前缀块在每轮请求中输出完全相同的字节——否则 provider 侧的 prompt cache 前缀每次都在变,缓存永远打不中。并发正是打破这个不变量的头号杀手。

proxy/internal/gateway/prefix_stability_test.go 的TestPrefixStableUnderConcurrency记录了一个真实回归:代理并发服务多个回合(并行 agent、subagent),而支撑前缀缓存的存储同时又是遥测 sink,查找总是与写入竞争。一旦竞争把命中降级为 miss,相同请求就会产生不同字节发往上游,provider 的 prompt cache 便不确定性地失配。

该测试用 8 个并发 goroutine 发送完全相同的请求体,断言上游收到的 n 个 body 的 SHA-256 必须全部一致(len(seen) != 1即失败)。修复后的约束是:任何并发路径都不允许把"本该命中的替换"降级成 miss。这个测试本身就是给使用者的警示:并发压测代理时,不要只看吞吐,还要抓上游原始请求的字节指纹,确认前缀稳定。

并发下的计量行为:买单 goroutine 与有损捕获

计量侧的并发防护同样明确:

  • body capture 是有损的。proxy/internal/gateway/capture.go 实现的本地请求体捕获(CAVE_CAPTURE_DIR开启)通过有界队列 + 单 writer goroutine落盘,队列满时直接丢弃记录并计数。设计注释写得很直白:诊断仪器"绝不能拖慢或打断流量",哈希、编码、写盘全部挪到 writer 侧,请求路径上最多付出一次 nil 检查和一个 channel 发送。所以高并发下如果你发现捕获文件出现 seq 空洞,那不是文件损坏,而是仪器主动丢弃——捕获用于诊断,不用于计费。
  • 观测式估计与上游往返并行。record 模式下若开启observe-estimate,tokenizer 加 Simulate 的估算成本被放到 goroutine 里与上游往返重叠执行(proxy/internal/gateway/proxy.go 的estimateWG),避免估算落在 time-to-first-byte 上;主路径在record()前统一estimateWG.Wait()保证 happens-before。并发计量的前提是:估算永远跑在拷贝上,绝不碰转发字节。

性能数字的可信度

并发压测时你还会发现一个指标分层:LatencyMS是总耗时,TTFBMS只对流式有意义;ResponseBytes来自countingWriter,只统计成功写到客户端的字节数。被客户端中断、被截断的响应,其字节数与 usage 都不会伪装成成功——streamResponse返回错误码后主路径会panic(http.ErrAbortHandler)主动打断 HTTP 帧,避免残缺 SSE 被客户端当成干净 EOF。所以读并发报告时,凡是cave_client_canceled/cave_upstream_body_read_failed的行,都不能计入"成功流量"的均值里。

隐患三:统计边界:什么该算、什么不算

这是三个隐患里最容易被忽略、也最影响对账可信度的部分。caveman 的统计边界哲学可以浓缩成一句:每笔数字必须能说清"怎么来的"。docs/technical/accounting-and-evidence.md 把证据基底(evidence basis)分成七类——measured、inferred、provider_reported、benchmark_counterfactual、observed、verified、unpriced,并规定"一个值不能在被聚合时悄悄更换基底"。

边界一:4 MiB 与多模态,请求侧估计的禁区

proxy/internal/gateway/stats_accounting.go 的requestAccounting是整条统计链上最严格的守门员:

if len(original) > requestAccountingMaxBytes || len(accepted) > requestAccountingMaxBytes { row.RequestMeasurementStatus = "request_payload_too_large" return }
  • 超过 4 MiB 的请求不做本地 token 估计(requestAccountingMaxBytes = 4 << 20),只保留 provider 侧真实 usage 和定价——大请求照常流转,但"压缩省了多少 token"这一项直接 unavailable。为什么?因为本地 o200k tokenizer 的估算对超大 payload 既不经济也不可信。
  • 多模态 payload 整个被排除。containsNonTextInput递归扫描 OpenAI 的image_url、Anthropic 的input_image、Bedrock 的bytes/s3Location、Gemini 的inlineData等形状;只要出现图片/音频/文件,就把 base64 字节当作文本 token 去估计是荒谬的(模型看到的是像素,不是 base64 字符),所以该请求直接标记unsupported_payload。
  • 归一化防止"作弊式节省"。两侧 JSON 都经compactTextRequest规范化——键序、转义(\u0061vsa)、HTML 转义差异全部抹平后再计数,避免重组器通过序列化差异"造出" token 节省。

这三条合起来说明:本地估计只对纯文本、大小适中、单次调用的请求可信。凡是多模态、超大、多调用恢复(recovery_multiple_calls)的请求,你在caveman stats里看到的将是unavailable,这不是缺陷,而是统计诚实性的体现。

边界二:反事实定价的保守规则

即使估计通过,折算美元时还有更多禁区:

  • 自定义上游源不定价:statsPricingOriginKnown只认官方域名(api.openai.com、api.anthropic.com、generativelanguage.googleapis.com、bedrock/vertex 正规主机),运营商自建 base URL 即便用同样协议和模型名,也可能按完全不同的费率计费,因此这类请求一律标custom_provider_origin,不套官方刊例价。
  • 未知模型绝不借兄弟模型的价格(docs/technical/stats-accounting.md):unpriced意味着"零加显式标记",零防止虚构成本进入合计,标记防止零被误读为免费。
  • 反事实阈值不可证明时拒绝定价:本地 BPE 差值无法证明 provider 的反事实计费档位(比如压缩可能把请求拉低到更低的价格档),requestAccounting里cost.ForInputTokens(price, int(baselineInput)) != effective时直接标counterfactual_tier_unknown——宁可不算,也不乱算。

边界三:什么不算节省

统计边界还要回答"哪些东西明确不该进节省账本":

  1. 失败的请求不算:压缩器产出了输出 ≠ 省了钱,requestAccounting在status < 200 || status >= 300 || errorCode != "" || usage.ProviderError时直接返回失败状态。
  2. 重放原始字节的请求不算:上游 4xx 拒绝改造后的请求时,proxy 用原始字节 fail-open 重试一次(proxy/internal/gateway/proxy.go 的字节安全回退),该重试"不认领任何优化、不记账任何节省"(The retry claims no optimization and books no savings)。
  3. 恢复请求不算:retrieved为真时压缩节省被整体拒绝,因为代理无法证明压缩请求与检索到的原始内容在模型侧等价。
  4. 按次计费场景无效:社区情报里多次提到的"对按请求计费平台无效"也是统计边界的一部分——token 压缩只对按 token 计费的模型有意义,这是工具的边界,不是缺陷。
  5. 订阅/OAuth 场景只展示等价价值:subscription_passthrough流量走 S0 直通,统计上记为 token-only、绝不折算成美元节省;订阅账单不会因为 proxy 压缩而减少,"等价 API 价值"与"真金白银的节省"被严格区分。

三个隐患的排查清单

把源码里的防护机制翻译成实测时的操作建议:

隐患现象排查入口
流式统计失真压缩率偏低、大流式响应缺 usage检查meta.Stream是否导致请求被排除压缩;检查CAVE_USAGE_SCAN_MAX_BYTES与response_scan_limit_exceeded标记;区分 TTFB 与非流式缓冲
并发偏差相同请求上游字节不同、捕获文件 seq 空洞参照TestPrefixStableUnderConcurrency抓上游字节指纹;CAVE_CAPTURE_DIR下空洞是丢弃而非损坏;勿把cave_client_canceled行计入成功均值
统计边界报表出现大量unavailable/unpriced对照 docs/technical/accounting-and-evidence.md 的基底表;4 MiB 与多模态请求本就该 unavailable;自定义 base URL 不套官方价

caveman proxy 最值得借鉴的不是压缩算法本身,而是它对"不可验证的数字宁可不给"的态度:流式路径为了恢复可靠性放弃压缩资格,并发路径用有损捕获换取流量零阻塞,统计边界则用unpriced、counterfactual_tier_unknown、response_scan_limit_exceeded一类显式状态把"不知道"如实说出来。对于任何把 proxy 当计量仪器用的团队,这三个隐患对应的不是三个 bug,而是三份边界说明书——读懂它们,你才能知道报表上每个数字到底证明了什么。

【免费下载链接】caveman🪨 why use many token when few token do trick. Viral skill + proxy for coding agents that cuts 65% of tokens by talking like a caveman.项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询