不会还有人把全部 Rule 塞进 alwaysApply 吧?Context 一炸,约束就蒸发 | 常驻与场景路由

不会还有人把全部 Rule 塞进 alwaysApply 吧?Context 一炸,约束就蒸发 | 常驻与场景路由

全文速览:五十条 Rule 全打「每轮必载」,结果助手选择性失明,安全条款读了个寂寞。Context 是有限窗户,噪声一多,要命的那几条先被挤出去。这篇讲哪些常驻、哪些按意图路由、打满了先扔什么。
系列:《从 Rule 到落地:AI 工程化实践》第 5 篇

标签:Rule · alwaysApply · 意图路由 · 上下文管理

专栏:从小白到AI落地大师

序言

上文我们把硬约束写清楚了。童鞋们一高兴,容易把五十条 Rule 全打上「每轮必载」。别急------Context 是有限窗户,噪声一多,真正要命的禁止项反而被挤到窗外。

说句自嘲的:我也曾迷信「规则越多越安全」,结果会话开头光读规则就半屏,助手开始选择性失明------安全条款读了个寂寞。本篇就解决这个疼:哪些常驻(alwaysApply),哪些按意图场景路由,打满了怎么降级。读完你要能给现有规则打上常驻或按需标签,设计一张简单的意图到规则包路由表,并知道上下文打满时先扔什么。

记住一句话:进常驻的门槛不是「重要」,是「任何会话缺失都会立刻危险」;降级次序必须写死,不能留给运行时碰运气。

概念

alwaysApply,就是每轮会话默认加载的常驻规则集。场景路由,是按意图、路径或命令只加载相关规则包。Context,是有限窗口,噪声会挤掉关键约束。Bundle,是一组可被路由选中的规则或技能集合。

可以理解为:常驻是随身门禁卡,场景包是按楼层领的临时通行证。门禁卡能带十张就不能带两百张------带多了钱包炸,真卡反而找不到。为什么不能全部 alwaysApply?不是「多读点没事」,而是窗口有限时,模型对约束的遵守概率会被噪声拉低。你塞进一百条命名建议,可能正好把「禁止明文密钥」挤到注意力边缘。简单来说就是:常驻只留真正永远不能错的。

常驻与按需不是互斥口号,是互补。安全红线常驻;业务域命名、发布流程细节按需。谁进哪一筐,要用「拿掉会不会立刻出事」来判,而不是用「写出来很有成就感」来判。

问题在哪

三类典型翻车,细心的朋友可能中过。

全家桶常驻。安全、命名、发布、某业务域黑话全 alwaysApply。会话还没干活,Context 先胖死。看起来规范很全,执行时关键句失踪。这是最常见的「勤奋型事故」。

该常驻的反而按需。「禁止提交密钥」写成某个业务 Bundle 里的一句,只有改适配时才加载------结果写文档、改前端时照样泄密。安全禁止项进错层,比没写还可怕,因为你以为有了。

路由不观测。命中了哪个包、本轮加载了啥,日志里没有。出了事只能猜「是不是规则没挂上」。没有观测,路由就是玄学。玄学在演示里好看,在复盘里害人。

还有第四类更隐蔽:降级策略没写。Context 打满时随机截断,可能先丢掉硬约束、留下建议。正确姿势相反------先砍建议类,硬约束最后砍。不写清楚,运行时就会按「谁碰巧在尾巴上」来决定生死。

举例

acme-rules 里可以这样拆,示意够用:

yaml 复制代码
routes:
  - intent: ["适配", "方言", "解析失败"]
    load: ["acme-rules/adaptor"]
  - intent: ["发布", "回滚"]
    load: ["acme-rules/release"]
  - always:
      - acme-rules/security

常驻候选用短名单思维:密钥与生产边界、不可逆操作、少数通用工程门禁。按需候选:某技术栈专项、某业务域命名、某发布流程细节。路由触发别只靠模型感觉:用户意图关键词、路径匹配、显式指定规则包、命令面板,都可以。

多包同时命中时要有优先级。降级不能停在伪代码上,能跑才算设计过------就四步:

步骤总览

  1. 每条规则打上类型(SEC 安全常驻 / GATE 工程门禁 / SCN 场景包 / SUG 建议 / DOM 业务域材料)和 Context 成本;
  2. 按优先级逐层装载:SEC → GATE 无条件进窗,SCN 要命中意图,SUG/DOM 预算够才轮得上;
  3. 预算装不下的,SEC 层豁免报警,其余按「越软越先砍」直接砍;
  4. 打印本轮命中关键词与装载集------这就是上文说的路由观测日志。
python 复制代码
# 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()

跑起来长这样:

text 复制代码
意图:「适配解析失败,顺手发布个预发」   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 的门槛不是「重要」,是「任何会话缺失都会立刻危险」;
  降级次序必须写死------不写,运行时就按「谁碰巧在尾巴上」决定生死。

输出你看什么

  1. 已用 50/50 顶格装满,安全常驻在窗内:2/2------这就是写死降级次序的效果:两条红线一条没丢,先消失的是命名建议和周报话术;
  2. R5「发布回滚双人确认」是靠 命中关键词 ['适配', '发布'] 进的窗------上文那个「发布会话误载适配包、挤掉回滚确认」的事故,在这版里既显式命中、又留了观测行,复盘时半小时能定位;
  3. 细看被砍的四条:R7 是预算顶出去的,但 R8/R10/R11 三条 billing 材料压根不该当候选------本轮意图没提账单,它们却占着候选位被「砍」给预算看。这就是这套简化模型的债:路由表必须给每类包配意图关键词 ,否则降级报告里全是这种「活该但走错片场」的条目。补上这条,DOM 层配 ["账单", "billing"] 后按未载入处理,下次重跑白捡 22 的预算余量;
  4. 若把 BUDGET 再调小让 SEC 装不下,输出会打 [严重] 而不是默默砍红线------「先砍建议、留硬约束」不是口号,是代码里的一行判断(第 27 篇终章清单里,它会变成一票否决项)。

观测也很便宜:在会话日志记一行「本轮加载了哪些规则包」。复盘误路由时,这行比「我觉得加载了」值钱一百倍。虚构的 acme 小团队就吃过亏------发布会话误载了适配包的命名细则,Context 挤掉一条回滚确认,差点把演练环境的强制推送特例当成日常权限。后来加上加载集日志,半小时就定位了。

再往前推一层,有个双通道判据值得钉死:禁止类内容必须走确定性常驻通道,不能赌宿主的相关性判断。常驻(文件写入即生效,会话启动自动加载)与按需(路由命中、检索召回)是两种触达性质;一条「禁止直改生产配置」的教训,若只躺在按需检索里,一定会在某次模型没意识到相关性的任务里缺席------而 AI 犯过的错,恰恰是模型自己预料不到的。宁可多注入,不能漏注入。当然常驻通道要有硬预算:超预算时不是现场截断,而是按最近采纳率把长期没被用到的条目降级回检索通道------降级不是删除,它哪天重新自证价值,哪天回来。上面 demo 里「先砍建议、留硬约束」的次序,就是这套判据的最小化版本。

自检

markdown 复制代码
# 常驻 / 按需自检

- [ ] 现有规则是否已打上「常驻 / 按需」标签?
- [ ] 安全禁止项是否在 always,而不是藏在业务 Bundle?
- [ ] 是否有意图→规则包的路由表(哪怕很短)?
- [ ] Context 打满时,是否约定「先丢建议、留硬约束」?
- [ ] 本轮加载集是否有日志可查?

两问更狠。第一,把 alwaysApply 列表念一遍------有没有「去掉也不会立刻出事」的?有就移到场景包。第二,若今天 Context 预算砍半,你先扔哪三条?答不上来,说明优先级没设计过。设计过的团队,砍预算像删枝叶;没设计过的,砍预算像闭眼拆零件。

选型时别把「常驻」当成荣誉勋章。进 always 的不是重要,而是「任何会话缺失都会立刻危险」。重要但低频的,请走开场景包通道------重要和常驻是两回事,混为一谈就会把钱包撑爆。

若你们使用多种助手或多种 IDE,路由表更要外置成配置,而不是写死在某个人的系统提示里。系统提示一换人就漂移;配置进仓库,才跟得上 SSOT 精神。

落地自检补充:把本篇的关键句用自己的业务语言重写一遍,写不出来就说明还没读进手里。第 1 轮重写往往比再收藏十篇文章管用。

场景路由最怕「关键词浪漫」。举个能抄的反例:把「重构」映射到大改目录的 Skill,结果用户说「重构一下变量名」也触发搬家。更稳的写法是:触发要带对象类型(文件/模块/映射表),并且高危路由强制二次确认。可以理解为:电梯里的「开门」和工地上的「爆破」不该共用一个按钮贴画。

常驻内核建议只留三类永远不能错的东西:泄密禁止、生产写禁止、越权路径禁止。其余「代码风格偏好」「注释语气」统统按意图加载。内核越瘦,关键条款越不容易在长会话里被挤出窗外。

再举一个常驻过肥的现场。有团队把「提交信息格式」「注释必须写作者」「变量命名偏爱」全塞进 alwaysApply,结果真正的「禁止把密钥写进仓库」条款,在长重构会话里排到了后半截上下文。模型不是故意作恶,是窗口满了先丢尾巴。把风格类偏好挪到 on: commit-msg 或「写注释」意图路由后,安全条款重新稳定命中。可以理解为:行李箱先放护照和药,外套可以压箱底------别反过来。

思考

三句收束。常驻只留真正永远不能错的约束。场景规则靠意图与路径路由,避免上下文淹溺。要记录本轮加载集,便于复盘误路由。

可以理解为:门禁卡瘦身,通行证按楼层领------助手才有机会把真正的红线读进脑子里。下篇我们拿虚构的多引擎适配场景,把最小 Rule 包真正写出来并演练验收。规则写在纸上不算数,演练过的才算数。


上一篇:《不会还有人把「尽量注意」当成 Rule 吧?不可判定=等于没写 | 硬约束与加载顺序》

下篇预告:《不会还有人改映射不带样例还敢提 PR 吧?假绿一时爽,回归火葬场 | 多引擎适配最小 Rule》------样例同行、禁止假绿,两条门禁就够扎心。

觉得有用的话,欢迎点赞、留言说说你 alwaysApply 里最该搬家的一条;有「规则越多越不听话」的体感,也欢迎对照本篇拆。

专栏:从小白到AI落地大师|《从 Rule 到落地:AI 工程化实践》

相关推荐
文谦南宁AI普工2 小时前
拆解 DeepSeek dsh-superagent:用 60 行代码模拟它的授权门控与 observe 模式
aigc·agent·ai编程
Hone2 小时前
我把「自动发文章到掘金」做成了一个 Agent 技能:juejin-skills
node.js·ai编程·前端工程化
前端双越老师3 小时前
程序员 1v1 访谈系列第十期:纯前端坚持不了多久了
前端·ai编程·全栈
threerocks5 小时前
运营 X(Twitter)的 Chrome 插件,看这篇就够了
aigc·openai·ai编程
谟松5 小时前
Claude Code 接 DeepSeek 一直刷 unrecognized_model?4 组对照实验,一次讲清
ai编程
lifallen6 小时前
ub-mode skill:软件工程原则
人工智能·学习·ai·软件工程·ai编程
TimeFine7 小时前
AI 编码工作流的设计(二)
android·ai编程
Dumbness7 小时前
二十行手写的 SKILL.md 头部解析,遇到 YAML 的折叠写法、单引号和嵌套键就读错:和 js-yaml 逐条对照
node.js·ai编程
ZzT8 小时前
vibe coding 做小游戏·问题排查篇:解决包体积、引擎适配与 iPhone 机型适配问题
ai编程·游戏开发