Skip to content

Agent 模型前并发时延优化与评测

状态:implemented 类型:simplification Owner:backend/package/yuxi/services/chat_service.py

问题

后端开发与性能评审需要解释不同用户并发时,从 API 接入到首次模型请求的增长位置,并降低不必要的等待。首 token 混入外部响应时间;单个 async 函数的墙钟耗时又混合当前工作、I/O 与其他协程的恢复调度,不能直接证明 CPU 或锁的归属。

本文统一保存准备、派发、checkpoint、追踪收尾、取消和计时的有效取舍,以及 2026-09-07 正式矩阵和必要历史对照。此前八份决策与独立报告的职责在此闭合,不保留重复维护入口。目标是缩短模型前时延并解释 P50/P95 的扩展曲线;不改变模型输出、权限、FIFO、lease、检查点持久化和 Request/Run 绑定,也不把结构整理冒充新的性能实测。

决策

源码拥有运行时事实,本文拥有取舍和聚合证据。优化按读取、派发、执行、收尾的真实边界收敛,不引入跨用户完整图缓存或新的后台调度框架。

性能工具按后端测试职责集中到 backend/test/performance/,单测位于 backend/test/unit/performance/。统一用户入口为 python -m backend.test.performance,提供矩阵采样、容量压测和离线报告子命令;阶段分析与报告一次生成。实验 Compose 和探针随工具归档,仅实验装配加载。独立脚本加转发壳会保留重复入口,因此直接迁移并更新消费者。验收要求是命令、容器装配和单测可用,已有样本复算一致;不改变采样协议,不新增付费采样。

计时边界与持久化

业务升级以 v0.7.2 tag 为唯一已发布来源,时间字段随完整业务 DDL 一次补齐;不维护未发布中间 revision 的逐级迁移函数。当前编号不重用,完整迁移成功后才记录版本,未知开发快照由发布入口拒绝;来源、验证和部署处置由Schema 迁移 Owner统一定义。

正式实验的主指标为 ASGI 接到 POST、鉴权与正文解析之前,到 Worker 首次进入 HTTPX.send 的耗时。测试探针只在实验 Compose 入口装配;终点早于实际 socket 写出,不代表供应商已经收到。它与 shipping 的持久化指标分开解释。

首次模型请求记录器在 LangChain on_chat_model_start 捕获 Run 的首次时间。自定义 Agent callback 统一归属 agents/callbacks,不独立建 service 或保留转发模块;聊天与 Worker 直接装配回调,持久化约束仍由 repository 拥有。有效 lease owner 在终态前 write-once 保存 first_model_request_at;已发起调用的 cancel_requested Run 也可由当前 owner 补写。写入失败保持 best-effort,不改变业务结果;崩溃导致缺失时明确计数。结果 API 的 first_model_request_latency_ms 从 Run 创建计起,与主表的 API 接入→HTTP 发送口径不同,回调也不等于 HTTP 发送入口。

Callback 归位不改变实现、触发点或持久化规则,对应单测位于 backend/test/unit/agents/callbacks/。Alpha uv run --no-sync --group test pytest test/unit -m 'not slow' -q --disable-warnings 为 1862 passed、52 skipped;真实 PG 用例 test/integration/services/test_agent_run_lease.py::test_first_model_request_timing_survives_owner_cancellation--confcutdir=test/integration/services 下通过,保留用例自身数据库准备、owner 反例、最终回读与清理。完整 integration 初始化在通用 ensure_live_api_schema 的事件循环关闭阶段等待,未算通过;聚焦执行仅排除与该 PG 用例无关的上级自动 fixture。旧 callback service 路径无残留,Ruff、文档构建及独立 Review 通过;不重新执行付费模型评测。

资源准备与队列派发

执行边界与 Owner保留的最小优化不变的约束
Skill 投影锁内比较路径、类型、执行位与内容,未变则跳过 staging 复制及删除fd-relative、no-follow、跨进程锁及锁内授权重读不变;非法来源清理旧投影并失败,非法目标重建
SubAgent 工具进程内只复用四个不可变输入 Schema工具实例、动态描述、闭包与父 Run 上下文逐次创建
聊天服务复用同 Run manifest 的规范化配置,直接调用拥有查询的 repository线程归属、Agent 可见性和 executor 权限不缓存、不跳过
短期事件发布XADD 与 EXPIRE 使用一次非事务 pipeline发布顺序、事件 ID 与 TTL 保留;PostgreSQL 终态不依赖 Redis 成功
PostgreSQL checkpoint factory每次构图创建独立 saver,连接池继续按进程共享单图内 saver 锁、SQL、持久化频率及迁移 advisory lock 不变
WorkerARQ 适配50 ms 轮询;仅过滤本进程尚未完成的 Task其余候选保持原序;Redis 队列、竞争、容量预留、重试、清理和 PG lease 继续由原 Owner 执行

开发、生产和诊断入口复用同一 Worker 工厂,健康检查仍读取 WorkerSettings。ARQ 升级须运行真实 Redis 回归,依赖声明和锁文件变更已纳入 system-tests 触发路径。优化不将 Redis 提升为业务终态 Owner,也不改变 PostgreSQL 提交后才能投递的顺序。

最终状态、追踪与取消收尾

BaseAgent 在真实图执行流耗尽、checkpoint 写入完成后读取一次最终状态。聊天与恢复的中断检测、状态输出和消息持久化共享该状态,不为三个收尾消费者重复构图;缺失最终 checkpoint 显式失败。同一 Run 内复用状态,不跨 Run 缓存;其他独立生命周期的 checkpoint 读取不在删除范围。

正常收尾直接使用最终状态;只有异常路径才从已经累积的根消息正文构建部分 AIMessage。子 Agent 输出不混入根消息,错误时仍保留部分正文、trace 和精确 Run 绑定。纯转发函数和重复配置解析入口不保留。

聊天与恢复的 finally 使用 await asyncio.to_thread(flush_langfuse),正常退出仍等待同步 flush 返回,不承诺远端 trace 已可查询。线程卸载允许 heartbeat 前进;续租返回 False 时,只有当前最后 attempt 的 worker token、outcome、非空 finished_at 与 Run 终态精确相符,才能继续已提交终态后的收尾。过期、其他 owner、缺失事实、读取异常和 lease_expired 继续 fail-closed。

每 Run 只创建一个取消等待器。Worker、聊天/恢复生成器、BaseAgent 到真实图执行使用完整异步关闭链;外层取消或消费侧错误退出后,执行 Task 与生成器关闭才允许交还 owner。清理屏蔽重复取消,关闭完成后再传播取消,避免旧执行留在后台发布结果。标准异步上下文管理已能表达这一顺序,不增设后台清理服务。

评测协议与代码维护

矩阵脚本使用不同普通用户、每通道固定 Thread;等待同 Run 的 SSE 结束并回读结果后立即补位,通道之间没有轮次屏障。默认预算为 Worker 1/4/8 × 并发 1/10/20/50/100,每通道分别 100/10/5/5/5 轮。输入 say hi,不额外预热、不限制输出、不添加沙盒监测、不自动补发失败请求。

提交响应丢失时通过精确 Request 找到已派发 Run 再取消;不能确认终态则保留对应身份/Thread并停止后续组。先保存已完成样本,再补充观测并再次落盘;这一失败保护由 record_group 独占,不保留调用方的第三次重复写盘。主指标和缺失数量复用同一次 14 维汇总。

阶段分析器一次提取顺序边界,同时生成均值与 P50/P95,保留现有 JSON 字段供离线结果读取;父子跨度和后台写入不重复相加。重复 Worker/并发组在刷新容器日志或覆盖结果前拒绝。报告生成器使用同一阶段汇总,保留样本数和不完整状态,不将额外失败或缺失值隐藏为成功。

探针默认记录轻量语义阶段与进程采样,细粒度诊断通过 MATRIX_FINE_TIMING=1 单独启用。细探针按协程恢复切片计算 CPU,并区分文件线程排队、工作和恢复等待;未知 CPU 不记为零。正式表是 3150 原始样本的聚合,本次代码整理只做离线等价验证,不刷新付费性能数字。

替代方案

替代方案取舍
保留原实现,仅增加 Worker 或连接池多 Worker 可利用多个核,但不能消除无用复制、重复 Schema 或位于池之前的 saver 锁;增加连接也有数据库容量代价
跨用户缓存权限、文件内容或完整 Graph需要额外的失效和授权及时性证明,带 Run 上下文的图及闭包不能安全共享
并发重写领取协议、Redis Lua 或修改第三方安装目录改变容量与竞争生命周期;薄适配已满足减少本地重复检查的需求
减少 checkpoint 写入或推迟到 Run 结束牺牲中途崩溃恢复粒度,不作为本轮性能修复
移除逐 Run flush、专用线程池或独立上报进程改变正常退出追踪刷新边界或增加新的生命周期;当前保留等待语义并使用标准线程卸载
对所有终态忽略续租失败,或释放 owner 后后台清理无法区分已提交终态与丢失执行权,会允许旧执行继续产生副作用
各轮等待所有通道、每轮新建 Thread、额外付费预热改变用户指定闭环多轮协议,分别引入全局屏障、排除历史或隐藏冷启动
九份文档交叉引用或机械拼接继续重复维护事实和失效协议;单一记录保存有效取舍、正式表与必要历史证据,原文仅本地备份

后果

每请求工作量减少仍不等于所有并发档位的 P50/P95 都降低。单事件循环继续执行 Python 消息、状态和图调度;更多 saver 允许后台写入交错,也会竞争连接与循环。多 Worker 提高多核利用率,同时增加进程内存和数据库/Redis 连接;50 ms 轮询将空闲轮询上限从约 2 次/秒提高到约 20 次/秒。

追踪 flush 占用默认执行器,慢上报仍可能与文件工作竞争线程池;取消不能强制终止已经运行的 Python 线程。执行关闭完成优先于尽快返回取消,保留 lease 和持久结果的安全边界。

正式矩阵在同一宿主机按顺序执行,包含各进程冷启动。同 Thread 的历史长度随轮数增加,外部模型速度影响完成与补位节奏;主指标不包含本请求首 token 仍不等于完全隔离外部影响。不同探针、样本量和 Worker 设置不能冒充随机对照的代码因果收益。不承诺多机、生产流量或硬件扩容后的容量。

验证

2026-09-07 正式矩阵共 3150 请求,全部 completed、单 attempt、同 Request/用户/Run 输出绑定正确;14 个时延维度均无缺失。测试身份和会话清理后再次回读 PostgreSQL,持久结果仍匹配。主表及四张副表与原始样本离线生成结果一致;数值不因文档合并或代码整理而更新。时间单位为 ms,分位数使用 nearest-rank。

旧能力不存在:八份旧决策和独立报告由本文完整接管有效职责;重复阶段统计函数、第三次结果写盘、正常收尾备用消息组装、单层查询转发、三处收尾重复构图、逐消息取消等待器及默认付费预热不再保留。细探针仍有小规模诊断 consumer,错误正文仍有持久化 consumer。

重新引入条件:只有新增独立生命周期或统计消费者、确有不同协议契约且明确 Owner 时重新讨论,不能以包装层恢复重复事实。

正式实验协议与环境

不同普通用户对应不同并发通道;同组每通道固定一个 Thread,上一轮完整结束并验证同 Run 结果后立即补位,通道之间没有轮次屏障。提示词固定为 say hi,沿用 Alpha 默认模型,不覆盖模型、不限制输出长度、不添加沙盒监测,也不额外预热或自动补发失败请求。

并发通道每通道轮数每个 Worker 档位的请求数
1100100
1010100
205100
505250
1005500

Worker 档位为 1、4、8,各执行 1050 个请求。独立核验了固定用户/Thread、通道内无重叠以及每通道轮数;上一轮客户端验证完成至下一轮开始的最大间隔为 0.116 ms。100 个临时用户跨组复用,但每组新建 Thread;测试后均通过 API 软删除,数据库确认可用测试用户为零,100 条软删除记录及审计数据仍保留。

环境为单台 Intel Core i7-14700KF(20 核、28 逻辑处理器)、约 64 GB 内存、单 API 进程,宿主机同时运行其他环境。Worker 未设置容器 CPU 或内存上限;每进程 ARQ 槽位 140、业务 PostgreSQL pool 120(overflow 40)、checkpoint pool 120、Redis 最大连接 256,PostgreSQL max_connections=600。Langfuse 保持启用。正式矩阵使用轻量语义探针及进程采样,细粒度协程切片探针不启用。

每个 Worker 档位开始时重建 Worker,然后依次执行各并发档位;不清理宿主机文件缓存。包含每个 Worker 首次处理请求的冷启动,非随机化多次重复实验。进程数是独立变量,不能把本表不同 Worker 间的收益归因于某一处代码修改。

主表:API 接入到首次模型 HTTP 提交

起点为 ASGI 收到 POST、鉴权及正文解析之前;终点为 HTTPX.send 入口,尚未等待供应商首 token。终点不代表网卡发包时间。样本包含各 Worker 冷启动;不将 Run 创建当作起点。

Worker并发实际/计划请求数据库验证完成接入→模型提交观测状态
11100/100100155.28 / 213.94 (n=100)完整
110100/100100213.96 / 922.72 (n=100)完整
120100/100100497.5 / 1476.56 (n=100)完整
150250/2502502560.99 / 5154.07 (n=250)完整
1100500/5005006320.93 / 8941.34 (n=500)完整
41100/100100154.23 / 276.3 (n=100)完整
410100/100100119.56 / 394.87 (n=100)完整
420100/100100139.0 / 889.37 (n=100)完整
450250/250250351.99 / 1968.85 (n=250)完整
4100500/500500389.97 / 2498.5 (n=500)完整
81100/100100162.3 / 1083.85 (n=100)完整
810100/100100121.27 / 288.33 (n=100)完整
820100/100100142.24 / 432.11 (n=100)完整
850250/250250215.55 / 1259.64 (n=250)完整
8100500/500500237.52 / 1682.79 (n=500)完整

副表:接入和 Worker 准备

各阶段独立计算分位数,不能把各列 P95 相加。领取列从 Run 创建计到数据库 started 时点。

Worker并发接入→Run 创建创建→startedstarted→preparedprepared→模型提交
119.0 / 16.19 (n=100)36.19 / 67.67 (n=100)47.8 / 97.16 (n=100)57.23 / 90.07 (n=100)
1109.75 / 188.6 (n=100)40.53 / 132.82 (n=100)96.62 / 496.6 (n=100)58.0 / 278.7 (n=100)
12010.79 / 163.39 (n=100)64.67 / 151.62 (n=100)272.2 / 745.38 (n=100)138.63 / 627.41 (n=100)
15011.92 / 640.21 (n=250)109.74 / 577.34 (n=250)1694.91 / 4551.38 (n=250)535.72 / 1031.11 (n=250)
110017.92 / 415.48 (n=500)423.75 / 2672.01 (n=500)4441.97 / 6469.48 (n=500)830.53 / 1611.25 (n=500)
4110.06 / 13.95 (n=100)21.17 / 65.68 (n=100)48.21 / 129.13 (n=100)62.38 / 103.72 (n=100)
41011.05 / 59.45 (n=100)19.15 / 49.2 (n=100)51.92 / 175.37 (n=100)35.69 / 107.62 (n=100)
42010.09 / 182.29 (n=100)31.13 / 118.44 (n=100)59.94 / 264.79 (n=100)38.65 / 301.03 (n=100)
45012.88 / 455.85 (n=250)72.67 / 272.45 (n=250)124.9 / 1082.62 (n=250)97.21 / 575.54 (n=250)
410013.58 / 309.8 (n=500)78.66 / 782.56 (n=500)137.95 / 1270.24 (n=500)97.83 / 615.89 (n=500)
8110.72 / 18.3 (n=100)18.38 / 56.4 (n=100)55.13 / 1015.29 (n=100)64.56 / 122.56 (n=100)
81010.85 / 66.88 (n=100)14.97 / 30.38 (n=100)54.83 / 135.97 (n=100)38.88 / 107.04 (n=100)
82013.65 / 166.98 (n=100)24.24 / 80.91 (n=100)59.2 / 143.75 (n=100)42.21 / 99.85 (n=100)
85017.45 / 383.15 (n=250)49.25 / 249.46 (n=250)83.64 / 428.99 (n=250)56.9 / 223.96 (n=250)
810015.11 / 395.71 (n=500)60.99 / 594.46 (n=500)84.68 / 389.11 (n=500)52.95 / 329.0 (n=500)

副表:供应商等待和完整对话

模型提交→首输出还包含网络和 Worker 接收处理,不能全部归因于供应商。

Worker并发模型提交→首输出接入→数据库完成客户端完整结果成功请求/秒
111248.77 / 1559.84 (n=100)1555.44 / 1967.58 (n=100)1838.36 / 2063.86 (n=100)0.54
1101359.16 / 1757.21 (n=100)1926.84 / 2908.63 (n=100)2093.76 / 3281.5 (n=100)4.09
1201403.8 / 2263.75 (n=100)2477.96 / 4408.25 (n=100)2636.31 / 4762.14 (n=100)5.95
1501588.21 / 2837.35 (n=250)5833.54 / 10545.67 (n=250)6558.44 / 11338.44 (n=250)6.26
11002739.3 / 5398.9 (n=500)13046.51 / 15141.35 (n=500)14556.71 / 16694.83 (n=500)6.82
411216.52 / 1499.18 (n=100)1561.98 / 2251.13 (n=100)1801.82 / 2849.29 (n=100)0.52
4101338.16 / 1842.05 (n=100)1681.84 / 2938.19 (n=100)1845.63 / 3086.32 (n=100)4.22
4201364.11 / 2198.39 (n=100)1862.24 / 3222.4 (n=100)1958.88 / 3451.2 (n=100)7.47
4501571.35 / 5957.66 (n=250)2678.92 / 6407.75 (n=250)2954.75 / 7040.47 (n=250)11.39
41004278.79 / 8932.22 (n=500)5911.93 / 10386.39 (n=500)6129.09 / 11058.97 (n=500)12.11
811331.5 / 1692.9 (n=100)1721.18 / 2625.17 (n=100)1909.67 / 3070.13 (n=100)0.48
8101323.36 / 1797.62 (n=100)1700.24 / 2480.13 (n=100)1844.59 / 2945.58 (n=100)4.4
8201281.73 / 2224.32 (n=100)1755.19 / 3182.24 (n=100)1896.32 / 3411.01 (n=100)8.43
8501489.1 / 5584.15 (n=250)2137.58 / 6009.27 (n=250)2308.04 / 6080.07 (n=250)11.93
81004603.23 / 8489.14 (n=500)5520.49 / 9335.73 (n=500)5960.99 / 9679.74 (n=500)13.2

副表:Worker CPU 与事件循环

CPU 以单核为 100%,均值按各 Worker 的采样时长加权;峰值是单 Worker 约 1 秒区间值。循环迟到仅记录超过 30 ms 的样本,0 表示未记录超过阈值的迟到。没有采样 API 或数据库 CPU。

Worker并发进程 CPU 均值%循环线程 CPU 均值%进程 CPU 峰值%循环迟到最大 ms
1110.98.531.2697.5
11061.550.595.6180.6
12080.766.7108.7222.0
15090.774.8111.2292.3
1100102.785.4122.3421.7
414.73.4203.5931.2
41021.118.464.20
42039.134.772.7168.1
45066.958.7107.4356.1
410079.269.9105.7408.5
813.82.7247.51206.0
81014.012.537.4190.5
82026.623.972.548.9
85049.945.6100.9358.8
810067.261.9104.1401.4

单 Worker 的 10→20 增量定位

均值用于加总解释;P50/P95 用于观察分布。阶段边界来自轻量语义探针。

阶段10 并发均值20 并发均值均值增量10 并发 P50/P9520 并发 P50/P95
API 接入至 Worker 开始69.35994.41725.0650.81 / 258.361.69 / 282.9
领取、读取输入/用户/工作目录28.13160.70932.5818.84 / 125.8963.04 / 106.83
生成并持久化执行清单48.22168.61620.420.76 / 122.8556.94 / 259.4
运行配置、附件与开始事件27.19565.48238.2913.34 / 95.8552.51 / 165.85
资源装配与构图68.387150.1281.7343.12 / 201.92117.14 / 483.94
记录 prepared、提交与启动图11.58429.64218.069.13 / 25.4720.77 / 60.03
读取首次 checkpoint4.178.4634.291.69 / 8.034.45 / 19.32
运行图前置节点至模型 HTTP 发送77.198171.50194.347.35 / 240.43112.38 / 577.34

增长发生在哪里,还能怎样优化

单 Worker 的 10→20 并发,总均值从 334.245 ms 增至 648.951 ms。其中“资源装配与构图”和“图前置节点至模型提交”贡献约 56% 的均值增量。但纯 create_agent 构图的均值为 9.45→8.87 ms,线程 CPU 为 9.27→8.69 ms,每请求均调用一次。不能把整个资源装配跨度归因于纯构图,也没有证据表明底层文件系统一次只能读取一个用户。

await 会让出执行机会,却不会增加一个事件循环同时执行 Python 代码的能力。各用户完成 I/O 后的回调、状态反序列化、消息/工具装配和图节点调度仍竞争同一个执行线程;运行中请求的输出与收尾也与下一请求准备重叠。此前细粒度闭环测量已观察到技能投影线程完成后,等待事件循环恢复的均值从 10 并发的 13.61 ms 增至 20 并发的 50.27 ms。这是历史诊断证据,不混入本次正式矩阵。

本轮单 Worker 从 10 到 100 并发,进程 CPU 均值由 61.5% 增至 102.7%,循环线程由 50.5% 增至 85.4%,而成功吞吐从 4.09 仅增至 6.82 请求/秒。增加 Worker 后,100 并发的准备阶段 P50 从 4442 ms 降至 138 ms(4 Worker)或 85 ms(8 Worker)。这些现象共同支持 Worker 局部 CPU/事件循环竞争是重要瓶颈,不支持“CPU 无关,换资源一定无效”。进程 CPU 包含后台线程,所以可超过单核 100%;宿主机还有空闲核不等于某个 Worker 的事件循环没有排队。

同时,4/8 Worker 下的 P95 在高并发仍明显增长;阶段跨度混合了 I/O、池等待、CPU 和恢复调度,当前证据不能把剩余等待全部归因于数据库锁或全部归因于 CPU。没有采样 API、PostgreSQL 或 Redis 的 CPU,也未做内存扩容或更换 CPU 对照。

后续可选方向当前证据与取舍
将普通负载分散到多个 Worker已实测有效;10→20 的 P50 倍数从单 Worker 的 2.33 降至 4/8 Worker 的 1.16/1.17。代价是更多进程内存与连接,需要部署容量约束。
继续减少运行资源装配、图前置节点的逐请求工作两阶段贡献主要增量;优先检查可复用的不可变元数据。不能跨用户缓存权限或复用带 Run 上下文的完整图。
进一步拆分高并发 P95 的连接池等待、查询执行与事件循环恢复用小规模细探针定位后再修改;本表不能证明扩大某个池就有效。
启动期提前装配进程级不可变资源可针对首次请求尾延迟;会增加启动耗时/内存,应由 readiness 反映,不以额外付费模型请求隐藏冷启动。

8 Worker、1 并发的 P95 为 1083.85 ms,并非稳定态突然比高并发慢:100 个请求中,8 个进程各自首次请求耗时 1045~1739 ms。其余 92 个请求 P50/P95 为 160.28/256.42 ms,仅用于说明冷启动影响;主表仍保留全部 100 个,不剔除慢请求。

历史定位证据

下列小实验支持具体等待位置,不与正式矩阵合并统计。局部阶段改善和整体变慢的反向结果同时保留。

诊断对象直接观测解释与边界
同用户 Skill 投影与固定 Schemawarm Skill 刷新均值约 590→77 ms;回调→HTTP 发送入口 P95 为 58→20 ms同用户独立 Thread、沙盒任务,计时起点/负载均不同于正式 say hi 矩阵
跨用户 checkpoint 实例锁1→10 并发,首次读取锁等待均值 0.005→60.507 ms;独立 saver 后约 0.005 ms,读取总时延 63.624→6.649 ms10 并发总 P50/P95 从 586.71/660.48 升至 650.08/682.06 ms;局部锁消除没有证明整体提升
文件线程结束后的循环恢复闭环 10/20 并发恢复等待均值 13.61/50.27 ms,线程自身 CPU 3.80/3.79 ms直接显示恢复调度增长;不能把所有文件读取耗时归因于用户锁、磁盘或 CPU
实际 Langfuse flush,无模型诊断同步墙钟 699.058 ms、线程 CPU 11.183 ms、无关协程迟到 679.125 ms;卸载后墙钟 678.300 ms、迟到 0.509 ms同步外部等待确实阻塞循环;不是整轮压测的 flush 分布,不含 SDK 后台线程 CPU
ARQ 本地任务过滤20 并发投递完成→Worker 入口 P95 为 2136.30→407.98 ms,创建→started 为 2194.15→667.40 ms同时 prepared→模型发送 P95 从 1038.87 增至 1571.77 ms,完整对话典型耗时未改善
flush 卸载后的准备竞争20 并发 Redis pipeline 累计等待均值 623.35→181.15 ms,文件线程完成后恢复等待 62.33→2.49 ms客户端跨度包括服务端等待及循环恢复,不等于 Redis 执行时间;父子跨度不能重复求和

最早同用户沙盒对照使用 Run 创建→ChatModel callback,四轮共 124 个正式 Run;1/10/20 并发的两轮 P95 分别由 243/275、1698/1140、2100/2078 ms 变为 91/99、800/587、1131/1438 ms。随后单批多用户实验共 666 个 Run(含 82 个预热),checkpoint 定位实验共 109 个(含预热及重复对照)。这些历史预算、用户和预热协议均不同,不混入以下闭环表和正式 3150 个请求。

早期单批多用户实验的 50 并发 P95:1/4 Worker 基线为 4351/1689 ms,仅复用配置为 3787/1312 ms,配置复用加事件合批为 4001/1444 ms。前置负载不完全一致;它支持继续减少准备工作,不能证明 pipeline 的单独收益。当前不保留早期重复并发档位/付费预热作为默认评测路径。

历史闭环对照

以下均为单 Worker、不同用户固定 Thread、1/10/20 并发各通道五轮,每次 155 请求、无额外预热。前 3 行使用细探针,后 2 行使用轻量语义探针;同组为 P50/P95(ms)。它们是按顺序执行的小对照,不是随机交错 A/B。

代码与观测快照并发 1(5 样本)并发 10(50 样本)并发 20(100 样本)
细探针闭环基线446.80 / 1177.01889.86 / 2804.693156.89 / 4985.59
细探针+ARQ 本地任务过滤387.25 / 1161.17838.24 / 1593.002549.84 / 3733.83
细探针+追踪 flush 线程卸载144.73 / 1270.07342.18 / 1082.341055.77 / 2162.61
轻量语义探针、最终状态共享前118.76 / 1403.98290.21 / 976.241052.63 / 1677.47
轻量语义探针、最终状态共享后122.41 / 1198.39174.72 / 1108.73711.30 / 1500.58

最后两行共 310 请求,10/20 并发 P50 降低约 40%/32%,但 10 并发 P95 上升,1 并发 P50 略升,10→20 的 P50 倍数没有改善。最后一行发生在完整取消关闭链完善之前;flush 卸载行发生在终态 heartbeat 例外完善之前,各自后续补丁由正确性回归证明,不把原性能表说成补丁后的再次测量。

正式矩阵包含上述完整修复,但并发 1/10 的样本量、历史长度不同,探针模式也与早期三行不同,不能直接相减宣称单项收益。1 并发五样本的 P95 等于最大值;探针降开销、冷启动差异和负载顺序不计作业务优化收益。

正确性与负向证据

下列 oracle 验证仍有效的边界;旧实现变异仅在隔离测试进程恢复,不改动运行中的源码或付费样本。

边界直接结果与负向证明
Skill 投影与撤权未变化投影禁止创建 staging;内容和执行位更新、祖先/叶节点符号链接、检查后替换、特殊文件,以及真实 PG 锁等待后的撤权重读均有回归
固定工具 Schema旧实现重复推导会失败;新工具仍通过各自父 Run 闭包执行,不把 runtime 暴露给模型
首次模型时点真实 PG 验证 cancelled Run 补写已发生调用;拒绝过期及其他 owner,旧 guard 因时间缺失失败
独立 checkpoint saver真实 PG 持有 A saver 锁时仍可读取 B,重建 saver 可恢复 checkpoint/pending writes,线程隔离;恢复进程共享 saver 时因读取超时失败
ARQ 领取与重试真实 Redis 验证本地旧任务不阻塞新任务、跨 Worker 单次竞争、Retry/取消后 attempt 2 和精确执行列表;原生 Worker 触发旧任务 WATCH guard 失败
追踪 flush 与 heartbeat受控阻塞验证 chat/resume 正常等待和取消;同步 flush 会阻塞无关协程。真实 PG 区分本 attempt 已提交终态与其他 owner/过期/旧 attempt/lease_expired,放宽判断会失败
执行关闭与最终状态缺失最终 checkpoint 必须失败;并发 Run 从各自实际执行图获取最终状态。恢复旧取消等待器、去掉真实图关闭或重复取消保护,分别触发执行未关闭、等待器重复或清理被打断的失败
本次等价整理正常完成只能消费最终 checkpoint,恢复备用 AIMessage 组装即失败;异常仍保存部分正文和 trace。重复统计组在刷新/写盘前失败,已有文件保持原字节;反序阶段、跨 Worker 标签、额外失败与缺失统计仍有负向覆盖

实验中的服务端首次输出是语义事件,不等同于客户端首 SSE 状态事件;模型发送→首输出还含网络和 Worker 处理,首输出→完成还包含后续模型生成,不能全部解释为内部收尾。首次 HTTPX 探针不统计 SDK 内部重试或后续模型调用,API 请求数不等同于供应商账单调用数。无置信区间或跨机器时钟验证,不把局部阶段、CPU 样本或完整对话等待全部归因于单一服务。

复算与整理回归

矩阵发送请求前登记样本并保守保留测试资源。整体取消或通道异常时,先同步保存本组已完成与在途请求,再按精确终态或已确认取消清理;未知终态保留用户与 Thread。主入口回归覆盖未确认取消、确认取消与通道异常三种路径,已付费证据不依赖 TaskGroup 正常退出。发布迁移与取消收敛后的交付回归为 1861 passed、52 skipped;真实 PG/Redis 的 lease、checkpoint、ARQ 与发布 Schema 集合为 39 passed。此前 3150 条正式性能样本不重新采集。

性能工具归档使用统一 matrix/load/report 入口,单测纳入后端 unit workflow。报告中的每通道轮数读取实际样本;失败或缺少模型前探针时,主表保留全部请求及有效计数,阶段表明确不可用,不填零,也不阻止报告生成。任一对照组阶段缺失时不计算增量。显式刷新后的阶段分析、报告与 complete.json 共享新事件。归档验证:61 项相关单测通过;Alpha 最终全量 unit 为 1862 passed、52 skipped,32.10 秒;独立 Review 无剩余问题。正常 uv run --group test 因容器挂载目录不允许创建 editable 包元数据而失败;实际回归使用 uv run --no-sync --group test pytest test/unit -m 'not slow' -q --disable-warnings,复用既有依赖,不修改权限;相关测试在项目虚拟环境执行 python -m pytest backend/test/unit/performance -q --disable-warnings

归档后的 15 组、3150 条原始样本经统一 report 入口离线复算,stages.json 字节一致,五张报告表逐行一致;报告文字仅将固定预算改为实际轮数说明。真实 Alpha 容器完成细探针 API 工厂与 Worker 适配器的加载检查,Worker 执行队列被替换为捕获装配的测试函数,没有运行模型。Compose 合并结果指向新探针路径;工程契约与 61 项契约单测、Ruff、文档构建及补丁检查通过。本轮不重新执行付费矩阵或产品 E2E,不把工具归档宣称为新的性能提升。

原始正式样本位于本地忽略目录 tmp/load-tests/continuous/final-20260907/matrix.json,包含无正文的请求标识、时点、探针及 PG 回读,不提交运行数据或凭据。八份旧 decision 和独立报告在合并前保留本地备份;本文保存全部当前取舍、正式五张表和有解释价值的历史对照,删除重复执行流水账。

正式采样的等价现行命令如下,前置为显式选择 Alpha、导出测试认证变量、装配实验 API 覆盖文件并设置 MATRIX_FINE_TIMING=0,详见隔离环境指南性能工具命令。重新运行会再创建 3150 请求,必须使用新输出目录。本次整理没有重新运行它。

bash
python -u -m backend.test.performance matrix --workers 1,4,8 --concurrency 1,10,20,50,100 --output-dir tmp/load-tests/continuous/final-20260907

下面命令从已有原始样本一次离线派生阶段与报告,不调用模型。实验容器恢复普通入口后不要加 --refresh。派生 report.md 是本地工具输出,持久可审阅结果只在本文维护。

bash
python -m backend.test.performance report tmp/load-tests/continuous/final-20260907/matrix.json

15 组、3150 样本的 14 维统计、8 阶段均值及 P50/P95 与整理前 JSON 精确一致。没有用重新生成的 expected output 覆盖旧 oracle;对照来源是修改前备份。正式时延表继续标注为 2026-09-07 性能快照,不宣称等价清理带来新的实测降幅。

工具归档前已执行的生产链路与决策合并检查结果与范围
Alpha pytest test/unit/services/test_chat_service_langfuse_stream.py test/unit/services/test_chat_service_sync.py test/unit/services/test_chat_stream_interrupt.py -q --disable-warnings67 passed,聊天输出、错误部分正文、中断与恢复边界
Alpha pytest test/unit -m 'not slow' -q --disable-warnings -o faulthandler_timeout=301801 passed、52 skipped、9 warnings,30.18 秒
Alpha pytest test/integration/services/test_agent_run_lease.py test/integration/services/test_checkpoint_concurrency.py test/integration/services/test_arq_worker_dispatch.py -q --disable-warnings31 passed,真实 PG/Redis,3.97 秒
性能工具迁移前的独立 unittest 检查52 passed,包含一次阶段提取与写入前拒绝重复组的新反例;迁移后的命令与结果见本节开头
Alpha pytest test/e2e/test_deterministic_agent_path_e2e.py -k 'replay_rejects_requests_outside_deterministic_contract or deterministic_agent_path_reaches_persisted_result or scheduled_task_run_now_reaches_exact_conversation_and_result or cancelled_run_keeps_trace_and_closes_running_model_audit or resume_with_offloaded_tool_result_publishes_stream_owned_audit' -q --disable-warnings -o faulthandler_timeout=355 passed、2 deselected、2 warnings,22.52 秒;重启后的普通 API/Worker 入口,回读真实结果、审计和文件,不调用外部模型
python3 scripts/verify_engineering_contracts.pypython3 -m unittest scripts.test_verify_engineering_contracts通过:78 decisions、127 docs;61 项自测通过。旧文档路径和入站引用搜索为零
变更 Python 的 Ruff check/format、git diff --checkcd docs && pnpm run build通过,54 个 Python 文件格式已核对;文档构建退出码为零,存在既有 VitePress/Rolldown 插件兼容、弃用 API 与 bundle 体积提示
独立代码与合并文档 Review通过,无 P1/P2;独立核对完整差异、15 组原始样本与旧 oracle、五张正式表、历史反向结果、旧路径及解释边界

后端检查使用 Compose API 容器中已安装的 pytest,外层有明确超时;历史 uv run --group test editable build 受源码挂载权限阻挡,未修改目录权限,不把该命令写为通过。测试层级由测试规范维护。真实 provider 矩阵本次 Not run(避免再次付费),多 API、多机、其他供应商协议与硬件更换亦未验证。

Alpha 保持普通启动入口及单 Worker,readiness 为 ready、无 degraded;临时 replay 已停止,测试账号/会话按 API 清理语义处理,历史软删除记录和 Run 审计保留。没有创建 commit 或 PR。

本项目基于 MIT License 开源,欢迎使用和贡献。