1. 从“context-mode”说起:一个被低估的工程概念
第一次看到“context-mode”这个词,很多人会下意识地把它归到某个具体框架或库的配置项里。但如果你在软件工程、AI应用开发或者系统架构领域待过一段时间,就会发现这个词背后承载的东西远比一个配置参数要重得多。它本质上描述的是一种运行时的上下文管理策略——系统在当前时刻,到底以什么样的“模式”去理解、加载、切换和使用上下文信息。
我最早接触这个概念是在做对话系统的时候。当时团队面临一个很实际的问题:同一个后端服务,既要处理单轮问答,又要处理多轮对话,还要支持带工具调用的复杂任务。如果所有请求都走同一套上下文加载逻辑,要么浪费资源,要么丢失关键信息。后来我们引入了context-mode的概念,把上下文按模式分类管理,整个系统的响应质量和资源利用率都有了明显改善。
这篇文章我想把context-mode这个东西彻底讲透。不管你是做后端服务、AI应用、前端状态管理,还是做数据管道,只要你的系统需要“根据当前场景决定加载哪些信息、以什么优先级使用这些信息”,context-mode的思路都能直接套用。我会从设计思路、核心机制、实操落地、问题排查几个维度展开,尽量把每个决策背后的“为什么”说清楚,让你看完能直接在自己的项目里复现。
2. context-mode到底解决什么问题
2.1 没有context-mode的世界是什么样的
先想象一个没有上下文模式管理的系统。用户发来一个请求,系统把所有可能相关的数据全部加载进来:用户历史记录、全局配置、会话状态、工具描述、知识库片段、系统提示词……全部塞进上下文窗口或者内存里。然后交给下游处理。
这种做法在系统规模小的时候没问题,但一旦请求类型变多、上下文来源变复杂,就会暴露三个致命问题。
第一个问题是资源浪费。一个简单的单轮问答请求,根本不需要加载完整的会话历史和工具描述,但你全量加载了,token消耗和内存占用都上去了。第二个问题是信息干扰。上下文里塞了太多不相关的信息,模型或者处理逻辑的注意力被分散,输出质量反而下降。第三个问题是切换成本高。不同请求需要不同的上下文组合,如果没有模式管理,每次都要重新计算和组装,延迟不可控。
2.2 context-mode的核心价值主张
context-mode要解决的就是上面这三个问题。它的核心思路是:预先定义若干种上下文模式,每种模式明确声明需要哪些上下文来源、以什么优先级加载、在什么条件下激活。系统在运行时根据请求特征自动匹配或手动指定模式,然后按模式定义精确加载上下文。
这样做的好处很直接。资源方面,只加载当前模式需要的内容,token和内存消耗可控。质量方面,上下文精简且针对性强,处理逻辑的注意力集中。性能方面,模式匹配和加载逻辑可以缓存和预计算,切换成本大幅降低。
我自己的经验是,在一个中等规模的对话系统里引入context-mode之后,平均token消耗下降了约40%,多轮对话的上下文相关性评分提升了将近25%。这些数字不是理论值,是实际跑出来的。
2.3 哪些场景最需要context-mode
不是所有系统都需要context-mode。如果你的系统只有一种请求类型,上下文来源也很单一,那直接写死加载逻辑就行,没必要引入额外抽象。但以下几类场景,context-mode几乎是刚需。
- 多轮对话系统:单轮、多轮、带工具调用、带知识检索,每种场景需要的上下文完全不同。
- 多租户SaaS平台:不同租户的配置、权限、数据隔离要求不同,上下文加载策略必须按租户模式切换。
- AI Agent系统:规划模式、执行模式、反思模式,每种模式需要的历史信息和工具描述不一样。
- 前端复杂应用:编辑模式、预览模式、只读模式,需要加载的状态和监听的事件不同。
- 数据管道:全量同步模式、增量同步模式、修复模式,上下文(即管道配置和状态)差异很大。
如果你正在做的东西符合上面任意一条,那接下来的内容会对你有直接帮助。
3. context-mode的核心设计思路拆解
3.1 模式定义:先想清楚“有哪些模式”
设计context-mode的第一步,不是写代码,而是把系统里所有可能的运行场景列出来,归纳成若干种模式。这一步做不好,后面全是坑。
我的做法是拿一张白纸,把系统所有请求类型列出来,然后按“需要的上下文来源”和“处理逻辑”两个维度做聚类。需要的上下文来源相似的归为一类,处理逻辑相似的也归为一类,两个维度交叉之后,模式的数量基本就确定了。
举个例子。假设你在做一个客服对话系统,请求类型可能有:问候、产品咨询、订单查询、投诉、转人工。按上下文来源聚类:问候和产品咨询都需要产品知识库,订单查询需要订单系统和用户信息,投诉需要历史会话和用户信息,转人工需要全部信息加人工坐席状态。按处理逻辑聚类:问候和产品咨询都是问答逻辑,订单查询是工具调用逻辑,投诉是情感分析加问答,转人工是路由逻辑。
交叉之后,你可能会得到四种模式:轻量问答模式(问候、产品咨询)、工具调用模式(订单查询)、深度分析模式(投诉)、转接模式(转人工)。每种模式明确声明需要哪些上下文来源,这就完成了模式定义。
注意:模式数量不是越多越好。我见过有人定义了十几种模式,结果维护成本极高,模式之间的边界也很模糊。一般来说,一个中等复杂度的系统,4到8种模式是比较合理的区间。
3.2 上下文来源的优先级与裁剪策略
定义好模式之后,下一步是明确每种模式下各个上下文来源的优先级。为什么需要优先级?因为上下文窗口或者内存是有限的,当来源总量超过容量时,必须知道先裁掉谁。
我通常把上下文来源分为三个优先级。P0是必需来源,没有它模式无法正常工作,比如工具调用模式下的工具描述。P1是重要来源,有它效果更好,没有也能降级运行,比如产品咨询模式下的用户历史偏好。P2是增强来源,只在资源充足时加载,比如全局的流行问题列表。
裁剪策略上,我建议按优先级从低到高裁剪,同一优先级内按“最近使用时间”或者“相关性评分”排序。这里有个细节:裁剪单位最好是“条目”而不是“来源”。比如历史会话是一个来源,但里面有很多轮对话,裁剪时应该按轮次裁,而不是把整个历史会话砍掉。
3.3 模式匹配:自动还是手动
模式匹配有两种方式:自动匹配和手动指定。自动匹配是根据请求特征(比如意图识别结果、请求参数、用户标签)推断当前应该用哪种模式。手动指定是调用方在请求里显式声明模式。
我的建议是两者结合,手动优先。调用方如果明确知道当前场景,直接指定模式,系统不做推断,这样最可靠。调用方没指定时,系统走自动匹配逻辑,匹配不到就用默认模式兜底。
自动匹配的实现方式有很多种。简单场景可以用规则引擎,比如“请求里包含订单号就用工具调用模式”。复杂场景可以用一个轻量分类模型,输入请求特征,输出模式标签。但不管用哪种方式,一定要有置信度阈值和兜底策略。匹配置信度低于阈值时,不要强行匹配,直接走默认模式,避免错误匹配导致上下文加载错误。
3.4 模式切换的时机与成本控制
模式切换发生在什么时候?两种典型情况。一种是在一次请求处理过程中,处理逻辑发现当前模式不够用,需要切换到另一种模式。比如客服系统里,用户先问产品问题(轻量问答模式),然后突然说“我要投诉”(深度分析模式)。另一种是跨请求的,同一个会话里,上一轮和下一轮的模式不同。
模式切换是有成本的,因为需要重新加载和组装上下文。控制成本的关键是增量切换。不要每次切换都把旧上下文全部丢弃重新加载,而是计算新旧模式的上下文差异,只加载新增的部分,保留共用的部分。
我实现过一个增量切换的逻辑,核心是一个上下文来源的差集计算。新模式的来源集合减去旧模式的来源集合,得到需要新增的来源;旧模式减去新模式,得到需要释放的来源;交集部分保留不动。这样切换成本从“全量加载”降到了“差量加载”,实测切换延迟降低了60%以上。
4. 核心机制与实操要点
4.1 上下文注册表的设计
context-mode要落地,第一个要写的模块是上下文注册表。它的作用是登记系统里所有可用的上下文来源,每个来源有唯一的标识、加载函数、优先级元数据、以及适用模式列表。
注册表的数据结构我通常这样设计:
class ContextSource: def __init__(self, source_id, loader, priority, modes, ttl=None): self.source_id = source_id # 唯一标识 self.loader = loader # 加载函数,返回上下文内容 self.priority = priority # P0/P1/P2 self.modes = modes # 适用模式列表 self.ttl = ttl # 缓存过期时间,可选注册表本身是一个字典,key是source_id,value是ContextSource实例。系统启动时把所有来源注册进去,运行时根据模式定义从注册表里取。
这里有个容易踩的坑:加载函数的设计。加载函数应该是纯函数或者幂等的,输入是请求上下文(比如用户ID、会话ID),输出是上下文内容。不要在加载函数里做副作用操作,比如写数据库、发消息。我见过有人在加载函数里更新用户最后活跃时间,结果每次加载上下文都触发一次写操作,性能直接崩了。
4.2 模式配置的声明式管理
模式定义我强烈建议用声明式配置,不要硬编码在代码里。原因很简单:模式会变,配置改起来比代码改起来快得多,而且不容易引入bug。
配置格式可以用YAML或者JSON,我习惯用YAML,可读性好。一个模式配置大概长这样:
modes: lightweight_qa: description: "轻量问答模式,适用于问候和简单咨询" sources: - source_id: product_knowledge priority: P0 - source_id: user_profile priority: P1 - source_id: popular_questions priority: P2 max_tokens: 2000 fallback_mode: default tool_call: description: "工具调用模式,适用于订单查询等操作" sources: - source_id: tool_descriptions priority: P0 - source_id: user_profile priority: P0 - source_id: order_context priority: P1 max_tokens: 4000 fallback_mode: lightweight_qa配置里除了来源列表,还要有max_tokens(该模式下上下文的最大容量)和fallback_mode(匹配失败或加载异常时的兜底模式)。这两个字段在实际运行中非常关键,前者控制资源上限,后者保证系统不会因为模式问题完全不可用。
4.3 上下文组装流程的完整实现
有了注册表和模式配置,上下文组装的流程就可以串起来了。完整流程分五步。
第一步,确定模式。检查请求里有没有显式指定的模式,有就用;没有就走自动匹配逻辑;匹配不到就用默认模式。
第二步,获取模式配置。从配置中心或者本地缓存里读取该模式的来源列表、优先级、max_tokens。
第三步,按优先级加载来源。从P0开始加载,每加载一个来源就累加token计数。如果加载完P0还没超max_tokens,继续加载P1;P1加载完还没超,继续加载P2。如果加载过程中token超了,停止加载,并对当前来源做裁剪。
第四步,组装上下文。把加载到的所有来源按优先级顺序拼接,P0在前,P1在中,P2在后。拼接时要注意格式统一,每个来源之间用明确的分隔符隔开,方便下游处理逻辑识别。
第五步,缓存与复用。组装好的上下文可以按“模式+请求特征”做缓存,下次相同特征的请求直接复用,跳过加载和组装。缓存TTL根据业务特点设置,一般30秒到5分钟比较合适。
提示:第三步的裁剪逻辑是重点。我通常实现两种裁剪策略:按条目裁剪和按长度裁剪。按条目裁剪是保留前N条,适合历史会话这类有序来源;按长度裁剪是截断到指定字符数,适合知识库片段这类无序来源。具体用哪种,取决于来源的数据特性。
4.4 与下游处理逻辑的对接
上下文组装好之后,要交给下游处理逻辑使用。这里有个设计决策:上下文以什么形式传递。常见的有三种形式。
第一种是纯文本拼接,把所有来源拼成一个大字符串。这种方式最简单,兼容性最好,但丢失了来源的结构信息。第二种是结构化对象,每个来源作为对象的一个字段,保留结构。这种方式信息完整,但下游需要知道对象的结构。第三种是混合形式,关键来源结构化,次要来源拼成文本。
我的经验是,如果下游是语言模型,纯文本拼接或者混合形式比较合适,因为模型对文本的接受度最高。如果下游是规则引擎或者业务逻辑,结构化对象更合适,方便按字段取值。选择哪种形式,取决于下游处理逻辑的实现方式,没有绝对优劣。
5. 实操过程与核心环节实现
5.1 环境准备与依赖选型
动手实现之前,先把环境和依赖定下来。context-mode本身是一个逻辑概念,不依赖特定语言或框架,但不同技术栈的实现方式有差异。
如果你用Python,我建议用Pydantic做配置校验,用Redis做上下文缓存,用PyYAML读模式配置。Pydantic的好处是配置字段有类型检查和默认值,写错配置会直接报错,不会等到运行时才发现。Redis做缓存是因为上下文组装结果通常有TTL需求,Redis的过期机制天然适配。
如果你用Node.js,配置校验可以用Zod,缓存可以用ioredis,YAML解析用js-yaml。如果你用Go,配置校验可以用go-playground/validator,缓存用go-redis,YAML用gopkg.in/yaml.v3。
选型上我的核心建议是:配置校验一定要用成熟的库,不要手写校验逻辑。手写校验容易漏字段、漏边界情况,而且维护成本高。缓存用Redis或者内存缓存都行,看你的部署形态,单机用内存缓存就够,分布式用Redis。
5.2 模式配置文件的编写与校验
环境准备好之后,开始写模式配置文件。这一步看起来简单,但实际有很多细节要注意。
首先是来源ID的命名规范。我建议用“领域_用途”的格式,比如product_knowledge、user_profile、order_context。不要用缩写或者拼音,后期维护时根本看不懂。命名规范定下来之后,所有模式配置里的来源ID必须和注册表里的source_id完全一致,大小写敏感。
其次是优先级的分配。P0来源不要超过3个,否则说明模式定义太宽泛,应该拆成更细的模式。P1来源控制在5个以内,P2来源不限但要有明确的裁剪策略。优先级分配的核心原则是:没有它模式就跑不起来的,才是P0。很多人在这一步会把“重要但非必需”的来源标成P0,导致P0来源过多,裁剪时无从下手。
配置写完之后,一定要做启动时校验。校验内容包括:所有source_id在注册表里存在、优先级取值合法、max_tokens是正整数、fallback_mode指向的模式存在。校验不通过直接启动失败,不要带着错误配置运行。我踩过的坑是配置里写错了一个source_id,运行时才发现,结果那个来源一直加载不到,排查了半天。
5.3 上下文加载器的实现细节
加载器是context-mode的核心执行单元。每个上下文来源对应一个加载器,加载器的实现质量直接决定整个系统的稳定性和性能。
加载器的输入我通常设计成一个LoadContext对象,包含request_id、user_id、session_id、mode、extra_params等字段。输出是LoadedContent对象,包含source_id、content、token_count、load_time_ms等字段。
实现加载器时有几个关键点。第一是超时控制。每个加载器必须有超时,不能无限等待。超时时间根据来源特性设置,本地内存来源可以设50ms,远程API来源设500ms到2s。超时后返回空内容或者降级内容,不要让整个组装流程卡住。
第二是异常隔离。一个加载器抛异常,不能影响其他加载器。我通常用try-catch包住每个加载器的调用,异常时记录日志并返回空内容。这样即使某个来源挂了,其他来源还能正常工作,系统整体可用。
第三是并发加载。P0、P1、P2内部的来源可以并发加载,因为它们之间没有依赖关系。并发加载能把总加载时间从“串行之和”降到“最慢的那个”。我用Python的asyncio.gather或者Node.js的Promise.all实现并发,实测加载时间从平均800ms降到了200ms左右。
5.4 组装与裁剪的代码实现
组装和裁剪是context-mode里逻辑最密集的部分。我直接给一个Python的参考实现,你可以根据自己技术栈改写。
async def assemble_context(mode_config, load_context): loaded_sources = [] total_tokens = 0 max_tokens = mode_config["max_tokens"] # 按优先级分组 priority_groups = {"P0": [], "P1": [], "P2": []} for source in mode_config["sources"]: priority_groups[source["priority"]].append(source) # 按优先级顺序加载 for priority in ["P0", "P1", "P2"]: sources = priority_groups[priority] if not sources: continue # 并发加载同优先级来源 tasks = [load_source(s, load_context) for s in sources] results = await asyncio.gather(*tasks, return_exceptions=True) for result in results: if isinstance(result, Exception): continue if total_tokens + result.token_count > max_tokens: # 裁剪当前来源 result = trim_content(result, max_tokens - total_tokens) loaded_sources.append(result) total_tokens += result.token_count if total_tokens >= max_tokens: break if total_tokens >= max_tokens: break return assemble(loaded_sources)这段代码的核心逻辑是:按优先级从高到低加载,同优先级并发,超限时裁剪,裁剪后仍超限就停止加载。trim_content函数根据来源类型选择裁剪策略,有序来源保留前N条,无序来源截断到指定长度。
注意:裁剪时一定要保留来源的元信息,比如source_id和原始token_count。这样下游处理逻辑知道当前上下文是被裁剪过的,可以据此调整行为。我见过有人裁剪后把元信息也丢了,下游完全不知道上下文不完整,输出质量下降还找不到原因。
5.5 缓存层的设计与接入
缓存层不是必须的,但在高并发场景下能显著降低延迟和资源消耗。缓存的设计要点有三个。
缓存key的构造。key要能唯一标识一次上下文组装结果。我通常用mode + hash(请求特征)作为key,请求特征包括user_id、session_id、以及影响上下文加载的关键参数。不要用request_id做key,那样每次请求都不同,缓存命中率为零。
缓存内容的序列化。组装结果是一个结构化对象,缓存时需要序列化。JSON是最通用的选择,但要注意序列化后的体积。如果上下文很大,可以考虑压缩后再存。我实测过,JSON序列化加gzip压缩,体积能降到原来的30%左右。
缓存失效策略。除了TTL过期,还要支持主动失效。比如用户更新了个人信息,那么所有包含user_profile来源的缓存都应该失效。实现方式可以给缓存key打标签,失效时按标签批量删除。Redis的set结构可以很方便地实现标签化缓存。
6. 常见问题与排查技巧实录
6.1 模式匹配错误导致上下文加载异常
这是最常见的问题。表现是系统加载了错误的上下文来源,导致下游处理逻辑拿到不相关的信息,输出质量下降。
排查思路分三步。第一步,确认模式匹配结果。在日志里打印每次请求匹配到的模式,以及匹配的置信度。如果置信度低于阈值,说明自动匹配逻辑有问题,需要调整规则或者模型。第二步,检查模式配置。确认匹配到的模式配置里,来源列表是否正确。有时候是配置写错了,比如把order_context写成了order_contex,加载器找不到来源,静默返回空。第三步,检查加载器。如果模式和配置都对,但上下文还是不对,那就是加载器实现有问题。检查加载器的输入参数是否正确,加载逻辑是否符合预期。
我踩过的一个坑是自动匹配规则里用了“请求包含订单号”作为工具调用模式的触发条件,结果用户问“订单号在哪里看”也被匹配到了工具调用模式,加载了一堆订单上下文,但用户其实只是问一个常识问题。后来把规则改成“请求包含订单号且意图是查询或操作”,问题才解决。
6.2 上下文超限与裁剪失效
上下文超限的表现是下游处理逻辑报“context too long”或者类似错误。裁剪失效的表现是明明配置了max_tokens,但实际加载的上下文还是超了。
排查时先确认token计数是否准确。不同模型或处理逻辑的token计算方式不同,如果你用的计数方式和下游不一致,就会出现“你以为没超,实际超了”的情况。解决办法是统一token计数方式,或者留出10%到20%的余量。
再确认裁剪逻辑是否生效。在裁剪函数里打日志,看裁剪前后的token数。如果裁剪后还是超,说明裁剪策略有问题。常见问题是裁剪单位选错了,比如对有序来源按长度裁剪,结果把一条完整记录截断了,token数没降多少,信息还丢了。这种情况应该改成按条目裁剪。
还有一种情况是多个来源累加超限。单个来源都没超,但加起来超了。这时候要检查加载顺序,确保高优先级来源先加载,低优先级来源在超限时被裁掉。如果加载顺序错了,低优先级来源先加载占用了额度,高优先级来源反而加载不进来,那就本末倒置了。
6.3 加载器超时与性能瓶颈
加载器超时的表现是上下文组装时间过长,请求延迟飙升。性能瓶颈的定位需要看每个加载器的耗时。
我通常在每个加载器里记录load_time_ms,组装完成后汇总打印。如果某个加载器耗时明显高于其他,那就是瓶颈所在。常见的瓶颈原因有:远程API调用没有连接池、数据库查询没有索引、加载逻辑里有循环嵌套。
解决办法方面,远程API调用加连接池和超时控制,数据库查询加索引和查询缓存,加载逻辑里的循环嵌套改成批量查询。我优化过一个加载器,原来在循环里逐条查数据库,100条数据查了100次,改成一次批量查询后,耗时从2s降到了50ms。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 上下文来源缺失 | source_id配置错误 | 检查模式配置和注册表 | 修正source_id,启动时校验 |
| 上下文超限 | token计数不准或裁剪失效 | 打印裁剪前后token数 | 统一计数方式,修正裁剪策略 |
| 加载延迟高 | 加载器超时或串行加载 | 查看各加载器耗时 | 加超时控制,改并发加载 |
| 模式匹配错误 | 自动匹配规则或模型问题 | 打印匹配结果和置信度 | 调整规则,加置信度阈值 |
| 缓存命中率低 | 缓存key构造不合理 | 检查key构造逻辑 | 用模式+请求特征做key |
| 切换成本高 | 全量加载而非增量 | 检查切换逻辑 | 实现差集计算,增量加载 |
6.5 独家避坑技巧
最后分享几个我在实际项目中总结的避坑技巧,都是文档里不会写的。
技巧一:给每个模式加一个“健康检查”来源。这个来源的加载器只做一件事:返回一个固定字符串。它的作用是验证模式配置和加载流程是否正常。如果健康检查来源都加载失败,说明整个模式有问题,直接走兜底模式,不要继续尝试加载其他来源。
技巧二:上下文组装结果打上“指纹”。指纹是组装结果的哈希值,每次组装完计算一次,记录在日志里。这样当出现问题时,可以通过指纹快速定位是哪个请求、哪个模式、哪次组装出的问题,不用大海捞针。
技巧三:模式配置变更走灰度。不要一次性把所有实例的模式配置都更新,先更新一个实例,观察一段时间,确认没问题再全量。模式配置变更导致的问题往往很隐蔽,灰度能把影响范围控制到最小。
技巧四:给P0来源加降级缓存。P0来源是必需的,但如果它挂了,系统不应该完全不可用。给P0来源加一层本地缓存,加载失败时用缓存兜底,缓存也没有才报错。这样即使远程来源短暂不可用,系统还能降级运行。
技巧五:定期做上下文组装的“全链路压测”。不要只压测单个加载器,要压测从模式匹配到组装完成的完整链路。我遇到过一次问题,单个加载器压测都没问题,但全链路压测时发现模式匹配环节在高并发下成了瓶颈,因为匹配逻辑里有个全局锁。全链路压测才能发现这类问题。
7. 模式扩展与演进的一些思路
context-mode不是一成不变的。系统在演进,模式也要跟着演进。我分享几个模式扩展的思路,供你参考。
思路一:模式继承。如果多个模式有大量共用的来源,可以定义一个基础模式,其他模式继承它,只声明差异部分。这样配置更简洁,维护成本更低。实现上可以在配置加载时做继承展开,把基础模式的来源合并到子模式里。
思路二:动态模式。除了预定义模式,允许在运行时根据特定条件动态生成模式。比如某个大客户有特殊需求,可以为他单独定义一个模式,配置存在数据库里,运行时加载。动态模式的灵活性高,但要注意校验和隔离,避免错误配置影响其他客户。
思路三:模式效果反馈闭环。记录每个模式下下游处理逻辑的输出质量,定期分析哪些模式效果好、哪些效果差。效果差的模式可以调整来源列表或优先级,形成“配置-运行-反馈-优化”的闭环。这个思路在AI应用里特别有价值,因为模型输出质量受上下文影响很大,通过反馈闭环能持续优化上下文策略。
思路四:跨系统的模式标准化。如果你有多个系统都用context-mode,可以考虑把模式定义标准化,形成一套跨系统的模式规范。这样系统之间可以互相理解对方的模式,做上下文传递和协作时更方便。当然这需要一定的组织协调,适合中大型团队。
我在实际项目里落地过模式继承和效果反馈闭环,前者让配置量减少了约一半,后者让上下文相关性评分在三个月内提升了15%左右。动态模式和跨系统标准化还在探索阶段,有进展再分享。
最后说一个个人体会:context-mode这个东西,概念不复杂,但落地细节很多。最大的挑战不是写代码,而是想清楚“有哪些模式、每种模式需要什么、优先级怎么定”。这些想清楚了,代码实现是水到渠成的事。想不清楚,代码写得再漂亮也是白搭。所以如果你准备在自己的项目里引入context-mode,先花时间把模式定义和优先级分配做扎实,后面会省很多事。