对应计划 v0.2 TP-11 与 M5 任务书第 3 条。每个数字都从原始记录算出来, 取不到的写
—,不补 0、不插值。时间一律上海时间。 脚本tools/stability/soak.py;两跑的原始记录与逐项报告在docs/evidence/M5/soak-testmachine/与docs/evidence/M5/soak-hk/。
主跑 · 测试机 34.133.8.3(2 核 8G) |
旁证 · 香港 34.92.73.207(8 核 29G) |
|
|---|---|---|
| 跑的是哪一版 | 就是要发布的 v0.1.1(我自己用最终代码拉起、自检全过) | v0.1.1 起跑,20:06 与 20:43 被另一方重建了 hca-web 镜像(他们在其上做品牌改造,20 个文件未提交,含适配层 index.ts);hca-daemon 全程没被动过 |
| 时长 / 轮数 | 3 小时 03 分 · 38 轮(20:14:37 → 23:17:40) | 4 小时 00 分 · 48 轮(18:51:49 → 22:51:57) |
| 成败 | 成功 35 · 失败 3(逐条见 §2.3) | 成功 48 · 失败 0 |
| 为什么没跑满 4 小时 | 23:14 共用这台机器的另一条链路重起了 docker(今天第 4 次:16:08 / 16:20 / 19:05 / 23:14),六个容器同时重起 | — |
| 它能证明什么 | 发布版在真实路径上的行为,含一次 P0-10 的真实复现(§3,本轮最重要的发现) | 连续 4 小时不间断的资源曲线与增长率(§4) |
两跑都不完美,合起来才够用:主跑跑的是对的版本但被外部打断;旁证跑满了 4 小时 但 BFF 那一层中途被换过。结论里凡是只有其中一跑支持的,都写明出处。
这也是本轮最该让用户看见的一条运维事实:两台机器都不是 HCA 独占的, 长时测试每次都要赌没人动它(见 M5 报告 U-15)。
| 项 | 实测 | 出处 |
|---|---|---|
| 内存有没有泄漏 | 没有看出泄漏。daemon 常驻约 0.95 GiB,两跑都是「在抖不是在涨」:旁证跑 4 小时拟合每小时 +1 MiB 而 R²=0.11(起 953 → 终 953 MiB,全程 947~961 MiB);主跑的最长不跨重起段拟合 R²=0.01 | 两跑 §2 |
| 会话文件会不会涨死 | 会一直涨,但速率很低。旁证跑 4 小时:会话文件 7 → 136 个(+129)、会话目录 168 KiB → 2.6 MiB(+2.48 MiB)。主跑 3 小时 02 分同样量级(+86 个文件 / +2.03 MiB) | 两跑 §3 |
| 审计与 guard 日志 | 旁证跑 4 小时:audit.jsonl +202 KiB、guard.jsonl +71 KiB。按这个速率,HCA_AUDIT_MAX_MB(默认 64)够用很久;guard 日志目前不轮转 |
两跑 §3 |
| MCP 连接 | 旁证跑 48 轮逐轮 9/9,一次没掉。主跑 38 轮里有 3 轮不是 9/9,全部集中在 P0-10 那一次重挂之后(§3) | 两跑 §1 |
| daemon / web / postgres / redis / llm-shim | 全程没有一个是「越跑越胖」的形状,最大的 hca-web 在 120~151 MiB 之间抖 |
两跑 §2 |
| 容器重启 | 两跑都被外部重起过,而 RestartCount 全程是 0 —— 只看 RestartCount 会打出「0 次」这种假的全绿 |
两跑 §1 |
| 失败 | 主跑 3 次(2 次是浸泡脚本自己掉票、1 次是真的空答案);旁证 0 次 | §2.3 |
stop_reason 分布 |
两跑全部是 stopped,没有出现 max_rounds 或错误态 —— 但 stopped 区分不了「做完了」和「做了一半」(P1-23),所以另按行为判了「半截回合」,两跑各 1 轮 |
两跑 §6/§7 |
| 4 小时烧了多少 token | 旁证跑 15,840,727(网关配额差值,48 轮);主跑前 36 轮 9,134,454、单轮中位 235,475 | 两跑 §1/§6 |
完整报告:docs/evidence/M5/soak-testmachine/报告-主.md
12 题循环(6 个技能 + 6 类 MCP 直用)、每 5 分钟一题、每 3 轮换一个会话,
走的是和用户一模一样的路径:api 登录 → web BFF 建会话 → 发消息 → 读历史
(故意不直连 daemon —— BFF 的 live-hub 一直挂着 /live,另开一条会抢不到 snapshot 帧,
而且直连就测不到 BFF)。
23:14 的外部 docker 重启之后只剩 2 轮,且都受影响,所以增长率按截到第 36 轮算。
| 项 | 起点 | 终点 | 增量 |
|---|---|---|---|
| 会话文件数 | 1,003 | 1,089 | +86 |
| 会话目录 | 13,876 KiB | 15,952 KiB | +2,077 KiB |
~/.atomcode 合计 |
13,901 KiB | 15,977 KiB | +2,077 KiB |
audit.jsonl |
529 KiB | 695 KiB | +166 KiB |
guard.jsonl |
202 KiB | 259 KiB | +57 KiB |
| daemon 日志 | 4 KiB | 4 KiB | 0 |
前 36 轮网关 token 合计 9,134,454,单轮中位 235,475。 daemon 内存在这段里 128~980 MiB —— 下界那个 128 MiB 不是泄漏也不是抖动, 是 §3 那次 MCP 重挂把 9 个子进程全杀了。
| 轮次 | 时刻 | 现象 | 是什么 |
|---|---|---|---|
r025-k1-deep_analysis |
22:12 | HTTP 401 {"error":"INVALID_TOKEN","needLogin":true} |
浸泡脚本自己的毛病:它只在开头登录一次、之后从不续签。已修(每轮先 GET /api/auth/me,401/403 就重新登录并把重登时刻记进 summary.json),并用故障注入验过 |
r037-k1-deep_analysis |
23:14 | HTTP 200、stop_reason=stopped、正文 0 字、工具 0 次 |
真的空答案。判成失败是对的 —— 这条严判据正是本轮补的(旧判据只看 HTTP 200,会打成「成功」)。发生时刻与外部 docker 重启同秒 |
r038-m1-quickview |
23:17 | 同 401 | 同 r025,docker 重启把 api 会话冲掉了 |
一句话:3 次失败里 2 次是测试脚本的缺陷、1 次与外部 docker 重启同时发生, 没有一次指向发行版本身。但也不能因此说「产品零失败」—— 这一跑没跑满 4 小时,样本就是 38 轮。
这是这次浸泡最有价值的产出,细节在 M5 报告 §3.13,证据在
docs/evidence/M5/p0-10-浸泡里真实复现-兜底反而变成持续失效.txt。
| 时刻 | 发生了什么 |
|---|---|
| 21:28(r016) | 问「看一下我的持仓」,模型一个 MCP 工具都没调,8 次调用全是 read_file/bash —— 而这一轮发消息前 /mcp/status 是 9/9 connected。这就是 P0-10 |
| 同刻 | BFF 的行为判定命中并打了日志:⚠️ 疑似 P0-10:…调了 7 次工具、一个 mcp__* 都没有…正在强制重挂 MCP。检测是对的 |
| 之后 | 重挂之后 9 个 stdio server 同时重新 initialize,2 核机器上要 2~4 分钟。而第一版 forceMcpReload() 只等 60 秒、且只在状态是 connecting 时等;超时后它们变成 error,循环立刻退出 |
| 21:37–21:39 | 我(当运维)照着「重挂能修」又手动发了三次 reload,每次都把 initialize 的计时清零 —— 实测从 1/9 掉到 0/9 |
| 21:39–21:43 | 什么都不做、干等 3 分半,自己回到 9/9 |
| r019 起 | 恢复正常 |
降级窗口是 r016~r018 三轮(约 12 分钟),期间 MCP 1/9。
已修(apps/web/app/lib/atomcode/live-hub.ts):单飞 + 冷却(默认 5 分钟)+ 等够
(默认 240 秒,且 error 也算「还在安定中」,因为子进程其实活着)。
判据抽成 mcpReloadAllowed() 并配 4 条单测,其中一条直接用实测那三次的间隔(48 秒)
钉住「这一步必须被拦住」。
运维要记住的一条:MCP 看起来不对时,重挂一次就走开。 连着重挂只会越修越坏;等 3~5 分钟再看。
完整报告:docs/evidence/M5/soak-hk/报告-旁证.md
| 项 | 实测 |
|---|---|
| 时长 / 轮数 | 4.00 小时 · 48 轮 · 成功 48 · 失败 0 |
| daemon 内存 | 起 953 MiB → 终 953 MiB,全程 947~961 MiB;拟合每小时 +1 MiB、R²=0.11 → 看不出趋势 |
| MCP | 逐轮 9/9 connected,48 次采样无一例外 |
| 会话文件 | 7 → 136 个(+129);会话目录 168 KiB → 2.6 MiB |
| 审计 / guard | audit.jsonl 11 KiB → 218 KiB;guard.jsonl 4 KiB → 77 KiB |
| 网关 token | 15,840,727 |
| 疑似半截回合 | 1(r005 问龙虎榜,模型只发一次 use_skill、正文 25 字「正在加载龙虎榜分析技能并查询…」就以 stopped 结束) |
| 容器重启 | hca-api(19:43)与 hca-web(20:43)各被重建过一次,RestartCount 全程是 0 —— 是另一方在这台机器上做品牌改造时重建的 |
这一跑的限制要写明:20:06 起 hca-web 用的已经不是我构建的那个镜像,
所以它证明的是「daemon 与整栈连续 4 小时的资源行为」,
不能用来证明「发布版的 BFF 连续 4 小时没问题」——那一半由主跑与 §4.4 的真浏览器回归承担。
4 小时里 daemon / web / redis / llm-shim 的错误行都是 0;
hca-api 87 行、hca-postgres 2 行,逐条查完是三个真问题,已记入待办池:
| # | 日志原文(节选) | 是什么 |
|---|---|---|
| P1-26 | [wl_agent] LLM 短评失败: 400 · model_not_allowed ·「内置额度不支持模型『gemini-3.5-flash』」 |
api 的子代理写死了 gemini-3.5-flash,而 HCA 的 key 只放行 hunter-chat / hunter-deep。自选股短评、新闻影响标注这些功能在 HCA 部署里一直是哑的,而界面上不报错(都有 except 兜着)。87 行里的主力就是它 |
| P1-24 | ERROR: relation "backtest_user_pool" does not exist |
这张表属于开源发行版里没有的那个 financedata 库。回测看板的「个人股票池」永远为空 —— M4/M5 回归里 R2 那句「股票池 0 只」就是它 |
| P1-25 | ERROR: column "deleted" does not exist |
internal_uzi.py:948 按 stocks.deleted 过滤,而容器里这张表根本没有这一列。UZI 取股票名那一跳静默退化 |
- daemon 是这套栈里最重的一个(常驻约 1 GiB:Rust 进程 + 9 个 stdio MCP 子进程)。
docker-compose.yml没有给它设内存上限 —— 单机私有化下这是对的(设了反而可能在 长报告那种峰值上被 OOM Kill),但部署前要确认这台机器至少 8 G。 - 监控只看一个数的话,看
hca-daemon的 RSS。绝对值本来就接近 1 GiB, 一惊一乍没意义。 - 但「每小时净增」这个数要连着 R² 一起看。第 2 节每一格都给了 R², 原因是:daemon 常驻约 1 GiB,几十 MiB 的正常抖动照样能拟合出一个 几十 MiB/小时 的斜率 —— 单看那个数会被读成内存泄漏。 R² < 0.5 就是在抖,不是在涨;判泄漏要的是「斜率明显为正 且 R² 高」, 两个条件缺一不可。这一版的实测值见第 2 节。
- 会话落在 daemon 容器的
/data/atomcode/sessions(compose 卷hca_atomcode-home), 每个会话是 5–6 个文件(.jsonl正文 +.meta+.ui.json+.rewind.json+ 锁)。 上游没有自动清理,长期跑会一直涨。 - 建议:按月看一次
du -sh,超过一两 GiB 再按修改时间清理旧会话 (find /data/atomcode/sessions -mtime +90)。别清正在用的: 清之前确认 daemon 上没有活跃回合。 .atomcode/audit.jsonl与guard.jsonl在工作区里,HCA_AUDIT_MAX_MB(默认 64) 会轮转一代;guard 日志目前不轮转,需要的话按同样的方式加。
-
/health与/mcp/status全绿不代表能用 —— P0-10/P0-12/P0-13 三条都是 「状态面板全绿但模型其实废了」。唯一可靠的健康判据是真发一条消息。 建议的探活:每天一次python3 tools/e2e/web_turn.py --ask "600519 现在什么价?" --name daily || 告警
它的退出码可以直接接监控(全过 0,有一题不过 1)。 判据在
judge_turn()里,三条同时成立才算过:HTTP 200 且stop_reason不是错误态 且 这一轮真有产出(正文非空或调过工具)。⚠️ 这条判据 v0.1.1 才修对 —— 之前只看 HTTP 200,而模型通道整个断掉时POST /message照样返回 200、stop_reason=provider_error、正文 0 字, 探活会报绿(M5 用 Ollama 通道实测到,并用故障注入验过修复)。 更严的做法是再加一条「这一轮调到了mcp__*工具」—— 那能一并盯住 P0-10。 -
不要在网页之外再开
/live消费者(P0-13)。调试也走tools/e2e/web_turn.py。 这四小时的浸泡就是按这条规矩跑的。
P0-10 / P0-13 那一族失效(模型手里没有 MCP 工具、退化成 bash/glob 乱翻)
救不回当时那一轮,但可以限制它的爆炸半径。两次实测的代价:
M1 §7 那次连发 26 次 bash、约 102 万 token;M4 §4.3 那次 30 次调用、
stop_reason=max_rounds、网关配额差值 982 548。
两道现成的闸:
# deploy/.env
ATOMCODE_TURN_MAX_ROUNDS=30 # 已是默认值,别调大
HCA_BUDGET_ENABLED=1 # 默认关,长期运行的部署建议打开
HCA_BUDGET_SESSION_TOOL_CALLS=80 # 单会话工具调用上限(这个计数最可靠)
HCA_BUDGET_DAILY_TOOL_CALLS=500工具调用次数这个计数一路可靠,token 那个不可靠(待办池 P1-12),所以硬限额优先用次数。
v0.1.1 起 BFF 还会在回合结束时做一次行为判定:一轮调了 ≥3 次工具、
一个 mcp__* 都没有、而且确实用了 bash/glob/read_file 这类兜底工具,
就往容器日志里打一条 ⚠️ 疑似 P0-10 并自动重挂一次 MCP(让下一轮能恢复)。
这是目前唯一的观测点 —— 监控建议直接 grep 这条:
docker compose -p hca logs web | grep '疑似 P0-10'- 投研会话贵:旁证跑 4 小时合计 15,840,727 token(48 轮);主跑单轮中位 235,475、最大 690,233(见 §1 与两份逐项报告的「分布」一节)。技能类问题 (深度分析 / UZI 扫描)比行情类贵一个数量级。
- 私有化部署如果按量计费,建议在网关侧配日限额,别指望在 agent 这一侧限
——
/live的tokens事件多数轮次是 0(待办池 P1-12),本地量不准。
- 换 AtomCode 底座之前先跑
tools/upstream_diff.sh --to <新版本>, 确认四个外部面没变;变了就逐条看。但源码比对只能证明接口没动,不能证明行为没变, 所以差异报告第 4 节那 5 项冒烟必须实跑。 - 升级与回滚都已实测(M4:451 s / 61 s)。回滚靠
install.sh --rollback。
- MCP 看起来不对时,重挂一次就走开。连着重挂只会越修越坏(§3 实测 1/9 → 0/9), 等 3~5 分钟再看。v0.1.1 起 BFF 自己也有冷却,运维手动操作请照同一条规矩。
- 别只看成功率,要抽查正文。「宣告了动作然后就停了」那一类(P1-23)在任何 只看 HTTP / 成功率的监控里都是绿的,两跑各出现 1 轮。
- 浸泡要看容器日志。这一跑 4 小时里产品功能一次没崩,而
hca-api的 87 行 WARNING 里藏着三个真问题(§4.1)——不翻日志就一条都发现不了。
| 问题 | 为什么没答 |
|---|---|
| 连续 4 小时的发布版 BFF | 主跑(跑的是发布版)被外部 docker 重启打断在 3 小时 03 分;旁证跑满了 4 小时但 BFF 中途被换过。要一次干净的 4 小时,得有一台 HCA 独占的机器(U-15) |
| 90 天以上的长期运行 | 只做到 4 小时。会话文件与日志按 §1 的速率外推:每 4 小时约 +2.5 MiB 会话 / +0.2 MiB 审计,一年约 +5.5 GB / +0.4 GB —— 这是外推不是实测,所以运维建议里写的是「按月看一眼 du -sh」而不是一个具体阈值 |
| 多人并发 | 已拍板决策 10 接受「同一时刻只能一个人在生成」,P0 不做 |
| P0-10 的触发条件 | 这次复现了一例(r016),但仍然不知道它为什么发生 —— 没有可观测手段(/live 的 snapshot 里没有工具清单,questions B12)。48+38 轮里只此一例,无法据此给出发生率 |