Mprompt · System Prompt 瘦身——三刀与收益递减 —— 开发文档(面试向)#
这篇讲的是为什么这么做、做了哪些取舍、踩了哪些坑,不讲具体代码。目标是读完能用大白话讲出来。 它也是一份方法论记录:怎么用数据驱动一个「看起来该做、其实收益递减」的优化。
一句话概括#
主 Agent 的 system prompt 从 11069 token 一路砍到 1991 token(-82%),全程红线(P0)不破。 但真正的结论不是「砍了多少」,而是:第一刀低风险高收益,第二、三刀收益递减、风险递增—— 因为省下的是 84% 命中率的「缓存打折 token」,而主循环的真实开销由「Agent 走几步」主导,不由 prompt 长度主导。瘦身最大的价值是副产品:一次 prompt 职责审计——想清楚每条指令到底该由 机制、schema 还是 prompt 来兜。
0. 背景:为什么想瘦#
主 Agent 和所有 fork 出去的子 Agent 共享同一份 system prompt(同质 fork 的硬约束)。这份 prompt 是每一步 LLM 调用都要携带的固定前缀,最初长到 11069 token。直觉上「这么长肯定有水分」, 于是想砍——省 token、省钱、也让 prompt 更好维护。
这个直觉一半对一半错,下面三刀会说清楚。
1. 第一刀(11069 → 4566,-58.7%):砍纯冗余,质量打平#
第一刀砍的全是不损失任何语义的冗余,靠四个手段:
- 不复述机制,只讲动机。 fork 深度上限、跨平台 fork 次数、子 Agent 检索预算、强制收尾……
这些规则
app/harness/里全有 Hook 硬拦(MAX_FORK_DEPTH、terminal_enforce等)。prompt 里再写一遍规则是重复——机制才是真正兜底的那层,prompt 只需讲「为什么」。 - 不复述工具能力。 每个工具的参数与能力在它自己的 docstring 里(进 tool schema,模型每步 都看得到)。prompt 只保留「同类工具选哪个」的决策边界。single source of truth。
- 子 Agent 的「执行方通则」移出 system prompt。 这段只有真正 fork 出去的子 Agent 用得上,
却让主循环每一步都为它付 token。改成 fork 时由
dispatch_tool拼进 demands 前缀——system prompt 对主/子仍逐字相同(同质 fork 不破、缓存前缀不破),但主循环不再为它买单。 - 正面陈述优先。 给替代动作,而不是裸「不要 X」。
结果:静态前缀 -58.7%,主循环累计输入 -37.9%、单条成本 -17.5%、步数 -18.8%。质量用双臂 Rubric 评测对照,基本打平(均分在噪声内,P0 通过率不降)。
面试怎么讲:prompt 里最该先砍的,是「已经有别人兜底的东西」——机制交给 Hook、能力交给 tool schema、子 Agent 的话只对子 Agent 说。这一刀是无损的,因为删的不是语义,是重复。
2. 怎么验证「质量没降」:双臂评测 + token 实测#
瘦身最怕「省了 token、悄悄把能力砍没了」。所以每一刀都用同一套方法验证:
- 双臂 Rubric 对照:同一份 18 条种子集,
PROMPT_VARIANT开关切 full / slim,各跑一遍, 同一个 judge、复用同一把评分细则缓存。比的是同一次对照内两臂的相对差。 - token 实测:不看理论前缀长度,看真实运行的
carried_input(主循环累计携带输入)、步数、 成本——因为理论和实际会差很远(见第 4 节)。
一个关键纪律:跨轮的均分不可直接比。种子集里 n=1 的桶太多,加上 Agent 本身有随机性,同一条 query 两次跑分能从 0 跳到 100。所以数字只在「同一次对照内」有意义,且单轮不足以对质量下定论。
3. 中途踩的坑:一个会污染对比的既有 Bug#
跑第一轮对照时,日志一直刷 Pipeline shoppingx_hybrid_pipeline is not defined、OpenSearch 检索 失败,降级为空召回。查下来是个与瘦身无关的既有 Bug:category_insight 的 OpenSearch 检索路
一直静默降级成空召回——代码常量里的索引名(shoppingx_category_kb)和实际索引(globex_ category_kb,2044 张卡、真实向量都在)对不上,且 hybrid search pipeline 从没注册过。
这坑的要命之处:它不报错、只是「搜不到品类知识」,会让「品类洞察 / 多约束精挑」几个桶的表现 普遍变差,从而掩盖掉 prompt 本身的差异。要是不先修,两臂都跑在「退化环境」上,对比结论不可信。
修法是把常量对齐到仓库实际名 + 注册 pipeline(历史索引数据本身是健康的,零重编码)。修完降级 次数归零、召回精准,对比才站得住。
面试怎么讲:做 A/B 对照前,先确认两臂跑在同一个健康环境里。一个静默降级的下游依赖,能让 你把「环境问题」误读成「改动效果」。评测的可信度,一半在评测之外。
4. 第二刀(4566 → 4301,-265):撞上「收益被随机性淹没」#
第二刀做激进去重 + few-shot 3→2(删掉的那条「收尾别口头宣告」,语义已被 <termination> 段逐字
覆盖,不算真丢)+ 收紧措辞。静态前缀又省了 265 token。
然后对照数据给了一个反直觉的结果:
| 主循环指标 | 上一版(4566) | 这版(4301) | Δ |
|---|---|---|---|
| carried_input(累计输入) | 98185 | 115701 | +17.8% |
| 步数(model_calls) | 6.4 | 7.1 | +12% |
system prompt 明明短了,实测累计输入反而涨了。 原因:carried_input = Σ(system_prompt + 历史) over steps。system prompt 每步是少了 265,但这一轮 Agent 恰好平均多走了 0.7 步,每多一步携带的
历史都更长——每步量级是 4300 token,0.7 步就把省下的 265 彻底盖住。
两个关键洞察:
- 主循环的 token 开销由「走几步」主导,不由 prompt 长度主导。 步数的随机波动(±1 步)对 总量的影响,量级远大于 system prompt 省的那点。
- 省的是「缓存打折 token」。 实测缓存命中率 84%,system prompt 是稳定前缀、绝大多数步都命中 缓存,缓存读 token 只按约 1 折计费。所以砍它,质量风险不打折,成本收益打了 1 折——性价比 开始倒挂。
面试怎么讲:优化前先问「我省的这个量,在总开销里占多大、是不是被更大的变量淹没」。这里主循环 成本的大头是步数 × 携带历史,不是静态前缀;而静态前缀又恰好是缓存最便宜的部分。第二刀本质是在 「测不出差异的区间」里操作。
5. 第三刀(4301 → 1991,冲进 2k):删起作用的语义,红线不破#
前两刀砍的是冗余,到第三刀已经没有无损空间了——要进 2k,只能删真正起作用的语义。这一刀是 明确的功能取舍,不再是瘦身:
- few-shot 全删(fewshot.py 警告过会伤 flash 档模型的指令遵循);
- workflow 的 evaluate 三源判断压成一句、删掉「宽品类按主流方向搜」的覆盖指导;
- tool_policy 删掉「定点定位两个参数各挡什么」的推导,只留结论;
- 各段解释性从句、注入防线的例句全删,只留纲领。
但有一条红线始终不动:两条 P0 诚实红线、category_insight 的「最多 2 次」上限(唯一没有 Hook
兜底的规则)、收尾判据 + 空召回硬路径、注入防线纲领——这些是「删了真没人管」的东西,一律保留。
三臂对照(15 条三臂都完成,同一次对照内):
| 版本 | system prompt | 均分 | P0 通过 | carried | 步数 |
|---|---|---|---|---|---|
| full | 11069 | 65.3 | 13/15 | 153391 | 7.5 |
| slim | 4301 | 81.6 | 14/15 | 108193 | 6.6 |
| ultra(2k) | 1991 | 74.2 | 15/15 | 93230 | 6.3 |
读法:
- 红线没破:ultra 的 P0 通过 15/15,三档最高。保 P0 / termination / security、只砍细节的设计 生效了——退化的是「质量分」,不是安全性。
- -7.4 分里近一半是噪声:ultra 比 slim 低 7.4 分,但其中 q07 一条就贡献 -73(这条本身在 0/27/ 100 之间乱跳,是最高方差样本)。剔掉 q07,差距收缩到 -2.6,基本落进噪声。 其余小跌方向和 砍掉的细节对得上,所以不能说完全没退化,但主体在噪声里。
- token 这次是干净的单调下降:full > slim > ultra,ultra 比 slim 又省 ~14% carried。但仍是缓存 打折区,换算成钱有限。
结论:2k 版可用且安全,但用「约 2–3 分可能真实的质量分 + few-shot/细节全删」换「缓存打折区
的 token 下降」,性价比不划算。slim(4301)是更优的平衡点。 三档都通过 PROMPT_VARIANT 保留,
按场景可切。
6. 方法论沉淀(比结论更值钱的部分)#
- 优化前先定位「大头」。 主循环成本的大头是步数 × 携带历史,不是静态前缀。砍错了地方,再努力 也被更大的变量淹没。
- 区分「打折 token」和「全价 token」。 缓存命中率高的稳定前缀是最便宜的部分,砍它省不下钱; 真要省成本,该盯的是「携带历史」和「步数」,那才是全价且随轮次膨胀的。
- A/B 前先保证环境健康。 一个静默降级的下游(OpenSearch 空召回)能让你把环境问题误读成改动 效果。
- 单轮不下质量结论。 n=1 桶多 + Agent 随机性,同一条能 0↔100 跳。要坐实差异得多轮取中位,或 至少识别出高方差样本单独看。均分容易被一两条极端值主导。
- 收益递减是常态。 第一刀删冗余(低风险高收益),第二三刀删语义(收益递减、风险递增)。知道 自己在曲线的哪一段,比「能砍到多少」重要。
7. 一句话收尾#
瘦身的价值不在「短」,在「想清楚每条指令的责任归属」——机制层、schema 层、prompt 层各兜各的, 能下沉的下沉。这次副产品是一次彻底的 prompt 职责审计 + 一个被顺手修掉的静默降级 Bug。至于 token, 第一刀已经吃掉了几乎所有值得吃的收益,后面两刀是「知道了它不划算,但把边界摸清楚了」。