性能、负载与内存

🔵 Agent OS 在负载下的表现:能承受多少并发请求、如何限制过载、每个会话占用多少内存,以及如何复现这些数据。另外还会在同一测试台上与其他 agent 服务器进行对比。

文中的数字来自一个特定的测试台(见 §34.2),仅作为规划时的参考,而非您硬件上的保证。


34.1 什么限制了负载

Agent OS 不是负载均衡器,也不自己做推理:LLM 是外部提供商,而内核只是规划对它的调用。因此「天花板」由三件事决定:内核限制、过载时的行为,以及持久化的成本。

杠杆 位置 默认值 作用
kernel.concurrency_limit server.yaml 3 到提供商的并发 LLM 流上限(内核信号量)。多余的轮次进入队列。
kernel.max_in_flight_turns server.yaml 0(关闭) HTTP 边缘的准入闸门:不超过 N 个「在途」轮次;超出则立即 429 Too Many Requests + Retry-After。被拒绝的请求不会触碰会话,也不会取消已启动的轮次。
kernel.session_ttl_secs server.yaml 300 无活动的会话在 TTL 后被杀死;清理器每 session_cleanup_interval_secs 运行一次(60)。限制长时间空闲下的内存增长。

一个会话——一个活动轮次。 如果同一会话收到第二个请求,它会取消前一个(last-wins),而不是排队。这可以防止同一会话内的竞态。

持久化。 存储会写入轮次数据:JSONL 日志器、EventDb(events.db)、消息存储(messages.db)和访问日志(access.db)。它们全部被放到带批处理的后台写入线程中(每批一个事务),因此不会阻塞 async 路径;但在非常高的负载下,它们的线程会开始相互竞争(见 §34.3)。JSONL 是可选的层;DuckDB 数据库(事件/转录/访问)承载主要功能。

运行时该看什么(GET /metrics,Prometheus):


34.2 如何复现测量

来自仓库的测试台(需要 mock_llm + loadgen,构建 release-fast):

cargo build -p agent_web_bot --profile release-fast \
    --bin agent-os --example mock_llm --example loadgen
./scripts/loadtest.ps1                 # свип 1,2,4,8,16,32,64
./scripts/loadtest.ps1 -Sweep 128,256,1000 -DurationSecs 15

下文数字的测试台配置:mock TTFT=2000ms, 200 tok/s, 8 токенов(轮次 ≈ 2.04 s),18 个逻辑 CPU,mock + 服务器 + 负载生成器在同一台机器上,每个点 12–15 s。最好在干净的机器上测量:无关进程和累积的数据库会明显扭曲结果。


34.3 吞吐量

Agent OS 无持久化(纯调度器 + HTTP)——内核的「天花板」:

c rps total p50 TTFT p50 错误
1 0.53 2135 ms 2028 ms 0
4 2.17 2126 ms 2027 ms 0
16 8.61 2140 ms 2029 ms 0
64 33.4 2138 ms 2040 ms 0
128 68.3 2151 ms 2045 ms 0
256 128.9 2163 ms 2065 ms 0
1000 459.5 2215 ms 2090 ms 0

延迟几乎持平: 从 c=1 到 c=1000,TTFT 从 2028 增长到 2090 ms,轮次总时间从 2135 增长到 2215 ms。内核本身的开销 ≈ TTFT +30 ms、轮次 +95 ms,且几乎不依赖于竞争。只要 CPU 和 permit 足够,吞吐量就线性增长。

Agent OS 带 DuckDB 数据库(EventDb + 消息存储 + 访问日志;JSONL 关闭):

c rps total p50 TTFT p50 备注
1 0.53 2142 ms 2025 ms —
4 2.15 2150 ms 2032 ms —
16 8.58 2149 ms 2033 ms —
64 34.3 2146 ms 2045 ms —
128 68.4 2150 ms 2047 ms —
256 136.2 2159 ms 2059 ms —
1000 451.0 2135 ms 2055 ms message_store_queue 至 7k;event_db_dropped 0

在约 1000 个并发轮次以内,数据库只消耗约 2% 的吞吐量(451 vs 459),且不影响延迟。超过约 3000 后,吞吐量会撞上相互竞争的 DuckDB 写入器(事件 + 转录 + 访问),消息存储队列溢出,部分事件在过载下被丢弃(event_db_dropped)——如果您要维持数千个带完整历史的并发会话,请考虑 retention/分片。


34.4 内存

我们测量服务器进程的两个量:WorkingSet(常驻内存,包括代码/DLL 的共享页)和 Private(私有内存)。在 Windows 上是 Get-Process;在 Linux 上是 /proc/<pid>/smaps_rollup 中的 VmRSS 和 Private_Dirty。

状态 WorkingSet Private
无存储,启动(steady) 31 MB 9 MB
带 DuckDB 数据库,启动 108 MB 98 MB
无存储,约 1450 个会话 218 MB —
带 DuckDB 数据库,约 1470 个会话 385 MB —

34.5 过载行为(429)

启用 kernel.max_in_flight_turns > 0(例如在 concurrency_limit: 3 时使用 8)——过载便不再堆积队列:

建议:在生产环境中始终启用 admission 门控,并将 concurrency_limit 按提供商配额设置(以避免触发外部 429/超时)。


34.6 与 opencode 的对比

相同测试台、相同 mock(TTFT=2000 ms,8 个 token),每 worker 一个会话。 opencode(opencode serve v1.18.32)通过自定义提供商 @ai-sdk/openai-compatible 指向同一 mock; 测量的是完整轮次时间 (POST /session/:id/message)。

重要——公平对比。 opencode 将会话/转录持久化到 SQLite,因此应将其与 Agent OS 含数据库(EventDb + message store + access;JSONL 关闭)对比,而非纯核心。此外,opencode 服务器是 单用户 coding agent 的服务器(会话绑定到项目, 无 admission 控制),而 Agent OS 是多租户聊天/agent 服务器。对比 关注的是扩展特性,而非产品对等性。

吞吐量(Agent OS 含数据库 vs opencode)

c Agent OS rps opencode rps Agent OS p50 opencode p50 opencode 错误
1 0.53 0.50 2142 ms 2229 ms 0
4 2.15 2.00 2150 ms 2285 ms 0
16 8.58 6.67 2149 ms 2791 ms 0
64 34.3 16.0 2146 ms 4386 ms 0
128 68.4 21.3 2150 ms 6611 ms 0
256 136.2 36.2 2159 ms 11883 ms 142
512 — 挂起 — >27 s 大量
1000 451.0 — 2135 ms — —

内存

Agent OS(无存储) Agent OS(DuckDB 数据库) opencode
基础(启动,WorkingSet) 31 MB 108 MB 208–336 MB(Node,因 GC 波动)
基础(private) 9 MB 98 MB —
每会话增量 ≈ 0.13 MB ≈ 0.19 MB ≈ 0.09 MB(空)
~1500 会话(WorkingSet) 218 MB 385 MB 负载下 500–680 MB

对比结论

Agent OS 针对竞争性多租户负载而优化:平稳延迟、 吞吐量线性增长、429 保护和 TTL 清理。opencode 服务器在此角色下 退化:延迟线性增长,过载时出现错误/挂起且无 保护。这在意料之中:它的任务不同(面向单用户的交互式 coding agent), 而非承载数千并发会话的服务器。


34.7 实践建议