📚 离职知识库

06 · 技术资料:服务器 Agent 层(P6)

#🚨 开工前先读这一条

2026-07-30 深夜的实地勘察发现:东京 VPS(tky66.42.32.208)此刻正处于一次进行中的高负载 / IO 阻塞事件,不是历史问题,是活的故障。

实测证据(勘察时 + 写文档时二次确认):

load average: 107.76 / 101.55 / 71.36     ← 8 核机器,正常应 < 8
%steal 94.46, %idle 0.34                  ← CPU 几乎被虚拟化层抢走
vmstat 的 b 态进程数:104 / 62            ← 正常应接近 0
swap 从 0 涨到 5.6% 并持续上涨
systemd-journald 因 watchdog 超时被 ABRT 杀死,restart counter = 3
<HOME>/monitor/sysmon.log 从 22:15 起断更  ← 监控脚本自己被卡死了
勘察时两条只读的 ps aux 卡死 10 分钟无输出

这套特征和历史上那次 inode_switch_wbs kworker 风暴(触发条件:密集短连接 SSH)高度吻合。

#因此 P6 的第一件事不是施工,是体检

# 健康门:必须全部满足才允许进入 P6 的任何新增操作
ssh <VPS> 'cat /proc/loadavg; tail -3 <HOME>/monitor/sysmon.log; systemctl is-active systemd-journald'
条件 门槛
1 分钟 load average < 8
<HOME>/monitor/sysmon.log 最近 10 分钟内有新行(监控活着)
systemd-journald active,且没有持续重启

任一条不满足 → P6 整体降级

  • ⛔ 不做任何新增部署(连建 profile 都不做)
  • 只做一次只读采证(下面 §1 的命令清单),把输出存进 docs/night/vps-health.md
  • blockers.md 顶部写一条 🚨 需要人类立即处理:东京 VPS 负载风暴
  • 转去做别的阶段或收尾

⚠️ 无论健康与否,全程都要控制 SSH 连接次数。 密集短连接正是历史触发条件。 ✅ 正确做法:一次 SSH 跑一串命令(用 && 或 heredoc 拼起来)。 ⛔ 错误做法:每条命令开一个新 ssh <VPS> '...'


#1. 只读采证命令(一次连接跑完)

ssh <VPS> 'bash -s' <<'EOF'
echo "===== 负载 ====="; cat /proc/loadavg; uptime
echo "===== 内存 ====="; free -h
echo "===== 磁盘 ====="; df -h /
echo "===== 监控最近 10 行 ====="; tail -10 <HOME>/monitor/sysmon.log
echo "===== journald ====="; systemctl is-active systemd-journald
echo "===== 端口 ====="; ss -tlnp 2>/dev/null | head -40
echo "===== docker ====="; docker ps --format '{{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}' 2>/dev/null
echo "===== 运行中的自定义服务 ====="; systemctl list-units --type=service --state=running --no-pager --no-legend | head -40
echo "===== hermes 目录结构 ====="; find <HOME>/.hermes -maxdepth 2 -type d 2>/dev/null
echo "===== hermes 帮助 ====="; timeout 30 /usr/local/lib/hermes-agent/venv/bin/python -m hermes_cli.main --help 2>&1 | head -60
echo "===== nginx server_name ====="; nginx -T 2>/dev/null | grep -E 'server_name|listen ' | head -30
echo "===== env 文件位置(只列路径) ====="; find /root -maxdepth 3 -name "*.env" -o -maxdepth 3 -name ".env" 2>/dev/null
EOF

把输出原样存进 docs/night/vps-health.md。⛔ 输出里若含任何密钥值,必须打码后再落盘


#2. 现场已知事实(已勘察确认,不用重查)

#2.1 机器

  • agent-tky-01,Ubuntu 24.04.4 LTS,内核 6.8.0-136
  • 8 核 / 62G 内存 / 450G 盘(已用 97G,余 335G)
  • 有 Docker 在跑(/var/lib/docker/rootfs/overlayfs/* 多个 overlay 挂载)→ Telegram bot / LINE bot 很可能是 Docker 容器,不在 systemd unit 里

#2.2 systemd 自定义服务清单(⛔ 全部碰不得)

unit 说明 级别
hermes-gateway.service Hermes 网关本体 🔴 生产核心
feishu-bot.service 拉斐尔飞书 bot 🔴 生产核心
grok-shim.service grok-4.5 反代,Hermes 大脑依赖 🔴 生产核心
ollama.service 本地 LLM,拉斐尔依赖 🔴 依赖链上
ashare-auto-git.service <HOME>/.hermes/.env 🟡 别碰
ashare-recommend.service 🟡 别碰
actions.runner.LeoLee0812-brain-daily... 自建 CI runner 🟡 别碰
scribe-api.service / scribe-worker@.service 白板视频渲染 🟡 别碰
n8n.service / redis.service 🟡 别碰
novnc.service / vncserver.service 图形桌面 🟢 辅助
tky-boot-alert.service 开机告警 🟢 辅助

#2.3 Hermes

# /etc/systemd/system/hermes-gateway.service(已读)
ExecStart=/usr/local/lib/hermes-agent/venv/bin/python -m hermes_cli.main gateway run
WorkingDirectory=<HOME>/.hermes
Environment="HERMES_HOME=<HOME>/.hermes"
ExecStopPost=-/usr/local/lib/hermes-agent/venv/bin/python -m gateway.cgroup_cleanup
Restart=always
  • 装在 /usr/local/lib/hermes-agent/(Python venv),HERMES_HOME = <HOME>/.hermes
  • ExecStopPostgateway.cgroup_cleanupHermes 内部用 cgroup 管子进程,多 profile 可能自带资源边界(推测,未证实
  • ⚠️ 最大信息缺口:-p <profile> 的确切语义没验证到。上一版方案里写的 hermes profile create emp_wang --clone 命令未经实证,⛔ 不要当成事实照抄。
  • P6 第一步必须先跑 hermes_cli.main --help 拿到真实的子命令,再决定怎么建 profile。

#2.4 拉斐尔飞书 bot(新 bot 照抄的范式)

# /etc/systemd/system/feishu-bot.service(已读)
Environment=BOT_BACKEND=hermes
Environment=OLLAMA_MODEL=qwen2.5:3b
ExecStart=/bin/bash -c 'lark-cli event consume im.message.receive_v1 --as bot < <(tail -f /dev/null) | /usr/bin/python3 <HOME>/night-run/bot_bridge.py'
Restart=always
  • 接入方式:长连接,但不是自己写 WebSocket,而是用 lark-cli event consume 输出 NDJSON,管道给 <HOME>/night-run/bot_bridge.py 处理
  • 新 bot 照抄这个结构:另起一份 unit 文件,换成新 App 的凭据和新的 bridge 脚本。⛔ 不要改 feishu-bot.service

#2.5 🔴 顺手发现的安全隐患

feishu-bot.service 里把 DEEPSEEK_API_KEY 明文写在 Environment=(不是 EnvironmentFile=)。任何能跑 systemctl cat feishu-bot 的人都能看到明文。

今晚 ⛔ 不要修它(改 unit + restart 会重启生产 bot,且机器正在风暴中)。写进 blockers.md 作为「明天建议人类处理」的一条。

#2.6 密钥文件

  • <HOME>/.hermes/.env(600)—— 被多个服务共读ashare-auto-git.service 明确 EnvironmentFile=- 引用它,Hermes 本体大概率也读它
  • <HOME>/monitor/monitor.env(600)—— 监控阈值 + 飞书 webhook
  • 绝对不要覆盖、不要重写这两个文件。 只允许追加,且追加前必须备份。

#3. 如果健康门通过 —— 增量施工步骤

总原则:只新增,不修改,不删除。每一步做完停下来看一眼负载。

#步骤 0 · 备份(不做这步不许往下走)

ssh <VPS> 'D=<HOME>/backup-baton-$(date +%Y%m%d-%H%M) && mkdir -p $D && \
  cp -a <HOME>/.hermes/.env $D/hermes.env.bak && \
  cp -a /etc/systemd/system/feishu-bot.service $D/ && \
  cp -a /etc/systemd/system/hermes-gateway.service $D/ && \
  chmod -R 600 $D && ls -la $D'

#步骤 1 · 摸清 Hermes 的真实 profile 机制

ssh <VPS> 'timeout 60 /usr/local/lib/hermes-agent/venv/bin/python -m hermes_cli.main --help 2>&1 | head -80; \
  echo "---"; find <HOME>/.hermes -maxdepth 2 -type d'

--help 的真实输出决定后面怎么做。 ⛔ 不要凭上一版文档里的命令硬试。 拿不到帮助 / 命令挂起 → 记 blocker,P6 到此为止。

#步骤 2 · 先建 1 个 profile,观察

只建 emp_wang 一个。建完:
1. 跑一次简单提问,确认能出结果
2. 等 5 分钟,再看 load average 和 sysmon.log
3. load 没涨、监控正常 → 才建第二个、第三个

禁止一次性把 3 个 profile 全建出来。 这台机器已经证明过自己会失控。

#步骤 3 · 每个 profile 写入 SOUL 铁律(AC-8.2.3)

这是整个 Agent 层最关键的一段。历史教训(raphael-lore 那次):即使强制预加载 skill,模型依然会跳过检索、凭训练记忆自信编造,连人名都编得很像。

必须写成和"铁律"同级的强制条款:

## 铁律(不可违背)

涉及任何客户、报价、合同、供应商、交接的问题,**必须先执行 baton-kb 检索**。
回答必须带出处(文件名 + 页码)。
**绝对禁止凭记忆编造。** 检索不到就明确说"我的资料里没有"。
涉及交接来的内容,必须标注「来源:<前任> 交接,<日期>」。
你只知道自己资料库里的东西。别人的资料你看不到,也不许猜。

⚠️ Hermes 的 MEMORY.md注入上限约 2200 字符,只放人设和指针,别塞知识。知识走 skill 反向 HTTP 调 Vercel 的 /api/search

#步骤 4 · 瘦服务(agentd)

  • 只绑 127.0.0.1,⛔ 不要暴露到公网
  • 端口必须先看 ss -tlnp 再选,⛔ 不要硬编码(8000-8100 段常被占)
  • 每员工一个队列:不同人并行,同一人串行(Hermes 是有状态会话,同一 profile 并发会互相踩上下文 —— 这是踩过的坑)
  • systemd unit 必须加资源限制(现有 unit 都没加,这是这台机器失控的原因之一):
    MemoryMax=2G
    CPUQuota=100%

#步骤 5 · 飞书新应用(若凭据可用)

  • ⚠️ 必须是新建的独立 App ID。复用拉斐尔的应用会两边抢答。
  • ✅ 已确认:不同 App ID 的长连接各收各的事件,不会抢消息(飞书事件订阅按应用维度隔离)
  • 新起一份 unit,照抄 feishu-bot.service 的结构,⛔ 不要改原文件
  • 凭据要人工在飞书后台创建 → 拿不到就跳过,记 blocker(AC-8.3.3)

#4. P6 的红线(再列一遍,因为最容易在深夜手滑)

rm / rm -rf                      —— 任何情况下都不许
⛔ systemctl stop | disable | mask   —— 任何现有服务
⛔ pip uninstall / apt remove
⛔ 覆盖 <HOME>/.hermes/.env 或任何现有 .env(只许追加,且先备份)
⛔ 修改任何现有的 systemd unit 文件
⛔ kill 任何不是你自己刚起的进程
⛔ 删掉 <HOME>/.hermes 或 mv 走它
⛔ 密集短连接 SSH(一次连接跑一串命令)
⛔ 一次性拉起多个新进程

如果你怀疑自己已经影响了现有服务:立刻停手 → 用备份恢复 → 在 blockers.md 最顶部写 🚨 需要人类立即处理 + 完整现场记录 → 只做验收包和邮件,别的什么都别做。


#5. P6 的降级路径(很可能会用上)

情况 降级到
机器仍在风暴中 只采证 + 写报告,AC-8.* 全部标 SKIPPED
hermes --help 拿不到 / profile 机制不明 只写一份「明天照着做的操作单」,AC-8.2.* 标 BLOCKED
飞书凭据拿不到 跳过飞书,只做命令行验证(AC-8.3.2 标 SKIPPED,这是 AC 里明确允许的)
时间不够(已过 T+8:30) 直接放弃 P6,去做 P7 验收包。这是总纲 §3.2 里第一个该砍的阶段

P6 全部跳过不算失败。 服务器安全 > 功能进度。明早人类看到一份诚实的「机器在风暴中,我没敢动」的报告,比看到一台被搞坏的服务器好一万倍。

来源:沉淀/03-项目方案与交接/接棒-通宵施工包-20260731/06-技术资料-服务器Agent层.md(整理于 2026-08-18)