先看结论与判断条件

  • 系统策略、工具权限和必需业务约束属于不可删除层,超预算时应拒绝请求或切换受控模型档位,而不是裁掉规则。
  • 上下文预算要使用目标模型实际 tokenizer、模板包裹和预留输出计算,字符数或平均 token 估算不能作为最终门禁。
  • 历史消息按完整 turn 处理,不能留下助手回复却删除其用户问题,也不能在摘要后打乱原始 sequence。
  • 摘要是版本化数据资产,必须保留约束 ID、未决事项、来源范围和覆盖消息区间,不能只生成一段流畅文字。
  • RAG 片段按当前问题相关性、来源权限与时效进入可淘汰层,文档中的指令不能提升自身优先级。
  • 每次截断记录 policyVersion、modelBundleId、generation、保留项、移除项和 token 统计,以便复现实际模型输入。

先定义不可删除、可摘要和可淘汰三类上下文

上下文截断不是普通文本压缩,而是一次策略决策。系统策略、工具 allowlist、数据范围、输出 schema 和当前请求中的必需参数构成 immutable 层;旧对话和已确认事实进入 summarizable 层;低优先级检索片段、重复解释和过期候选进入 evictable 层。每个块在创建时就带 layer,不能等超预算后让模型猜重要性。

immutable 不代表永远无限增长。系统与业务团队要把规则拆成稳定 ID,删除重复表述,模板发布时计算固定成本。若不可删除层加当前请求已经超过模型输入上限,正确终态是 context_policy_overflow,提示缩小任务或采用已审核的更大上下文档位;不能为了请求成功悄悄裁掉授权或输出约束。

层级来自受控装配器,不接受用户、检索文档或历史摘要自报优先级。用户可以标记业务偏好,但它仍属于 request 层;检索片段即便写着“必须永久保留”也只是数据。优先级与消息 role、来源和 policyVersion 绑定,从结构上阻止低信任内容挤占系统预算。

上下文层级与截断权限
层级典型内容允许动作超预算终态
immutable_policy系统策略、工具与数据范围仅模板版本替换拒绝请求
required_request当前问题与必需参数用户显式修改要求缩小输入
conversation_turn历史问答轮次按完整 turn 摘要或移除记录覆盖区间
retrieval_dataRAG 事实片段按相关性和权限淘汰减少引用
diagnostic_context非关键调试说明优先移除不进入模型

预算必须包含模板包裹、工具声明和输出预留

模型输入成本不只来自可见消息。system instruction、消息角色标记、模板分隔符、工具 schema、结构化输出 schema 和运行时包装都会占用 token。规划器要针对 modelBundleId 使用实际 tokenizer 编码最终消息对象,并预留最大允许输出和安全余量;不能把中文字符数乘固定系数当作发布门禁。

工具声明可能比历史对话更大,且不同任务使用的 allowlist 不同。预算事件要分别记录 policyTokens、toolTokens、requestTokens、historyTokens、retrievalTokens、reservedOutputTokens 和 framingTokens。只有总和低于 inputLimit 才可执行,统计缺少任一项就拒绝,避免 SDK 隐式包装在最后一步溢出。

端侧模型、云模型和 LoRA 组合可能使用不同 tokenizer、最大上下文与模板格式。modelBundleId 要绑定基础模型、适配器、tokenizer 和模板版本。切换后必须重新规划,不能复用旧 token 计数。某个 Android 推理 API 的模型与后端约束也不能外推给其他运行时,配置按实际依赖版本审查。

上下文预算的组成
预算项计算来源能否截断证据
policyTokens发布模板实际编码否policyVersion
toolTokens当前 allowlist schema通常否toolSetVersion
requestTokens当前用户请求有限重述requestId
historyTokens完整历史轮次可摘要或淘汰sequence 区间
retrievalTokens授权检索片段可淘汰citationId
reservedOutputTokens产品输出上限按档位固定outputPolicy
framingTokensSDK 与模板包裹否runtimeVersion

历史按完整轮次和 sequence 处理

最简单的“保留最后 N 条消息”会破坏成对语义。若删除用户问题却保留助手答案,模型无法知道答案针对什么;若只删除工具请求却保留工具结果,结果可能被当成无来源事实。conversation turn 应包含用户消息、助手候选、工具调用与结果的完整关系,截断以 turn 为最小单位。

每条消息带单调 sequence,会话保存 generation。规划器只能保留原有相对顺序,摘要块放在其覆盖区间的逻辑位置,并声明 coversFrom 与 coversTo。页面切换、重新发送或分支对话产生新 generation,旧规划结果不得写回新会话;迟到的 token 统计同样按 generation 拒绝。

当前未完成轮次通常不可被摘要,因为工具结果或用户确认还没到齐。状态机区分 open、completed、cancelled 和 superseded;只有 completed turn 能进入摘要队列。cancelled 可以按审计策略保留最小终态,不能把半个助手流式回复汇入长期记忆,避免未确认内容被后续当作事实。

对话轮次的截断状态
turn 状态可否摘要可否淘汰必须保留
open否否当前请求与已到达工具结果
completed是摘要后可淘汰原文sequence 与约束
cancelled按策略仅终态可以取消原因摘要
superseded否可以代际切换记录
tool_pending否否调用与授权上下文
policy_blocked否可保留最小回执阻止原因

摘要必须保留约束清单、未决事项和来源

自由文本摘要容易把否定条件、数字单位、责任主体和未决问题压掉。摘要 schema 应包含 summaryText、requiredConstraintIds、openItems、confirmedFacts、citationIds、coversFrom、coversTo、summaryVersion 和 sourceGeneration。文本帮助模型理解,结构字段用于装配器确定性验证,两者任一缺失都不替换原历史。

requiredConstraintIds 来自原 turn 中标记的业务约束,不由摘要模型自行创造。生成后,门禁比较覆盖区间内所有 mustPreserve ID 是否都出现在摘要中;多出未知 ID 也应拒绝,因为它可能把普通陈述升级为约束。confirmedFacts 必须携带来源或用户确认,不把助手猜测写入长期记忆。

摘要本身也会老化。后续用户撤销偏好、权限变化或业务事实更新时,新 turn 生成 supersedes 关系,下一次规划排除已撤销约束。不能在旧摘要文本上直接字符串替换;要基于结构化事件重建新的 summaryVersion,并保存旧摘要的覆盖与失效回执。

可审计摘要的必需字段
字段回答问题验证规则失败处理
coversFrom、coversTo覆盖哪些消息区间有效且连续拒绝摘要
requiredConstraintIds哪些约束必须留下与源区间集合一致保留原 turn
openItems哪些事项未完成状态仍为 open不当作事实
confirmedFacts哪些事实已确认携带来源或确认 ID拒绝无来源项
citationIds事实来自哪里属于源上下文删除越界引用
sourceGeneration属于哪个会话代际等于当前 generation拒绝旧摘要

检索片段先按权限过滤,再按价值淘汰

RAG 片段进入预算前先做租户、数据集、时效和来源校验。越权或过期内容不是低优先级,而是根本不得进入上下文。通过授权后,才按当前问题相关性、来源等级、重复度和引用必要性排序。淘汰回执保存 citationId 与 reason,不复制敏感原文。

检索内容只提供事实,不能修改 layer、toolTokens 或 policyTokens。文档中的祈使句和伪 system 标签仍在 retrieval_data 层。装配器将片段放入固定引用区,超预算时先移除重复和低价值片段;若回答必须引用的证据全部被移除,终态改为 insufficient_evidence,而不是生成无来源答案。

历史摘要与当前检索可能冲突。规划器不能把“更短”作为唯一选择,而要让新鲜权威来源进入上下文,并标记旧 confirmedFact 为 disputed,交给业务规则或用户确认。冲突本身占用预算,需要预留说明空间;若无法在限制内表达来源与不确定性,应拒绝高影响回答。

模板和模型切换必须重新计算并验证策略

服务端模板入口、模板版本、应用版本和回退值需要可追溯。模板可能同时改变 system instructions、输入 schema、工具声明和正文,这些变化都会改变 token 成本与不可删除字段。远程模板激活前应在目标 App 能力矩阵计算预算;获取失败或版本越界时使用应用内已审核默认模板。

system instruction 能影响模型行为,却不承担权限与截断安全。即使完整保留系统文字,工具网关仍要独立校验授权,响应仍要执行 schema 和敏感字段检查。上下文规划器的完成条件是结构与预算正确,不是模型一定遵循每条规则;输出行为需单独测试。

instrumented test 覆盖端侧 tokenizer、模板包装、进程重建、旋转、后台恢复和离线模型切换。运行时或模型包变化后,用新 modelBundleId 重新编码所有预算样本。单一设备通过不代表全部系统、ABI 与厂商,报告保留未覆盖项,不将模拟 token 计数冒充真实执行。

配置变化对上下文规划的影响
变化必须重算必须重测旧回执能否沿用
system instructionpolicyTokens约束保留否
工具 schematoolTokens授权与装配否
modelBundleId全部 token端侧执行否
templateVersionframing 与输入回退矩阵否
检索策略retrievalTokens来源与淘汰仅历史参考
App 加固候选运行行为生命周期与缓存否

用预算规划器拒绝删除策略和打乱顺序

公开安全的计划文件可以包含 maxInputTokens、reservedOutputTokens、generation、requiredPolicyIds、items 和 removed。每个 item 声明 id、layer、sequence、tokens 与 constraintIds,不含真实对话内容。门禁先检查预算正数、generation 一致和 sequence 单调,再确认所有必需策略与约束仍在保留集合。

下面的 Python 校验器从命令行读取计划。输入缺失时直接非零退出;它拒绝删除 immutable_policy、重复或逆序 sequence、预算总和越界、摘要遗漏必需约束,以及 removed 中仍出现在保留集合的 ID。代码不调用模型、不接触用户文本,只验证截断结果的结构和证据关系。

真实系统还需用实际 tokenizer 产生 tokens,并验证 summary 覆盖区间、citation 权限和工具 schema。结构门禁通过不证明摘要语义完整,也不证明模型会遵守策略。完成证据要结合摘要测试集、端侧运行回执、工具网关拒绝和输出校验。

LLM 上下文预算规划校验器
import json
import sys
from pathlib import Path

IMMUTABLE = {"immutable_policy", "required_request"}

def stop(message):
    print(f"context plan rejected: {message}", file=sys.stderr)
    raise SystemExit(2)

def load_plan(path_text):
    path = Path(path_text)
    if not path.is_file():
        print("context plan rejected: plan file is missing", file=sys.stderr)
        raise SystemExit(2)
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        stop(f"cannot parse plan: {exc}")

def validate(data):
    limit = data.get("maxInputTokens")
    reserved = data.get("reservedOutputTokens")
    items = data.get("items")
    removed = set(data.get("removedIds", []))
    required_policy = set(data.get("requiredPolicyIds", []))
    required_constraints = set(data.get("requiredConstraintIds", []))
    if type(limit) is not int or type(reserved) is not int or limit <= reserved:
        stop("token limits are invalid")
    if not isinstance(items, list) or not items:
        stop("items must be a non-empty list")
    ids = [item.get("id") for item in items]
    if None in ids or len(ids) != len(set(ids)):
        stop("item ids must be present and unique")
    if removed.intersection(ids):
        stop("removed items still appear in the retained plan")
    if any(item.get("layer") in IMMUTABLE and item.get("id") in removed for item in items):
        stop("an immutable item was removed")
    sequences = [item.get("sequence") for item in items if item.get("sequence") is not None]
    if any(type(value) is not int for value in sequences) or sequences != sorted(sequences):
        stop("message sequence is invalid or reordered")
    retained_policy = {item["id"] for item in items if item.get("layer") == "immutable_policy"}
    if not required_policy.issubset(retained_policy):
        stop("required policy items are missing")
    retained_constraints = set().union(*(set(item.get("constraintIds", [])) for item in items))
    if not required_constraints.issubset(retained_constraints):
        stop("a summary lost required constraints")
    total = sum(item.get("tokens", 0) for item in items) + reserved
    if total > limit:
        stop("retained context exceeds the input budget")
    print(f"context plan accepted: {len(items)} items")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        stop("usage: validate_context_plan.py plan.json")
    validate(load_plan(sys.argv[1]))

发布门禁要能复现最终模型输入

每次请求的回执记录 modelBundleId、templateVersion、policyVersion、generation、token 分项、摘要版本、保留 ID、移除 ID 和最终输入摘要。发生问题时,可以重建结构相同的模型输入并确认哪一层被压缩;普通日志不保存完整敏感对话,原文按授权在独立数据层管理。

候选 App、模型、模板、tokenizer、工具集合、摘要 schema 或加固配置变化后,要重跑超预算、轮次截断、旧摘要、权限撤销、检索冲突和生命周期用例。旧 token 回执不能替新 modelBundleId 签字。任何 immutable 缺失或约束丢失都阻止发布,不通过扩大输入上限掩盖规划错误。

站内的 RAG 指令边界文章可继续检查低信任片段不能提升权限;准备评估商业 App 的端侧 LLM、上下文管理和加固范围时,可通过页面行动按钮进入御盾中央平台提交模板、模型包和预算策略。提交只启动评估,策略保留结论仍以同一候选回执为准。

  • 系统策略和必需请求项标为不可删除层。
  • 使用目标 modelBundleId 的实际 tokenizer 计算全部预算。
  • 历史按完整 turn 截断并保持 sequence 单调。
  • 摘要保留约束 ID、未决事项、来源和覆盖区间。
  • 检索片段先做权限校验再按价值淘汰。
  • 每次规划保存 generation、保留项、移除项和 token 回执。

事实依据与适用边界

以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。

本文判断事实或工程依据适用限制
端侧模型与 LoRA 路径存在具体模型和后端约束,切换模型包需重算上下文。Google AI Edge LLM Inference for Android 说明基础模型、LoRA 与后端适用条件。该 API 已是 maintenance-only,其约束不能外推为所有端侧运行时标准。
系统指令应完整保留,但不能单独承担工具和输出安全。Firebase AI Logic system instructions 说明系统指令用于影响模型行为。模型仍可能不遵循,应用要独立控制输入、输出和工具权限。
模板入口要与模板版本、应用版本和回退值建立追踪关系。Firebase server prompt templates get started 说明应用通过模板 ID 获取服务端配置。获取模板不证明来源、兼容和失败回退已经正确治理。
模板的模型配置、系统指令、输入 schema、工具声明和正文应同版预算。Firebase server prompt template syntax 展示这些字段共同构成模板资产。模板语法不保证模型行为,也不替代客户端和服务端授权。
端侧 tokenizer、生命周期和离线切换应在 Android 设备回归。Android instrumented tests 说明设备端测试可访问应用上下文和平台 API。单一设备通过不能覆盖完整 API、ABI、厂商和模型组合。
上下文策略变更需要保留来源、构建、验证和变更证据。NIST SP 800-218 SSDF 提供安全开发与供应链治理实践。组织级框架不定义具体截断算法,也不证明候选通过。
不可删除层超出预算时应拒绝请求,而不是裁掉系统规则。工程判断:删除授权和输出边界会使后续模型输入失去必要约束。可采用的更大模型档位必须预先审核,不能临时任意切换。
当前不能声称目标 App 的截断策略已保留全部关键约束。项目证据尚未接入;需要模板、tokenizer、摘要样本与设备回执。文章提供结构和门禁代码,不替代真实会话验证。

工程常见问题

从最旧消息开始删除是不是最合理的截断方法?

不一定。最旧轮次可能包含仍有效的业务约束,必须先按层级和完整 turn 判断,并通过摘要保留约束 ID 与来源。

字符数能否近似代替 token 预算?

只能做早期估算。最终门禁要用目标 modelBundleId 的实际 tokenizer,并包含模板包装、工具 schema 和输出预留。

系统提示词完整保留是否就不会越权?

不能保证。系统指令影响模型行为,工具权限、数据范围和输出校验仍要由确定性应用与服务端边界执行。

让模型自由总结旧对话有什么风险?

它可能丢失否定条件、未决事项、来源或约束。摘要需采用结构 schema,并检查 requiredConstraintIds 和覆盖区间。

检索片段写着必须保留时能否提升优先级?

不能。优先级来自受控装配器和来源通道,文档内容始终属于 retrieval_data,只能提供事实引用。

App 加固后为什么要重测上下文截断?

加固可能影响模板资源、tokenizer、缓存、序列化和生命周期。候选身份变化后,旧预算与摘要回执不能证明新包行为。

想用自己的 App 验证?

提交候选包、目标系统和关键业务路径,申请御盾 PoC 与兼容性评估。

继续阅读: 移动 AI 应用的运行时安全边界