logo NodeSeekbeta

Codex订阅额度消耗机制研究

Codex订阅额度消耗机制研究

一、背景与分析思路

相信大家每天盯着自己的Codex订阅额度变化,每次用Codex时,都要先纠结一下额度,这是非常令人头疼的。所以我就非常想弄清楚,Codex订阅额度的消耗机制。

Codex的每次API请求,会返回已用额度的百分比数据,以便在软件中显示。这个数据是会连同token是详细使用量,记录在本地的硬盘中文件。第三方程序可以读取这些文件,分析数据,来尽量还原Codex的订阅额度消耗机制。

我这边的分析方法是:首先使用脚本(文字最后有附),从本机电脑上读取Codex的使用记录文件,从中提取有用数据输出,并导出文件。主要的数据包括每次请求时返回的5H额度和周额度的已用值,以及每次返回的各种token的具体使用量,包括缓存输入、费缓存输入、推理输出、非推理输出。然后把这个文件扔给ChatGPT网页版的Chat模式,进行分析。

主要的分析思路是:在真实的使用数据里面,先找一些较为连续的任务,以任务为单位,进行拟合分析。这里的一个任务,要求是使用同一个模型的,并且在时间上比较连续,可能跨度几十分钟这样。这个任务要显著消耗一些5H额度窗口,否则分析起来容易误差太大。这里并不是找一个完整的5H额度窗口,然后计算总的相对于API原价的“订阅额度价值”,而是采用了一种“局部采样分析”的思路,用多个样本,去验证猜想。

主要从两个方面去推测:1. 我们先不考虑美元的价格,而是从使用数据的token量与额度消耗中,拟合计算以订阅额度为单位的价格,这里主要使用5H额度的百分点。这个推算需要先假设三种token的计费比例,我使用的是6系列为1/0.1/5,5.6系列为1/0.1/6。2. 先预定义一套标准价格,然后再从使用数据的token量与额度消耗中,反过来去拟合5H额度,之后再预定义一个5H额度(10刀,后来精确到10.3刀),计算模型的“价格倍率”。

二、我的账号与使用情况

最初我一直用的是那种10块钱的月抛Team订阅,买一次最多能用37天。后来从4月底开始,Team这种渠道没有了,而正好论坛又出现了48个月Team优惠的活动,于是5月10日左右,当时正好出来了英区11英镑的48Team,马上买了一个。上车后发现长期价格非常实惠,于是618那天,论坛再次爆出英区11英镑的48Team的优惠码,于是一口气开了三个,结果第二天就被封号,申诉无果后,后续最后通过银行争议退回钱款。几天后的7月1日,又出现了11英镑的48Team的优惠码,这次感觉靠谱,马上又买了4个号,并一直续费到现在。所以说相当于所有账号都是官方叫 Business Standard 的套餐,按照官方说法,额度跟Plus是一样的。

虽然一时冲动屯了很多号,但是我并没有马上把他们用起来,首当其冲的原因是需要准备多个长效手机号接码,其次是自己暂时没有很大量的Token需求。之前我的开发方式都是先跟AI对话,把具体细节都聊清楚了确定清楚了,再让AI一口气给干完。因为我很不喜欢那种AI先随便干,完事了我看很多地方不是我想要的,再让AI返工去改,认为这样1是浪费token,2是来回返工可能会让代码库的shi山含量增加。所以说这种开发方式上,对话的讨论、打字要消耗很多时间,token消耗并不快。7-8月份很长一段时间没有5H额度限制,额度使用可以非常灵活。8月底加入5H额度限制后,我又研究了下给网页版接入MCP使用网页版Chat模式开发(后来发现网页版的5.6Sol是为聊天专门优化的模型,实际产出代码的工程效果并不是很好),所以Codex竟然一个号一直够用。

最近9-10月份,我意识到不能总是白交钱,必须利用起来。于是9月份,我先用GG卡接码(怕GG卡后续失效,还是给子号用的),把其中唯一一个周额度的空间先用上。之后10月份,我又开了个 Amaysim 的 ESIM,这个比较靠谱一些,号码可以携号转网很方便,然后给几个母号绑上。

所以说,早期的大量数据都是基于一个号的,最近十一假期,我拿多个号频繁切换使用,并且开始学习“许愿式开发”,token消耗量大大增加,也带来了大量的数据样本,用来验证不同账号之间的额度也没有差异。

三、一些比较可靠的前提假设

这些前提假设是在前期分析的时候,通过我的账号的大量实际使用数据得出来的。这些结论的可信度比较高,因此在后续分析里面,是以这些结论为基础,来继续验证和修复的。这里需要先列举一下:

  1. 额度百分比是使用 nearest rounding 的方式取整
    每次请求会返回两个额度的已用百分比(不是剩余),这个百分比是整数(虽然Codex的代码里是兼容小数的返回值的),估计是官方防止大家逆推Codex额度消耗机制,故意设计的。
    经过大量数据的验证,可以比较确信的是:这些整数百分比,是通过 nearest rounding 的方式取整的,也就是说四舍五入,这个的也可信度是比较高的。

  2. 周额度是5H额度的6.4倍
    或者说5H额度是周额度的1/6.4。这个6.4是比较准确的数字,误差在+-0.05以内。
    由于百分比取整在周额度上带来的误差太大,所以我们主要推算的是5H额度。对于周额度,直接使用5H额度x6.4计算就行了。

  3. 每次请求返回的额度百分比的延迟机制
    每次请求返回的额度使用百分比,是这个请求开始执行之前的状态,不是执行完成之后。所以相当于lag=1,但是也会有额度扣减有延迟的情况。
    大多数的数据点按照lag=1,就能拟合的很好。

  4. 不同模型几种token的价格比例
    这个也是经过一些数据的实测,可以得到 非缓存输入/缓存输入/输出 这三种token的价格比例。结果是 GPT-6 系列为1/0.1/5,GPT-5.6 系列为1/0.1/6。
    这个结论是有很多实测数据拟合支持的。很多站内佬友认为订阅额度消耗是把缓内输入x2了,这里要解释一下,就是大家使用Codex的情况,大概95%的token都是缓内输入,因此账单占比也是缓内输入占了绝大部分。因此,很多整体的模型倍率变化,似乎也可以用缓内输入x2来解释。

  5. 特殊模型的特殊政策
    经过数据实测后,大部分模型都可以按照API原价作为“基准价”的。但是有2个例外:
    首先是 GPT-6.1 Sol,API价格把缓内输入比常理砍半了。但是经过大量数据拟合,目前更加支持的结论就是对于订阅额度消耗,这个砍半的特别待遇并不存在。也就是仍然遵守 GPT-6 系列 1/0.1/5 的比例。
    其次是 GPT-5.6 Sol,这个模型之前官方打8折降价,变成 4/0.4/20,但是官方同时说明订阅额度的消耗并不受这个优惠的影响(5.6的Terra和Luna的折扣同时适用于订阅额度的消耗)。中转站目前也基本都还在使用 5/0.5/30 的价格,因此我们的“基准价”都还是这个价格。
    这样,我们就得到了各个模型的“基准价”,具体将在下一章展示。

四、深入分析过程

首先,如果额度要用“美元”来算(实际上并不能说是钱),我们需要给几个模型定义一下基本的价格,即为“基准价”,用于后续分析。这个价格表是是这样的:

模型 价格(缓外输入/缓内输入/输出)
GPT-6 Astra 10/1/50
GPT-6.1 Sol 2/0.2/10
GPT-6 Sol 2/0.2/10
GPT-6 Luna 0.1/0.01/0.5
GPT-5.6 Sol 5/0.5/30
GPT-5.6 Terra 2/0.2/12
GPT-5.6 Luna 0.2/0.02/1.2
GPT-5.5 5/0.5/30

然后,我们需要先假设一个初始“美元”的额度,再对此进行微调。根据之前对大量 GPT-6 Astra 的使用数据分析,特别是有一些只用Astra然后直接用完5H额度窗口的情况,发现5H额度大概是10刀左右。因此我们就定义这个初始“美元”额度为5H额度10刀,周额度64刀。这是一个比较整的数,按照官方 1刀 = 25 credits 来换算的话,相当于5H额度 250 credits,周额度 1600 credits。

下一步,我们就开始进行进一步分析了,具体将在下一章展示。

五、订阅额度消耗机制的研究结论

之后我们就使用预先定义的基准价和初始额度,在真实数据中去进行拟合,得到了每个模型的倍率,和修正后的额度。

由于分析过程主要由AI进行,而本站不允许放AI生成的内容,截图也太长,所以我这里就直接说结论了:

模型 基准价格(缓外输入/缓内输入/输出) 倍率 额度价格(基准价格x倍率) 相对Astra的额度消耗比例
GPT-6 Astra 10/1/50 1.0x 10/1/50 1
GPT-6.1 Sol 2/0.2/10 0.825x 1.65/0.165/8.25 1/6
GPT-6.1 Sol (修正前) 2/0.2/10 0.8x (修正前) - -
GPT-6 Sol 2/0.2/10 1.0x 2/0.2/10 1/5
GPT-5.6 Sol 5/0.5/30 0.5x 2.5/0.25/15 1/4
GPT-5.6 Terra 2/0.2/12 1.0x 2/0.2/12 1/5
GPT-5.6 Luna 0.2/0.02/1.2 1.0x 0.2/0.02/1.2 1/50
GPT-6 Luna 0.1/0.01/0.5 1.0x 0.1/0.01/0.5 1/100
GPT-5.5 5/0.5/30 0.5x 2.5/0.25/15 1/4

这里解释一下额度修正的情况:
就是在使用5H额度为10刀的假设去拟合的时候,发现 GPT-6.1 Sol 的0.8x是很准的,但是其他模型的1.0x和0.5x,总是小一点点。如果把5H额度修正成10.3刀,1.0x和0.5x就很准了,但是0.8x要响应放大到0.825x,不过这也正好导致相对Astra的价格比例从1/5变成了1/6。

这样修正后的额度为:
5H额度 10.3 刀,周额度 66 刀。
之后使用 基准价格x倍率 得到的 额度价格 来扣减额度。

脏数据问题

另外在分析的时候,还发现了一个脏数据会干扰结论的情况。这个脏数据可能是多个对话并行运行产生的,让AI才开始得出了不同账号额度不同的结论,有的账号算出来70几、80几刀的周额度。但是很快AI自动修正了。

并且AI发现,几个账号的额度都是差不多的,没有明显的差别,误差大概在5H上下0.5刀,1周上下2-3刀的范围。以及包括只有周限额账号,周额度也没有缩水。

同时这里要说明一下结论可信度:

  • 较新较常用的几个模型 6 Astra,6.1 Sol,6 Sol,5.6 Sol 的结论可信度是比较高的。其他模型包括Terra、Luna和5.5,由于样本量较少,结论不能特别保证。
  • 这些结论都是基于我的 Business Standard 套餐得出来的,不能保证Plus、Pro等套餐都是一样的机制。
  • 主要拟合比较好的时间就是最近几个月,最新截止到10月5日。特别是今天Tibo提高了 6 Astra 和 6.1Sol 的吐字速度后,不能保证额度扣减不会发生变化。

六、对OpenAI定价机制思路的推测

从这些结论里,我们可以推测一下OpenAI定价的思路。特别是最近6.1Sol发布的时候,Tibo表示他们更注重的是用户能用额度干多少事情,而不是额度按API价格能换算成多少美元。因此这个定价思路大概是这样的:

首先,对于当前的主力模型,比如 5.4、5.5、5.6 Sol 担当主力的时期,在订阅额度使用上是会给予优惠的,大约相当于是半价。同时,对于更强大的、昂贵、紧缺的模型(Astra),和更便宜的小模型,对于订阅额度则是没有这个优惠的。这是为了一方面让大家的额度能完成一定的工作量,以确保订阅套餐的竞争力;同时避免高阶昂贵模型算力紧缺;也同时避免用户为了省额度只去用便宜小模型,而无法体验到主力模型性能上的真正竞争力。

然后,到了 6 Sol、6.1 Sol,官方API的价格已经降价了,因此订阅不再提供这个折扣,这样实际上订阅给的token量还是差不多的,而且可以逐渐拉低订阅和官方API的价格差。

这里要叠个甲:OpenAI 的定价思路,是内部商业策略,不公开也不可能公开的。因此,这一章节的结论,纯属瞎推测,仅供娱乐。

七、对于中转站的影响

实际上 6.1 Sol、6 Sol 的新额度策略,对比 5.6 Sol、5.5,对于用户来说,订阅内的token量还是差不多的,甚至 6.1 Sol 在这次测试里已经到了 5.6 Sol 的1.5倍(只是目前速度还是很慢)。因此对于正常走官方订阅的用户,如果是 Plus、Pro 100刀,是并没有怎么受影响的。对于Pro 200刀来说,主要的影响是20x变回了10x。

但是对于中转站来说,之前的定价一直是基于主力模型 Pro 20x 全部使用 5.6 Sol、5.5模型,周限额 2400刀(相当于 Plus 120刀)来定价的,因此即使是Pro号池,不走歪路子的,也可以在人民币充值美元1:1的前提下,做到0.2-0.3的倍率。

这样到了现在的6系时代,再叠加上 Pro 200刀 的20x降级回10x,如果还使用官方API的价格定价,那就相当于倍率要翻4倍,变成1.2x。这对于用户来说,似乎是一个明显的4倍价格上涨。但是实际上真正的影响,主要还是Pro 200刀的额度半价没了,导致实际成本仅仅是翻倍,因为订阅套餐的主力模型token量还是没怎么变化的。

八、结语

我这次测试与对于Codex订阅额度消耗机制的推测研究,局限性还是很大的,主要是我的账号全是 Business Slandered 订阅,然后数据样本也没有很大。所以这次我把完整思路和结论发布出来,大家可以都用自己的数据去验证。

记住一个核心思路是:不是拿到一整个5H、一整周的记录再去算额度,而是把一段比较连续的任务作为一个样本点,然后对每个API请求的token用量记录与额度扣减来进行拟合。

这样最后的话,希望大家可以把自己账号的情况也都测一下,然后一起交流反馈一下。重点验证下等效一份Plus的订阅额度,是不是相当于5H额度10.3刀,周额度66刀;以及 6.1 Sol 是不是相当于0.825x,早期主力模型0.5x,其他模型1x。希望佬友们一起,能够把OpenAI背后的额度机制,研究得更加透明。

九、附录

最后再附一下我使用的Codex使用记录导出的 PowerShell 脚本,脚本由AI编写。

$from = [datetimeoffset]"2022-10-01T00:00:00+08:00" # 导出时段开始时间,可自定义修改选择精确时段
$to   = [datetimeoffset]"2028-10-31T00:00:00+08:00" # 导出时段结束时间,可自定义修改选择精确时段

$sessionRoot = "$env:USERPROFILE\.codex\sessions\2026" # Codex数据目录的位置,一般是当前用户的Home目录里
$outFile = "$env:USERPROFILE\Desktop\codex_quota_20221001_20281031.txt" # 导出文件位置和文件名,注意文件名如果重名会自动覆盖

$rows = [System.Collections.Generic.List[object]]::new()

$files = Get-ChildItem $sessionRoot -Recurse -Filter "rollout-*.jsonl" -File

foreach ($file in $files) {
    $model = $null
    $effort = $null
    $turnId = $null

    foreach ($line in [System.IO.File]::ReadLines($file.FullName)) {
        try { $o = $line | ConvertFrom-Json -ErrorAction Stop }
        catch { continue }

        # 当前 turn 的模型和 reasoning effort
        if ($o.type -eq "turn_context") {
            if ($null -ne $o.payload.model) {
                $model = [string]$o.payload.model
            }
            if ($null -ne $o.payload.effort) {
                $effort = [string]$o.payload.effort
            }
            elseif ($null -ne $o.payload.reasoning_effort) {
                $effort = [string]$o.payload.reasoning_effort
            }
            if ($null -ne $o.payload.turn_id) {
                $turnId = [string]$o.payload.turn_id
            }
            continue
        }

        if (
            $o.type -ne "event_msg" -or
            $o.payload.type -ne "token_count" -or
            $null -eq $o.payload.info
        ) { continue }

        try { $t = [datetimeoffset]$o.timestamp }
        catch { continue }
        if ($t -lt $from -or $t -ge $to) { continue }
        
        $last  = $o.payload.info.last_token_usage
        $total = $o.payload.info.total_token_usage
        $rl    = $o.payload.rate_limits
        if ($null -eq $last) { continue }
        
        $five = $null
        $week = $null

        if ($null -ne $rl) {
            foreach ($w in @($rl.primary, $rl.secondary)) {
                if ($null -eq $w) { continue }
                if ($w.window_minutes -eq 300) { $five = $w }
                elseif ($w.window_minutes -eq 10080) { $week = $w }
            }
        }

        $fiveResetLocal = $null
        $weekResetLocal = $null
        if ($null -ne $five -and $null -ne $five.resets_at) {
            $fiveResetLocal =
                [datetimeoffset]::FromUnixTimeSeconds(
                    [long]$five.resets_at
                ).ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")
        }
        if ($null -ne $week -and $null -ne $week.resets_at) {
            $weekResetLocal =
                [datetimeoffset]::FromUnixTimeSeconds(
                    [long]$week.resets_at
                ).ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")
        }

        $rows.Add([pscustomobject]@{
            TimeLocal = $t.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fff")
            TimeUTC   = $t.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss.fff")
            
            Model     = $model
            Effort    = $effort
            TurnId    = $turnId
            
            Input          = [long]$last.input_tokens
            Cached         = [long]$last.cached_input_tokens
            Uncached       = [long]$last.input_tokens - [long]$last.cached_input_tokens
            CacheWrite     = [long]$last.cache_write_input_tokens
            Output         = [long]$last.output_tokens
            Reasoning      = [long]$last.reasoning_output_tokens
            Total          = [long]$last.total_tokens

            CumInput       = [long]$total.input_tokens
            CumCached      = [long]$total.cached_input_tokens
            CumOutput      = [long]$total.output_tokens
            CumReasoning   = [long]$total.reasoning_output_tokens
            CumTotal       = [long]$total.total_tokens

            ContextWindow  = $o.payload.info.model_context_window

            FiveUsed       = if ($null -ne $five) { $five.used_percent } else { $null }
            WeeklyUsed     = if ($null -ne $week) { $week.used_percent } else { $null }

            FiveResetEpoch = if ($null -ne $five) { $five.resets_at } else { $null }
            WeekResetEpoch = if ($null -ne $week) { $week.resets_at } else { $null }

            FiveResetLocal = $fiveResetLocal
            WeekResetLocal = $weekResetLocal

            LimitId        = if ($null -ne $rl) { $rl.limit_id } else { $null }

            Ordinal        = $o.ordinal
            Rollout        = $file.FullName
        })
    }
}

$rows |
    Sort-Object TimeUTC, Rollout, Ordinal |
    ConvertTo-Csv -Delimiter "`t" -NoTypeInformation |
    Set-Content $outFile -Encoding UTF8

Write-Host ""
Write-Host "Saved:"
Write-Host $outFile
Write-Host ""
Write-Host "Rows:" $rows.Count

Write-Host ""
Write-Host "By model:"
$rows |
    Group-Object Model |
    Sort-Object Count -Descending |
    Select-Object Count, Name |
    Format-Table -AutoSize

Write-Host ""
Write-Host "By model + effort:"
$rows |
    Group-Object Model, Effort |
    Sort-Object Count -Descending |
    Select-Object Count, Name |
    Format-Table -AutoSize

免责声明:脚本请谨慎使用,不对执行脚本产生的任何问题负责。

  • 牛逼啊,看了一下我的几个号,也基本支持楼主的结论,感觉很正确。

  • 牛🐮,我用plus,纯lunacpa推测大概45刀左右 xhj003

  • nb,我两个team号,5H限和楼主差不多,都是10$左右,预测出的周限也和楼主说的差不多,大概是60$-70$。

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有72518位seeker

🎉欢迎新用户🎉