不会还有人把全部 Rule 塞进 alwaysApply 吧?Context 一炸,约束就蒸发 | 常驻与场景路由
全文速览:五十条 Rule 全打「每轮必载」,结果助手选择性失明,安全条款读了个寂寞。Context 是有限窗户,噪声一多,要命的那几条先被挤出去。这篇讲哪些常驻、哪些按意图路由、打满了先扔什么。
系列:《从 Rule 到落地:AI 工程化实践》第 5 篇
标签:Rule · alwaysApply · 意图路由 · 上下文管理
专栏:从小白到AI落地大师
序言
上文我们把硬约束写清楚了。童鞋们一高兴,容易把五十条 Rule 全打上「每轮必载」。别急——Context 是有限窗户,噪声一多,真正要命的禁止项反而被挤到窗外。
说句自嘲的:我也曾迷信「规则越多越安全」,结果会话开头光读规则就半屏,助手开始选择性失明——安全条款读了个寂寞。本篇就解决这个疼:哪些常驻(alwaysApply),哪些按意图场景路由,打满了怎么降级。读完你要能给现有规则打上常驻或按需标签,设计一张简单的意图到规则包路由表,并知道上下文打满时先扔什么。
记住一句话:进常驻的门槛不是「重要」,是「任何会话缺失都会立刻危险」;降级次序必须写死,不能留给运行时碰运气。
概念
alwaysApply,就是每轮会话默认加载的常驻规则集。场景路由,是按意图、路径或命令只加载相关规则包。Context,是有限窗口,噪声会挤掉关键约束。Bundle,是一组可被路由选中的规则或技能集合。
可以理解为:常驻是随身门禁卡,场景包是按楼层领的临时通行证。门禁卡能带十张就不能带两百张——带多了钱包炸,真卡反而找不到。为什么不能全部 alwaysApply?不是「多读点没事」,而是窗口有限时,模型对约束的遵守概率会被噪声拉低。你塞进一百条命名建议,可能正好把「禁止明文密钥」挤到注意力边缘。简单来说就是:常驻只留真正永远不能错的。
常驻与按需不是互斥口号,是互补。安全红线常驻;业务域命名、发布流程细节按需。谁进哪一筐,要用「拿掉会不会立刻出事」来判,而不是用「写出来很有成就感」来判。
问题在哪
三类典型翻车,细心的朋友可能中过。
全家桶常驻。安全、命名、发布、某业务域黑话全 alwaysApply。会话还没干活,Context 先胖死。看起来规范很全,执行时关键句失踪。这是最常见的「勤奋型事故」。
该常驻的反而按需。「禁止提交密钥」写成某个业务 Bundle 里的一句,只有改适配时才加载——结果写文档、改前端时照样泄密。安全禁止项进错层,比没写还可怕,因为你以为有了。
路由不观测。命中了哪个包、本轮加载了啥,日志里没有。出了事只能猜「是不是规则没挂上」。没有观测,路由就是玄学。玄学在演示里好看,在复盘里害人。
还有第四类更隐蔽:降级策略没写。Context 打满时随机截断,可能先丢掉硬约束、留下建议。正确姿势相反——先砍建议类,硬约束最后砍。不写清楚,运行时就会按「谁碰巧在尾巴上」来决定生死。
举例
acme-rules 里可以这样拆,示意够用:
routes:
- intent: ["适配", "方言", "解析失败"]
load: ["acme-rules/adaptor"]
- intent: ["发布", "回滚"]
load: ["acme-rules/release"]
- always:
- acme-rules/security
常驻候选用短名单思维:密钥与生产边界、不可逆操作、少数通用工程门禁。按需候选:某技术栈专项、某业务域命名、某发布流程细节。路由触发别只靠模型感觉:用户意图关键词、路径匹配、显式指定规则包、命令面板,都可以。
多包同时命中时要有优先级。降级不能停在伪代码上,能跑才算设计过——就四步:
步骤总览
- 每条规则打上类型(SEC 安全常驻 / GATE 工程门禁 / SCN 场景包 / SUG 建议 / DOM 业务域材料)和 Context 成本;
- 按优先级逐层装载:SEC → GATE 无条件进窗,SCN 要命中意图,SUG/DOM 预算够才轮得上;
- 预算装不下的,SEC 层豁免报警,其余按「越软越先砍」直接砍;
- 打印本轮命中关键词与装载集——这就是上文说的路由观测日志。
# context_budget.py —— alwaysApply 与场景路由:预算打满时,先扔谁
# 纯标准库,复制即跑。用法:python context_budget.py
# acme-rules 规则清单:(id, 类型, 内容, 成本),成本 ≈ 占用 Context 的份数
RULES = [
("R1", "SEC", "禁止把密钥 / 令牌明文写进仓库、日志与提交", 8),
("R2", "SEC", "未经确认禁止改 adaptor/public 入口", 6),
("R3", "GATE", "改公共 API 前先补充 / 更新契约测试", 8),
("R4", "SCN", "适配方言解析失败:按归类→最小复现→正反例执行", 10),
("R5", "SCN", "发布与回滚需双人确认并留审计字段", 6),
("R6", "SUG", "提交信息建议写清动机与影响面", 6),
("R7", "SUG", "变量命名建议语义化", 4),
("R8", "DOM", "acme-billing 域黑话对照表(改账单才需要)", 10),
("R9", "GATE", "映射变更必须样例同行(详见第 6 篇)", 6),
("R10", "DOM", "billing 表字段命名沿革(历史考古材料)", 6),
("R11", "DOM", "billing 周报话术模板", 6),
]
# 装载次序 = 优先级;丢弃正好反着来:先砍软砖,最后砍承重墙
TIERS = ["SEC", "GATE", "SCN", "SUG", "DOM"]
NEVER_DROP = {"SEC"} # 安全常驻:豁免一切降级
INTENT = "适配解析失败,顺手发布个预发" # 本轮用户意图
HITS = {"SCN": {"适配": ["R4"], "发布": ["R5"], "回滚": ["R5"]}}
BUDGET = 50
def route(intent):
hit, kw = set(), []
for bucket in HITS.values():
for keyword, ids in bucket.items():
if keyword in intent:
hit.update(ids)
kw.append(keyword)
return hit, kw
def load(rules, budget, intent):
picked, over = set(), []
hits, kw = route(intent)
misses = []
cost = 0
for tier in TIERS: # 按优先级逐层装
for rid, t, text, c in rules:
if t != tier:
continue
# SEC/GATE 常驻;SCN 要命中意图;SUG/DOM 是低优先候选,预算够才轮得上
candidate = t in ("SEC", "GATE") or rid in hits or t in ("SUG", "DOM")
if not candidate:
if rid not in misses:
misses.append(rid) # SCN 没命中意图不装,记审计(去重)
continue
if cost + c <= budget:
picked.add(rid); cost += c
else:
over.append(rid) # 预算装不下的
dropped = [r for r in over if r not in
{rid for rid, t, *_ in rules if t in NEVER_DROP}]
protected = [r for r in over if r in
{rid for rid, t, *_ in rules if t in NEVER_DROP}]
return picked, cost, dropped, protected, kw, misses
def main():
picked, cost, dropped, protected, kw, misses = load(RULES, BUDGET, INTENT)
print(f"意图:「{INTENT}」 Context 预算:{BUDGET}")
print(f"路由观测:命中关键词 {kw}")
print("\n== 装载结果 ==")
for rid, t, text, c in RULES:
if rid in picked:
print(f" [载入 {c:2}] {rid} {t:4} {text}")
print(f"\n 已用 {cost}/{BUDGET}")
print("\n== 降级与欠账 ==")
for rid, t, text, c in RULES:
if rid in dropped:
print(f" [砍掉] {rid} {t:4} {text} <- {t} 类不豁免")
for rid in protected:
print(f" [豁免] {rid}:安全常驻不许砍,预算不够是设计的锅")
for rid in misses:
print(f" [未载入] {rid}:意图没命中,本来就不该进窗(观测日志留痕)")
sec_all = [r for r, t, *_ in RULES if t == "SEC"]
sec_in = [r for r in sec_all if r in picked]
print(f"\n 安全常驻在窗内:{len(sec_in)}/{len(sec_all)}")
if len(sec_in) < len(sec_all):
print(" [严重] 有安全条款没进窗——先回去给 SEC 层预留配额,再谈别的。")
else:
print(" 承重墙都在。被挤掉的全是非承重:软砖与建议,活该。")
print("\n== 一句话 ==")
print(" 进 always 的门槛不是「重要」,是「任何会话缺失都会立刻危险」;")
print(" 降级次序必须写死——不写,运行时就按「谁碰巧在尾巴上」决定生死。")
if __name__ == "__main__":
main()
跑起来长这样:
意图:「适配解析失败,顺手发布个预发」 Context 预算:50
路由观测:命中关键词 ['适配', '发布']
== 装载结果 ==
[载入 8] R1 SEC 禁止把密钥 / 令牌明文写进仓库、日志与提交
[载入 6] R2 SEC 未经确认禁止改 adaptor/public 入口
[载入 8] R3 GATE 改公共 API 前先补充 / 更新契约测试
[载入 10] R4 SCN 适配方言解析失败:按归类→最小复现→正反例执行
[载入 6] R5 SCN 发布与回滚需双人确认并留审计字段
[载入 6] R6 SUG 提交信息建议写清动机与影响面
[载入 6] R9 GATE 映射变更必须样例同行(详见第 6 篇)
已用 50/50
== 降级与欠账 ==
[砍掉] R7 SUG 变量命名建议语义化 <- SUG 类不豁免
[砍掉] R8 DOM acme-billing 域黑话对照表(改账单才需要) <- DOM 类不豁免
[砍掉] R10 DOM billing 表字段命名沿革(历史考古材料) <- DOM 类不豁免
[砍掉] R11 DOM billing 周报话术模板 <- DOM 类不豁免
安全常驻在窗内:2/2
承重墙都在。被挤掉的全是非承重:软砖与建议,活该。
== 一句话 ==
进 always 的门槛不是「重要」,是「任何会话缺失都会立刻危险」;
降级次序必须写死——不写,运行时就按「谁碰巧在尾巴上」决定生死。
输出你看什么
已用 50/50顶格装满,安全常驻在窗内:2/2——这就是写死降级次序的效果:两条红线一条没丢,先消失的是命名建议和周报话术;- R5「发布回滚双人确认」是靠
命中关键词 ['适配', '发布']进的窗——上文那个「发布会话误载适配包、挤掉回滚确认」的事故,在这版里既显式命中、又留了观测行,复盘时半小时能定位; - 细看被砍的四条:R7 是预算顶出去的,但 R8/R10/R11 三条 billing 材料压根不该当候选——本轮意图没提账单,它们却占着候选位被「砍」给预算看。这就是这套简化模型的债:路由表必须给每类包配意图关键词,否则降级报告里全是这种「活该但走错片场」的条目。补上这条,DOM 层配
["账单", "billing"]后按未载入处理,下次重跑白捡 22 的预算余量; - 若把 BUDGET 再调小让 SEC 装不下,输出会打
[严重]而不是默默砍红线——「先砍建议、留硬约束」不是口号,是代码里的一行判断(第 27 篇终章清单里,它会变成一票否决项)。
观测也很便宜:在会话日志记一行「本轮加载了哪些规则包」。复盘误路由时,这行比「我觉得加载了」值钱一百倍。虚构的 acme 小团队就吃过亏——发布会话误载了适配包的命名细则,Context 挤掉一条回滚确认,差点把演练环境的强制推送特例当成日常权限。后来加上加载集日志,半小时就定位了。
再往前推一层,有个双通道判据值得钉死:禁止类内容必须走确定性常驻通道,不能赌宿主的相关性判断。常驻(文件写入即生效,会话启动自动加载)与按需(路由命中、检索召回)是两种触达性质;一条「禁止直改生产配置」的教训,若只躺在按需检索里,一定会在某次模型没意识到相关性的任务里缺席——而 AI 犯过的错,恰恰是模型自己预料不到的。宁可多注入,不能漏注入。当然常驻通道要有硬预算:超预算时不是现场截断,而是按最近采纳率把长期没被用到的条目降级回检索通道——降级不是删除,它哪天重新自证价值,哪天回来。上面 demo 里「先砍建议、留硬约束」的次序,就是这套判据的最小化版本。
自检
# 常驻 / 按需自检
- [ ] 现有规则是否已打上「常驻 / 按需」标签?
- [ ] 安全禁止项是否在 always,而不是藏在业务 Bundle?
- [ ] 是否有意图→规则包的路由表(哪怕很短)?
- [ ] Context 打满时,是否约定「先丢建议、留硬约束」?
- [ ] 本轮加载集是否有日志可查?
两问更狠。第一,把 alwaysApply 列表念一遍——有没有「去掉也不会立刻出事」的?有就移到场景包。第二,若今天 Context 预算砍半,你先扔哪三条?答不上来,说明优先级没设计过。设计过的团队,砍预算像删枝叶;没设计过的,砍预算像闭眼拆零件。
选型时别把「常驻」当成荣誉勋章。进 always 的不是重要,而是「任何会话缺失都会立刻危险」。重要但低频的,请走开场景包通道——重要和常驻是两回事,混为一谈就会把钱包撑爆。
若你们使用多种助手或多种 IDE,路由表更要外置成配置,而不是写死在某个人的系统提示里。系统提示一换人就漂移;配置进仓库,才跟得上 SSOT 精神。
落地自检补充:把本篇的关键句用自己的业务语言重写一遍,写不出来就说明还没读进手里。第 1 轮重写往往比再收藏十篇文章管用。
场景路由最怕「关键词浪漫」。举个能抄的反例:把「重构」映射到大改目录的 Skill,结果用户说「重构一下变量名」也触发搬家。更稳的写法是:触发要带对象类型(文件/模块/映射表),并且高危路由强制二次确认。可以理解为:电梯里的「开门」和工地上的「爆破」不该共用一个按钮贴画。
常驻内核建议只留三类永远不能错的东西:泄密禁止、生产写禁止、越权路径禁止。其余「代码风格偏好」「注释语气」统统按意图加载。内核越瘦,关键条款越不容易在长会话里被挤出窗外。
再举一个常驻过肥的现场。有团队把「提交信息格式」「注释必须写作者」「变量命名偏爱」全塞进 alwaysApply,结果真正的「禁止把密钥写进仓库」条款,在长重构会话里排到了后半截上下文。模型不是故意作恶,是窗口满了先丢尾巴。把风格类偏好挪到 on: commit-msg 或「写注释」意图路由后,安全条款重新稳定命中。可以理解为:行李箱先放护照和药,外套可以压箱底——别反过来。
思考
三句收束。常驻只留真正永远不能错的约束。场景规则靠意图与路径路由,避免上下文淹溺。要记录本轮加载集,便于复盘误路由。
可以理解为:门禁卡瘦身,通行证按楼层领——助手才有机会把真正的红线读进脑子里。下篇我们拿虚构的多引擎适配场景,把最小 Rule 包真正写出来并演练验收。规则写在纸上不算数,演练过的才算数。
上一篇:《不会还有人把「尽量注意」当成 Rule 吧?不可判定=等于没写 | 硬约束与加载顺序》
下篇预告:《不会还有人改映射不带样例还敢提 PR 吧?假绿一时爽,回归火葬场 | 多引擎适配最小 Rule》——样例同行、禁止假绿,两条门禁就够扎心。
觉得有用的话,欢迎点赞、留言说说你 alwaysApply 里最该搬家的一条;有「规则越多越不听话」的体感,也欢迎对照本篇拆。
专栏:从小白到AI落地大师|《从 Rule 到落地:AI 工程化实践》