第 17 章 红线与协作约定
这一章是给下一个 agent / 新同学的第一页操作规则。
每条红线都配"凭什么是红线"——全部来自 18 章或部署文档里的真实事故,不是偏好。
来源:~/.agent/goal.md红线 6 条、docs/项目背景与问题总览.md§8 硬约束 + §12 协作约定、
容器规范~/CLAUDE.md(§1 端口 / §2 持久化 / §5 MinIO / §8 负载感知)。
17.1 九条红线
| # | 红线 | 一句话 |
|---|---|---|
| 1 | 停服先问 | 改引擎配置 / 换二进制 = 重启 = 停服,必须先经用户同意 |
| 2 | 压测只打普道 8092 | 快道 8091 留给真实用户 |
| 3 | 钉核不可破 | A taskset 0-15、B 38-53;构建用 16-31;不许把两者混到同一批核 |
| 4 | 密钥不进代码 / git / 文档 / 日志 | 包括不抄进书、不 echo 到终端 |
| 5 | 结论必须带实测数字与出处 | 测于何时、何配置、多少;口径冲突写出来不静默选 |
| 6 | 并发改动必须过逐 token 一致性 | 官方 tools/batch_test.py;A/B 要交替顺序防偏 |
| 7 | 改了有意义的东西记一条 note | ~/bin/note --agent "…",北京时间 |
| 8 | 用户问过的信息要汇总 | 追加 ~/docs/notes/agent-qa.md;长期偏好另写 memory |
| 9 | 重要产出备份到 MinIO | 桶 qwen-3.8-flash;本机路径 + 桶里一份 |
红线 1:停服先问
凭什么是红线:服务对外公网在线($PUBLIC_URL → frps → Pod :8080),
重启一条 lane 的真实代价是 6–13 分钟不可用(16 章 §16.6 实测 4m55s / 6m02s / 6m36s / 7m15s),
而且网关不会 failover(16 章 §16.4)——被切那条 lane 对应的模型 ID 会直接报错给真实用户。
switch_engine.py 因此在代码里实现了这道闸门:不带 --yes 只打印计划;
10-17 20:36 两次 rollback 都是 [dry-run],就是这道闸门在起作用。
执行机制:switch-engine 包装脚本的头注释第一行就写"涉及重启,须遵守停服须先经用户同意的红线";
README-switch.md §7 检查清单第一条是"用户已同意停服/重启窗口"。
边界提醒:supervisorctl restart rag-proxy 只需 5 秒,但它清空网关内存里的全部会话/嵌入缓存/组装块
(硬约束 10)——所以"秒级"不等于"可以随手做",仍需先问。
红线 2:压测只打普道
凭什么是红线:10-07 晚的驱逐风暴(18.3)里,1M 精度全量套件刻意全部打到 strata-b
(ctx1m_full.sh 注释原文:“不碰 strata-a(快道 8091,留给真实用户)”),
当晚 strata-b 全天 5318 条请求 / prefill 16,500 秒。压测流量与真实用户共享普道,
所以这条红线的另一面必须一起记住:普道被打满时,真实用户就在同一队列里。
执行机制:bench/ctx1m_full.sh、run_guard.sh 默认 --model strata-b;
dual_lane_test.py 直连 8091/8092 时也是显式指定。
红线 3:钉核
凭什么是红线:事故 1(18.2)——两个引擎的 CPU 专家池在 32 核 cgroup 里互相抢核,
solo 从 76.8 tok/s 崩到 9.3,8 路里 6 路拿不到 token;降 worker 数无效,钉核即翻案到 98–107。
这是本项目唯一一条"改错就立刻崩一个数量级"的配置。
执行机制与残留风险:run-iq3_xxs-{a,b}.sh 里 TASKSET 有默认值(0-15 / 38-53),
但它是变量——任何 active-<lane>.env 覆盖它都必须复查(15 章 §15.3)。
构建侧同样守这条:switch_engine.py 编译时用 taskset -c 16-31,刻意避开生产核段。
红线 4:密钥
凭什么是红线:这不是抽象风险——引擎配置文件里确实有 api_key 字段:
deploy/engines/profiles/ssebed/strata-a.json / strata-b.json、Strata/strata-iq3_xxs-{a,b}.json
都含明文 key(权限 0600,deploy/engines/ 在 .gitignore 里);
服务鉴权 key 落在 ~/logs/strata-api-key.txt;layout-restore.sh 会 cat 它来探健康。
总览 §12.4 特别点名"~/docs/notes/ 里已有的明文凭据不要再抄到别处"。
本书与工具的执行方式(可照抄的做法):
- 读含 key 的配置:
grep -viE 'api_key|secret|token|password' <file>整行丢弃,或jq del(.api_key);
不要用cat/sed -n过滤后输出。 - 用 key 发请求:
KEY=$(cat ~/logs/strata-api-key.txt); curl -H "Authorization: Bearer $KEY" …
—— key 只进 shell 变量,永不出现在命令回显或文档里。 - 压测脚本一律从文件读:
KEY = open("/home/cloud/logs/strata-api-key.txt").read().strip()(13 章)。 switch_engine.py从不打印 key,status只显示路径与引擎名。
红线 5:数字带出处
凭什么是红线:14 章 §14.11 列了 15 条已过期/冲突口径,其中只有 4 条是"数字真的变了"
(核数 16→32、内存 100→216 GiB、BABILong 0/5→2/15、并发矩阵两档),
其余全是行号漂移、端口过期、口径换时代。事故 6(18.7)更是直接由"四个计数口径没人声明谁数的"引起。
执行机制:README 写作约定 2;14 章作为全书数字的单一索引;
每个数字必须能回答"测于何时、何配置、出处文件",否则不进书。
红线 6:逐 token 一致性
凭什么是红线:批槽 / MTP 链 / 专家分组这类改动会改变生成结果而不只是改变速度。
总览 §12.3 要求并发相关改动必须过官方 tools/batch_test.py 的逐 token 一致性
(10-06 实测 48/48 IDENTICAL ×4,10 章);A/B 要交替顺序防系统性偏移。
注意:batch_test.py 要独占 GPU,只能在停服窗口跑(docs/reports/pr559-review.md)→ 它同时触发红线 1。
红线 7–9:记录与备份
见 17.3。凭什么是红线:这台 Pod 的 /home/cloud 之外全是临时盘(CLAUDE.md §2),
且"谁改了什么"只有靠 note 才能事后还原——本书 14 章一半的出处就是 note 与报告。
17.2 协作约定(不是红线,但必须照做)
| 约定 | 内容 | 出处 |
|---|---|---|
| 前端发布 | cd ~/Strata/serve/webapp && bash tools/deploy.sh,不用重启引擎;产物落 Strata/serve/web/(serve 直接读盘) |
总览 §12.6 |
| 改代理才重启代理 | 改 rag-proxy/rag_proxy.py 才 supervisorctl -c ~/deploy/supervisord.conf restart rag-proxy |
同上 |
| 不改 supervisord.conf 的目录约定 | ~/deploy/supervisord.conf 与 deploy/<name>/ 是平台约定;换引擎靠 active-<lane>.env,不靠改 conf |
16 章设计目标 1 |
| 不硬编码公网地址 | 用 $PUBLIC_URL / $PUBLIC_HOST / $PUBLIC_PORT(只在部署进程环境里) |
CLAUDE.md §1 |
| 只有 8080 对外 | 8091/8092 只绑 127.0.0.1;要新端口找管理员,别改集群 manifest | CLAUDE.md §1、§9 |
| 重要数据别只放本地 | /home/cloud 持久,/tmp 等会丢;长期保存进 MinIO |
CLAUDE.md §2、§5 |
| 权重不进 home | 模型权重/公共数据集在只读 /shared,代码按路径读 |
CLAUDE.md §3 |
| 启动重活前看负载 | /shared/pressure/pressure.json 任一 level=="high" 就降级或退避 |
CLAUDE.md §8 |
| pip 用镜像 | pip install --break-system-packages -i https://mirrors.aliyun.com/pypi/simple(公网 PyPI 不通) |
CLAUDE.md §9 |
| 时间一律北京时间 | Pod 是 UTC;TZ=Asia/Shanghai date;~/bin/note 已内置换算 |
CLAUDE.md「时区」 |
| 日志时区要先换算 | supervisord.log 与 rag-proxy 的 [HH:MM:SS] 前缀 = UTC;switch-engine.log、infer_store 归档名 = 北京时间 |
18.7 教训 |
| 改文件自己动手 | subagent 只可用于只读检索;并行 subagent 上限 1–2(模型 API 并发上限 2,429 后可恢复) | memory no-subagent-for-edits、subagent-concurrency-cap |
| 不要把特殊 token 字面量写进正文 | 引擎会当 EOS(事故 3);改含这类字面量的代码行用行号/脚本,别让模型逐字复述 | memory claude-local-early-stop |
| 绝对路径先验证 | 先 ls/realpath;不要把从未成功读取过的路径当成"曾经存在"(事故 4) |
memory verify-paths-before-use |
17.3 记录纪律三件套
① note —— 谁改了什么
~/bin/note --agent "一句话总结(含关键路径或结果)" # 北京时间(脚本内置 +8 换算)
- 触发条件:完成对
/home/cloud下代码 / 配置 / 部署 / 文档的有意义的改动后跑一次;
一次任务记一条;琐碎读取与临时试验不必记。 - 落点:
~/docs/notes/notes.jsonl,是「写周报」(skillweekly-report)的素材源之一。
② agent-qa —— 用户问过的信息要留下
每次实质性问答(不是寒暄、不是一次性闲聊)得到回答后,把
「问题 + 结论/关键事实 + 代码或文档出处」追加到 ~/docs/notes/agent-qa.md(北京时间、Markdown 小标题)。
- 只记可复用的知识:架构/原理、决策与理由、实测数字、踩坑、配置/命令;
- 不要把整段对话原样粘贴,不要把密钥写进去;
- 产生了长期有效的偏好/事实 → 另写一条 memory(
~/.claude/projects/-home-cloud/memory/)并在MEMORY.md加索引。
③ MinIO 备份 —— 每份重要产出都留一份
默认动作,不是"以后再说":有价值的产出写进 ~/docs 之类的本机路径之后,再往桶里备份一份。
set -a; . ~/.minio.env; set +a # 凭证(chmod 600,已在 .gitignore 里)
# 用系统 python3(minio 已装;Strata/.venv 里没装)
- 桶
qwen-3.8-flash,端点集群内 S3 兼容(以~/.minio.env里的为准); - 写桶前先看目标前缀、别覆盖已有对象;大件用
fput_object;上传日志留~/logs/; - 凭证绝不进代码 / git / 文档 / 日志。
17.4 给 agent 的默认动作边界
本项目对 agent 的默认要求是只读优先,写操作要显式授权。本书写作任务就是范例:
| 允许 | 禁止 |
|---|---|
| 读代码、读日志、读留档、读配置(脱敏方式见红线 4) | 重启任何服务、改引擎配置、supervisorctl restart |
读线上 /v1/status、/health(只读 GET,一次) |
打压测流量、循环发请求、跑 batch_test.py |
写 ~/book/、~/docs/、memory |
改 supervisord.conf、改 active-*.env、改 lane 配置 |
| 需要实测数字 → 从报告/留档取并标注口径 | 为了拿数字而现场跑实验 |
"需要实测数字一律从留档取"这条不是保守,是纪律:现场跑一次 50k prompt 请求会占 GPU、
会污染前缀缓存、还可能触发红线 1。留档里已经有足够多的数字(14 章)。
17.5 本章小结
- 九条红线里,六条有明确的事故编号(停服先问 ← 6–13 分钟冷启动;压测只打普道 ← 驱逐风暴;
钉核 ← 9.3 tok/s;密钥 ← 配置文件里真有明文 key;数字带出处 ← 15 条过期口径;逐 token 一致性 ← 批槽会改变结果)。 - 红线与约定的区别在于违反后果:红线会造成用户可见的故障或不可逆损失;约定会造成混乱与返工。
- 三条记录纪律(note / agent-qa / MinIO)不是行政要求:
这台机器随时可能被重建,只有~/home/cloud与 MinIO 里的东西能留下来(15 章 §15.7)。 - 对 agent 最重要的一条其实是 17.4:先假设自己只能读。需要写、需要重启、需要跑实验,都要先问。