
LLM 并发调优里最让人难受的指标往往是 TTFTTime to First Token模型不是不回而是第一个 token 总是姗姗来迟。原文把一次推理拆成 Prefill、Generation 和 KV-Cache 三块并给出 TTFT 的主要影响因素——网络速度、输入序列长度、模型大小、排队时间。真到线上排查时很少人会对着这四个词逐项做实验我习惯先打开 TaoToken 拿一把 API Key把 Codex 的 Base URL 填成 https://taotoken.net/api再让 Codex 照着“TTFT 为什么高”做一轮交互式诊断。同样一个高 TTFT入口不同结论可能完全不同。如果你把一段 3000 token 的日志贴进对话模型必须先对全部输入做一次 Prefill计算 attention 并生成 KV Cache这时候 TTFT 高多半是输入太长、计算太重。但如果你只发一个“你好”TTFT 仍然超过 3 秒那大概率要怀疑网络抖动或服务端排队。这两种场景的调法完全相反所以排障第一步不是急着改参数而是把现场拆清楚。Codex 在这里的价值就是帮你把公式拆成可执行的检查项但它自己要先能稳定连上一个模型这就是为什么我要先走一遍 TaoToken。下面先回到 LLM 推理现场把 TTFT 偏高的几种原因分开再给出 Codex 的接入方式、排查流程、KV-Cache 的判断方法最后是控制台对账和常见报错。整个过程不要求你打开任何复杂的监控面板只要有一把 Key、一个能跑 Codex 的终端以及愿意把耗时数据贴回对话的耐心。1. TTFT 偏高先分现场是 Prefill 重还是排队久1.1 用「首字慢」和「全程慢」区分 TTFT 与 TPOT原文把响应速度拆成 TTFT 和 TPOT 两个独立维度TTFT 是请求发出到收到第一个 token 的等待时间TPOT 是后续每个输出 token 的平均耗时。两者对用户感知的影响不一样。TTFT 决定“转圈多久才开口”TPOT 决定“开口之后吐字是否流畅”。如果用户反馈是“等了很久才开始出字一旦开始就正常”问题在 TTFT如果反馈是“第一个字很快后面越说越慢”那更应该查 TPOT、输出长度和生成阶段的吞吐。指标度量区间用户感知TTFT请求发出到首个 token 返回转圈多久才开口TPOT单个输出 token 的平均耗时开口后吐字是否流畅总体延迟请求发出到生成结束端到端等待时间总体延迟在原文里约等于 TTFT 加上 TPOT 乘以要生成的 token 数量。这个公式还有一层含义输出长度固定时TTFT 越低用户体验越好TTFT 高但 TPOT 正常说明瓶颈不在生成阶段而在“准备生成”的阶段。1.2 Prefill一次读完整张输入才落笔原文对 Prefill 的定义很简单处理输入 prompt 的所有 token并行计算 attention生成并缓存 KV Cache。它通常耗时较长但只会执行一次。你可以把它想象成“读完整张试卷才开始写答案”试卷越长读卷时间越长题目再简单你也得先看完题目。放到线上场景里如果你把长文档、聊天历史、系统提示词全部塞进 messagesPrefill 阶段的计算量会随输入 token 数量线性甚至超线性上涨。TTFT 偏高时第一个要问的问题就是这次请求的输入到底有多长。很多时候不是模型变慢了而是你每轮都在让它重读一遍全文。1.3 排队时间往往伪装成网络抖动TTFT 公式里还有一个容易被忽略的变量排队时间。当 GPU 显存不足或服务端并发过高时请求不会报错而是在服务端等待处理。等待时间会计入 TTFT但用户感知到的只是“首字慢”。排队问题和网络问题的表现不太一样。网络慢通常会让所有请求的首字节普遍变慢排队则更容易出现尖刺——前几次请求都很快某一次突然飙到几秒下一轮又恢复。判断方法也很直接固定一段短 prompt连续发 10 次请求记下每次的 TTFT。如果整体抬高查网络链路如果偶发尖刺且抖动明显查服务端并发。2. 排障前先把 Codex 的 Base URL 指到 TaoToken2.1 从官网拿 Key 并到模型广场确认模型 ID排障环境需要三样东西一把能用的 API Key、一个 Base URL、一个正确的模型 ID。Key 在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_content 注册后创建创建完成后回到官网的模型广场查看你实际能用的模型 ID不要凭记忆填。官网页面是给人注册和管理的Codex 里要填的 Base URL 是另一回事两者不能混用。注意这个区别打开 TaoToken 官网、创建 Key、看用量用 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_content 这个地址填进 Codex 配置文件的 Base URL 则是 https://taotoken.net/api末尾不要补 /v1。多写一个 /v1反而可能让请求路径对不上。2.2 ~/.codex/config.toml 新增 TaoToken providerCodex 从配置文件读取模型供应商信息常见路径是~/.codex/config.toml。可以新增一个 provider 指向 TaoToken然后让 model_provider 指向它# ~/.codex/config.toml model YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key OPENAI_API_KEY wire_api chat这里的YOUR_MODEL_ID要替换成模型广场里实际列出的 IDAPI Key 不直接写进文件而是放在名为OPENAI_API_KEY的环境变量里由 Codex 启动时读取。不同版本的 Codex 字段名可能略有差异但你只需要关心两个核心值base_url和env_key。前者决定请求打到哪后者决定用哪把 Key 鉴权。2.3 短请求验证 Codex 已走通启动前先导出 Key再进入 Codex 会话export OPENAI_API_KEYYOUR_API_KEY codex进入会话后不要急着问 TTFT 问题先让它说一句简单的确认比如“请回复‘已就绪’”。如果它正常回答说明 Codex 已经通过 TaoToken 通道完成了一次完整的 Prefill 和 Generation。这一步同时验证了网络、Key、Base URL、模型 ID 四项后面再分析 TTFT 时就不会把这几个因素混进结论。3. 让 Codex 按 TTFT 公式开一轮交互诊断3.1 直接把公式作为上下文交给 CodexCodex 擅长把模糊的问题拆成检查项。你可以把原文的 TTFT 公式直接作为上下文贴给它我的场景TTFT 偏高TPOT 正常。公式是 TTFT 网络传输 排队时间 Prefill 计算时间主要变量包括输入长度、模型大小、服务端排队和 KV-Cache 命中情况。请帮我列一份诊断清单每一项说明测量方式、判定阈值、可能的结论。Codex 会返回一份可执行的检查清单。你只需要一项一项往下走把测量结果再贴回给它。这里有一点要强调Codex 负责生成脚本和分析数据不负责在你的生产环境执行任何操作所以凡是能跑的脚本都在你本地运行再把输出贴回对话。3.2 网络项首字节时间要单独记很多排查把“TTFT 高”直接等同于“网络慢”这是不对的。网络开销主要影响的是从请求发出到收到第一个字节的时间而 Prefill 和排队发生在首字节之后。想分开看需要先安装 httpx再跑一个流式计时脚本import httpx import time url https://taotoken.net/api/chat/completions # 以文档实际路由为准 headers {Authorization: Bearer YOUR_API_KEY} payload { model: YOUR_MODEL_ID, stream: True, messages: [{role: user, content: 用一句话解释 Prefill}], } start time.perf_counter() first_byte_time None first_token_time None with httpx.stream(POST, url, headersheaders, jsonpayload, timeout60) as r: for line in r.iter_lines(): if first_byte_time is None: first_byte_time time.perf_counter() - start if line.startswith(data: ) and line ! data: [DONE]: first_token_time time.perf_counter() - start break print(ffirst byte: {first_byte_time:.3f}s) print(ffirst token: {first_token_time:.3f}s)脚本在你自己机器上执行。如果first_byte本身就很高说明链路或服务端建立连接慢如果first_byte正常但first_token明显更大问题才真正进入 Prefill 和排队环节。这个区分能帮你避免拿着网络优化的方案去解计算问题。3.3 输入长度项同一模型做增量 prompt 对照判断 Prefill 是否是 TTFT 的主要来源最直接的办法是控制变量。用同一个模型 ID、同一把 Key分别发 100、1000、3000 token 的 prompt记录三次 TTFT。你可以让 Codex 生成这个脚本也可以基于上一节的片段改造成循环。如果三次 TTFT 差距很小说明当前问题不是输入长度如果 TTFT 随输入长度明显上升尤其到了 3000 token 以后成倍增加那基本可以落定在 Prefill 计算量上。注意每次请求之间隔开一点时间避免多个测试请求在服务端相互排队测出虚假的延迟。3.4 排队项连续短请求看 TTFT 噪音输入长度实验没问题但 TTFT 还是忽高忽低时就要查排队。用固定的一段短 prompt 连续发 10 次请求记录每次 TTFT。前几次都在几百毫秒某一次突然飙到几秒之后又降回去这就是服务端并发压力的典型信号。网络问题通常让所有请求普遍变慢而排队问题往往表现为偶发尖刺。这一步对走 TaoToken 的单用户调试场景来说出现概率不高但如果你在写并发压测脚本或者同一把 Key 被多个项目同时使用就值得认真测一遍。把连续请求的时间戳数据贴给 Codex让它帮你判断是“整体链路慢”还是“偶发排队”。3.5 模型大小项用轻量模型 ID 做交叉验证原文明确指出模型参数量越大Prefill 计算时间越长。想验证当前模型是不是太重可以在模型广场找一个更轻量的模型 ID用同一段输入、同一套脚本重跑增量对照。如果轻量模型的 TTFT 明显下降说明当前模型的计算开销是瓶颈之一。如果换了更轻量的模型TTFT 依然没有改善那结论就要回到网络或排队上。注意这里不要凭印象选模型以模型广场当时列出的 ID 和实际响应为准。这把 Key 可以复用到不同模型上本身就是切换模型做交叉验证的便利之处。4. KV-Cache 命中与否决定第二次提问快不快4.1 回到原文看缓存的定位原文里那段 KV-Cache 代码核心思想是空间换时间推理阶段只需要计算当前查询向量与已有 Key、Value 的关系之前已经算好的 k 和 v 没必要每次重算。无缓存时每一轮输入都要拼接历史再重新编码有缓存时输入只是最新 token历史计算结果直接复用。放到 API 场景里KV-Cache 是否命中会直接影响 Prefill 的负载。一个请求带 4000 token 历史如果服务端能命中缓存Prefill 只需要处理新增的少量 token如果缓存没有命中服务端就要把这 4000 token 全部重算一遍TTFT 自然上升。这就是为什么长对话场景下第二次、第三次提问往往比第一次快也可能第一次就非常慢。4.2 让 Codex 判断你属于「长上下文重算」还是「短请求排队」Codex 可以帮你估算请求的真实输入规模并判断你属于哪一种。比如把下列内容贴给它我的 messages 里带了最近 10 轮对话约 4000 token。现在每次 TTFT 都要 4 秒。请帮我估算这轮请求的 Prefill 输入量并说明在服务端没有缓存命中时重建 KV-Cache 的代价。它会给出 token 量级和优化方向。注意这只是分析过程Codex 不会去读你的聊天记录文件你只需要把消息结构复制给它。它可以告诉你这种情况下 TTFT 高是正常的因为每一轮都在重新处理大量历史。4.3 长对话场景的实测改法如果确认是长上下文重算收敛方式通常是压缩历史把前面几轮对话摘要成一段摘要消息只保留最近两轮完整消息或者精简系统提示词。改完后再跑一次增量 prompt 对照TTFT 应当随输入长度下降。如果下降明显说明问题本质是请求负载而不是通道或模型 ID 配置错误。反过来说如果压缩历史之后 TTFT 没有任何变化那就可以放弃“长上下文重算”这个假设把精力转回网络和排队。KV-Cache 只是一个解释维度不能替代实测数据。5. 跑完测量后回控制台对账与排障5.1 每次 Codex 调用都在控制台留痕Codex 分析过程中会发起很多次模型调用这些调用都应该能在 TaoToken 控制台里找到。打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_content 后到 API Keys 或用量页面核对刚才 Codex 里那几次短请求是否已经记上账。如果记录存在说明 Key 和通道没问题时间全部花在模型响应上如果连记录都没有说明请求根本没有到达模型服务要回到配置层检查。这一步相当于给整个排障过程做一个对账你在本地看到的时间戳和平台记录的实际调用量是否能对上。对不上就说明某个环节的请求被拦截或走错了地址。5.2 401 / 404 / 填成官网链接的三个排查点配置过程中最容易遇到的是三类错误。第一401 Unauthorized环境变量里的 Key 还是占位符或者复制时多了一个空格。第二404 Not FoundBase URL 末尾被加了 /v1或者模型 ID 不在模型广场列表里。第三Codex 一直转圈但不出字很可能是把官网地址当成 API 地址填了。官网页面是给人管理和创建 Key 用的Codex 里要填的是 https://taotoken.net/api顶多用文档给的实际路由不要把带 UTM 的页面链接塞进配置文件。这三类问题有一个共同特征它们都不会返回一段清晰的错误说明而是表现为“TTFT 特别高”或“请求直接失败”。所以排障时看到 401、404先回控制台看是否有这次调用记录再查配置值比盲目调参数更有效。5.3 把采集到的数据贴回 Codex 继续收敛完成网络、输入长度、排队、模型大小四组实验后你应该有了一张包含首字节时间、首 token 时间、不同输入长度 TTFT、连续请求抖动的时间表。把这些数据按顺序贴给 Codex它会结合 TTFT 公式给出下一轮判断是缩短输入、压缩历史还是换轻量模型、降低并发。整个过程里Codex 始终扮演分析助手所有实际请求都由你的本地脚本发出。这一轮排障走完TTFT 高的原因大概率已经被缩小到输入长度、排队或网络三选一。如果接下来要复现某些请求特征可以用 模型对话 手动发一条同样长度的消息直观感受首字返回时间。长期用 Codex 写代码的话建议在 Coding Plan 页面确认套餐是否覆盖这些排障流量需要新增或轮换 Key直接到 控制台 API Keys 创建。用量和调用记录都在同一个控制台排查时顺手扫一眼比对着日志猜要快得多。