# Agent 与任务压测报告 > 日期:2026-09-06。范围:当前第二阶段 Agent Runtime、任务 API、任务列表及 Trace 组件。测试在 Windows 11、Python 3.12.6、独立无头 Chrome 下执行,前端为 Vite 开发模式,纸间时光版本 1.8.1。 ## 1. 结论 功能正确性检查通过:Agent 工具调用、事件序号与回放、终态持久化恢复、容量拒绝、取消、权限等待、故障隔离,以及任务增删改查和分页均得到预期结果。性能仍有三项明确问题: | 优先级 | 问题 | 本轮证据 | 建议方向 | | --- | --- | --- | --- | | P1 | 任务列表未加载后续页;Agent 历史列表也没有后续分页入口 | 100、1000 条任务的实际页面均只请求 `/api/tasks` 并显示 50 条;Agent store 同样仅调用默认分页一次,接口默认 50 | 增加服务端筛选与分页/加载更多,显示总量,防止统计仅基于已加载项 | | P1 | 高并发 Agent 阻塞事件循环 | 200 并发的完成延迟 P95 约 10.55 秒;进程内心跳最大延迟约 3.56 秒 | 分析同步持久化,设计有界执行调度及顺序写入,保留事件落盘、终态与回放一致性 | | P1 | 大 Trace 的树形筛选长时间阻塞 | 2000 事件树形搜索约 483–617 ms;10000 事件约 25.91 秒 | 为匹配的 sequence/tool/model ID 建 Set,避免逐节点重复遍历事件;再评估分段显示及增量更新 | 本次新增的是可复现压测与报告,没有把上述生产问题标记为已修复。页面滚动与树形搜索是不同瓶颈:10000 事件的滚动 P95 约 40 ms,并不能说明搜索交互也流畅。 ## 2. 隔离与方法 - 后端脚本在导入任何 app 模块前,将 `APP_DATA_DIR`、`APP_DB_PATH`、`APP_VAULT_PATH` 指向临时目录,结束后清理;不读写用户 Vault、现有任务及 Provider 凭据。 - Agent 使用内置 Mock Provider,每轮模型调用注入 50 ms 异步等待,正常样本包含一次 `system.echo` 工具调用和两轮模型调用;不调用真实厂商,也不衡量回答质量或真实模型吞吐。 - 进程内运行直接调用真实 Runtime、SQLite 和任务 ASGI 路由。任务 HTTP 复核另起独立 Uvicorn 进程,用真实回环连接执行,避免将 ASGI 驱动的连续无让出执行误解为网络服务延迟。 - 前端挂载真实 TasksView、TraceTimeline 和 Pinia store。压测页拦截全部 fetch,不向真实后端发出请求。任务接口夹具保留默认 50 条分页,先测实际加载条数,再注入完整列表测试渲染上限,两者分开记录。 - Trace 由确定性模型开始/完成、工具调用/结果事件组成,带调用与父节点 ID;测量首次渲染、时间线过滤、树形切换、匹配全部节点的树形搜索。 - 滚动用 CDP 派发 120 次滚轮事件,记录真实滚动容器及 `maxScrollTop`,防止对未滚动页面统计帧间隔。Trace 测试容器显式解除应用根节点的裁剪,由独立滚动区承担真实 Agent 页滚动容器的职责。 本轮每个前端配置测一次;开发编译、后台负载、缓存和浏览器调度都会影响结果,数据用于发现瓶颈,不作为生产环境 SLA。首次渲染计时不包含模块下载,包含 Vue 更新及两个 animation frame。 ## 3. Agent 后端 ### 3.1 正常工具调用与回放 | 并发数 | 运行数 | 完成延迟中位数(ms) | 完成延迟 P95(ms) | 场景心跳最大延迟(ms) | | ---: | ---: | ---: | ---: | ---: | | 1 | 20 | 146.20 | 170.13 | 26.84 | | 10 | 20 | 405.75 | 505.74 | 136.71 | | 50 | 50 | 2234.45 | 2304.42 | 798.84 | | 200 | 200 | 10262.47 | 10554.52 | 3559.97 | 290 个正常运行全部完成,工具返回内容逐项一致;实时订阅事件序号连续,`after_sequence=2` 回放与原事件后缀一致。新的 Runtime 实例可以从 SQLite 恢复这些终态运行。订阅者集合最终清空,进程内保留记录不超过 200。 “并发”是同时提交的协程数,不代表独立 CPU 工作线程。当前 queued 是启动前状态,Runtime 并没有把超额活跃运行无限排入执行队列。场景心跳还包含提交、持久化回放与恢复校验,不能将其全部归为模型执行耗时;50、200 并发场景的心跳采样数仅 8 个,表中因此列最大值而非宣称稳定分位数。 代码定位:`AgentRuntime._publish()` 同步调用 `AgentTraceRepository.append_event()`,后者逐事件连接 SQLite 并提交事务;`connect()` 还加载扩展和检查迁移。这是需要进一步拆分测量的阻塞路径,本轮未完成各子步骤 CPU/磁盘成本归因。 ### 3.2 容量、取消与故障注入 - 保持 200 个模型调用活跃,第 201 次创建得到 `AgentCapacityError`;随后取消全部 200 个运行,全部进入 cancelled,后台任务均结束。 - 20 个运行等待权限:10 个允许后完成,10 个等待中取消,订阅者均释放。 - 20 个混合运行:5 个正常完成、5 个 Provider 异常、5 个模型超时、5 个工具超时,均获得预期状态或错误码;工具执行器剩余数量为 0。 - 正常回放和故障场景合计创建 530 个运行,临时数据库最终约 2.95 MB。工具超时被记录为 ToolResult 错误,后续 Mock 模型仍可能完成运行,不将它误算为整个 Run 必然失败。 这里的“恢复”是新 Runtime 读取终态持久化数据,未模拟 OS 强杀时的写入中断。SSE 通过 Runtime 的事件生成器验证序号及断点回放,没有进行真实网络慢读者/断网压力测试。 ## 4. 任务 API 进程内分别对 100、1000 条任务执行创建、逐页读取、更新为 done、删除,所有 ID 完整,最终总量为 0。默认接口每页 50 条,显式每页 100 条遍历可以取回全部数据。 ASGITransport 的 1000 条突发操作中,心跳出现约 14 秒延迟:测试客户端与同步路由处于同一事件循环,连续就绪协程缺少网络等待,不能将其视作真实 HTTP 健康检查延迟。因此增加独立 Uvicorn + 回环 HTTP 复核: | 操作 | 数量 | P95(ms) | 最大值(ms) | | --- | ---: | ---: | ---: | | 创建 | 1000 | 116.45 | 134.91 | | 更新 | 1000 | 159.29 | 193.37 | | 删除 | 1000 | 156.43 | 190.10 | | 分页/收尾查询 | 11 | 20.96 | 20.96 | | 并行健康检查 | 117 | 157.00 | 195.07 | HTTP 客户端并发 20,总耗时约 18.31 秒;健康检查零失败,分页无重复/遗漏,最终任务数为 0。此处是本机单次结果,未覆盖跨网络、长期持久负载或多进程同时写同一数据库。 ## 5. 前端 ### 5.1 任务列表 100、1000 条数据的实际加载阶段都只有一次 `/api/tasks` 请求,store 中均为 50 条。以下是额外注入完整 1000 条数据后的结果,不是生产页面当前可以加载 1000 条的证明。 | 主题 | 完整列表渲染(ms) | 完成状态筛选(ms) | | --- | ---: | ---: | | 默认浅色 | 117.4 | 44.1 | | 默认深色 | 137.8 | 47.8 | | 纸间时光 1.8.1 | 150.0 | 59.5 | 1000 条列表约 11007 个 DOM 节点,实际滚动最远达到 81000 px;筛选 done 得到 334 条,与夹具期望一致。100 条样本筛选得到 34 条,也一致。默认浅色的 1000 条滚动 P95 约 30.2 ms,纸间时光约 50.1 ms;样本数少,暂不据此认定新的单一 CSS 根因。 ### 5.2 Agent Trace | 事件数/主题 | 首次渲染(ms) | 时间线搜索(ms) | 树形切换(ms) | 树形全匹配搜索(ms) | | --- | ---: | ---: | ---: | ---: | | 2000 / 默认浅色 | 283.8 | 97.4 | 64.0 | 617.2 | | 2000 / 默认深色 | 289.0 | 74.5 | 62.9 | 483.0 | | 2000 / 纸间时光 | 337.3 | 86.3 | 62.1 | 540.1 | | 10000 / 纸间时光 | 1803.9 | 582.8 | 317.5 | 25914.8 | 200 事件的树形搜索约 20–29 ms。2000 事件时间线约 20542 个 DOM 节点,10000 事件约 102542 个。查询“压力测试 1”分别命中 444、4444 条事件,与直接检查输入数据的结果一致;树形搜索“压力测试”匹配所有事件,会自动展开匹配子树。 代码定位:`TraceTimeline.filteredTree` 为每个节点调用 `filteredEvents.some(...)`。节点数和匹配事件数一起增加时,会产生近似二次增长的匹配工作,随后还需构造并渲染展开子树。建议先用匹配 ID 集合消除嵌套扫描,再测 DOM 更新成本;不要仅用防抖隐藏单次 25 秒阻塞。 10000 是扩大数据规模的诊断测试,高于 Runtime 默认内存事件保留量 2000;它直接向组件传入事件,不代表当前单次 API 页或实时 store 默认会收到这一数量。也未覆盖逐事件 SSE 增量刷新的端到端成本。 ## 6. 复现与原始数据 ```powershell # 后端:独立临时数据,离线 Mock backend/.venv/Scripts/python.exe backend/scripts/agent-task-stress.py --output .local-plans/agent-task-backend.json # 真实 HTTP:独立 Uvicorn、随机回环端口 backend/.venv/Scripts/python.exe backend/scripts/task-http-stress.py --count 1000 --concurrency 20 --output .local-plans/task-http.json # 前端:先启动独立 Vite,另一个终端执行后续命令 npm --prefix frontend run dev -- --port 5175 --strictPort backend/.venv/Scripts/python.exe frontend/tests/performance/run-stress.py --url 'http://127.0.0.1:5175/tests/performance/agent-task.html?kind=tasks&theme=paper-moments' --scroll --sizes 100 1000 --runs 1 --output .local-plans/task-ui.json backend/.venv/Scripts/python.exe frontend/tests/performance/run-stress.py --url 'http://127.0.0.1:5175/tests/performance/agent-task.html?kind=trace&theme=paper-moments' --scroll --sizes 200 2000 10000 --runs 1 --output .local-plans/trace-ui.json ``` URL 的 theme 可换为 light、dark。测试应串行运行,避免不同负载相互争用;10k Trace 树形筛选可能长时间占用测试浏览器。浏览器使用临时用户目录,不接管用户当前浏览器。 - [Agent 与进程内任务 API 数据](performance/2026-09-06-agent-task-backend.json) - [真实 HTTP 任务数据](performance/2026-09-06-task-http.json) - [前端 13 组样本数据](performance/2026-09-06-agent-task-ui.json) - [前端压测工具](../../frontend/tests/performance/README.md) 附加回归:`test_agent_core.py`、`test_api.py` 共 26 项通过。脚本中的状态、序号、输出、分页与筛选断言均通过。未提交或推送本次材料。 ## 7. 修复后复测(2026-09-06) 以上保留首次压测基线。本节对应后台 Trace 批量写入、任务后台写入、完整列表加载、Trace 集合匹配/分页和统一日志接入后的实现。样本开启新操作日志,仍只调用隔离 Mock。 | 项目 | 修复前 | 修复后 | | --- | ---: | ---: | | 200 并发 Agent 完成 P95 | 10,554.52 ms | 1,233.12 ms | | 200 并发测量区间事件循环最大延迟 | 3,559.97 ms | 509.00 ms | | 纸间时光 10k Trace 首次渲染 | 1,803.9 ms | 94.4 ms | | 纸间时光 10k Trace 树形全匹配筛选 | 25,914.8 ms | 189.2 ms | | 纸间时光 10k Trace 首屏 DOM 节点 | 约 102,542 | 1,852 | | 1,000 条任务首次实际加载 | 50 条 | 1,000 条(10 次 API 请求) | Agent 测量区间还包含同步读回、校验和重启恢复,最大心跳延迟不是纯执行阶段指标。290 次普通运行、200 个容量/取消场景、20 个权限场景、20 个失败/超时场景全部通过,订阅者和工具执行器释放。不能声称消除了所有主线程工作。 Trace 搜索检查完整数据,只有渲染分页。10k 样本滚动 frame gap P95 为 10.1 ms,无 longtask;时间线筛选 284.9 ms,树形切换 100.9 ms。这是单次本机样本,不代表所有硬件与实时 SSE 输入。 1,000 条任务界面每页 100 条;筛选后数据总数 334,当前页 100。筛选 18.7 ms,滚动 frame gap P95 为 30 ms,无 longtask。断言同时检查完整数据量和分页渲染数量。 真实 HTTP 测试 1,000 条任务、20 并发,CRUD 全部成功,分页完整,最终任务数 0: | 指标 | 修复前 | 修复后 | | --- | ---: | ---: | | health P95 | 157.00 ms | 38.64 ms | | 创建 P95 | 116.45 ms | 157.68 ms | | 更新 P95 | 159.29 ms | 208.68 ms | | 删除 P95 | 156.43 ms | 205.45 ms | | 总耗时 | 18.31 s | 24.51 s | 后台排队和操作记录改善了无关请求的响应,但这组样本写入吞吐下降,不能描述为 CRUD 全面提速。后续可以评估任务事务批量化与连接初始化开销;本次没有降低 SQLite 持久化级别。 - [Agent / 进程内任务复测](performance/2026-09-06-agent-task-backend-fixed.json) - [真实 HTTP 任务复测](performance/2026-09-06-task-http-fixed.json) - [Trace 浏览器复测](performance/2026-09-06-agent-trace-ui-fixed.json) - [任务浏览器复测](performance/2026-09-06-task-ui-fixed.json) - [日志架构与验收说明](后台运行日志与压力问题修复.md)