Skip to content

Latest commit

 

History

History
249 lines (188 loc) · 16.9 KB

File metadata and controls

249 lines (188 loc) · 16.9 KB

HunterCode · AtomCode 发行版 · 4 小时稳定性浸泡报告

对应计划 v0.2 TP-11 与 M5 任务书第 3 条。每个数字都从原始记录算出来, 取不到的写 ,不补 0、不插值。时间一律上海时间。 脚本 tools/stability/soak.py;两跑的原始记录与逐项报告在 docs/evidence/M5/soak-testmachine/docs/evidence/M5/soak-hk/

0. 先说清楚:为什么是两跑,各自能证明什么

主跑 · 测试机 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)。


1. 一页结论

实测 出处
内存有没有泄漏 没有看出泄漏。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 KiBguard.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

2. 主跑(测试机)——跑的就是要发布的那一版

完整报告:docs/evidence/M5/soak-testmachine/报告-主.md

2.1 跑了什么

12 题循环(6 个技能 + 6 类 MCP 直用)、每 5 分钟一题、每 3 轮换一个会话, 走的是和用户一模一样的路径:api 登录 → web BFF 建会话 → 发消息 → 读历史 (故意不直连 daemon —— BFF 的 live-hub 一直挂着 /live,另开一条会抢不到 snapshot 帧, 而且直连就测不到 BFF)。

2.2 干净窗口的数字(第 1~36 轮,20:11:51 → 23:07:49,3 小时 02 分)

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 个子进程全杀了。

2.3 三次失败,逐条说清是什么

轮次 时刻 现象 是什么
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 轮。


3. 主跑里最重要的发现:P0-10 真的复现了,而我们的兜底把它变严重了

这是这次浸泡最有价值的产出,细节在 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 分钟再看。


4. 旁证跑(香港)——唯一跑满 4 小时的那一份

完整报告: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 KiBguard.jsonl 4 KiB → 77 KiB
网关 token 15,840,727
疑似半截回合 1r005 问龙虎榜,模型只发一次 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.1 错误日志里挖出来的三条(浸泡不看日志就发现不了)

4 小时里 daemon / web / redis / llm-shim 的错误行都是 0hca-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:948stocks.deleted 过滤,而容器里这张表根本没有这一列。UZI 取股票名那一跳静默退化

5. 运维建议

5.1 内存

  • 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 节。

5.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.jsonlguard.jsonl 在工作区里,HCA_AUDIT_MAX_MB(默认 64) 会轮转一代;guard 日志目前不轮转,需要的话按同样的方式加。

5.3 错误与告警

  • /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。 这四小时的浸泡就是按这条规矩跑的。

5.4 给 P0-10 装一个兜底闸(强烈建议)

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'

5.5 成本

  • 投研会话:旁证跑 4 小时合计 15,840,727 token(48 轮);主跑单轮中位 235,475、最大 690,233(见 §1 与两份逐项报告的「分布」一节)。技能类问题 (深度分析 / UZI 扫描)比行情类贵一个数量级。
  • 私有化部署如果按量计费,建议在网关侧配日限额,别指望在 agent 这一侧限 —— /livetokens 事件多数轮次是 0(待办池 P1-12),本地量不准。

5.6 升级

  • 换 AtomCode 底座之前先跑 tools/upstream_diff.sh --to <新版本>, 确认四个外部面没变;变了就逐条看。但源码比对只能证明接口没动,不能证明行为没变, 所以差异报告第 4 节那 5 项冒烟必须实跑。
  • 升级与回滚都已实测(M4:451 s / 61 s)。回滚靠 install.sh --rollback

5.7 这一轮血泪换来的三条

  1. MCP 看起来不对时,重挂一次就走开。连着重挂只会越修越坏(§3 实测 1/9 → 0/9), 等 3~5 分钟再看。v0.1.1 起 BFF 自己也有冷却,运维手动操作请照同一条规矩。
  2. 别只看成功率,要抽查正文。「宣告了动作然后就停了」那一类(P1-23)在任何 只看 HTTP / 成功率的监控里都是绿的,两跑各出现 1 轮。
  3. 浸泡要看容器日志。这一跑 4 小时里产品功能一次没崩,而 hca-api 的 87 行 WARNING 里藏着三个真问题(§4.1)——不翻日志就一条都发现不了。

6. 这份报告没有回答的问题

问题 为什么没答
连续 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 轮里只此一例,无法据此给出发生率