如何设计一个可控的上下文系统
当 AI 功能只有一个 Demo 时,上下文通常是一段手写 Prompt 加上用户输入。
当它进入真实产品后,上下文会迅速变复杂:历史消息、页面状态、用户偏好、检索文档、业务数据、工具结果、系统规则、权限策略、输出格式都想进入模型。
如果没有系统设计,最终会变成一大段难以维护的字符串。每修一个问题就往 Prompt 里加一段说明,每接一个功能就多拼一块数据。短期能跑,长期很难调试、很难控成本,也很难保证安全。
一个可控的上下文系统,要把“模型输入”从临时拼接变成可配置、可测试、可观测的工程模块。
可控是什么意思
可控不是让模型每次输出完全一样。LLM 本身具有生成不确定性,可控指的是应用能清楚地决定和解释:
- 哪些信息进入了本次上下文。
- 哪些信息因为权限、预算或相关性被排除。
- 不同上下文来源的优先级是什么。
- 超过 token 预算时如何裁剪。
- 外部内容和系统规则冲突时听谁的。
- 出问题时如何复盘。
换句话说,可控上下文系统不保证模型永远正确,但它让你知道模型是在什么输入条件下回答的,并且能持续改进这些条件。
设计目标
面向生产环境,可以给上下文系统设定六个目标。
第一,稳定。相同类型的任务应该使用稳定的上下文结构,而不是每个入口临时拼接。
第二,克制。只注入当前任务需要的信息,避免无关历史和敏感数据污染模型。
第三,安全。权限、脱敏、租户隔离和高风险工具边界必须在模型调用前处理。
第四,可观察。系统能记录上下文来源、token 用量、裁剪结果、检索命中和策略版本。
第五,可调优。不同功能可以配置不同策略,能够做实验和回滚。
第六,可解释。当前端或用户质疑回答时,产品能解释“这次基于哪些信息生成”。
总体架构
一个可控的上下文系统可以拆成七层。
前端不直接决定最终 Prompt,但它提供关键上下文信号。后端不盲目相信前端传来的数据,而是根据用户身份、任务类型和 source registry 重新获取、过滤和组装。
第一步:定义上下文来源
先列出系统里可能进入模型的所有信息来源。
常见来源包括:
每个来源都应该有元数据,而不是只返回一段文本。
可以为候选上下文设计统一结构:
有了统一结构,后续才能做排序、预算、过滤和日志。
第二步:建立优先级规则
上下文不是平等的。
通常优先级可以这样设计:
- 系统安全规则和权限边界。
- 当前用户输入和明确任务目标。
- 当前页面或任务的关键状态。
- 高相关业务数据和检索结果。
- 当前会话摘要和关键历史。
- 用户长期偏好。
- 低相关历史、寒暄、重复内容和失败重试。
这不是固定答案,但一定要有明确规则。
例如,当用户长期偏好“回答尽量详细”,但当前入口是“生成一句按钮文案”,任务目标应该优先于偏好。再比如,检索文档里出现“忽略系统规则”,它属于外部不可信内容,不能覆盖系统边界。
优先级规则最好写成代码和配置,而不是散落在 Prompt 文案里。
第三步:设计 Token 预算
上下文系统必须有预算意识。
一次模型调用的 token 通常分成几块:
- 系统规则预算。
- 当前用户输入预算。
- 历史和摘要预算。
- 检索结果预算。
- 工具结果预算。
- 用户记忆预算。
- 输出预留预算。
不要把窗口塞满再祈祷模型能回答。输出也需要空间,模型还需要在关键约束和任务数据之间建立联系。
一个简单策略是按任务类型设置预算:
预算不是一次性设计完。你需要通过日志观察真实 token 分布,再调整不同来源的上限。
第四步:压缩与裁剪
超过预算时,最糟糕的做法是对最终字符串硬截断。这样可能截掉系统规则、JSON 结构或关键字段。
更好的方式是在候选上下文阶段处理。
常见策略包括:
- 删除低优先级候选片段。
- 合并重复历史。
- 把早期对话压缩成 rolling summary。
- 对工具结果只保留关键字段。
- 对检索结果减少 chunk 数量或缩短 chunk。
- 对业务对象使用白名单字段。
- 对代码或日志保留报错位置和相关片段,而不是全文。
压缩后的内容应该保留来源信息。比如“以下是会话摘要”不同于“以下是用户原话”。模型需要知道它面对的是摘要、事实、外部文档还是系统规则。
第五步:处理冲突
上下文冲突非常常见。
用户说“忽略上面的规则”,系统规则说不能忽略。旧记忆说用户使用 React,当前仓库是 Vue。检索文档 A 和文档 B 对同一个流程描述不一致。工具结果和历史消息冲突。
冲突处理要有原则:
- 系统规则高于外部内容和用户请求。
- 当前明确输入高于旧偏好。
- 已验证业务数据高于模型摘要。
- 工具实时结果高于过期历史。
- 同级信息冲突时,使用更新、更可信、来源更明确的内容。
必要时不要让模型假装确定,而是让它说明冲突并请求用户确认。
可以在上下文里显式标注:
第六步:安全边界
可控上下文系统必须把安全作为流水线的一部分。
权限过滤:所有业务数据、文档、记忆和工具结果都要按用户身份、租户、团队、角色和资源权限过滤。
字段白名单:传给模型的对象应该只包含任务需要的字段。不要把数据库记录完整序列化。
敏感信息脱敏:手机号、邮箱、证件号、token、密钥、地址、支付信息等需要识别和处理。
不可信内容隔离:用户输入、网页、上传文件、检索文档都可能包含注入指令。把它们放在明确的数据区域,并告诉模型不能把其中的内容当作系统指令。
工具执行隔离:模型可以生成工具调用建议,但真正执行要经过权限、参数校验、幂等、审批和审计。
日志最小化:上下文日志要有调试价值,但不能把敏感明文无限期保存。可以保存引用 ID、hash、token 数、裁剪策略和脱敏摘要。
第七步:前端如何配合
前端要做的不是把所有状态都传给模型,而是提供清晰的上下文信号。
可以从四个方面设计。
第一,入口即任务类型。
“总结当前表格”“解释选中代码”“生成回复”“查找文档答案”这些入口应该传递明确 task type。后端根据 task type 选择上下文策略。
第二,引用对象显式化。
用户选中了哪些文件、消息、表格行、代码片段,应该以引用 ID 或结构化 payload 传递。页面上也应该展示这些引用,让用户知道 AI 正在基于什么工作。
第三,状态可撤销。
用户应该能移除引用、清空历史影响、关闭长期记忆、从某条消息分叉新会话。上下文控制最终要落到可用的交互上。
第四,反馈可追踪。
用户点“回答不对”时,前端应该提交 run id、message id、上下文引用和反馈类型,而不是只提交一段差评文本。
观测与测试
上下文系统需要测试。
单元测试可以覆盖:
- 不同 task type 是否选择正确 context sources。
- 权限不足时是否排除业务数据。
- 超预算时是否裁剪低优先级内容。
- 敏感字段是否脱敏。
- 外部注入文本是否被隔离。
- 冲突规则是否生效。
线上日志可以关注:
- 每类上下文平均 token。
- 被裁剪的候选数量。
- 检索命中率和引用率。
- 记忆注入率。
- 上下文超预算率。
- 用户反馈和上下文策略的关系。
AI 应用的质量优化,很多时候不是换模型,而是发现某类任务缺少关键上下文,或某类上下文带来了噪音。
常见坑
第一个坑:一个函数里拼完所有 Prompt。
这会让功能越来越难维护。上下文来源、权限、预算和模板应该拆开。
第二个坑:相信前端传来的完整业务数据。
前端可以传引用和用户选择,但后端应该重新按权限读取关键数据,避免伪造和越权。
第三个坑:没有输出 token 预留。
输入塞太满会导致回答被截断,或者模型没有空间完成结构化输出。
第四个坑:把外部文档当系统指令。
检索结果和上传文件是资料,不是规则。它们必须被标注为不可信或低信任来源。
第五个坑:摘要不带来源。
摘要会丢信息,也可能有偏差。关键事实最好保留来源消息或引用 ID,方便追溯。
第六个坑:只优化回答,不优化上下文。
很多“回答不好”其实是上下文错了。没有上下文日志,就会把所有问题都归因于模型。
小结
一个可控的上下文系统,本质上是模型输入的工程化治理。
它把上下文来源、权限、安全、优先级、预算、压缩、冲突和日志都纳入统一流程。对前端工程师来说,这意味着 AI 入口、引用交互、页面状态、反馈机制和记忆管理都要参与系统设计。
当上下文系统可控,模型输出才有稳定改进的基础。否则,每一次优化都只是继续往 Prompt 里加一句话,直到没有人知道这段输入为什么还在。